diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index c0e7c47469fc..98783510d2f1 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -471,6 +471,11 @@ 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 */ + + /* used during DAPM updates */ + int dev_power; + struct list_head list; + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_dapm; #endif diff --git a/include/sound/soc.h b/include/sound/soc.h index d5fb8618fdba..74921f20a1d8 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -663,6 +663,7 @@ struct snd_soc_card { struct list_head widgets; struct list_head paths; + struct list_head dapm_list; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_card_root; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index bd183a7ed696..a233607a73c6 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1427,6 +1427,7 @@ static int soc_probe_codec(struct snd_soc_card *card, /* 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); return ret; } @@ -1881,6 +1882,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); soc_init_card_debugfs(card); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8731e89646ae..f362d1de78f3 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -847,19 +847,22 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, 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); + dapm_seq_run_coalesced(cur_dapm, &pending); INIT_LIST_HEAD(&pending); cur_sort = -1; cur_reg = SND_SOC_NOPM; + cur_dapm = NULL; } switch (w->id) { @@ -903,6 +906,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, /* 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; } @@ -929,20 +933,22 @@ 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; trace_snd_soc_dapm_start(card); + list_for_each_entry(d, &card->dapm_list, list) + if (d->n_widgets) + d->dev_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); @@ -960,7 +966,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) else power = 1; if (power) - sys_power = 1; + w->dapm->dev_power = 1; if (w->power == power) continue; @@ -984,22 +990,22 @@ 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->dev_power = 1; break; case SND_SOC_DAPM_STREAM_STOP: - sys_power = !!dapm->codec->active; + dapm->dev_power = !!dapm->codec->active; break; case SND_SOC_DAPM_STREAM_SUSPEND: - sys_power = 0; + dapm->dev_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->dev_power = 0; break; default: - sys_power = 1; + dapm->dev_power = 1; break; } break; @@ -1008,21 +1014,24 @@ 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) - dev_err(dapm->dev, - "Failed to turn on bias: %d\n", ret); - } + list_for_each_entry(d, &dapm->card->dapm_list, list) { + if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_STANDBY); + if (ret != 0) + dev_err(d->dev, + "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) - dev_err(dapm->dev, - "Failed to prepare bias: %d\n", ret); + /* If we're changing to all on or all off then prepare */ + if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) || + (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_PREPARE); + if (ret != 0) + dev_err(d->dev, + "Failed to prepare bias: %d\n", ret); + } } /* Power down widgets first; try to avoid amplifying pops. */ @@ -1031,29 +1040,36 @@ 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) - dev_err(dapm->dev, - "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->dev_power) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_STANDBY); + if (ret != 0) + dev_err(d->dev, + "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) - dev_err(dapm->dev, - "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) + dev_err(d->dev, + "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) - dev_err(dapm->dev, - "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->dev_power) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_ON); + if (ret != 0) + dev_err(d->dev, + "Failed to apply active bias: %d\n", + ret); + } } pop_dbg(dapm->dev, card->pop_time, @@ -2309,6 +2325,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);