Commit 452c5eaa authored by Mark Brown's avatar Mark Brown

ASoC: Integrate bias management with DAPM power management

Rather than managing the bias level of the system based on if there is
an active audio stream manage it based on there being an active DAPM
widget. This simplifies the code a little, moving the power handling
into one place, and improves audio performance for bypass paths when no
playbacks or captures are active.
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent aef90843
...@@ -279,8 +279,6 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec, ...@@ -279,8 +279,6 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
/* dapm events */ /* dapm events */
int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream, int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
int event); int event);
int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
enum snd_soc_bias_level level);
/* dapm sys fs - used by the core */ /* dapm sys fs - used by the core */
int snd_soc_dapm_sys_add(struct device *dev); int snd_soc_dapm_sys_add(struct device *dev);
......
...@@ -339,6 +339,7 @@ struct snd_soc_codec { ...@@ -339,6 +339,7 @@ struct snd_soc_codec {
struct module *owner; struct module *owner;
struct mutex mutex; struct mutex mutex;
struct device *dev; struct device *dev;
struct snd_soc_device *socdev;
struct list_head list; struct list_head list;
......
...@@ -299,7 +299,6 @@ static void close_delayed_work(struct work_struct *work) ...@@ -299,7 +299,6 @@ static void close_delayed_work(struct work_struct *work)
{ {
struct snd_soc_card *card = container_of(work, struct snd_soc_card, struct snd_soc_card *card = container_of(work, struct snd_soc_card,
delayed_work.work); delayed_work.work);
struct snd_soc_device *socdev = card->socdev;
struct snd_soc_codec *codec = card->codec; struct snd_soc_codec *codec = card->codec;
struct snd_soc_dai *codec_dai; struct snd_soc_dai *codec_dai;
int i; int i;
...@@ -315,27 +314,10 @@ static void close_delayed_work(struct work_struct *work) ...@@ -315,27 +314,10 @@ static void close_delayed_work(struct work_struct *work)
/* are we waiting on this codec DAI stream */ /* are we waiting on this codec DAI stream */
if (codec_dai->pop_wait == 1) { if (codec_dai->pop_wait == 1) {
/* Reduce power if no longer active */
if (codec->active == 0) {
pr_debug("pop wq D1 %s %s\n", codec->name,
codec_dai->playback.stream_name);
snd_soc_dapm_set_bias_level(socdev,
SND_SOC_BIAS_PREPARE);
}
codec_dai->pop_wait = 0; codec_dai->pop_wait = 0;
snd_soc_dapm_stream_event(codec, snd_soc_dapm_stream_event(codec,
codec_dai->playback.stream_name, codec_dai->playback.stream_name,
SND_SOC_DAPM_STREAM_STOP); SND_SOC_DAPM_STREAM_STOP);
/* Fall into standby if no longer active */
if (codec->active == 0) {
pr_debug("pop wq D3 %s %s\n", codec->name,
codec_dai->playback.stream_name);
snd_soc_dapm_set_bias_level(socdev,
SND_SOC_BIAS_STANDBY);
}
} }
} }
mutex_unlock(&pcm_mutex); mutex_unlock(&pcm_mutex);
...@@ -399,10 +381,6 @@ static int soc_codec_close(struct snd_pcm_substream *substream) ...@@ -399,10 +381,6 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
snd_soc_dapm_stream_event(codec, snd_soc_dapm_stream_event(codec,
codec_dai->capture.stream_name, codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_STOP); SND_SOC_DAPM_STREAM_STOP);
if (codec->active == 0 && codec_dai->pop_wait == 0)
snd_soc_dapm_set_bias_level(socdev,
SND_SOC_BIAS_STANDBY);
} }
mutex_unlock(&pcm_mutex); mutex_unlock(&pcm_mutex);
...@@ -467,36 +445,16 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -467,36 +445,16 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
cancel_delayed_work(&card->delayed_work); cancel_delayed_work(&card->delayed_work);
} }
/* do we need to power up codec */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
if (codec->bias_level != SND_SOC_BIAS_ON) { snd_soc_dapm_stream_event(codec,
snd_soc_dapm_set_bias_level(socdev, codec_dai->playback.stream_name,
SND_SOC_BIAS_PREPARE); SND_SOC_DAPM_STREAM_START);
else
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) snd_soc_dapm_stream_event(codec,
snd_soc_dapm_stream_event(codec, codec_dai->capture.stream_name,
codec_dai->playback.stream_name, SND_SOC_DAPM_STREAM_START);
SND_SOC_DAPM_STREAM_START);
else
snd_soc_dapm_stream_event(codec,
codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
snd_soc_dai_digital_mute(codec_dai, 0);
} else {
/* codec already powered - power on widgets */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dapm_stream_event(codec,
codec_dai->playback.stream_name,
SND_SOC_DAPM_STREAM_START);
else
snd_soc_dapm_stream_event(codec,
codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
snd_soc_dai_digital_mute(codec_dai, 0); snd_soc_dai_digital_mute(codec_dai, 0);
}
out: out:
mutex_unlock(&pcm_mutex); mutex_unlock(&pcm_mutex);
...@@ -1372,6 +1330,7 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid) ...@@ -1372,6 +1330,7 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
return ret; return ret;
} }
codec->socdev = socdev;
codec->card->dev = socdev->dev; codec->card->dev = socdev->dev;
codec->card->private_data = codec; codec->card->private_data = codec;
strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver)); strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
......
...@@ -94,6 +94,30 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( ...@@ -94,6 +94,30 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
} }
/**
* snd_soc_dapm_set_bias_level - set the bias level for the system
* @socdev: audio device
* @level: level to configure
*
* Configure the bias (power) levels for the SoC audio device.
*
* Returns 0 for success else error.
*/
static int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
enum snd_soc_bias_level level)
{
struct snd_soc_card *card = socdev->card;
struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
if (card->set_bias_level)
ret = card->set_bias_level(card, level);
if (ret == 0 && codec->set_bias_level)
ret = codec->set_bias_level(codec, level);
return ret;
}
/* set up initial codec paths */ /* set up initial codec paths */
static void dapm_set_path_status(struct snd_soc_dapm_widget *w, static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_path *p, int i) struct snd_soc_dapm_path *p, int i)
...@@ -707,9 +731,11 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event, ...@@ -707,9 +731,11 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event,
*/ */
static int dapm_power_widgets(struct snd_soc_codec *codec, int event) static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
{ {
struct snd_soc_device *socdev = codec->socdev;
struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *w;
int ret = 0; int ret = 0;
int i, power; int i, power;
int sys_power = 0;
INIT_LIST_HEAD(&codec->up_list); INIT_LIST_HEAD(&codec->up_list);
INIT_LIST_HEAD(&codec->down_list); INIT_LIST_HEAD(&codec->down_list);
...@@ -731,6 +757,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) ...@@ -731,6 +757,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
continue; continue;
power = w->power_check(w); power = w->power_check(w);
if (power)
sys_power = 1;
if (w->power == power) if (w->power == power)
continue; continue;
...@@ -745,6 +774,15 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) ...@@ -745,6 +774,15 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
} }
} }
/* If we're changing to all on or all off then prepare */
if ((sys_power && codec->bias_level == SND_SOC_BIAS_STANDBY) ||
(!sys_power && codec->bias_level == SND_SOC_BIAS_ON)) {
ret = snd_soc_dapm_set_bias_level(socdev,
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. */ /* Power down widgets first; try to avoid amplifying pops. */
for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) { for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) {
list_for_each_entry(w, &codec->down_list, power_list) { list_for_each_entry(w, &codec->down_list, power_list) {
...@@ -773,6 +811,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) ...@@ -773,6 +811,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
} }
} }
/* If we just powered the last thing off drop to standby bias */
if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
ret = snd_soc_dapm_set_bias_level(socdev,
SND_SOC_BIAS_STANDBY);
if (ret != 0)
pr_err("Failed to apply standby bias: %d\n", ret);
}
/* If we just powered up then move to active bias */
if (codec->bias_level == SND_SOC_BIAS_PREPARE && sys_power) {
ret = snd_soc_dapm_set_bias_level(socdev,
SND_SOC_BIAS_ON);
if (ret != 0)
pr_err("Failed to apply active bias: %d\n", ret);
}
return 0; return 0;
} }
...@@ -1720,30 +1774,6 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, ...@@ -1720,30 +1774,6 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
} }
EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
/**
* snd_soc_dapm_set_bias_level - set the bias level for the system
* @socdev: audio device
* @level: level to configure
*
* Configure the bias (power) levels for the SoC audio device.
*
* Returns 0 for success else error.
*/
int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
enum snd_soc_bias_level level)
{
struct snd_soc_card *card = socdev->card;
struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
if (card->set_bias_level)
ret = card->set_bias_level(card, level);
if (ret == 0 && codec->set_bias_level)
ret = codec->set_bias_level(codec, level);
return ret;
}
/** /**
* snd_soc_dapm_enable_pin - enable pin. * snd_soc_dapm_enable_pin - enable pin.
* @codec: SoC codec * @codec: SoC codec
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment