diff options
author | Mark Brown <broonie@kernel.org> | 2014-11-04 06:53:49 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2014-11-04 06:53:49 -0500 |
commit | ff1b1c3fef511ff051db0d5de332afd15acad363 (patch) | |
tree | e5bbb9597074eafaf665b4daa41fe6a340783636 /sound/soc/soc-dapm.c | |
parent | 2a374b78f5c2b5f31d35f8a7cd004989d6936756 (diff) | |
parent | 92a99ea439c4e27fc6e32eb6d51c5d091c6084bd (diff) |
Merge branch 'topic/dapm' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into asoc-core
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r-- | sound/soc/soc-dapm.c | 753 |
1 files changed, 396 insertions, 357 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c61cb9cedbcd..6bf2c9795df2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -159,27 +159,135 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) | |||
159 | } | 159 | } |
160 | } | 160 | } |
161 | 161 | ||
162 | void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm) | 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 | |||
272 | void dapm_mark_endpoints_dirty(struct snd_soc_card *card) | ||
163 | { | 273 | { |
164 | struct snd_soc_card *card = dapm->card; | ||
165 | struct snd_soc_dapm_widget *w; | 274 | struct snd_soc_dapm_widget *w; |
166 | 275 | ||
167 | mutex_lock(&card->dapm_mutex); | 276 | mutex_lock(&card->dapm_mutex); |
168 | 277 | ||
169 | list_for_each_entry(w, &card->widgets, list) { | 278 | list_for_each_entry(w, &card->widgets, list) { |
170 | switch (w->id) { | 279 | if (w->is_sink || w->is_source) { |
171 | case snd_soc_dapm_input: | 280 | dapm_mark_dirty(w, "Rechecking endpoints"); |
172 | case snd_soc_dapm_output: | 281 | if (w->is_sink) |
173 | dapm_mark_dirty(w, "Rechecking inputs and outputs"); | 282 | dapm_widget_invalidate_output_paths(w); |
174 | break; | 283 | if (w->is_source) |
175 | default: | 284 | dapm_widget_invalidate_input_paths(w); |
176 | break; | ||
177 | } | 285 | } |
178 | } | 286 | } |
179 | 287 | ||
180 | mutex_unlock(&card->dapm_mutex); | 288 | mutex_unlock(&card->dapm_mutex); |
181 | } | 289 | } |
182 | EXPORT_SYMBOL_GPL(dapm_mark_io_dirty); | 290 | EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty); |
183 | 291 | ||
184 | /* create a new dapm widget */ | 292 | /* create a new dapm widget */ |
185 | static inline struct snd_soc_dapm_widget *dapm_cnew_widget( | 293 | static inline struct snd_soc_dapm_widget *dapm_cnew_widget( |
@@ -386,8 +494,6 @@ static void dapm_reset(struct snd_soc_card *card) | |||
386 | list_for_each_entry(w, &card->widgets, list) { | 494 | list_for_each_entry(w, &card->widgets, list) { |
387 | w->new_power = w->power; | 495 | w->new_power = w->power; |
388 | w->power_checked = false; | 496 | w->power_checked = false; |
389 | w->inputs = -1; | ||
390 | w->outputs = -1; | ||
391 | } | 497 | } |
392 | } | 498 | } |
393 | 499 | ||
@@ -469,10 +575,9 @@ out: | |||
469 | 575 | ||
470 | /* connect mux widget to its interconnecting audio paths */ | 576 | /* connect mux widget to its interconnecting audio paths */ |
471 | static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, | 577 | static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, |
472 | struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, | 578 | struct snd_soc_dapm_path *path, const char *control_name) |
473 | struct snd_soc_dapm_path *path, const char *control_name, | ||
474 | const struct snd_kcontrol_new *kcontrol) | ||
475 | { | 579 | { |
580 | const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0]; | ||
476 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 581 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
477 | unsigned int val, item; | 582 | unsigned int val, item; |
478 | int i; | 583 | int i; |
@@ -493,10 +598,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, | |||
493 | 598 | ||
494 | for (i = 0; i < e->items; i++) { | 599 | for (i = 0; i < e->items; i++) { |
495 | if (!(strcmp(control_name, e->texts[i]))) { | 600 | if (!(strcmp(control_name, e->texts[i]))) { |
496 | list_add(&path->list, &dapm->card->paths); | 601 | path->name = e->texts[i]; |
497 | list_add(&path->list_sink, &dest->sources); | ||
498 | list_add(&path->list_source, &src->sinks); | ||
499 | path->name = (char*)e->texts[i]; | ||
500 | if (i == item) | 602 | if (i == item) |
501 | path->connect = 1; | 603 | path->connect = 1; |
502 | else | 604 | else |
@@ -509,11 +611,10 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, | |||
509 | } | 611 | } |
510 | 612 | ||
511 | /* set up initial codec paths */ | 613 | /* set up initial codec paths */ |
512 | static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, | 614 | static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i) |
513 | struct snd_soc_dapm_path *p, int i) | ||
514 | { | 615 | { |
515 | struct soc_mixer_control *mc = (struct soc_mixer_control *) | 616 | struct soc_mixer_control *mc = (struct soc_mixer_control *) |
516 | w->kcontrol_news[i].private_value; | 617 | p->sink->kcontrol_news[i].private_value; |
517 | unsigned int reg = mc->reg; | 618 | unsigned int reg = mc->reg; |
518 | unsigned int shift = mc->shift; | 619 | unsigned int shift = mc->shift; |
519 | unsigned int max = mc->max; | 620 | unsigned int max = mc->max; |
@@ -522,7 +623,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, | |||
522 | unsigned int val; | 623 | unsigned int val; |
523 | 624 | ||
524 | if (reg != SND_SOC_NOPM) { | 625 | if (reg != SND_SOC_NOPM) { |
525 | soc_dapm_read(w->dapm, reg, &val); | 626 | soc_dapm_read(p->sink->dapm, reg, &val); |
526 | val = (val >> shift) & mask; | 627 | val = (val >> shift) & mask; |
527 | if (invert) | 628 | if (invert) |
528 | val = max - val; | 629 | val = max - val; |
@@ -534,19 +635,15 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, | |||
534 | 635 | ||
535 | /* connect mixer widget to its interconnecting audio paths */ | 636 | /* connect mixer widget to its interconnecting audio paths */ |
536 | static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, | 637 | static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, |
537 | struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, | ||
538 | struct snd_soc_dapm_path *path, const char *control_name) | 638 | struct snd_soc_dapm_path *path, const char *control_name) |
539 | { | 639 | { |
540 | int i; | 640 | int i; |
541 | 641 | ||
542 | /* search for mixer kcontrol */ | 642 | /* search for mixer kcontrol */ |
543 | for (i = 0; i < dest->num_kcontrols; i++) { | 643 | for (i = 0; i < path->sink->num_kcontrols; i++) { |
544 | if (!strcmp(control_name, dest->kcontrol_news[i].name)) { | 644 | if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) { |
545 | list_add(&path->list, &dapm->card->paths); | 645 | path->name = path->sink->kcontrol_news[i].name; |
546 | list_add(&path->list_sink, &dest->sources); | 646 | dapm_set_mixer_path_status(path, i); |
547 | list_add(&path->list_source, &src->sinks); | ||
548 | path->name = dest->kcontrol_news[i].name; | ||
549 | dapm_set_mixer_path_status(dest, path, i); | ||
550 | return 0; | 647 | return 0; |
551 | } | 648 | } |
552 | } | 649 | } |
@@ -738,8 +835,10 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) | |||
738 | if (ret < 0) | 835 | if (ret < 0) |
739 | return ret; | 836 | return ret; |
740 | 837 | ||
741 | list_for_each_entry(path, &w->sources, list_sink) | 838 | list_for_each_entry(path, &w->sources, list_sink) { |
742 | dapm_kcontrol_add_path(w->kcontrols[0], path); | 839 | if (path->name) |
840 | dapm_kcontrol_add_path(w->kcontrols[0], path); | ||
841 | } | ||
743 | 842 | ||
744 | return 0; | 843 | return 0; |
745 | } | 844 | } |
@@ -754,34 +853,6 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w) | |||
754 | return 0; | 853 | return 0; |
755 | } | 854 | } |
756 | 855 | ||
757 | /* reset 'walked' bit for each dapm path */ | ||
758 | static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm, | ||
759 | struct list_head *sink) | ||
760 | { | ||
761 | struct snd_soc_dapm_path *p; | ||
762 | |||
763 | list_for_each_entry(p, sink, list_source) { | ||
764 | if (p->walked) { | ||
765 | p->walked = 0; | ||
766 | dapm_clear_walk_output(dapm, &p->sink->sinks); | ||
767 | } | ||
768 | } | ||
769 | } | ||
770 | |||
771 | static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm, | ||
772 | struct list_head *source) | ||
773 | { | ||
774 | struct snd_soc_dapm_path *p; | ||
775 | |||
776 | list_for_each_entry(p, source, list_sink) { | ||
777 | if (p->walked) { | ||
778 | p->walked = 0; | ||
779 | dapm_clear_walk_input(dapm, &p->source->sources); | ||
780 | } | ||
781 | } | ||
782 | } | ||
783 | |||
784 | |||
785 | /* We implement power down on suspend by checking the power state of | 856 | /* We implement power down on suspend by checking the power state of |
786 | * the ALSA card - when we are suspending the ALSA state for the card | 857 | * the ALSA card - when we are suspending the ALSA state for the card |
787 | * is set to D3. | 858 | * is set to D3. |
@@ -856,61 +927,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, | |||
856 | 927 | ||
857 | DAPM_UPDATE_STAT(widget, path_checks); | 928 | DAPM_UPDATE_STAT(widget, path_checks); |
858 | 929 | ||
859 | switch (widget->id) { | 930 | if (widget->is_sink && widget->connected) { |
860 | case snd_soc_dapm_supply: | 931 | widget->outputs = snd_soc_dapm_suspend_check(widget); |
861 | case snd_soc_dapm_regulator_supply: | 932 | return widget->outputs; |
862 | case snd_soc_dapm_clock_supply: | ||
863 | case snd_soc_dapm_kcontrol: | ||
864 | return 0; | ||
865 | default: | ||
866 | break; | ||
867 | } | ||
868 | |||
869 | switch (widget->id) { | ||
870 | case snd_soc_dapm_adc: | ||
871 | case snd_soc_dapm_aif_out: | ||
872 | case snd_soc_dapm_dai_out: | ||
873 | if (widget->active) { | ||
874 | widget->outputs = snd_soc_dapm_suspend_check(widget); | ||
875 | return widget->outputs; | ||
876 | } | ||
877 | default: | ||
878 | break; | ||
879 | } | ||
880 | |||
881 | if (widget->connected) { | ||
882 | /* connected pin ? */ | ||
883 | if (widget->id == snd_soc_dapm_output && !widget->ext) { | ||
884 | widget->outputs = snd_soc_dapm_suspend_check(widget); | ||
885 | return widget->outputs; | ||
886 | } | ||
887 | |||
888 | /* connected jack or spk ? */ | ||
889 | if (widget->id == snd_soc_dapm_hp || | ||
890 | widget->id == snd_soc_dapm_spk || | ||
891 | (widget->id == snd_soc_dapm_line && | ||
892 | !list_empty(&widget->sources))) { | ||
893 | widget->outputs = snd_soc_dapm_suspend_check(widget); | ||
894 | return widget->outputs; | ||
895 | } | ||
896 | } | 933 | } |
897 | 934 | ||
898 | list_for_each_entry(path, &widget->sinks, list_source) { | 935 | list_for_each_entry(path, &widget->sinks, list_source) { |
899 | DAPM_UPDATE_STAT(widget, neighbour_checks); | 936 | DAPM_UPDATE_STAT(widget, neighbour_checks); |
900 | 937 | ||
901 | if (path->weak) | 938 | if (path->weak || path->is_supply) |
902 | continue; | 939 | continue; |
903 | 940 | ||
904 | if (path->walking) | 941 | if (path->walking) |
905 | return 1; | 942 | return 1; |
906 | 943 | ||
907 | if (path->walked) | ||
908 | continue; | ||
909 | |||
910 | trace_snd_soc_dapm_output_path(widget, path); | 944 | trace_snd_soc_dapm_output_path(widget, path); |
911 | 945 | ||
912 | if (path->sink && path->connect) { | 946 | if (path->connect) { |
913 | path->walked = 1; | ||
914 | path->walking = 1; | 947 | path->walking = 1; |
915 | 948 | ||
916 | /* do we need to add this widget to the list ? */ | 949 | /* do we need to add this widget to the list ? */ |
@@ -952,73 +985,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, | |||
952 | 985 | ||
953 | DAPM_UPDATE_STAT(widget, path_checks); | 986 | DAPM_UPDATE_STAT(widget, path_checks); |
954 | 987 | ||
955 | switch (widget->id) { | 988 | if (widget->is_source && widget->connected) { |
956 | case snd_soc_dapm_supply: | 989 | widget->inputs = snd_soc_dapm_suspend_check(widget); |
957 | case snd_soc_dapm_regulator_supply: | 990 | return widget->inputs; |
958 | case snd_soc_dapm_clock_supply: | ||
959 | case snd_soc_dapm_kcontrol: | ||
960 | return 0; | ||
961 | default: | ||
962 | break; | ||
963 | } | ||
964 | |||
965 | /* active stream ? */ | ||
966 | switch (widget->id) { | ||
967 | case snd_soc_dapm_dac: | ||
968 | case snd_soc_dapm_aif_in: | ||
969 | case snd_soc_dapm_dai_in: | ||
970 | if (widget->active) { | ||
971 | widget->inputs = snd_soc_dapm_suspend_check(widget); | ||
972 | return widget->inputs; | ||
973 | } | ||
974 | default: | ||
975 | break; | ||
976 | } | ||
977 | |||
978 | if (widget->connected) { | ||
979 | /* connected pin ? */ | ||
980 | if (widget->id == snd_soc_dapm_input && !widget->ext) { | ||
981 | widget->inputs = snd_soc_dapm_suspend_check(widget); | ||
982 | return widget->inputs; | ||
983 | } | ||
984 | |||
985 | /* connected VMID/Bias for lower pops */ | ||
986 | if (widget->id == snd_soc_dapm_vmid) { | ||
987 | widget->inputs = snd_soc_dapm_suspend_check(widget); | ||
988 | return widget->inputs; | ||
989 | } | ||
990 | |||
991 | /* connected jack ? */ | ||
992 | if (widget->id == snd_soc_dapm_mic || | ||
993 | (widget->id == snd_soc_dapm_line && | ||
994 | !list_empty(&widget->sinks))) { | ||
995 | widget->inputs = snd_soc_dapm_suspend_check(widget); | ||
996 | return widget->inputs; | ||
997 | } | ||
998 | |||
999 | /* signal generator */ | ||
1000 | if (widget->id == snd_soc_dapm_siggen) { | ||
1001 | widget->inputs = snd_soc_dapm_suspend_check(widget); | ||
1002 | return widget->inputs; | ||
1003 | } | ||
1004 | } | 991 | } |
1005 | 992 | ||
1006 | list_for_each_entry(path, &widget->sources, list_sink) { | 993 | list_for_each_entry(path, &widget->sources, list_sink) { |
1007 | DAPM_UPDATE_STAT(widget, neighbour_checks); | 994 | DAPM_UPDATE_STAT(widget, neighbour_checks); |
1008 | 995 | ||
1009 | if (path->weak) | 996 | if (path->weak || path->is_supply) |
1010 | continue; | 997 | continue; |
1011 | 998 | ||
1012 | if (path->walking) | 999 | if (path->walking) |
1013 | return 1; | 1000 | return 1; |
1014 | 1001 | ||
1015 | if (path->walked) | ||
1016 | continue; | ||
1017 | |||
1018 | trace_snd_soc_dapm_input_path(widget, path); | 1002 | trace_snd_soc_dapm_input_path(widget, path); |
1019 | 1003 | ||
1020 | if (path->source && path->connect) { | 1004 | if (path->connect) { |
1021 | path->walked = 1; | ||
1022 | path->walking = 1; | 1005 | path->walking = 1; |
1023 | 1006 | ||
1024 | /* do we need to add this widget to the list ? */ | 1007 | /* do we need to add this widget to the list ? */ |
@@ -1061,20 +1044,24 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, | |||
1061 | struct snd_soc_dapm_widget_list **list) | 1044 | struct snd_soc_dapm_widget_list **list) |
1062 | { | 1045 | { |
1063 | struct snd_soc_card *card = dai->card; | 1046 | struct snd_soc_card *card = dai->card; |
1047 | struct snd_soc_dapm_widget *w; | ||
1064 | int paths; | 1048 | int paths; |
1065 | 1049 | ||
1066 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); | 1050 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); |
1067 | dapm_reset(card); | ||
1068 | 1051 | ||
1069 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | 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 | } | ||
1060 | |||
1061 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
1070 | paths = is_connected_output_ep(dai->playback_widget, list); | 1062 | paths = is_connected_output_ep(dai->playback_widget, list); |
1071 | dapm_clear_walk_output(&card->dapm, | 1063 | else |
1072 | &dai->playback_widget->sinks); | ||
1073 | } else { | ||
1074 | paths = is_connected_input_ep(dai->capture_widget, list); | 1064 | paths = is_connected_input_ep(dai->capture_widget, list); |
1075 | dapm_clear_walk_input(&card->dapm, | ||
1076 | &dai->capture_widget->sources); | ||
1077 | } | ||
1078 | 1065 | ||
1079 | trace_snd_soc_dapm_connected(paths, stream); | 1066 | trace_snd_soc_dapm_connected(paths, stream); |
1080 | mutex_unlock(&card->dapm_mutex); | 1067 | mutex_unlock(&card->dapm_mutex); |
@@ -1163,44 +1150,10 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) | |||
1163 | DAPM_UPDATE_STAT(w, power_checks); | 1150 | DAPM_UPDATE_STAT(w, power_checks); |
1164 | 1151 | ||
1165 | in = is_connected_input_ep(w, NULL); | 1152 | in = is_connected_input_ep(w, NULL); |
1166 | dapm_clear_walk_input(w->dapm, &w->sources); | ||
1167 | out = is_connected_output_ep(w, NULL); | 1153 | out = is_connected_output_ep(w, NULL); |
1168 | dapm_clear_walk_output(w->dapm, &w->sinks); | ||
1169 | return out != 0 && in != 0; | 1154 | return out != 0 && in != 0; |
1170 | } | 1155 | } |
1171 | 1156 | ||
1172 | /* Check to see if an ADC has power */ | ||
1173 | static int dapm_adc_check_power(struct snd_soc_dapm_widget *w) | ||
1174 | { | ||
1175 | int in; | ||
1176 | |||
1177 | DAPM_UPDATE_STAT(w, power_checks); | ||
1178 | |||
1179 | if (w->active) { | ||
1180 | in = is_connected_input_ep(w, NULL); | ||
1181 | dapm_clear_walk_input(w->dapm, &w->sources); | ||
1182 | return in != 0; | ||
1183 | } else { | ||
1184 | return dapm_generic_check_power(w); | ||
1185 | } | ||
1186 | } | ||
1187 | |||
1188 | /* Check to see if a DAC has power */ | ||
1189 | static int dapm_dac_check_power(struct snd_soc_dapm_widget *w) | ||
1190 | { | ||
1191 | int out; | ||
1192 | |||
1193 | DAPM_UPDATE_STAT(w, power_checks); | ||
1194 | |||
1195 | if (w->active) { | ||
1196 | out = is_connected_output_ep(w, NULL); | ||
1197 | dapm_clear_walk_output(w->dapm, &w->sinks); | ||
1198 | return out != 0; | ||
1199 | } else { | ||
1200 | return dapm_generic_check_power(w); | ||
1201 | } | ||
1202 | } | ||
1203 | |||
1204 | /* Check to see if a power supply is needed */ | 1157 | /* Check to see if a power supply is needed */ |
1205 | static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) | 1158 | static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) |
1206 | { | 1159 | { |
@@ -1219,9 +1172,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) | |||
1219 | !path->connected(path->source, path->sink)) | 1172 | !path->connected(path->source, path->sink)) |
1220 | continue; | 1173 | continue; |
1221 | 1174 | ||
1222 | if (!path->sink) | ||
1223 | continue; | ||
1224 | |||
1225 | if (dapm_widget_power_check(path->sink)) | 1175 | if (dapm_widget_power_check(path->sink)) |
1226 | return 1; | 1176 | return 1; |
1227 | } | 1177 | } |
@@ -1636,27 +1586,14 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, | |||
1636 | /* If we changed our power state perhaps our neigbours changed | 1586 | /* If we changed our power state perhaps our neigbours changed |
1637 | * also. | 1587 | * also. |
1638 | */ | 1588 | */ |
1639 | list_for_each_entry(path, &w->sources, list_sink) { | 1589 | list_for_each_entry(path, &w->sources, list_sink) |
1640 | if (path->source) { | 1590 | dapm_widget_set_peer_power(path->source, power, path->connect); |
1641 | dapm_widget_set_peer_power(path->source, power, | 1591 | |
1592 | /* Supplies can't affect their outputs, only their inputs */ | ||
1593 | if (!w->is_supply) { | ||
1594 | list_for_each_entry(path, &w->sinks, list_source) | ||
1595 | dapm_widget_set_peer_power(path->sink, power, | ||
1642 | path->connect); | 1596 | path->connect); |
1643 | } | ||
1644 | } | ||
1645 | switch (w->id) { | ||
1646 | case snd_soc_dapm_supply: | ||
1647 | case snd_soc_dapm_regulator_supply: | ||
1648 | case snd_soc_dapm_clock_supply: | ||
1649 | case snd_soc_dapm_kcontrol: | ||
1650 | /* Supplies can't affect their outputs, only their inputs */ | ||
1651 | break; | ||
1652 | default: | ||
1653 | list_for_each_entry(path, &w->sinks, list_source) { | ||
1654 | if (path->sink) { | ||
1655 | dapm_widget_set_peer_power(path->sink, power, | ||
1656 | path->connect); | ||
1657 | } | ||
1658 | } | ||
1659 | break; | ||
1660 | } | 1597 | } |
1661 | 1598 | ||
1662 | if (power) | 1599 | if (power) |
@@ -1863,10 +1800,14 @@ static ssize_t dapm_widget_power_read_file(struct file *file, | |||
1863 | if (!buf) | 1800 | if (!buf) |
1864 | return -ENOMEM; | 1801 | return -ENOMEM; |
1865 | 1802 | ||
1866 | in = is_connected_input_ep(w, NULL); | 1803 | /* Supply widgets are not handled by is_connected_{input,output}_ep() */ |
1867 | dapm_clear_walk_input(w->dapm, &w->sources); | 1804 | if (w->is_supply) { |
1868 | out = is_connected_output_ep(w, NULL); | 1805 | in = 0; |
1869 | dapm_clear_walk_output(w->dapm, &w->sinks); | 1806 | out = 0; |
1807 | } else { | ||
1808 | in = is_connected_input_ep(w, NULL); | ||
1809 | out = is_connected_output_ep(w, NULL); | ||
1810 | } | ||
1870 | 1811 | ||
1871 | ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", | 1812 | ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", |
1872 | w->name, w->power ? "On" : "Off", | 1813 | w->name, w->power ? "On" : "Off", |
@@ -2011,32 +1952,45 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) | |||
2011 | 1952 | ||
2012 | #endif | 1953 | #endif |
2013 | 1954 | ||
1955 | /* | ||
1956 | * soc_dapm_connect_path() - Connects or disconnects a path | ||
1957 | * @path: The path to update | ||
1958 | * @connect: The new connect state of the path. True if the path is connected, | ||
1959 | * false if it is disconneted. | ||
1960 | * @reason: The reason why the path changed (for debugging only) | ||
1961 | */ | ||
1962 | static void soc_dapm_connect_path(struct snd_soc_dapm_path *path, | ||
1963 | bool connect, const char *reason) | ||
1964 | { | ||
1965 | if (path->connect == connect) | ||
1966 | return; | ||
1967 | |||
1968 | path->connect = connect; | ||
1969 | dapm_mark_dirty(path->source, reason); | ||
1970 | dapm_mark_dirty(path->sink, reason); | ||
1971 | dapm_path_invalidate(path); | ||
1972 | } | ||
1973 | |||
2014 | /* test and update the power status of a mux widget */ | 1974 | /* test and update the power status of a mux widget */ |
2015 | static int soc_dapm_mux_update_power(struct snd_soc_card *card, | 1975 | static int soc_dapm_mux_update_power(struct snd_soc_card *card, |
2016 | struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) | 1976 | struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) |
2017 | { | 1977 | { |
2018 | struct snd_soc_dapm_path *path; | 1978 | struct snd_soc_dapm_path *path; |
2019 | int found = 0; | 1979 | int found = 0; |
1980 | bool connect; | ||
2020 | 1981 | ||
2021 | lockdep_assert_held(&card->dapm_mutex); | 1982 | lockdep_assert_held(&card->dapm_mutex); |
2022 | 1983 | ||
2023 | /* find dapm widget path assoc with kcontrol */ | 1984 | /* find dapm widget path assoc with kcontrol */ |
2024 | dapm_kcontrol_for_each_path(path, kcontrol) { | 1985 | dapm_kcontrol_for_each_path(path, kcontrol) { |
2025 | if (!path->name || !e->texts[mux]) | ||
2026 | continue; | ||
2027 | |||
2028 | found = 1; | 1986 | found = 1; |
2029 | /* we now need to match the string in the enum to the path */ | 1987 | /* we now need to match the string in the enum to the path */ |
2030 | if (!(strcmp(path->name, e->texts[mux]))) { | 1988 | if (!(strcmp(path->name, e->texts[mux]))) |
2031 | path->connect = 1; /* new connection */ | 1989 | connect = true; |
2032 | dapm_mark_dirty(path->source, "mux connection"); | 1990 | else |
2033 | } else { | 1991 | connect = false; |
2034 | if (path->connect) | 1992 | |
2035 | dapm_mark_dirty(path->source, | 1993 | soc_dapm_connect_path(path, connect, "mux update"); |
2036 | "mux disconnection"); | ||
2037 | path->connect = 0; /* old connection must be powered down */ | ||
2038 | } | ||
2039 | dapm_mark_dirty(path->sink, "mux change"); | ||
2040 | } | 1994 | } |
2041 | 1995 | ||
2042 | if (found) | 1996 | if (found) |
@@ -2075,9 +2029,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, | |||
2075 | /* find dapm widget path assoc with kcontrol */ | 2029 | /* find dapm widget path assoc with kcontrol */ |
2076 | dapm_kcontrol_for_each_path(path, kcontrol) { | 2030 | dapm_kcontrol_for_each_path(path, kcontrol) { |
2077 | found = 1; | 2031 | found = 1; |
2078 | path->connect = connect; | 2032 | soc_dapm_connect_path(path, connect, "mixer update"); |
2079 | dapm_mark_dirty(path->source, "mixer connection"); | ||
2080 | dapm_mark_dirty(path->sink, "mixer update"); | ||
2081 | } | 2033 | } |
2082 | 2034 | ||
2083 | if (found) | 2035 | if (found) |
@@ -2255,8 +2207,11 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, | |||
2255 | return -EINVAL; | 2207 | return -EINVAL; |
2256 | } | 2208 | } |
2257 | 2209 | ||
2258 | if (w->connected != status) | 2210 | if (w->connected != status) { |
2259 | 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 | } | ||
2260 | 2215 | ||
2261 | w->connected = status; | 2216 | w->connected = status; |
2262 | if (status == 0) | 2217 | if (status == 0) |
@@ -2309,6 +2264,53 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) | |||
2309 | } | 2264 | } |
2310 | EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); | 2265 | EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); |
2311 | 2266 | ||
2267 | /* | ||
2268 | * dapm_update_widget_flags() - Re-compute widget sink and source flags | ||
2269 | * @w: The widget for which to update the flags | ||
2270 | * | ||
2271 | * Some widgets have a dynamic category which depends on which neighbors they | ||
2272 | * are connected to. This function update the category for these widgets. | ||
2273 | * | ||
2274 | * This function must be called whenever a path is added or removed to a widget. | ||
2275 | */ | ||
2276 | static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) | ||
2277 | { | ||
2278 | struct snd_soc_dapm_path *p; | ||
2279 | |||
2280 | switch (w->id) { | ||
2281 | case snd_soc_dapm_input: | ||
2282 | w->is_source = 1; | ||
2283 | list_for_each_entry(p, &w->sources, list_sink) { | ||
2284 | if (p->source->id == snd_soc_dapm_micbias || | ||
2285 | p->source->id == snd_soc_dapm_mic || | ||
2286 | p->source->id == snd_soc_dapm_line || | ||
2287 | p->source->id == snd_soc_dapm_output) { | ||
2288 | w->is_source = 0; | ||
2289 | break; | ||
2290 | } | ||
2291 | } | ||
2292 | break; | ||
2293 | case snd_soc_dapm_output: | ||
2294 | w->is_sink = 1; | ||
2295 | list_for_each_entry(p, &w->sinks, list_source) { | ||
2296 | if (p->sink->id == snd_soc_dapm_spk || | ||
2297 | p->sink->id == snd_soc_dapm_hp || | ||
2298 | p->sink->id == snd_soc_dapm_line || | ||
2299 | p->sink->id == snd_soc_dapm_input) { | ||
2300 | w->is_sink = 0; | ||
2301 | break; | ||
2302 | } | ||
2303 | } | ||
2304 | break; | ||
2305 | case snd_soc_dapm_line: | ||
2306 | w->is_sink = !list_empty(&w->sources); | ||
2307 | w->is_source = !list_empty(&w->sinks); | ||
2308 | break; | ||
2309 | default: | ||
2310 | break; | ||
2311 | } | ||
2312 | } | ||
2313 | |||
2312 | static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, | 2314 | static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, |
2313 | struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, | 2315 | struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, |
2314 | const char *control, | 2316 | const char *control, |
@@ -2318,6 +2320,27 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, | |||
2318 | struct snd_soc_dapm_path *path; | 2320 | struct snd_soc_dapm_path *path; |
2319 | int ret; | 2321 | int ret; |
2320 | 2322 | ||
2323 | if (wsink->is_supply && !wsource->is_supply) { | ||
2324 | dev_err(dapm->dev, | ||
2325 | "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n", | ||
2326 | wsource->name, wsink->name); | ||
2327 | return -EINVAL; | ||
2328 | } | ||
2329 | |||
2330 | if (connected && !wsource->is_supply) { | ||
2331 | dev_err(dapm->dev, | ||
2332 | "connected() callback only supported for supply widgets (%s -> %s)\n", | ||
2333 | wsource->name, wsink->name); | ||
2334 | return -EINVAL; | ||
2335 | } | ||
2336 | |||
2337 | if (wsource->is_supply && control) { | ||
2338 | dev_err(dapm->dev, | ||
2339 | "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n", | ||
2340 | wsource->name, control, wsink->name); | ||
2341 | return -EINVAL; | ||
2342 | } | ||
2343 | |||
2321 | path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); | 2344 | path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); |
2322 | if (!path) | 2345 | if (!path) |
2323 | return -ENOMEM; | 2346 | return -ENOMEM; |
@@ -2330,85 +2353,49 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, | |||
2330 | INIT_LIST_HEAD(&path->list_source); | 2353 | INIT_LIST_HEAD(&path->list_source); |
2331 | INIT_LIST_HEAD(&path->list_sink); | 2354 | INIT_LIST_HEAD(&path->list_sink); |
2332 | 2355 | ||
2333 | /* check for external widgets */ | 2356 | if (wsource->is_supply || wsink->is_supply) |
2334 | if (wsink->id == snd_soc_dapm_input) { | 2357 | path->is_supply = 1; |
2335 | if (wsource->id == snd_soc_dapm_micbias || | ||
2336 | wsource->id == snd_soc_dapm_mic || | ||
2337 | wsource->id == snd_soc_dapm_line || | ||
2338 | wsource->id == snd_soc_dapm_output) | ||
2339 | wsink->ext = 1; | ||
2340 | } | ||
2341 | if (wsource->id == snd_soc_dapm_output) { | ||
2342 | if (wsink->id == snd_soc_dapm_spk || | ||
2343 | wsink->id == snd_soc_dapm_hp || | ||
2344 | wsink->id == snd_soc_dapm_line || | ||
2345 | wsink->id == snd_soc_dapm_input) | ||
2346 | wsource->ext = 1; | ||
2347 | } | ||
2348 | |||
2349 | dapm_mark_dirty(wsource, "Route added"); | ||
2350 | dapm_mark_dirty(wsink, "Route added"); | ||
2351 | 2358 | ||
2352 | /* connect static paths */ | 2359 | /* connect static paths */ |
2353 | if (control == NULL) { | 2360 | if (control == NULL) { |
2354 | list_add(&path->list, &dapm->card->paths); | ||
2355 | list_add(&path->list_sink, &wsink->sources); | ||
2356 | list_add(&path->list_source, &wsource->sinks); | ||
2357 | path->connect = 1; | 2361 | path->connect = 1; |
2358 | return 0; | 2362 | } else { |
2359 | } | 2363 | /* connect dynamic paths */ |
2360 | 2364 | switch (wsink->id) { | |
2361 | /* connect dynamic paths */ | 2365 | case snd_soc_dapm_mux: |
2362 | switch (wsink->id) { | 2366 | ret = dapm_connect_mux(dapm, path, control); |
2363 | case snd_soc_dapm_adc: | 2367 | if (ret != 0) |
2364 | case snd_soc_dapm_dac: | 2368 | goto err; |
2365 | case snd_soc_dapm_pga: | 2369 | break; |
2366 | case snd_soc_dapm_out_drv: | 2370 | case snd_soc_dapm_switch: |
2367 | case snd_soc_dapm_input: | 2371 | case snd_soc_dapm_mixer: |
2368 | case snd_soc_dapm_output: | 2372 | case snd_soc_dapm_mixer_named_ctl: |
2369 | case snd_soc_dapm_siggen: | 2373 | ret = dapm_connect_mixer(dapm, path, control); |
2370 | case snd_soc_dapm_micbias: | 2374 | if (ret != 0) |
2371 | case snd_soc_dapm_vmid: | 2375 | goto err; |
2372 | case snd_soc_dapm_pre: | 2376 | break; |
2373 | case snd_soc_dapm_post: | 2377 | default: |
2374 | case snd_soc_dapm_supply: | 2378 | dev_err(dapm->dev, |
2375 | case snd_soc_dapm_regulator_supply: | 2379 | "Control not supported for path %s -> [%s] -> %s\n", |
2376 | case snd_soc_dapm_clock_supply: | 2380 | wsource->name, control, wsink->name); |
2377 | case snd_soc_dapm_aif_in: | 2381 | ret = -EINVAL; |
2378 | case snd_soc_dapm_aif_out: | ||
2379 | case snd_soc_dapm_dai_in: | ||
2380 | case snd_soc_dapm_dai_out: | ||
2381 | case snd_soc_dapm_dai_link: | ||
2382 | case snd_soc_dapm_kcontrol: | ||
2383 | list_add(&path->list, &dapm->card->paths); | ||
2384 | list_add(&path->list_sink, &wsink->sources); | ||
2385 | list_add(&path->list_source, &wsource->sinks); | ||
2386 | path->connect = 1; | ||
2387 | return 0; | ||
2388 | case snd_soc_dapm_mux: | ||
2389 | ret = dapm_connect_mux(dapm, wsource, wsink, path, control, | ||
2390 | &wsink->kcontrol_news[0]); | ||
2391 | if (ret != 0) | ||
2392 | goto err; | ||
2393 | break; | ||
2394 | case snd_soc_dapm_switch: | ||
2395 | case snd_soc_dapm_mixer: | ||
2396 | case snd_soc_dapm_mixer_named_ctl: | ||
2397 | ret = dapm_connect_mixer(dapm, wsource, wsink, path, control); | ||
2398 | if (ret != 0) | ||
2399 | goto err; | 2382 | goto err; |
2400 | break; | 2383 | } |
2401 | case snd_soc_dapm_hp: | ||
2402 | case snd_soc_dapm_mic: | ||
2403 | case snd_soc_dapm_line: | ||
2404 | case snd_soc_dapm_spk: | ||
2405 | list_add(&path->list, &dapm->card->paths); | ||
2406 | list_add(&path->list_sink, &wsink->sources); | ||
2407 | list_add(&path->list_source, &wsource->sinks); | ||
2408 | path->connect = 0; | ||
2409 | return 0; | ||
2410 | } | 2384 | } |
2411 | 2385 | ||
2386 | list_add(&path->list, &dapm->card->paths); | ||
2387 | list_add(&path->list_sink, &wsink->sources); | ||
2388 | list_add(&path->list_source, &wsource->sinks); | ||
2389 | |||
2390 | dapm_update_widget_flags(wsource); | ||
2391 | dapm_update_widget_flags(wsink); | ||
2392 | |||
2393 | dapm_mark_dirty(wsource, "Route added"); | ||
2394 | dapm_mark_dirty(wsink, "Route added"); | ||
2395 | |||
2396 | if (dapm->card->instantiated && path->connect) | ||
2397 | dapm_path_invalidate(path); | ||
2398 | |||
2412 | return 0; | 2399 | return 0; |
2413 | err: | 2400 | err: |
2414 | kfree(path); | 2401 | kfree(path); |
@@ -2489,6 +2476,7 @@ err: | |||
2489 | static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, | 2476 | static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, |
2490 | const struct snd_soc_dapm_route *route) | 2477 | const struct snd_soc_dapm_route *route) |
2491 | { | 2478 | { |
2479 | struct snd_soc_dapm_widget *wsource, *wsink; | ||
2492 | struct snd_soc_dapm_path *path, *p; | 2480 | struct snd_soc_dapm_path *path, *p; |
2493 | const char *sink; | 2481 | const char *sink; |
2494 | const char *source; | 2482 | const char *source; |
@@ -2526,10 +2514,19 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, | |||
2526 | } | 2514 | } |
2527 | 2515 | ||
2528 | if (path) { | 2516 | if (path) { |
2529 | dapm_mark_dirty(path->source, "Route removed"); | 2517 | wsource = path->source; |
2530 | dapm_mark_dirty(path->sink, "Route removed"); | 2518 | wsink = path->sink; |
2519 | |||
2520 | dapm_mark_dirty(wsource, "Route removed"); | ||
2521 | dapm_mark_dirty(wsink, "Route removed"); | ||
2522 | if (path->connect) | ||
2523 | dapm_path_invalidate(path); | ||
2531 | 2524 | ||
2532 | dapm_free_path(path); | 2525 | dapm_free_path(path); |
2526 | |||
2527 | /* Update any path related flags */ | ||
2528 | dapm_update_widget_flags(wsource); | ||
2529 | dapm_update_widget_flags(wsink); | ||
2533 | } else { | 2530 | } else { |
2534 | dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n", | 2531 | dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n", |
2535 | source, sink); | 2532 | source, sink); |
@@ -3087,40 +3084,44 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, | |||
3087 | } | 3084 | } |
3088 | 3085 | ||
3089 | switch (w->id) { | 3086 | switch (w->id) { |
3090 | case snd_soc_dapm_switch: | 3087 | case snd_soc_dapm_mic: |
3091 | case snd_soc_dapm_mixer: | 3088 | case snd_soc_dapm_input: |
3092 | case snd_soc_dapm_mixer_named_ctl: | 3089 | w->is_source = 1; |
3093 | w->power_check = dapm_generic_check_power; | 3090 | w->power_check = dapm_generic_check_power; |
3094 | break; | 3091 | break; |
3095 | case snd_soc_dapm_mux: | 3092 | case snd_soc_dapm_spk: |
3093 | case snd_soc_dapm_hp: | ||
3094 | case snd_soc_dapm_output: | ||
3095 | w->is_sink = 1; | ||
3096 | w->power_check = dapm_generic_check_power; | 3096 | w->power_check = dapm_generic_check_power; |
3097 | break; | 3097 | break; |
3098 | case snd_soc_dapm_dai_out: | 3098 | case snd_soc_dapm_vmid: |
3099 | w->power_check = dapm_adc_check_power; | 3099 | case snd_soc_dapm_siggen: |
3100 | break; | 3100 | w->is_source = 1; |
3101 | case snd_soc_dapm_dai_in: | 3101 | w->power_check = dapm_always_on_check_power; |
3102 | w->power_check = dapm_dac_check_power; | ||
3103 | break; | 3102 | break; |
3103 | case snd_soc_dapm_mux: | ||
3104 | case snd_soc_dapm_switch: | ||
3105 | case snd_soc_dapm_mixer: | ||
3106 | case snd_soc_dapm_mixer_named_ctl: | ||
3104 | case snd_soc_dapm_adc: | 3107 | case snd_soc_dapm_adc: |
3105 | case snd_soc_dapm_aif_out: | 3108 | case snd_soc_dapm_aif_out: |
3106 | case snd_soc_dapm_dac: | 3109 | case snd_soc_dapm_dac: |
3107 | case snd_soc_dapm_aif_in: | 3110 | case snd_soc_dapm_aif_in: |
3108 | case snd_soc_dapm_pga: | 3111 | case snd_soc_dapm_pga: |
3109 | case snd_soc_dapm_out_drv: | 3112 | case snd_soc_dapm_out_drv: |
3110 | case snd_soc_dapm_input: | ||
3111 | case snd_soc_dapm_output: | ||
3112 | case snd_soc_dapm_micbias: | 3113 | case snd_soc_dapm_micbias: |
3113 | case snd_soc_dapm_spk: | ||
3114 | case snd_soc_dapm_hp: | ||
3115 | case snd_soc_dapm_mic: | ||
3116 | case snd_soc_dapm_line: | 3114 | case snd_soc_dapm_line: |
3117 | case snd_soc_dapm_dai_link: | 3115 | case snd_soc_dapm_dai_link: |
3116 | case snd_soc_dapm_dai_out: | ||
3117 | case snd_soc_dapm_dai_in: | ||
3118 | w->power_check = dapm_generic_check_power; | 3118 | w->power_check = dapm_generic_check_power; |
3119 | break; | 3119 | break; |
3120 | case snd_soc_dapm_supply: | 3120 | case snd_soc_dapm_supply: |
3121 | case snd_soc_dapm_regulator_supply: | 3121 | case snd_soc_dapm_regulator_supply: |
3122 | case snd_soc_dapm_clock_supply: | 3122 | case snd_soc_dapm_clock_supply: |
3123 | case snd_soc_dapm_kcontrol: | 3123 | case snd_soc_dapm_kcontrol: |
3124 | w->is_supply = 1; | ||
3124 | w->power_check = dapm_supply_check_power; | 3125 | w->power_check = dapm_supply_check_power; |
3125 | break; | 3126 | break; |
3126 | default: | 3127 | default: |
@@ -3137,6 +3138,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, | |||
3137 | INIT_LIST_HEAD(&w->dirty); | 3138 | INIT_LIST_HEAD(&w->dirty); |
3138 | list_add(&w->list, &dapm->card->widgets); | 3139 | list_add(&w->list, &dapm->card->widgets); |
3139 | 3140 | ||
3141 | w->inputs = -1; | ||
3142 | w->outputs = -1; | ||
3143 | |||
3140 | /* machine layer set ups unconnected pins and insertions */ | 3144 | /* machine layer set ups unconnected pins and insertions */ |
3141 | w->connected = 1; | 3145 | w->connected = 1; |
3142 | return w; | 3146 | return w; |
@@ -3484,6 +3488,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, | |||
3484 | case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: | 3488 | case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: |
3485 | break; | 3489 | break; |
3486 | } | 3490 | } |
3491 | |||
3492 | if (w->id == snd_soc_dapm_dai_in) { | ||
3493 | w->is_source = w->active; | ||
3494 | dapm_widget_invalidate_input_paths(w); | ||
3495 | } else { | ||
3496 | w->is_sink = w->active; | ||
3497 | dapm_widget_invalidate_output_paths(w); | ||
3498 | } | ||
3487 | } | 3499 | } |
3488 | } | 3500 | } |
3489 | 3501 | ||
@@ -3610,7 +3622,15 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, | |||
3610 | } | 3622 | } |
3611 | 3623 | ||
3612 | 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); |
3613 | 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 | } | ||
3614 | w->force = 1; | 3634 | w->force = 1; |
3615 | dapm_mark_dirty(w, "force enable"); | 3635 | dapm_mark_dirty(w, "force enable"); |
3616 | 3636 | ||
@@ -3788,35 +3808,54 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, | |||
3788 | } | 3808 | } |
3789 | EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); | 3809 | EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); |
3790 | 3810 | ||
3811 | /** | ||
3812 | * dapm_is_external_path() - Checks if a path is a external path | ||
3813 | * @card: The card the path belongs to | ||
3814 | * @path: The path to check | ||
3815 | * | ||
3816 | * Returns true if the path is either between two different DAPM contexts or | ||
3817 | * between two external pins of the same DAPM context. Otherwise returns | ||
3818 | * false. | ||
3819 | */ | ||
3820 | static bool dapm_is_external_path(struct snd_soc_card *card, | ||
3821 | struct snd_soc_dapm_path *path) | ||
3822 | { | ||
3823 | dev_dbg(card->dev, | ||
3824 | "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n", | ||
3825 | path->source->name, path->source->id, path->source->dapm, | ||
3826 | path->sink->name, path->sink->id, path->sink->dapm); | ||
3827 | |||
3828 | /* Connection between two different DAPM contexts */ | ||
3829 | if (path->source->dapm != path->sink->dapm) | ||
3830 | return true; | ||
3831 | |||
3832 | /* Loopback connection from external pin to external pin */ | ||
3833 | if (path->sink->id == snd_soc_dapm_input) { | ||
3834 | switch (path->source->id) { | ||
3835 | case snd_soc_dapm_output: | ||
3836 | case snd_soc_dapm_micbias: | ||
3837 | return true; | ||
3838 | default: | ||
3839 | break; | ||
3840 | } | ||
3841 | } | ||
3842 | |||
3843 | return false; | ||
3844 | } | ||
3845 | |||
3791 | static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card, | 3846 | static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card, |
3792 | struct snd_soc_dapm_widget *w) | 3847 | struct snd_soc_dapm_widget *w) |
3793 | { | 3848 | { |
3794 | struct snd_soc_dapm_path *p; | 3849 | struct snd_soc_dapm_path *p; |
3795 | 3850 | ||
3796 | list_for_each_entry(p, &card->paths, list) { | 3851 | list_for_each_entry(p, &w->sources, list_sink) { |
3797 | if ((p->source == w) || (p->sink == w)) { | 3852 | if (dapm_is_external_path(card, p)) |
3798 | dev_dbg(card->dev, | 3853 | return true; |
3799 | "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n", | 3854 | } |
3800 | p->source->name, p->source->id, p->source->dapm, | ||
3801 | p->sink->name, p->sink->id, p->sink->dapm); | ||
3802 | 3855 | ||
3803 | /* Connected to something other than the codec */ | 3856 | list_for_each_entry(p, &w->sinks, list_source) { |
3804 | if (p->source->dapm != p->sink->dapm) | 3857 | if (dapm_is_external_path(card, p)) |
3805 | return true; | 3858 | return true; |
3806 | /* | ||
3807 | * Loopback connection from codec external pin to | ||
3808 | * codec external pin | ||
3809 | */ | ||
3810 | if (p->sink->id == snd_soc_dapm_input) { | ||
3811 | switch (p->source->id) { | ||
3812 | case snd_soc_dapm_output: | ||
3813 | case snd_soc_dapm_micbias: | ||
3814 | return true; | ||
3815 | default: | ||
3816 | break; | ||
3817 | } | ||
3818 | } | ||
3819 | } | ||
3820 | } | 3859 | } |
3821 | 3860 | ||
3822 | return false; | 3861 | return false; |