aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPiotr Stankiewicz <piotrs@opensource.wolfsonmicro.com>2016-05-13 12:03:55 -0400
committerMark Brown <broonie@kernel.org>2016-05-30 11:15:10 -0400
commit6742064aef7f1fba8e68d30b2e726918a5d66790 (patch)
tree3123c0ad039f1f74bade193c38485ff7afe7eb28
parent1a695a905c18548062509178b98bc91e67510864 (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.h5
-rw-r--r--sound/soc/soc-dapm.c58
-rw-r--r--sound/soc/soc-pcm.c3
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;
358struct regulator; 358struct regulator;
359struct snd_soc_dapm_widget_list; 359struct snd_soc_dapm_widget_list;
360struct snd_soc_dapm_update; 360struct snd_soc_dapm_update;
361enum snd_soc_dapm_direction;
361 362
362int dapm_regulator_event(struct snd_soc_dapm_widget *w, 363int 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 */
453int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, 454int 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
456struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( 459struct 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 */
1074static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, 1074static __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 */
1123static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, 1135static 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 */
1134static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, 1153static 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 */
1153int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, 1181int 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");