[alsa-devel] [RFC_ii/iv 1/3] ASoC: Move DAPM paths from DAPM context to snd_soc_card
Decoupling DAPM paths from DAPM context is a first prerequisite when extending ASoC core to cross-device paths. This patch is almost a nullop and does not allow to construct cross-device setup but the path clean-up part in dapm_free_widgets is prepared to remove cross-device paths between a device being removed and others.
Signed-off-by: Jarkko Nikula jhnikula@gmail.com --- include/sound/soc-dapm.h | 2 +- include/sound/soc.h | 2 + sound/soc/codecs/tlv320aic3x.c | 2 +- sound/soc/soc-core.c | 3 +- sound/soc/soc-dapm.c | 41 ++++++++++++++++++++++++++------------- 5 files changed, 33 insertions(+), 17 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index c6dd14a..29b6808 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -466,7 +466,6 @@ struct snd_soc_dapm_widget { struct snd_soc_dapm_context { u32 pop_time; struct list_head widgets; - struct list_head paths; enum snd_soc_bias_level bias_level; enum snd_soc_bias_level suspend_bias_level; struct delayed_work delayed_work; @@ -474,6 +473,7 @@ struct snd_soc_dapm_context {
struct device *dev; /* from parent - for debug */ struct snd_soc_codec *codec; /* parent codec */ + struct snd_soc_card *card; /* parent card */ #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_dapm; #endif diff --git a/include/sound/soc.h b/include/sound/soc.h index 4bbd65f..4e3f0e5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -589,6 +589,8 @@ struct snd_soc_card { struct list_head codec_dev_list; struct list_head platform_dev_list; struct list_head dai_dev_list; + + struct list_head paths; };
/* SoC machine DAI configuration, glues a codec and cpu DAI together */ diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index d007e05..25b2517 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -183,7 +183,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) { /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->paths, list) { + list_for_each_entry(path, &widget->dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue;
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3c1c2ac..010728a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1406,6 +1406,7 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
/* probe the CODEC */ if (!codec->probed) { + codec->dapm->card = card; if (codec->driver->probe) { ret = codec->driver->probe(codec); if (ret < 0) { @@ -1662,6 +1663,7 @@ static int soc_probe(struct platform_device *pdev) INIT_LIST_HEAD(&card->dai_dev_list); INIT_LIST_HEAD(&card->codec_dev_list); INIT_LIST_HEAD(&card->platform_dev_list); + INIT_LIST_HEAD(&card->paths);
ret = snd_soc_register_card(card); if (ret != 0) { @@ -2964,7 +2966,6 @@ static struct snd_soc_dapm_context *soc_new_dapm_context(struct device *dev) dapm = kzalloc(sizeof(struct snd_soc_dapm_context), GFP_KERNEL); if (dapm) { INIT_LIST_HEAD(&dapm->widgets); - INIT_LIST_HEAD(&dapm->paths); dapm->bias_level = SND_SOC_BIAS_OFF; dapm->dev = dev; } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index cee0c96..62d3e0c 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -251,7 +251,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
for (i = 0; i < e->max; i++) { if (!(strcmp(control_name, e->texts[i]))) { - list_add(&path->list, &dapm->paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &dest->sources); list_add(&path->list_source, &src->sinks); path->name = (char*)e->texts[i]; @@ -273,7 +273,7 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, /* search for mixer kcontrol */ for (i = 0; i < dest->num_kcontrols; i++) { if (!strcmp(control_name, dest->kcontrols[i].name)) { - list_add(&path->list, &dapm->paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &dest->sources); list_add(&path->list_source, &src->sinks); path->name = dest->kcontrols[i].name; @@ -429,7 +429,7 @@ static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_path *p;
- list_for_each_entry(p, &dapm->paths, list) + list_for_each_entry(p, &dapm->card->paths, list) p->walked = 0; }
@@ -1133,7 +1133,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, return 0;
/* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->paths, list) { + list_for_each_entry(path, &widget->dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue;
@@ -1167,7 +1167,7 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, return -ENODEV;
/* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->paths, list) { + list_for_each_entry(path, &widget->dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue;
@@ -1257,14 +1257,27 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
list_for_each_entry_safe(w, next_w, &dapm->widgets, list) { list_del(&w->list); + /* + * remove source and sink paths associated to this widget. + * While removing the path, remove reference to it from both + * source and sink widgets so that path is removed only once. + */ + list_for_each_entry_safe(p, next_p, &w->sources, list_sink) { + list_del(&p->list_sink); + list_del(&p->list_source); + list_del(&p->list); + kfree(p->long_name); + kfree(p); + } + list_for_each_entry_safe(p, next_p, &w->sinks, list_source) { + list_del(&p->list_sink); + list_del(&p->list_source); + list_del(&p->list); + kfree(p->long_name); + kfree(p); + } kfree(w); } - - list_for_each_entry_safe(p, next_p, &dapm->paths, list) { - list_del(&p->list); - kfree(p->long_name); - kfree(p); - } }
static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, @@ -1357,7 +1370,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
/* connect static paths */ if (control == NULL) { - list_add(&path->list, &dapm->paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 1; @@ -1378,7 +1391,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_supply: case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: - list_add(&path->list, &dapm->paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 1; @@ -1401,7 +1414,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_mic: case snd_soc_dapm_line: case snd_soc_dapm_spk: - list_add(&path->list, &dapm->paths); + list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); path->connect = 0;
Decoupling widgets from DAPM context is required when extending the ASoC core to cross-device paths. Even the list of widgets are now kept in struct snd_soc_card, the widget listing in sysfs and debugs remain sorted per device.
This patch makes possible to build cross-device paths but does not extend yet the DAPM to handle codec bias and widget power changes of an another device.
Cross-device paths are registered by listing the widgets from device A in a map for device B. An example below shows a path that connects MONO out of A into Line In of B:
static const struct snd_soc_dapm_route mapA[] = { {"MONO", NULL, "DAC"}, };
static const struct snd_soc_dapm_route mapB[] = { {"Line In", NULL, "MONO"}, };
Signed-off-by: Jarkko Nikula jhnikula@gmail.com --- include/sound/soc-dapm.h | 2 +- include/sound/soc.h | 1 + sound/soc/codecs/wm8960.c | 4 ++- sound/soc/soc-core.c | 2 +- sound/soc/soc-dapm.c | 70 +++++++++++++++++++++++++++++++------------- 5 files changed, 55 insertions(+), 24 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 29b6808..7f20804 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -465,7 +465,7 @@ struct snd_soc_dapm_widget { /* DAPM context */ struct snd_soc_dapm_context { u32 pop_time; - struct list_head widgets; + int n_widgets; /* number of widgets in this context */ enum snd_soc_bias_level bias_level; enum snd_soc_bias_level suspend_bias_level; struct delayed_work delayed_work; diff --git a/include/sound/soc.h b/include/sound/soc.h index 4e3f0e5..b752c52 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -590,6 +590,7 @@ struct snd_soc_card { struct list_head platform_dev_list; struct list_head dai_dev_list;
+ struct list_head widgets; struct list_head paths; };
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 7c589f3..b25ed82 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -417,7 +417,9 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) * list each time to find the desired power state do so now * and save the result. */ - list_for_each_entry(w, &codec->dapm->widgets, list) { + list_for_each_entry(w, &codec->card->widgets, list) { + if (w->dapm != codec->dapm) + continue; if (strcmp(w->name, "LOUT1 PGA") == 0) wm8960->lout1 = w; if (strcmp(w->name, "ROUT1 PGA") == 0) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 010728a..571de78 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1663,6 +1663,7 @@ static int soc_probe(struct platform_device *pdev) INIT_LIST_HEAD(&card->dai_dev_list); INIT_LIST_HEAD(&card->codec_dev_list); INIT_LIST_HEAD(&card->platform_dev_list); + INIT_LIST_HEAD(&card->widgets); INIT_LIST_HEAD(&card->paths);
ret = snd_soc_register_card(card); @@ -2965,7 +2966,6 @@ static struct snd_soc_dapm_context *soc_new_dapm_context(struct device *dev)
dapm = kzalloc(sizeof(struct snd_soc_dapm_context), GFP_KERNEL); if (dapm) { - INIT_LIST_HEAD(&dapm->widgets); dapm->bias_level = SND_SOC_BIAS_OFF; dapm->dev = dev; } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 62d3e0c..fc1b943 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -906,7 +906,9 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) /* Check which widgets we need to power and store them in * lists indicating if they should be powered up or down. */ - list_for_each_entry(w, &dapm->widgets, list) { + list_for_each_entry(w, &card->widgets, list) { + if (w->dapm != dapm) + continue; switch (w->id) { case snd_soc_dapm_pre: dapm_seq_insert(w, &down_list, dapm_down_seq); @@ -942,7 +944,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) /* If there are no DAPM widgets then try to figure out power from the * event type. */ - if (list_empty(&dapm->widgets)) { + if (!dapm->n_widgets) { switch (event) { case SND_SOC_DAPM_STREAM_START: case SND_SOC_DAPM_STREAM_RESUME: @@ -1098,8 +1100,8 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm) if (!dapm->debugfs_dapm) return;
- list_for_each_entry(w, &dapm->widgets, list) { - if (!w->name) + list_for_each_entry(w, &dapm->card->widgets, list) { + if (!w->name || w->dapm != dapm) continue;
d = debugfs_create_file(w->name, 0444, @@ -1194,8 +1196,9 @@ static ssize_t dapm_widget_show(struct device *dev, int count = 0; char *state = "not set";
- list_for_each_entry(w, &codec->dapm->widgets, list) { - + list_for_each_entry(w, &codec->card->widgets, list) { + if (w->dapm != codec->dapm) + continue; /* only display widgets that burnm power */ switch (w->id) { case snd_soc_dapm_hp: @@ -1255,7 +1258,9 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) struct snd_soc_dapm_widget *w, *next_w; struct snd_soc_dapm_path *p, *next_p;
- list_for_each_entry_safe(w, next_w, &dapm->widgets, list) { + list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; list_del(&w->list); /* * remove source and sink paths associated to this widget. @@ -1285,7 +1290,9 @@ static int snd_soc_dapm_set_pin(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->dapm != dapm) + continue; if (!strcmp(w->name, pin)) { pr_debug("dapm: %s: pin %s\n", dapm->codec->name, pin); w->connected = status; @@ -1321,22 +1328,34 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_path *path; struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; + struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; const char *sink = route->sink; const char *control = route->control; const char *source = route->source; int ret = 0;
- /* find src and dest widgets */ - list_for_each_entry(w, &dapm->widgets, list) { - + /* + * find src and dest widgets over all widgets but favor a widget from + * current DAPM context + */ + list_for_each_entry(w, &dapm->card->widgets, list) { if (!wsink && !(strcmp(w->name, sink))) { - wsink = w; + wtsink = w; + if (w->dapm == dapm) + wsink = w; continue; } if (!wsource && !(strcmp(w->name, source))) { - wsource = w; + wtsource = w; + if (w->dapm == dapm) + wsource = w; } } + /* use widget from another DAPM context if not found from this */ + if (!wsink) + wsink = wtsink; + if (!wsource) + wsource = wtsource;
if (wsource == NULL || wsink == NULL) return -ENODEV; @@ -1474,7 +1493,7 @@ int snd_soc_dapm_new_widgets(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->new) continue; @@ -1958,12 +1977,13 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, if ((w = dapm_cnew_widget(widget)) == NULL) return -ENOMEM;
+ dapm->n_widgets++; w->dapm = dapm; w->codec = dapm->codec; INIT_LIST_HEAD(&w->sources); INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); - list_add(&w->list, &dapm->widgets); + list_add(&w->list, &dapm->card->widgets);
/* machine layer set ups unconnected pins and insertions */ w->connected = 1; @@ -2006,9 +2026,9 @@ static void soc_dapm_stream_event(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) + if (!w->sname || w->dapm != dapm) continue; pr_debug("widget %s\n %s stream %s event %d\n", w->name, w->sname, stream, event); @@ -2090,7 +2110,9 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char { 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->dapm != dapm) + continue; if (!strcmp(w->name, pin)) { pr_debug("dapm: %s: pin %s\n", dapm->codec->name, pin); w->connected = 1; @@ -2153,7 +2175,9 @@ int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, const char *p { 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->dapm != dapm) + continue; if (!strcmp(w->name, pin)) return w->connected; } @@ -2177,7 +2201,9 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_codec *codec, const char *pin) { struct snd_soc_dapm_widget *w;
- list_for_each_entry(w, &codec->dapm->widgets, list) { + list_for_each_entry(w, &codec->card->widgets, list) { + if (w->dapm != codec->dapm) + continue; if (!strcmp(w->name, pin)) { w->ignore_suspend = 1; return 0; @@ -2208,7 +2234,9 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) LIST_HEAD(down_list); int powerdown = 0;
- list_for_each_entry(w, &dapm->widgets, list) { + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; if (w->power) { dapm_seq_insert(w, &down_list, dapm_down_seq); w->power = 0;
On Fri, Oct 29, 2010 at 03:02:21PM +0300, Jarkko Nikula wrote:
Decoupling widgets from DAPM context is required when extending the ASoC core to cross-device paths. Even the list of widgets are now kept in struct snd_soc_card, the widget listing in sysfs and debugs remain sorted per device.
This also looks good.
Power change event like stream start/stop or kcontrol change in a cross-device path originates from one device but codec bias and widget power changes must be populated to another devices on that path as well.
This patch implements this by modifying the dapm_power_widgets so that it checks all the widgets on snd_soc_card that needs power change. Also all DAPM contexts are traversed if there is a need to change bias state of them. Only exception is widgetless codec whose bias state is changed only if power change is originating from that context.
Functions dapm_seq_run and dapm_seq_run_coalesced are modified so that a new write is issued when the sequence extends to an another device and DAPM context of a widget instead of originating DAPM context is used when doing pop waits and register writes.
Signed-off-by: Jarkko Nikula jhnikula@gmail.com --- include/sound/soc-dapm.h | 3 + include/sound/soc.h | 1 + sound/soc/soc-core.c | 2 + sound/soc/soc-dapm.c | 107 +++++++++++++++++++++++++-------------------- 4 files changed, 65 insertions(+), 48 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 7f20804..17b1825 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -477,6 +477,9 @@ struct snd_soc_dapm_context { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_dapm; #endif + /* used during DAPM updates */ + int sys_power; + struct list_head list; };
#endif diff --git a/include/sound/soc.h b/include/sound/soc.h index b752c52..4a67177 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -592,6 +592,7 @@ struct snd_soc_card {
struct list_head widgets; struct list_head paths; + struct list_head dapm_list; };
/* SoC machine DAI configuration, glues a codec and cpu DAI together */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 571de78..5f4b53c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1421,6 +1421,7 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* mark codec as probed and add to card codec list */ codec->probed = 1; list_add(&codec->card_list, &card->codec_dev_list); + list_add(&codec->dapm->list, &card->dapm_list); }
/* probe the platform */ @@ -1665,6 +1666,7 @@ static int soc_probe(struct platform_device *pdev) INIT_LIST_HEAD(&card->platform_dev_list); INIT_LIST_HEAD(&card->widgets); INIT_LIST_HEAD(&card->paths); + INIT_LIST_HEAD(&card->dapm_list);
ret = snd_soc_register_card(card); if (ret != 0) { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index fc1b943..2bccc9d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -717,14 +717,15 @@ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, struct list_head *pending) { - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w, *wt; int reg, power, ret; unsigned int value = 0; unsigned int mask = 0; unsigned int cur_mask;
- reg = list_first_entry(pending, struct snd_soc_dapm_widget, - power_list)->reg; + wt = list_first_entry(pending, struct snd_soc_dapm_widget, + power_list); + reg = wt->reg;
list_for_each_entry(w, pending, power_list) { cur_mask = 1 << w->shift; @@ -746,7 +747,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, /* power up pre event */ if (w->power && w->event && (w->event_flags & SND_SOC_DAPM_PRE_PMU)) { - pop_dbg(dapm->pop_time, "pop test : %s PRE_PMU\n", + pop_dbg(w->dapm->pop_time, "pop test : %s PRE_PMU\n", w->name); ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU); if (ret < 0) @@ -757,7 +758,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, /* power down pre event */ if (!w->power && w->event && (w->event_flags & SND_SOC_DAPM_PRE_PMD)) { - pop_dbg(dapm->pop_time, "pop test : %s PRE_PMD\n", + pop_dbg(w->dapm->pop_time, "pop test : %s PRE_PMD\n", w->name); ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD); if (ret < 0) @@ -767,11 +768,11 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, }
if (reg >= 0) { - pop_dbg(dapm->pop_time, + pop_dbg(wt->dapm->pop_time, "pop test : Applying 0x%x/0x%x to %x in %dms\n", - value, mask, reg, dapm->pop_time); - pop_wait(dapm->pop_time); - snd_soc_update_bits(dapm->codec, reg, mask, value); + value, mask, reg, wt->dapm->pop_time); + pop_wait(wt->dapm->pop_time); + snd_soc_update_bits(wt->dapm->codec, reg, mask, value); }
list_for_each_entry(w, pending, power_list) { @@ -815,19 +816,22 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, struct list_head *li LIST_HEAD(pending); int cur_sort = -1; int cur_reg = SND_SOC_NOPM; + struct snd_soc_dapm_context *cur_dapm = NULL; int ret;
list_for_each_entry_safe(w, n, list, power_list) { ret = 0;
/* Do we need to apply any queued changes? */ - if (sort[w->id] != cur_sort || w->reg != cur_reg) { + if (sort[w->id] != cur_sort || w->reg != cur_reg || + w->dapm != cur_dapm) { if (!list_empty(&pending)) dapm_seq_run_coalesced(dapm, &pending);
INIT_LIST_HEAD(&pending); cur_sort = -1; cur_reg = SND_SOC_NOPM; + cur_dapm = NULL; }
switch (w->id) { @@ -871,6 +875,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, struct list_head *li /* Queue it up for application */ cur_sort = sort[w->id]; cur_reg = w->reg; + cur_dapm = w->dapm; list_move(&w->power_list, &pending); break; } @@ -897,18 +902,19 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) { struct snd_soc_card *card = dapm->codec->card; struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_context *d; LIST_HEAD(up_list); LIST_HEAD(down_list); int ret = 0; int power; - int sys_power = 0; + + list_for_each_entry(d, &dapm->card->dapm_list, list) + d->sys_power = 0;
/* Check which widgets we need to power and store them in * lists indicating if they should be powered up or down. */ list_for_each_entry(w, &card->widgets, list) { - if (w->dapm != dapm) - continue; switch (w->id) { case snd_soc_dapm_pre: dapm_seq_insert(w, &down_list, dapm_down_seq); @@ -926,7 +932,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) else power = 1; if (power) - sys_power = 1; + w->dapm->sys_power = 1;
if (w->power == power) continue; @@ -948,19 +954,19 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) switch (event) { case SND_SOC_DAPM_STREAM_START: case SND_SOC_DAPM_STREAM_RESUME: - sys_power = 1; + dapm->sys_power = 1; break; case SND_SOC_DAPM_STREAM_SUSPEND: - sys_power = 0; + dapm->sys_power = 0; break; case SND_SOC_DAPM_STREAM_NOP: switch (dapm->bias_level) { case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_OFF: - sys_power = 0; + dapm->sys_power = 0; break; default: - sys_power = 1; + dapm->sys_power = 1; break; } break; @@ -969,19 +975,21 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) } }
- if (sys_power && dapm->bias_level == SND_SOC_BIAS_OFF) { - ret = snd_soc_dapm_set_bias_level(card, dapm, - SND_SOC_BIAS_STANDBY); - if (ret != 0) - pr_err("Failed to turn on bias: %d\n", ret); - } + list_for_each_entry(d, &dapm->card->dapm_list, list) { + if (d->sys_power && d->bias_level == SND_SOC_BIAS_OFF) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_STANDBY); + if (ret != 0) + pr_err("Failed to turn on bias: %d\n", ret); + }
- /* If we're changing to all on or all off then prepare */ - if ((sys_power && dapm->bias_level == SND_SOC_BIAS_STANDBY) || - (!sys_power && dapm->bias_level == SND_SOC_BIAS_ON)) { - ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_PREPARE); - if (ret != 0) - pr_err("Failed to prepare bias: %d\n", ret); + /* If we're changing to all on or all off then prepare */ + if ((d->sys_power && d->bias_level == SND_SOC_BIAS_STANDBY) || + (!d->sys_power && d->bias_level == SND_SOC_BIAS_ON)) { + ret = snd_soc_dapm_set_bias_level(card, d, SND_SOC_BIAS_PREPARE); + if (ret != 0) + pr_err("Failed to prepare bias: %d\n", ret); + } }
/* Power down widgets first; try to avoid amplifying pops. */ @@ -990,26 +998,28 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) /* Now power up. */ dapm_seq_run(dapm, &up_list, event, dapm_up_seq);
- /* If we just powered the last thing off drop to standby bias */ - if (dapm->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) { - ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_STANDBY); - if (ret != 0) - pr_err("Failed to apply standby bias: %d\n", ret); - } + list_for_each_entry(d, &dapm->card->dapm_list, list) { + /* If we just powered the last thing off drop to standby bias */ + if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->sys_power) { + ret = snd_soc_dapm_set_bias_level(card, d, SND_SOC_BIAS_STANDBY); + if (ret != 0) + pr_err("Failed to apply standby bias: %d\n", ret); + }
- /* If we're in standby and can support bias off then do that */ - if (dapm->bias_level == SND_SOC_BIAS_STANDBY && - dapm->idle_bias_off) { - ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_OFF); - if (ret != 0) - pr_err("Failed to turn off bias: %d\n", ret); - } + /* If we're in standby and can support bias off then do that */ + if (d->bias_level == SND_SOC_BIAS_STANDBY && + d->idle_bias_off) { + ret = snd_soc_dapm_set_bias_level(card, d, SND_SOC_BIAS_OFF); + if (ret != 0) + pr_err("Failed to turn off bias: %d\n", ret); + }
- /* If we just powered up then move to active bias */ - if (dapm->bias_level == SND_SOC_BIAS_PREPARE && sys_power) { - ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_ON); - if (ret != 0) - pr_err("Failed to apply active bias: %d\n", ret); + /* If we just powered up then move to active bias */ + if (d->bias_level == SND_SOC_BIAS_PREPARE && d->sys_power) { + ret = snd_soc_dapm_set_bias_level(card, d, SND_SOC_BIAS_ON); + if (ret != 0) + pr_err("Failed to apply active bias: %d\n", ret); + } }
pop_dbg(dapm->pop_time, "DAPM sequencing finished, waiting %dms\n", @@ -2225,6 +2235,7 @@ void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm) { snd_soc_dapm_sys_remove(dapm->dev); dapm_free_widgets(dapm); + list_del(&dapm->list); } EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
On Fri, Oct 29, 2010 at 03:02:22PM +0300, Jarkko Nikula wrote:
Functions dapm_seq_run and dapm_seq_run_coalesced are modified so that a new write is issued when the sequence extends to an another device and DAPM context of a widget instead of originating DAPM context is used when doing pop waits and register writes.
I'm having a hard time parsing this bit of the changelog, I'm afraid. What I'd expect is that the comparison operation used for coalescing would be extended to compare based on the CODEC/DAPM context too and the split per device should naturally fall out of that. I didn't notice the comparison being updated?
Otherwise this looks pretty good, these are stylistic things:
/* power down pre event */ if (!w->power && w->event && (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
pop_dbg(dapm->pop_time, "pop test : %s PRE_PMD\n",
pop_dbg(w->dapm->pop_time, "pop test : %s PRE_PMD\n", w->name);
We probably need to start putting the dev_names in these.
if (power)
sys_power = 1;
w->dapm->sys_power = 1;
sys_power needs to be renamed - probably dev_power or something.
- /* If we're changing to all on or all off then prepare */
- if ((sys_power && dapm->bias_level == SND_SOC_BIAS_STANDBY) ||
(!sys_power && dapm->bias_level == SND_SOC_BIAS_ON)) {
ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_PREPARE);
if (ret != 0)
pr_err("Failed to prepare bias: %d\n", ret);
/* If we're changing to all on or all off then prepare */
if ((d->sys_power && d->bias_level == SND_SOC_BIAS_STANDBY) ||
(!d->sys_power && d->bias_level == SND_SOC_BIAS_ON)) {
ret = snd_soc_dapm_set_bias_level(card, d, SND_SOC_BIAS_PREPARE);
if (ret != 0)
pr_err("Failed to prepare bias: %d\n", ret);
}
We can switch these over to dev_ prints now, I think.
On Fri, 29 Oct 2010 14:03:10 -0700 Mark Brown broonie@opensource.wolfsonmicro.com wrote:
On Fri, Oct 29, 2010 at 03:02:22PM +0300, Jarkko Nikula wrote:
Functions dapm_seq_run and dapm_seq_run_coalesced are modified so that a new write is issued when the sequence extends to an another device and DAPM context of a widget instead of originating DAPM context is used when doing pop waits and register writes.
I'm having a hard time parsing this bit of the changelog, I'm afraid. What I'd expect is that the comparison operation used for coalescing would be extended to compare based on the CODEC/DAPM context too and the split per device should naturally fall out of that. I didn't notice the comparison being updated?
It was these diffs for dapm_seq_run:
/* Do we need to apply any queued changes? */ - if (sort[w->id] != cur_sort || w->reg != cur_reg) { + if (sort[w->id] != cur_sort || w->reg != cur_reg || + w->dapm != cur_dapm) { if (!list_empty(&pending)) dapm_seq_run_coalesced(dapm, &pending);
So if DAPM is different the dapm_seq_run_coalesced is run now and next iterations are done for another DAPM context.
Looking at this now I think I can leave out dapm_seq_run_coalesced changes by changing the line above to dapm_seq_run_coalesced(cur_dapm, &pending). I'll check it for the next version.
Otherwise this looks pretty good, these are stylistic things:
Good notes. I'll change them.
On Sun, Oct 31, 2010 at 08:11:53PM +0200, Jarkko Nikula wrote:
Mark Brown broonie@opensource.wolfsonmicro.com wrote:
On Fri, Oct 29, 2010 at 03:02:22PM +0300, Jarkko Nikula wrote:
Functions dapm_seq_run and dapm_seq_run_coalesced are modified so that a new write is issued when the sequence extends to an another device and DAPM context of a widget instead of originating DAPM context is used when doing pop waits and register writes.
I'm having a hard time parsing this bit of the changelog, I'm afraid. What I'd expect is that the comparison operation used for coalescing would be extended to compare based on the CODEC/DAPM context too and the split per device should naturally fall out of that. I didn't notice the comparison being updated?
It was these diffs for dapm_seq_run:
/* Do we need to apply any queued changes? */
if (sort[w->id] != cur_sort || w->reg != cur_reg) {
if (sort[w->id] != cur_sort || w->reg != cur_reg ||
w->dapm != cur_dapm) { if (!list_empty(&pending)) dapm_seq_run_coalesced(dapm, &pending);
So if DAPM is different the dapm_seq_run_coalesced is run now and next iterations are done for another DAPM context.
Pretty much - that was the code I was looking for but you were talking about only the execution code, not the comparison operation.
On Fri, Oct 29, 2010 at 03:02:20PM +0300, Jarkko Nikula wrote:
Decoupling DAPM paths from DAPM context is a first prerequisite when extending ASoC core to cross-device paths. This patch is almost a nullop and does not allow to construct cross-device setup but the path clean-up part in dapm_free_widgets is prepared to remove cross-device paths between a device being removed and others.
Signed-off-by: Jarkko Nikula jhnikula@gmail.com
This looks good to me.
participants (2)
-
Jarkko Nikula
-
Mark Brown