diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 55c6d3d1894f..66ff4c124dbd 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -341,6 +341,7 @@ int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin); int snd_soc_dapm_sync(struct snd_soc_codec *codec); int snd_soc_dapm_force_enable_pin(struct snd_soc_codec *codec, const char *pin); +int snd_soc_dapm_ignore_suspend(struct snd_soc_codec *codec, const char *pin); /* dapm widget types */ enum snd_soc_dapm_type { @@ -428,6 +429,7 @@ struct snd_soc_dapm_widget { unsigned char new:1; /* cnew complete */ unsigned char ext:1; /* has external widgets */ unsigned char force:1; /* force state */ + unsigned char ignore_suspend:1; /* kept enabled over suspend */ int (*power_check)(struct snd_soc_dapm_widget *w); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 95739767ec45..8661e5b4adb1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -927,8 +927,19 @@ static int soc_suspend(struct device *dev) SND_SOC_DAPM_STREAM_SUSPEND); } - if (codec_dev->suspend) - codec_dev->suspend(pdev, PMSG_SUSPEND); + /* If there are paths active then the CODEC will be held with + * bias _ON and should not be suspended. */ + if (codec_dev->suspend) { + switch (codec->bias_level) { + case SND_SOC_BIAS_STANDBY: + case SND_SOC_BIAS_OFF: + codec_dev->suspend(pdev, PMSG_SUSPEND); + break; + default: + dev_dbg(socdev->dev, "CODEC is on over suspend\n"); + break; + } + } for (i = 0; i < card->num_links; i++) { struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; @@ -975,8 +986,21 @@ static void soc_resume_deferred(struct work_struct *work) cpu_dai->resume(cpu_dai); } - if (codec_dev->resume) - codec_dev->resume(pdev); + /* If the CODEC was idle over suspend then it will have been + * left with bias OFF or STANDBY and suspended so we must now + * resume. Otherwise the suspend was suppressed. + */ + if (codec_dev->resume) { + switch (codec->bias_level) { + case SND_SOC_BIAS_STANDBY: + case SND_SOC_BIAS_OFF: + codec_dev->resume(pdev); + break; + default: + dev_dbg(socdev->dev, "CODEC was on over suspend\n"); + break; + } + } for (i = 0; i < codec->num_dai; i++) { char *stream = codec->dai[i].playback.stream_name; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8c8b291320a8..fefb6c44fc81 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -441,7 +441,9 @@ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) switch (snd_power_get_state(codec->card)) { case SNDRV_CTL_POWER_D3hot: case SNDRV_CTL_POWER_D3cold: - return 0; + if (widget->ignore_suspend) + pr_debug("%s ignoring suspend\n", widget->name); + return widget->ignore_suspend; default: return 1; } @@ -2136,6 +2138,33 @@ int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin) } EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status); +/** + * snd_soc_dapm_ignore_suspend - ignore suspend status for DAPM endpoint + * @codec: audio codec + * @pin: audio signal pin endpoint (or start point) + * + * Mark the given endpoint or pin as ignoring suspend. When the + * system is disabled a path between two endpoints flagged as ignoring + * suspend will not be disabled. The path must already be enabled via + * normal means at suspend time, it will not be turned on if it was not + * already enabled. + */ +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) { + if (!strcmp(w->name, pin)) { + w->ignore_suspend = 1; + return 0; + } + } + + pr_err("Unknown DAPM pin: %s\n", pin); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); + /** * snd_soc_dapm_free - free dapm resources * @socdev: SoC device