diff options
author | Liam Girdwood <lrg@ti.com> | 2011-01-31 20:33:07 +0000 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2011-06-14 09:06:12 -0700 |
commit | 8895ae53fc50078a23a8cd92c847c44954f32ed1 (patch) | |
tree | 45252b8f0108331cd4b9d9439ae3bcb2963179c9 | |
parent | 7bc2b53d27569cc334d15b26e94638644b0ea89a (diff) | |
download | kernel_samsung_espresso10-8895ae53fc50078a23a8cd92c847c44954f32ed1.zip kernel_samsung_espresso10-8895ae53fc50078a23a8cd92c847c44954f32ed1.tar.gz kernel_samsung_espresso10-8895ae53fc50078a23a8cd92c847c44954f32ed1.tar.bz2 |
ASoC: dapm - Add API call to query valid DAPM paths
In preparation for ASoC DSP support.
Add a DAPM API call to determine whether a DAPM audio path is valid between
source and sink widgets. This also takes into account all kcontrol mux and mixer
settings in between the source and sink widgets to validate the audio path.
This will be used by the DSP core to determine the runtime DAI mappings
between FE and BE DAIs in order to run PCM operations.
Signed-off-by: Liam Girdwood <lrg@ti.com>
-rw-r--r-- | include/sound/soc-dapm.h | 16 | ||||
-rw-r--r-- | sound/soc/soc-dapm.c | 332 |
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; } |