diff options
-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; } |