diff options
-rw-r--r-- | include/sound/soc-dapm.h | 5 | ||||
-rw-r--r-- | include/trace/events/asoc.h | 80 | ||||
-rw-r--r-- | sound/soc/soc-dapm.c | 122 |
3 files changed, 197 insertions, 10 deletions
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index bea0c8658aa0..e3833d9f1914 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h | |||
@@ -321,6 +321,7 @@ struct snd_soc_dapm_pin; | |||
321 | struct snd_soc_dapm_route; | 321 | struct snd_soc_dapm_route; |
322 | struct snd_soc_dapm_context; | 322 | struct snd_soc_dapm_context; |
323 | struct regulator; | 323 | struct regulator; |
324 | struct snd_soc_dapm_widget_list; | ||
324 | 325 | ||
325 | int dapm_reg_event(struct snd_soc_dapm_widget *w, | 326 | int dapm_reg_event(struct snd_soc_dapm_widget *w, |
326 | struct snd_kcontrol *kcontrol, int event); | 327 | struct snd_kcontrol *kcontrol, int event); |
@@ -403,6 +404,10 @@ void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec); | |||
403 | /* Mostly internal - should not normally be used */ | 404 | /* Mostly internal - should not normally be used */ |
404 | void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason); | 405 | void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason); |
405 | 406 | ||
407 | /* dapm path query */ | ||
408 | int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, | ||
409 | struct snd_soc_dapm_widget_list **list); | ||
410 | |||
406 | /* dapm widget types */ | 411 | /* dapm widget types */ |
407 | enum snd_soc_dapm_type { | 412 | enum snd_soc_dapm_type { |
408 | snd_soc_dapm_input = 0, /* input pin */ | 413 | snd_soc_dapm_input = 0, /* input pin */ |
diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index ab26f8aa3c78..6d8efb1cc8ce 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h | |||
@@ -7,6 +7,8 @@ | |||
7 | #include <linux/ktime.h> | 7 | #include <linux/ktime.h> |
8 | #include <linux/tracepoint.h> | 8 | #include <linux/tracepoint.h> |
9 | 9 | ||
10 | #define DAPM_DIRECT "(direct)" | ||
11 | |||
10 | struct snd_soc_jack; | 12 | struct snd_soc_jack; |
11 | struct snd_soc_codec; | 13 | struct snd_soc_codec; |
12 | struct snd_soc_platform; | 14 | struct snd_soc_platform; |
@@ -241,6 +243,84 @@ TRACE_EVENT(snd_soc_dapm_walk_done, | |||
241 | (int)__entry->path_checks, (int)__entry->neighbour_checks) | 243 | (int)__entry->path_checks, (int)__entry->neighbour_checks) |
242 | ); | 244 | ); |
243 | 245 | ||
246 | TRACE_EVENT(snd_soc_dapm_output_path, | ||
247 | |||
248 | TP_PROTO(struct snd_soc_dapm_widget *widget, | ||
249 | struct snd_soc_dapm_path *path), | ||
250 | |||
251 | TP_ARGS(widget, path), | ||
252 | |||
253 | TP_STRUCT__entry( | ||
254 | __string( wname, widget->name ) | ||
255 | __string( pname, path->name ? path->name : DAPM_DIRECT) | ||
256 | __string( psname, path->sink->name ) | ||
257 | __field( int, path_sink ) | ||
258 | __field( int, path_connect ) | ||
259 | ), | ||
260 | |||
261 | TP_fast_assign( | ||
262 | __assign_str(wname, widget->name); | ||
263 | __assign_str(pname, path->name ? path->name : DAPM_DIRECT); | ||
264 | __assign_str(psname, path->sink->name); | ||
265 | __entry->path_connect = path->connect; | ||
266 | __entry->path_sink = (int)path->sink; | ||
267 | ), | ||
268 | |||
269 | TP_printk("%c%s -> %s -> %s\n", | ||
270 | (int) __entry->path_sink && | ||
271 | (int) __entry->path_connect ? '*' : ' ', | ||
272 | __get_str(wname), __get_str(pname), __get_str(psname)) | ||
273 | ); | ||
274 | |||
275 | TRACE_EVENT(snd_soc_dapm_input_path, | ||
276 | |||
277 | TP_PROTO(struct snd_soc_dapm_widget *widget, | ||
278 | struct snd_soc_dapm_path *path), | ||
279 | |||
280 | TP_ARGS(widget, path), | ||
281 | |||
282 | TP_STRUCT__entry( | ||
283 | __string( wname, widget->name ) | ||
284 | __string( pname, path->name ? path->name : DAPM_DIRECT) | ||
285 | __string( psname, path->source->name ) | ||
286 | __field( int, path_source ) | ||
287 | __field( int, path_connect ) | ||
288 | ), | ||
289 | |||
290 | TP_fast_assign( | ||
291 | __assign_str(wname, widget->name); | ||
292 | __assign_str(pname, path->name ? path->name : DAPM_DIRECT); | ||
293 | __assign_str(psname, path->source->name); | ||
294 | __entry->path_connect = path->connect; | ||
295 | __entry->path_source = (int)path->source; | ||
296 | ), | ||
297 | |||
298 | TP_printk("%c%s <- %s <- %s\n", | ||
299 | (int) __entry->path_source && | ||
300 | (int) __entry->path_connect ? '*' : ' ', | ||
301 | __get_str(wname), __get_str(pname), __get_str(psname)) | ||
302 | ); | ||
303 | |||
304 | TRACE_EVENT(snd_soc_dapm_connected, | ||
305 | |||
306 | TP_PROTO(int paths, int stream), | ||
307 | |||
308 | TP_ARGS(paths, stream), | ||
309 | |||
310 | TP_STRUCT__entry( | ||
311 | __field( int, paths ) | ||
312 | __field( int, stream ) | ||
313 | ), | ||
314 | |||
315 | TP_fast_assign( | ||
316 | __entry->paths = paths; | ||
317 | __entry->stream = stream; | ||
318 | ), | ||
319 | |||
320 | TP_printk("%s: found %d paths\n", | ||
321 | __entry->stream ? "capture" : "playback", __entry->paths) | ||
322 | ); | ||
323 | |||
244 | TRACE_EVENT(snd_soc_jack_irq, | 324 | TRACE_EVENT(snd_soc_jack_irq, |
245 | 325 | ||
246 | TP_PROTO(const char *name), | 326 | TP_PROTO(const char *name), |
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d7ee73a60ca5..214323f53956 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -705,11 +705,51 @@ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) | |||
705 | } | 705 | } |
706 | } | 706 | } |
707 | 707 | ||
708 | /* add widget to list if it's not already in the list */ | ||
709 | static int dapm_list_add_widget(struct snd_soc_dapm_widget_list **list, | ||
710 | struct snd_soc_dapm_widget *w) | ||
711 | { | ||
712 | struct snd_soc_dapm_widget_list *wlist; | ||
713 | int wlistsize, wlistentries, i; | ||
714 | |||
715 | if (*list == NULL) | ||
716 | return -EINVAL; | ||
717 | |||
718 | wlist = *list; | ||
719 | |||
720 | /* is this widget already in the list */ | ||
721 | for (i = 0; i < wlist->num_widgets; i++) { | ||
722 | if (wlist->widgets[i] == w) | ||
723 | return 0; | ||
724 | } | ||
725 | |||
726 | /* allocate some new space */ | ||
727 | wlistentries = wlist->num_widgets + 1; | ||
728 | wlistsize = sizeof(struct snd_soc_dapm_widget_list) + | ||
729 | wlistentries * sizeof(struct snd_soc_dapm_widget *); | ||
730 | *list = krealloc(wlist, wlistsize, GFP_KERNEL); | ||
731 | if (*list == NULL) { | ||
732 | dev_err(w->dapm->dev, "can't allocate widget list for %s\n", | ||
733 | w->name); | ||
734 | return -ENOMEM; | ||
735 | } | ||
736 | wlist = *list; | ||
737 | |||
738 | /* insert the widget */ | ||
739 | dev_dbg(w->dapm->dev, "added %s in widget list pos %d\n", | ||
740 | w->name, wlist->num_widgets); | ||
741 | |||
742 | wlist->widgets[wlist->num_widgets] = w; | ||
743 | wlist->num_widgets++; | ||
744 | return 1; | ||
745 | } | ||
746 | |||
708 | /* | 747 | /* |
709 | * Recursively check for a completed path to an active or physically connected | 748 | * Recursively check for a completed path to an active or physically connected |
710 | * output widget. Returns number of complete paths. | 749 | * output widget. Returns number of complete paths. |
711 | */ | 750 | */ |
712 | static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) | 751 | static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, |
752 | struct snd_soc_dapm_widget_list **list) | ||
713 | { | 753 | { |
714 | struct snd_soc_dapm_path *path; | 754 | struct snd_soc_dapm_path *path; |
715 | int con = 0; | 755 | int con = 0; |
@@ -765,9 +805,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) | |||
765 | if (path->walked) | 805 | if (path->walked) |
766 | continue; | 806 | continue; |
767 | 807 | ||
808 | trace_snd_soc_dapm_output_path(widget, path); | ||
809 | |||
768 | if (path->sink && path->connect) { | 810 | if (path->sink && path->connect) { |
769 | path->walked = 1; | 811 | path->walked = 1; |
770 | con += is_connected_output_ep(path->sink); | 812 | |
813 | /* do we need to add this widget to the list ? */ | ||
814 | if (list) { | ||
815 | int err; | ||
816 | err = dapm_list_add_widget(list, path->sink); | ||
817 | if (err < 0) { | ||
818 | dev_err(widget->dapm->dev, "could not add widget %s\n", | ||
819 | widget->name); | ||
820 | return con; | ||
821 | } | ||
822 | } | ||
823 | |||
824 | con += is_connected_output_ep(path->sink, list); | ||
771 | } | 825 | } |
772 | } | 826 | } |
773 | 827 | ||
@@ -780,7 +834,8 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) | |||
780 | * Recursively check for a completed path to an active or physically connected | 834 | * Recursively check for a completed path to an active or physically connected |
781 | * input widget. Returns number of complete paths. | 835 | * input widget. Returns number of complete paths. |
782 | */ | 836 | */ |
783 | static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) | 837 | static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, |
838 | struct snd_soc_dapm_widget_list **list) | ||
784 | { | 839 | { |
785 | struct snd_soc_dapm_path *path; | 840 | struct snd_soc_dapm_path *path; |
786 | int con = 0; | 841 | int con = 0; |
@@ -848,9 +903,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) | |||
848 | if (path->walked) | 903 | if (path->walked) |
849 | continue; | 904 | continue; |
850 | 905 | ||
906 | trace_snd_soc_dapm_input_path(widget, path); | ||
907 | |||
851 | if (path->source && path->connect) { | 908 | if (path->source && path->connect) { |
852 | path->walked = 1; | 909 | path->walked = 1; |
853 | con += is_connected_input_ep(path->source); | 910 | |
911 | /* do we need to add this widget to the list ? */ | ||
912 | if (list) { | ||
913 | int err; | ||
914 | err = dapm_list_add_widget(list, path->sink); | ||
915 | if (err < 0) { | ||
916 | dev_err(widget->dapm->dev, "could not add widget %s\n", | ||
917 | widget->name); | ||
918 | return con; | ||
919 | } | ||
920 | } | ||
921 | |||
922 | con += is_connected_input_ep(path->source, list); | ||
854 | } | 923 | } |
855 | } | 924 | } |
856 | 925 | ||
@@ -859,6 +928,39 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) | |||
859 | return con; | 928 | return con; |
860 | } | 929 | } |
861 | 930 | ||
931 | /** | ||
932 | * snd_soc_dapm_get_connected_widgets - query audio path and it's widgets. | ||
933 | * @dai: the soc DAI. | ||
934 | * @stream: stream direction. | ||
935 | * @list: list of active widgets for this stream. | ||
936 | * | ||
937 | * Queries DAPM graph as to whether an valid audio stream path exists for | ||
938 | * the initial stream specified by name. This takes into account | ||
939 | * current mixer and mux kcontrol settings. Creates list of valid widgets. | ||
940 | * | ||
941 | * Returns the number of valid paths or negative error. | ||
942 | */ | ||
943 | int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, | ||
944 | struct snd_soc_dapm_widget_list **list) | ||
945 | { | ||
946 | struct snd_soc_card *card = dai->card; | ||
947 | int paths; | ||
948 | |||
949 | mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); | ||
950 | dapm_reset(card); | ||
951 | |||
952 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
953 | paths = is_connected_output_ep(dai->playback_widget, list); | ||
954 | else | ||
955 | paths = is_connected_input_ep(dai->playback_widget, list); | ||
956 | |||
957 | trace_snd_soc_dapm_connected(paths, stream); | ||
958 | dapm_clear_walk(&card->dapm); | ||
959 | mutex_unlock(&card->dapm_mutex); | ||
960 | |||
961 | return paths; | ||
962 | } | ||
963 | |||
862 | /* | 964 | /* |
863 | * Handler for generic register modifier widget. | 965 | * Handler for generic register modifier widget. |
864 | */ | 966 | */ |
@@ -915,9 +1017,9 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) | |||
915 | 1017 | ||
916 | DAPM_UPDATE_STAT(w, power_checks); | 1018 | DAPM_UPDATE_STAT(w, power_checks); |
917 | 1019 | ||
918 | in = is_connected_input_ep(w); | 1020 | in = is_connected_input_ep(w, NULL); |
919 | dapm_clear_walk(w->dapm); | 1021 | dapm_clear_walk(w->dapm); |
920 | out = is_connected_output_ep(w); | 1022 | out = is_connected_output_ep(w, NULL); |
921 | dapm_clear_walk(w->dapm); | 1023 | dapm_clear_walk(w->dapm); |
922 | return out != 0 && in != 0; | 1024 | return out != 0 && in != 0; |
923 | } | 1025 | } |
@@ -940,7 +1042,7 @@ static int dapm_adc_check_power(struct snd_soc_dapm_widget *w) | |||
940 | DAPM_UPDATE_STAT(w, power_checks); | 1042 | DAPM_UPDATE_STAT(w, power_checks); |
941 | 1043 | ||
942 | if (w->active) { | 1044 | if (w->active) { |
943 | in = is_connected_input_ep(w); | 1045 | in = is_connected_input_ep(w, NULL); |
944 | dapm_clear_walk(w->dapm); | 1046 | dapm_clear_walk(w->dapm); |
945 | return in != 0; | 1047 | return in != 0; |
946 | } else { | 1048 | } else { |
@@ -956,7 +1058,7 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w) | |||
956 | DAPM_UPDATE_STAT(w, power_checks); | 1058 | DAPM_UPDATE_STAT(w, power_checks); |
957 | 1059 | ||
958 | if (w->active) { | 1060 | if (w->active) { |
959 | out = is_connected_output_ep(w); | 1061 | out = is_connected_output_ep(w, NULL); |
960 | dapm_clear_walk(w->dapm); | 1062 | dapm_clear_walk(w->dapm); |
961 | return out != 0; | 1063 | return out != 0; |
962 | } else { | 1064 | } else { |
@@ -1558,9 +1660,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file, | |||
1558 | if (!buf) | 1660 | if (!buf) |
1559 | return -ENOMEM; | 1661 | return -ENOMEM; |
1560 | 1662 | ||
1561 | in = is_connected_input_ep(w); | 1663 | in = is_connected_input_ep(w, NULL); |
1562 | dapm_clear_walk(w->dapm); | 1664 | dapm_clear_walk(w->dapm); |
1563 | out = is_connected_output_ep(w); | 1665 | out = is_connected_output_ep(w, NULL); |
1564 | dapm_clear_walk(w->dapm); | 1666 | dapm_clear_walk(w->dapm); |
1565 | 1667 | ||
1566 | ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", | 1668 | ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", |