aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sound/soc-dapm.h16
-rw-r--r--sound/soc/soc-dapm.c332
2 files changed, 337 insertions, 11 deletions
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 4321420..7800e79 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -310,6 +310,7 @@ struct snd_soc_dapm_path;
struct snd_soc_dapm_pin;
struct snd_soc_dapm_route;
struct snd_soc_dapm_context;
+struct snd_soc_dapm_widget_list;
int dapm_reg_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
@@ -348,6 +349,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm);
void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num);
+int snd_soc_dapm_query_path(struct snd_soc_dapm_context *dapm,
+ const char *source_name, const char *sink_name, int stream);
const char *snd_soc_dapm_get_aif(struct snd_soc_dapm_context *dapm,
const char *stream_name, enum snd_soc_dapm_type type);
@@ -375,6 +378,13 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
const char *pin);
+/* dapm path query */
+int snd_soc_dapm_get_connected_widgets_type(struct snd_soc_dapm_context *dapm,
+ const char *stream_name, struct snd_soc_dapm_widget_list **list,
+ int stream, enum snd_soc_dapm_type type);
+int snd_soc_dapm_get_connected_widgets_name(struct snd_soc_dapm_context *dapm,
+ const char *name, struct snd_soc_dapm_widget_list **list, int stream);
+
/* dapm widget types */
enum snd_soc_dapm_type {
snd_soc_dapm_input = 0, /* input pin */
@@ -431,6 +441,7 @@ struct snd_soc_dapm_path {
/* status */
u32 connect:1; /* source and sink widgets are connected */
u32 walked:1; /* path has been walked */
+ u32 length:6; /* path length - used by route mapper */
int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink);
@@ -455,6 +466,8 @@ struct snd_soc_dapm_widget {
unsigned char shift; /* bits to shift */
unsigned int saved_value; /* widget saved value */
unsigned int value; /* widget current value */
+ unsigned int path_idx;
+ unsigned int hops;
unsigned int mask; /* non-shifted mask */
unsigned int on_val; /* on state value */
unsigned int off_val; /* off state value */
@@ -510,12 +523,15 @@ struct snd_soc_dapm_context {
struct device *dev; /* from parent - for debug */
struct snd_soc_codec *codec; /* parent codec */
+ struct snd_soc_platform *platform; /*parent platform */
struct snd_soc_card *card; /* parent card */
/* used during DAPM updates */
int dev_power;
struct list_head list;
+ int num_valid_paths;
+
int (*stream_event)(struct snd_soc_dapm_context *dapm);
#ifdef CONFIG_DEBUG_FS
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 3d1880d..3848d6e 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -48,6 +48,8 @@
#include <trace/events/asoc.h>
+#define PATH_MAX_HOPS 16
+
/* dapm power sequences - make this per codec in the future */
static int dapm_up_seq[] = {
[snd_soc_dapm_pre] = 0,
@@ -191,6 +193,322 @@ int soc_widget_test_bits(struct snd_soc_dapm_widget *w, unsigned short reg,
return change;
}
+/* reset 'walked' bit for each dapm path */
+static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm)
+{
+ struct snd_soc_dapm_path *p;
+
+ list_for_each_entry(p, &dapm->card->paths, list)
+ p->walked = 0;
+}
+
+static void dapm_clear_paths(struct snd_soc_dapm_context *dapm)
+{
+ struct snd_soc_dapm_path *p;
+ struct snd_soc_dapm_widget *w;
+ struct list_head *l;
+
+ list_for_each(l, &dapm->paths) {
+ p = list_entry(l, struct snd_soc_dapm_path, list);
+ p->length = 0;
+ }
+ list_for_each(l, &dapm->card->widgets) {
+ w = list_entry(l, struct snd_soc_dapm_widget, list);
+ w->hops = 0;
+ }
+ dapm_clear_walk(dapm);
+}
+
+static int dapm_add_unique_widget(struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dapm_widget_list **list, struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_widget_list *wlist;
+ int wlistsize, wlistentries, i;
+
+ /* is the list empty ? */
+ if (*list == NULL) {
+
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+ sizeof(struct snd_soc_dapm_widget *);
+ *list = kzalloc(wlistsize, GFP_KERNEL);
+ if (*list == NULL) {
+ dev_err(dapm->dev, "can't allocate widget list for %s\n", w->name);
+ return -ENOMEM;
+ }
+ } else {
+
+ wlist = *list;
+ /* is this widget already in the list */
+ for (i = 0; i < wlist->num_widgets; i++) {
+ if (wlist->widgets[i] == w)
+ return 0;
+ }
+
+ wlistentries = wlist->num_widgets + 1;
+ wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+ wlistentries * sizeof(struct snd_soc_dapm_widget *);
+ *list = krealloc(wlist, wlistsize, GFP_KERNEL);
+ if (*list == NULL) {
+ dev_err(dapm->dev, "can't allocate widget list for %s\n", w->name);
+ return -ENOMEM;
+ }
+ }
+ wlist = *list;
+
+ /* insert the widget */
+ dev_dbg(dapm->dev, "added %s in widget list pos %d\n",
+ w->name, wlist->num_widgets);
+ wlist->widgets[wlist->num_widgets] = w;
+ wlist->num_widgets++;
+ return 1;
+}
+
+static int is_output_widget_ep(struct snd_soc_dapm_widget *widget)
+{
+ switch (widget->id) {
+ case snd_soc_dapm_adc:
+ case snd_soc_dapm_aif_out:
+ return 1;
+ case snd_soc_dapm_output:
+ if (widget->connected && !widget->ext)
+ return 1;
+ else
+ return 0;
+ case snd_soc_dapm_hp:
+ case snd_soc_dapm_spk:
+ case snd_soc_dapm_line:
+ return !list_empty(&widget->sources);
+ default:
+ return 0;
+ }
+}
+
+static int is_input_widget_ep(struct snd_soc_dapm_widget *widget)
+{
+ switch (widget->id) {
+ case snd_soc_dapm_dac:
+ case snd_soc_dapm_aif_in:
+ return 1;
+ case snd_soc_dapm_input:
+ if (widget->connected && !widget->ext)
+ return 1;
+ else
+ return 0;
+ case snd_soc_dapm_mic:
+ return !list_empty(&widget->sources);
+ default:
+ return 0;
+ }
+}
+
+/*
+ * find all the paths between source and sink
+ */
+static int dapm_find_playback_paths(struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dapm_widget *root,
+ struct snd_soc_dapm_widget_list **list, int hops)
+{
+ struct list_head *lp;
+ struct snd_soc_dapm_path *path;
+ int dist = 0;
+
+ if (hops > PATH_MAX_HOPS)
+ return 0;
+
+ if (is_output_widget_ep(root) && hops != 1) {
+ dev_dbg(dapm->dev," ! %d: valid playback route found\n", hops);
+ dapm->num_valid_paths++;
+ return 1;
+ }
+
+ if (root->hops && root->hops <= hops)
+ return 0;
+ root->hops = hops;
+
+ /* check all the output paths on this source widget by walking
+ * from source to sink */
+ list_for_each(lp, &root->sinks) {
+ path = list_entry(lp, struct snd_soc_dapm_path, list_source);
+
+ dev_dbg(dapm->dev," %c %d: %s -> %s -> %s\n",
+ path->connect ? '*' : ' ', hops,
+ root->name, path->name, path->sink->name);
+
+ /* been here before ? */
+ if (path->length && path->length <= hops)
+ continue;
+
+ /* check down the next path if connected */
+ if (path->sink && path->connect &&
+ dapm_find_playback_paths(dapm, path->sink, list, hops + 1)) {
+ path->length = hops;
+
+ /* add widget to list */
+ dapm_add_unique_widget(dapm, list, path->sink);
+
+ if (!dist || dist > path->length)
+ dist = path->length;
+ }
+ }
+
+ return dist;
+}
+
+static int dapm_find_capture_paths(struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dapm_widget *root,
+ struct snd_soc_dapm_widget_list **list, int hops)
+{
+ struct list_head *lp;
+ struct snd_soc_dapm_path *path;
+ int dist = 0;
+
+ if (hops > PATH_MAX_HOPS)
+ return 0;
+
+ if (is_input_widget_ep(root) && hops != 1) {
+ dev_dbg(dapm->dev," ! %d: valid capture route found\n", hops);
+ dapm->num_valid_paths++;
+ return 1;
+ }
+
+ if (root->hops && root->hops <= hops)
+ return 0;
+ root->hops = hops;
+
+ /* check all the output paths on this source widget by walking from
+ * sink to source */
+ list_for_each(lp, &root->sources) {
+ path = list_entry(lp, struct snd_soc_dapm_path, list_sink);
+
+ dev_dbg(dapm->dev," %c %d: %s <- %s <- %s\n",
+ path->connect ? '*' : ' ', hops,
+ root->name, path->name, path->source->name);
+
+ /* been here before ? */
+ if (path->length && path->length <= hops)
+ continue;
+
+ /* check down the next path if connected */
+ if (path->source && path->connect &&
+ dapm_find_capture_paths(dapm, path->source, list, hops + 1)) {
+ path->length = hops;
+
+ /* add widget to list */
+ dapm_add_unique_widget(dapm, list, path->source);
+
+ if (!dist || dist > path->length)
+ dist = path->length;
+ }
+ }
+
+ return dist;
+}
+
+/*
+ * traverse the tree from sink to source via the shortest path
+ */
+static int dapm_get_playback_paths(struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dapm_widget *root,
+ struct snd_soc_dapm_widget_list **list)
+{
+ dev_dbg(dapm->dev, "Playback: checking paths from %s\n",root->name);
+ dapm_find_playback_paths(dapm, root, list, 1);
+ return dapm->num_valid_paths;
+}
+
+static int dapm_get_capture_paths(struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dapm_widget *root,
+ struct snd_soc_dapm_widget_list **list)
+{
+ dev_dbg(dapm->dev, "Capture: checking paths to %s\n", root->name);
+ dapm_find_capture_paths(dapm, root, list, 1);
+ return dapm->num_valid_paths;
+}
+
+/**
+ * snd_soc_dapm_get_connected_widgets_type - query audio path and it's widgets.
+ * @dapm: the dapm context.
+ * @stream_name: stream name.
+ * @list: list of active widgets for this stream.
+ * @stream: stream direction.
+ * @type: Initial widget type.
+ *
+ * Queries DAPM graph as to whether an valid audio stream path exists for
+ * the DAPM stream and initial widget type specified. This takes into account
+ * current mixer and mux kcontrol settings. Creates list of valid widgets.
+ *
+ * Returns the number of valid paths or negative error.
+ */
+int snd_soc_dapm_get_connected_widgets_type(struct snd_soc_dapm_context *dapm,
+ const char *stream_name, struct snd_soc_dapm_widget_list **list,
+ int stream, enum snd_soc_dapm_type type)
+{
+ struct snd_soc_dapm_widget *w;
+ int paths;
+
+ /* get stream root widget AIF, DAC or ADC from stream string and direction */
+ list_for_each_entry(w, &dapm->card->widgets, list) {
+
+ if (!w->sname)
+ continue;
+
+ if (w->id != type)
+ continue;
+
+ if (strstr(w->sname, stream_name))
+ goto found;
+ }
+ dev_err(dapm->dev, "root widget not found\n");
+ return 0;
+
+found:
+ dapm->num_valid_paths = 0;
+ *list = NULL;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ paths = dapm_get_playback_paths(dapm, w, list);
+ else
+ paths = dapm_get_capture_paths(dapm, w, list);
+
+ dapm_clear_paths(dapm);
+ return paths;
+}
+/**
+ * snd_soc_dapm_get_connected_widgets_name - query audio path and it's widgets.
+ * @dapm: the dapm context.
+ * @name: initial widget name.
+ * @list: list of active widgets for this stream.
+ * @stream: stream direction.
+ *
+ * Queries DAPM graph as to whether an valid audio stream path exists for
+ * the initial widget specified by name. This takes into account
+ * current mixer and mux kcontrol settings. Creates list of valid widgets.
+ *
+ * Returns the number of valid paths or negative error.
+ */
+int snd_soc_dapm_get_connected_widgets_name(struct snd_soc_dapm_context *dapm,
+ const char *name, struct snd_soc_dapm_widget_list **list, int stream)
+{
+ struct snd_soc_dapm_widget *w;
+ int paths;
+
+ /* get stream root widget AIF, DAC or ADC from stream string and direction */
+ list_for_each_entry(w, &dapm->card->widgets, list) {
+
+ if (strstr(w->name, name))
+ goto found;
+ }
+ dev_err(dapm->dev, "root widget %s not found\n", name);
+ return 0;
+
+found:
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ paths = dapm_get_playback_paths(dapm, w, list);
+ else
+ paths = dapm_get_capture_paths(dapm, w, list);
+
+ dapm_clear_paths(dapm);
+ return paths;
+}
/**
* snd_soc_dapm_set_bias_level - set the bias level for the system
@@ -612,15 +930,6 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w)
return 0;
}
-/* reset 'walked' bit for each dapm path */
-static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm)
-{
- struct snd_soc_dapm_path *p;
-
- list_for_each_entry(p, &dapm->card->paths, list)
- p->walked = 0;
-}
-
/* We implement power down on suspend by checking the power state of
* the ALSA card - when we are suspending the ALSA state for the card
* is set to D3.
@@ -1544,7 +1853,7 @@ static ssize_t widget_show(struct snd_soc_dapm_context *dapm,
count += sprintf(buf + count, "\n%s\n", name);
- list_for_each_entry(w, &dapm->widgets, list) {
+ list_for_each_entry(w, &dapm->card->widgets, list) {
/* only display widgets that burnm power */
switch (w->id) {
@@ -1972,13 +2281,14 @@ const char *snd_soc_dapm_get_aif(struct snd_soc_dapm_context *dapm,
{
struct snd_soc_dapm_widget *w;
- list_for_each_entry(w, &dapm->widgets, list) {
+ list_for_each_entry(w, &dapm->card->widgets, list) {
if (!w->sname)
continue;
if (w->id == type && strstr(w->sname, stream_name))
return w->name;
+
}
return NULL;
}