diff options
author | Piotr Stankiewicz <piotrs@opensource.wolfsonmicro.com> | 2016-05-13 12:03:55 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-05-30 11:15:10 -0400 |
commit | 6742064aef7f1fba8e68d30b2e726918a5d66790 (patch) | |
tree | 3123c0ad039f1f74bade193c38485ff7afe7eb28 | |
parent | 1a695a905c18548062509178b98bc91e67510864 (diff) |
ASoC: dapm: support user-defined stop condition in dai_get_connected_widgets
Certain situations may warrant examining DAPM paths only to a certain
arbitrary point, as opposed to always following them to the end. For
instance, when establishing a connection between a front-end DAI link
and a back-end DAI link in a DPCM path, it does not make sense to walk
the DAPM graph beyond the first widget associated with a back-end link.
This patch introduces a mechanism which lets a user of
dai_get_connected_widgets supply a function which will be called for
every node during the graph walk. When invoked, this function can
execute arbitrary logic to decide whether the walk, given a DAPM widget
and walk direction, should be terminated at that point or continued
as normal.
Signed-off-by: Piotr Stankiewicz <piotrs@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | include/sound/soc-dapm.h | 5 | ||||
-rw-r--r-- | sound/soc/soc-dapm.c | 58 | ||||
-rw-r--r-- | sound/soc/soc-pcm.c | 3 |
3 files changed, 51 insertions, 15 deletions
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3101d53468aa..ca77db443499 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h | |||
@@ -358,6 +358,7 @@ struct snd_soc_dapm_context; | |||
358 | struct regulator; | 358 | struct regulator; |
359 | struct snd_soc_dapm_widget_list; | 359 | struct snd_soc_dapm_widget_list; |
360 | struct snd_soc_dapm_update; | 360 | struct snd_soc_dapm_update; |
361 | enum snd_soc_dapm_direction; | ||
361 | 362 | ||
362 | int dapm_regulator_event(struct snd_soc_dapm_widget *w, | 363 | int dapm_regulator_event(struct snd_soc_dapm_widget *w, |
363 | struct snd_kcontrol *kcontrol, int event); | 364 | struct snd_kcontrol *kcontrol, int event); |
@@ -451,7 +452,9 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card); | |||
451 | 452 | ||
452 | /* dapm path query */ | 453 | /* dapm path query */ |
453 | int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, | 454 | int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, |
454 | struct snd_soc_dapm_widget_list **list); | 455 | struct snd_soc_dapm_widget_list **list, |
456 | bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, | ||
457 | enum snd_soc_dapm_direction)); | ||
455 | 458 | ||
456 | struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( | 459 | struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( |
457 | struct snd_kcontrol *kcontrol); | 460 | struct snd_kcontrol *kcontrol); |
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c4464858bf01..db781f6faaec 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -1073,7 +1073,11 @@ static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list, | |||
1073 | */ | 1073 | */ |
1074 | static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, | 1074 | static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, |
1075 | struct list_head *list, enum snd_soc_dapm_direction dir, | 1075 | struct list_head *list, enum snd_soc_dapm_direction dir, |
1076 | int (*fn)(struct snd_soc_dapm_widget *, struct list_head *)) | 1076 | int (*fn)(struct snd_soc_dapm_widget *, struct list_head *, |
1077 | bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, | ||
1078 | enum snd_soc_dapm_direction)), | ||
1079 | bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, | ||
1080 | enum snd_soc_dapm_direction)) | ||
1077 | { | 1081 | { |
1078 | enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); | 1082 | enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); |
1079 | struct snd_soc_dapm_path *path; | 1083 | struct snd_soc_dapm_path *path; |
@@ -1088,6 +1092,9 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, | |||
1088 | if (list) | 1092 | if (list) |
1089 | list_add_tail(&widget->work_list, list); | 1093 | list_add_tail(&widget->work_list, list); |
1090 | 1094 | ||
1095 | if (custom_stop_condition && custom_stop_condition(widget, dir)) | ||
1096 | return con; | ||
1097 | |||
1091 | if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) { | 1098 | if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) { |
1092 | widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget); | 1099 | widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget); |
1093 | return widget->endpoints[dir]; | 1100 | return widget->endpoints[dir]; |
@@ -1106,7 +1113,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, | |||
1106 | 1113 | ||
1107 | if (path->connect) { | 1114 | if (path->connect) { |
1108 | path->walking = 1; | 1115 | path->walking = 1; |
1109 | con += fn(path->node[dir], list); | 1116 | con += fn(path->node[dir], list, custom_stop_condition); |
1110 | path->walking = 0; | 1117 | path->walking = 0; |
1111 | } | 1118 | } |
1112 | } | 1119 | } |
@@ -1119,23 +1126,37 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, | |||
1119 | /* | 1126 | /* |
1120 | * Recursively check for a completed path to an active or physically connected | 1127 | * Recursively check for a completed path to an active or physically connected |
1121 | * output widget. Returns number of complete paths. | 1128 | * output widget. Returns number of complete paths. |
1129 | * | ||
1130 | * Optionally, can be supplied with a function acting as a stopping condition. | ||
1131 | * This function takes the dapm widget currently being examined and the walk | ||
1132 | * direction as an arguments, it should return true if the walk should be | ||
1133 | * stopped and false otherwise. | ||
1122 | */ | 1134 | */ |
1123 | static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, | 1135 | static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, |
1124 | struct list_head *list) | 1136 | struct list_head *list, |
1137 | bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i, | ||
1138 | enum snd_soc_dapm_direction)) | ||
1125 | { | 1139 | { |
1126 | return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT, | 1140 | return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT, |
1127 | is_connected_output_ep); | 1141 | is_connected_output_ep, custom_stop_condition); |
1128 | } | 1142 | } |
1129 | 1143 | ||
1130 | /* | 1144 | /* |
1131 | * Recursively check for a completed path to an active or physically connected | 1145 | * Recursively check for a completed path to an active or physically connected |
1132 | * input widget. Returns number of complete paths. | 1146 | * input widget. Returns number of complete paths. |
1147 | * | ||
1148 | * Optionally, can be supplied with a function acting as a stopping condition. | ||
1149 | * This function takes the dapm widget currently being examined and the walk | ||
1150 | * direction as an arguments, it should return true if the walk should be | ||
1151 | * stopped and false otherwise. | ||
1133 | */ | 1152 | */ |
1134 | static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, | 1153 | static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, |
1135 | struct list_head *list) | 1154 | struct list_head *list, |
1155 | bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i, | ||
1156 | enum snd_soc_dapm_direction)) | ||
1136 | { | 1157 | { |
1137 | return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN, | 1158 | return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN, |
1138 | is_connected_input_ep); | 1159 | is_connected_input_ep, custom_stop_condition); |
1139 | } | 1160 | } |
1140 | 1161 | ||
1141 | /** | 1162 | /** |
@@ -1143,15 +1164,24 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, | |||
1143 | * @dai: the soc DAI. | 1164 | * @dai: the soc DAI. |
1144 | * @stream: stream direction. | 1165 | * @stream: stream direction. |
1145 | * @list: list of active widgets for this stream. | 1166 | * @list: list of active widgets for this stream. |
1167 | * @custom_stop_condition: (optional) a function meant to stop the widget graph | ||
1168 | * walk based on custom logic. | ||
1146 | * | 1169 | * |
1147 | * Queries DAPM graph as to whether an valid audio stream path exists for | 1170 | * Queries DAPM graph as to whether an valid audio stream path exists for |
1148 | * the initial stream specified by name. This takes into account | 1171 | * the initial stream specified by name. This takes into account |
1149 | * current mixer and mux kcontrol settings. Creates list of valid widgets. | 1172 | * current mixer and mux kcontrol settings. Creates list of valid widgets. |
1150 | * | 1173 | * |
1174 | * Optionally, can be supplied with a function acting as a stopping condition. | ||
1175 | * This function takes the dapm widget currently being examined and the walk | ||
1176 | * direction as an arguments, it should return true if the walk should be | ||
1177 | * stopped and false otherwise. | ||
1178 | * | ||
1151 | * Returns the number of valid paths or negative error. | 1179 | * Returns the number of valid paths or negative error. |
1152 | */ | 1180 | */ |
1153 | int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, | 1181 | int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, |
1154 | struct snd_soc_dapm_widget_list **list) | 1182 | struct snd_soc_dapm_widget_list **list, |
1183 | bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, | ||
1184 | enum snd_soc_dapm_direction)) | ||
1155 | { | 1185 | { |
1156 | struct snd_soc_card *card = dai->component->card; | 1186 | struct snd_soc_card *card = dai->component->card; |
1157 | struct snd_soc_dapm_widget *w; | 1187 | struct snd_soc_dapm_widget *w; |
@@ -1171,9 +1201,11 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, | |||
1171 | } | 1201 | } |
1172 | 1202 | ||
1173 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) | 1203 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) |
1174 | paths = is_connected_output_ep(dai->playback_widget, &widgets); | 1204 | paths = is_connected_output_ep(dai->playback_widget, &widgets, |
1205 | custom_stop_condition); | ||
1175 | else | 1206 | else |
1176 | paths = is_connected_input_ep(dai->capture_widget, &widgets); | 1207 | paths = is_connected_input_ep(dai->capture_widget, &widgets, |
1208 | custom_stop_condition); | ||
1177 | 1209 | ||
1178 | /* Drop starting point */ | 1210 | /* Drop starting point */ |
1179 | list_del(widgets.next); | 1211 | list_del(widgets.next); |
@@ -1268,8 +1300,8 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) | |||
1268 | 1300 | ||
1269 | DAPM_UPDATE_STAT(w, power_checks); | 1301 | DAPM_UPDATE_STAT(w, power_checks); |
1270 | 1302 | ||
1271 | in = is_connected_input_ep(w, NULL); | 1303 | in = is_connected_input_ep(w, NULL, NULL); |
1272 | out = is_connected_output_ep(w, NULL); | 1304 | out = is_connected_output_ep(w, NULL, NULL); |
1273 | return out != 0 && in != 0; | 1305 | return out != 0 && in != 0; |
1274 | } | 1306 | } |
1275 | 1307 | ||
@@ -1928,8 +1960,8 @@ static ssize_t dapm_widget_power_read_file(struct file *file, | |||
1928 | in = 0; | 1960 | in = 0; |
1929 | out = 0; | 1961 | out = 0; |
1930 | } else { | 1962 | } else { |
1931 | in = is_connected_input_ep(w, NULL); | 1963 | in = is_connected_input_ep(w, NULL, NULL); |
1932 | out = is_connected_output_ep(w, NULL); | 1964 | out = is_connected_output_ep(w, NULL, NULL); |
1933 | } | 1965 | } |
1934 | 1966 | ||
1935 | ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", | 1967 | ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d", |
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index aa99dac31b3b..c2b0aa82f3f1 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c | |||
@@ -1294,7 +1294,8 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe, | |||
1294 | int paths; | 1294 | int paths; |
1295 | 1295 | ||
1296 | /* get number of valid DAI paths and their widgets */ | 1296 | /* get number of valid DAI paths and their widgets */ |
1297 | paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list); | 1297 | paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list, |
1298 | NULL); | ||
1298 | 1299 | ||
1299 | dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths, | 1300 | dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths, |
1300 | stream ? "capture" : "playback"); | 1301 | stream ? "capture" : "playback"); |