Browse Source

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>
Piotr Stankiewicz 9 years ago
parent
commit
6742064aef
3 changed files with 51 additions and 15 deletions
  1. 4 1
      include/sound/soc-dapm.h
  2. 45 13
      sound/soc/soc-dapm.c
  3. 2 1
      sound/soc/soc-pcm.c

+ 4 - 1
include/sound/soc-dapm.h

@@ -358,6 +358,7 @@ struct snd_soc_dapm_context;
 struct regulator;
 struct regulator;
 struct snd_soc_dapm_widget_list;
 struct snd_soc_dapm_widget_list;
 struct snd_soc_dapm_update;
 struct snd_soc_dapm_update;
+enum snd_soc_dapm_direction;
 
 
 int dapm_regulator_event(struct snd_soc_dapm_widget *w,
 int dapm_regulator_event(struct snd_soc_dapm_widget *w,
 			 struct snd_kcontrol *kcontrol, int event);
 			 struct snd_kcontrol *kcontrol, int event);
@@ -451,7 +452,9 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card);
 
 
 /* dapm path query */
 /* dapm path query */
 int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
 int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
-	struct snd_soc_dapm_widget_list **list);
+	struct snd_soc_dapm_widget_list **list,
+	bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
+				      enum snd_soc_dapm_direction));
 
 
 struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
 struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
 	struct snd_kcontrol *kcontrol);
 	struct snd_kcontrol *kcontrol);

+ 45 - 13
sound/soc/soc-dapm.c

@@ -1073,7 +1073,11 @@ static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list,
  */
  */
 static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
 static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
 	struct list_head *list, enum snd_soc_dapm_direction dir,
 	struct list_head *list, enum snd_soc_dapm_direction dir,
-	int (*fn)(struct snd_soc_dapm_widget *, struct list_head *))
+	int (*fn)(struct snd_soc_dapm_widget *, struct list_head *,
+		  bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
+						enum snd_soc_dapm_direction)),
+	bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
+				      enum snd_soc_dapm_direction))
 {
 {
 	enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
 	enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
 	struct snd_soc_dapm_path *path;
 	struct snd_soc_dapm_path *path;
@@ -1088,6 +1092,9 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
 	if (list)
 	if (list)
 		list_add_tail(&widget->work_list, list);
 		list_add_tail(&widget->work_list, list);
 
 
+	if (custom_stop_condition && custom_stop_condition(widget, dir))
+		return con;
+
 	if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) {
 	if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) {
 		widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget);
 		widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget);
 		return widget->endpoints[dir];
 		return widget->endpoints[dir];
@@ -1106,7 +1113,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
 
 
 		if (path->connect) {
 		if (path->connect) {
 			path->walking = 1;
 			path->walking = 1;
-			con += fn(path->node[dir], list);
+			con += fn(path->node[dir], list, custom_stop_condition);
 			path->walking = 0;
 			path->walking = 0;
 		}
 		}
 	}
 	}
@@ -1119,23 +1126,37 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
 /*
 /*
  * Recursively check for a completed path to an active or physically connected
  * Recursively check for a completed path to an active or physically connected
  * output widget. Returns number of complete paths.
  * output widget. Returns number of complete paths.
+ *
+ * Optionally, can be supplied with a function acting as a stopping condition.
+ * This function takes the dapm widget currently being examined and the walk
+ * direction as an arguments, it should return true if the walk should be
+ * stopped and false otherwise.
  */
  */
 static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
 static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
-	struct list_head *list)
+	struct list_head *list,
+	bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,
+				      enum snd_soc_dapm_direction))
 {
 {
 	return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT,
 	return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT,
-			is_connected_output_ep);
+			is_connected_output_ep, custom_stop_condition);
 }
 }
 
 
 /*
 /*
  * Recursively check for a completed path to an active or physically connected
  * Recursively check for a completed path to an active or physically connected
  * input widget. Returns number of complete paths.
  * input widget. Returns number of complete paths.
+ *
+ * Optionally, can be supplied with a function acting as a stopping condition.
+ * This function takes the dapm widget currently being examined and the walk
+ * direction as an arguments, it should return true if the walk should be
+ * stopped and false otherwise.
  */
  */
 static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
 static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
-	struct list_head *list)
+	struct list_head *list,
+	bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,
+				      enum snd_soc_dapm_direction))
 {
 {
 	return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN,
 	return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN,
-			is_connected_input_ep);
+			is_connected_input_ep, custom_stop_condition);
 }
 }
 
 
 /**
 /**
@@ -1143,15 +1164,24 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
  * @dai: the soc DAI.
  * @dai: the soc DAI.
  * @stream: stream direction.
  * @stream: stream direction.
  * @list: list of active widgets for this stream.
  * @list: list of active widgets for this stream.
+ * @custom_stop_condition: (optional) a function meant to stop the widget graph
+ *                         walk based on custom logic.
  *
  *
  * Queries DAPM graph as to whether an valid audio stream path exists for
  * Queries DAPM graph as to whether an valid audio stream path exists for
  * the initial stream specified by name. This takes into account
  * the initial stream specified by name. This takes into account
  * current mixer and mux kcontrol settings. Creates list of valid widgets.
  * current mixer and mux kcontrol settings. Creates list of valid widgets.
  *
  *
+ * Optionally, can be supplied with a function acting as a stopping condition.
+ * This function takes the dapm widget currently being examined and the walk
+ * direction as an arguments, it should return true if the walk should be
+ * stopped and false otherwise.
+ *
  * Returns the number of valid paths or negative error.
  * Returns the number of valid paths or negative error.
  */
  */
 int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
 int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
-	struct snd_soc_dapm_widget_list **list)
+	struct snd_soc_dapm_widget_list **list,
+	bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
+				      enum snd_soc_dapm_direction))
 {
 {
 	struct snd_soc_card *card = dai->component->card;
 	struct snd_soc_card *card = dai->component->card;
 	struct snd_soc_dapm_widget *w;
 	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,
 	}
 	}
 
 
 	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
 	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-		paths = is_connected_output_ep(dai->playback_widget, &widgets);
+		paths = is_connected_output_ep(dai->playback_widget, &widgets,
+				custom_stop_condition);
 	else
 	else
-		paths = is_connected_input_ep(dai->capture_widget, &widgets);
+		paths = is_connected_input_ep(dai->capture_widget, &widgets,
+				custom_stop_condition);
 
 
 	/* Drop starting point */
 	/* Drop starting point */
 	list_del(widgets.next);
 	list_del(widgets.next);
@@ -1268,8 +1300,8 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
 
 
 	DAPM_UPDATE_STAT(w, power_checks);
 	DAPM_UPDATE_STAT(w, power_checks);
 
 
-	in = is_connected_input_ep(w, NULL);
-	out = is_connected_output_ep(w, NULL);
+	in = is_connected_input_ep(w, NULL, NULL);
+	out = is_connected_output_ep(w, NULL, NULL);
 	return out != 0 && in != 0;
 	return out != 0 && in != 0;
 }
 }
 
 
@@ -1928,8 +1960,8 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
 		in = 0;
 		in = 0;
 		out = 0;
 		out = 0;
 	} else {
 	} else {
-		in = is_connected_input_ep(w, NULL);
-		out = is_connected_output_ep(w, NULL);
+		in = is_connected_input_ep(w, NULL, NULL);
+		out = is_connected_output_ep(w, NULL, NULL);
 	}
 	}
 
 
 	ret = snprintf(buf, PAGE_SIZE, "%s: %s%s  in %d out %d",
 	ret = snprintf(buf, PAGE_SIZE, "%s: %s%s  in %d out %d",

+ 2 - 1
sound/soc/soc-pcm.c

@@ -1294,7 +1294,8 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
 	int paths;
 	int paths;
 
 
 	/* get number of valid DAI paths and their widgets */
 	/* get number of valid DAI paths and their widgets */
-	paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list);
+	paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list,
+			NULL);
 
 
 	dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
 	dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
 			stream ? "capture" : "playback");
 			stream ? "capture" : "playback");