diff options
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r-- | sound/soc/soc-dapm.c | 92 |
1 files changed, 89 insertions, 3 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f42e8b9fb17..1f55ded4047 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -39,6 +39,7 @@ #include <linux/platform_device.h> #include <linux/jiffies.h> #include <linux/debugfs.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -339,6 +340,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, case snd_soc_dapm_output: case snd_soc_dapm_adc: case snd_soc_dapm_input: + case snd_soc_dapm_siggen: case snd_soc_dapm_dac: case snd_soc_dapm_micbias: case snd_soc_dapm_vmid: @@ -772,6 +774,11 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) return widget->inputs; } + /* signal generator */ + if (widget->id == snd_soc_dapm_siggen) { + widget->inputs = snd_soc_dapm_suspend_check(widget); + return widget->inputs; + } } list_for_each_entry(path, &widget->sources, list_sink) { @@ -1200,6 +1207,9 @@ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) /* If we're off and we're not supposed to be go into STANDBY */ if (d->bias_level == SND_SOC_BIAS_OFF && d->target_bias_level != SND_SOC_BIAS_OFF) { + if (d->dev) + pm_runtime_get_sync(d->dev); + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); if (ret != 0) dev_err(d->dev, @@ -1239,6 +1249,9 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie) ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF); if (ret != 0) dev_err(d->dev, "Failed to turn off bias: %d\n", ret); + + if (d->dev) + pm_runtime_put_sync(d->dev); } /* If we just powered up then move to active bias */ @@ -1413,7 +1426,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) dapm->target_bias_level = SND_SOC_BIAS_ON; break; case SND_SOC_DAPM_STREAM_STOP: - if (dapm->codec->active) + if (dapm->codec && dapm->codec->active) dapm->target_bias_level = SND_SOC_BIAS_ON; else dapm->target_bias_level = SND_SOC_BIAS_STANDBY; @@ -1725,8 +1738,7 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, static ssize_t dapm_widget_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct snd_soc_pcm_runtime *rtd = - container_of(dev, struct snd_soc_pcm_runtime, dev); + struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); struct snd_soc_codec *codec =rtd->codec; struct snd_soc_dapm_widget *w; int count = 0; @@ -1982,6 +1994,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_out_drv: case snd_soc_dapm_input: case snd_soc_dapm_output: + case snd_soc_dapm_siggen: case snd_soc_dapm_micbias: case snd_soc_dapm_vmid: case snd_soc_dapm_pre: @@ -2947,6 +2960,79 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); +static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card, + struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p; + + list_for_each_entry(p, &card->paths, list) { + if ((p->source == w) || (p->sink == w)) { + dev_dbg(card->dev, + "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n", + p->source->name, p->source->id, p->source->dapm, + p->sink->name, p->sink->id, p->sink->dapm); + + /* Connected to something other than the codec */ + if (p->source->dapm != p->sink->dapm) + return true; + /* + * Loopback connection from codec external pin to + * codec external pin + */ + if (p->sink->id == snd_soc_dapm_input) { + switch (p->source->id) { + case snd_soc_dapm_output: + case snd_soc_dapm_micbias: + return true; + default: + break; + } + } + } + } + + return false; +} + +/** + * snd_soc_dapm_auto_nc_codec_pins - call snd_soc_dapm_nc_pin for unused pins + * @codec: The codec whose pins should be processed + * + * Automatically call snd_soc_dapm_nc_pin() for any external pins in the codec + * which are unused. Pins are used if they are connected externally to the + * codec, whether that be to some other device, or a loop-back connection to + * the codec itself. + */ +void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec) +{ + struct snd_soc_card *card = codec->card; + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_widget *w; + + dev_dbg(codec->dev, "Auto NC: DAPMs: card:%p codec:%p\n", + &card->dapm, &codec->dapm); + + list_for_each_entry(w, &card->widgets, list) { + if (w->dapm != dapm) + continue; + switch (w->id) { + case snd_soc_dapm_input: + case snd_soc_dapm_output: + case snd_soc_dapm_micbias: + dev_dbg(codec->dev, "Auto NC: Checking widget %s\n", + w->name); + if (!snd_soc_dapm_widget_in_card_paths(card, w)) { + dev_dbg(codec->dev, + "... Not in map; disabling\n"); + snd_soc_dapm_nc_pin(dapm, w->name); + } + break; + default: + break; + } + } +} + /** * snd_soc_dapm_free - free dapm resources * @dapm: DAPM context |