if (pop_time) {
vprintk(fmt, args);
- pop_wait(pop_time);
}
va_end(args);
/**
* snd_soc_dapm_set_bias_level - set the bias level for the system
- * @socdev: audio device
+ * @card: 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)
+static int snd_soc_dapm_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_codec *codec, enum snd_soc_bias_level level)
{
- struct snd_soc_card *card = socdev->card;
- struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
switch (level) {
case SND_SOC_BIAS_ON:
- dev_dbg(socdev->dev, "Setting full bias\n");
+ dev_dbg(codec->dev, "Setting full bias\n");
break;
case SND_SOC_BIAS_PREPARE:
- dev_dbg(socdev->dev, "Setting bias prepare\n");
+ dev_dbg(codec->dev, "Setting bias prepare\n");
break;
case SND_SOC_BIAS_STANDBY:
- dev_dbg(socdev->dev, "Setting standby bias\n");
+ dev_dbg(codec->dev, "Setting standby bias\n");
break;
case SND_SOC_BIAS_OFF:
- dev_dbg(socdev->dev, "Setting bias off\n");
+ dev_dbg(codec->dev, "Setting bias off\n");
break;
default:
- dev_err(socdev->dev, "Setting invalid bias %d\n", level);
+ dev_err(codec->dev, "Setting invalid bias %d\n", level);
return -EINVAL;
}
- if (card->set_bias_level)
+ if (card && card->set_bias_level)
ret = card->set_bias_level(card, level);
if (ret == 0) {
- if (codec->set_bias_level)
- ret = codec->set_bias_level(codec, level);
+ if (codec->driver->set_bias_level)
+ ret = codec->driver->set_bias_level(codec, level);
else
codec->bias_level = level;
}
pop_dbg(codec->pop_time, "pop test %s : %s in %d ms\n",
widget->name, widget->power ? "on" : "off",
codec->pop_time);
- snd_soc_write(codec, widget->reg, new);
pop_wait(codec->pop_time);
+ snd_soc_write(codec, widget->reg, new);
}
pr_debug("reg %x old %x new %x change %d\n", widget->reg,
old, new, change);
return change;
}
-/* ramps the volume up or down to minimise pops before or after a
- * DAPM power event */
-static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power)
-{
- const struct snd_kcontrol_new *k = widget->kcontrols;
-
- if (widget->muted && !power)
- return 0;
- if (!widget->muted && power)
- return 0;
-
- if (widget->num_kcontrols && k) {
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)k->private_value;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- int max = mc->max;
- unsigned int mask = (1 << fls(max)) - 1;
- unsigned int invert = mc->invert;
-
- if (power) {
- int i;
- /* power up has happended, increase volume to last level */
- if (invert) {
- for (i = max; i > widget->saved_value; i--)
- snd_soc_update_bits(widget->codec, reg, mask, i);
- } else {
- for (i = 0; i < widget->saved_value; i++)
- snd_soc_update_bits(widget->codec, reg, mask, i);
- }
- widget->muted = 0;
- } else {
- /* power down is about to occur, decrease volume to mute */
- int val = snd_soc_read(widget->codec, reg);
- int i = widget->saved_value = (val >> shift) & mask;
- if (invert) {
- for (; i < mask; i++)
- snd_soc_update_bits(widget->codec, reg, mask, i);
- } else {
- for (; i > 0; i--)
- snd_soc_update_bits(widget->codec, reg, mask, i);
- }
- widget->muted = 1;
- }
- }
- return 0;
-}
-
/* create new dapm mixer control */
static int dapm_new_mixer(struct snd_soc_codec *codec,
struct snd_soc_dapm_widget *w)
path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
path->long_name);
- ret = snd_ctl_add(codec->card, path->kcontrol);
+ ret = snd_ctl_add(codec->card->snd_card, path->kcontrol);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to add dapm kcontrol %s: %d\n",
path->long_name,
}
kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
- ret = snd_ctl_add(codec->card, kcontrol);
+ ret = snd_ctl_add(codec->card->snd_card, kcontrol);
if (ret < 0)
goto err;
static int dapm_new_pga(struct snd_soc_codec *codec,
struct snd_soc_dapm_widget *w)
{
- struct snd_kcontrol *kcontrol;
- int ret = 0;
-
- if (!w->num_kcontrols)
- return -EINVAL;
+ if (w->num_kcontrols)
+ pr_err("asoc: PGA controls not supported: '%s'\n", w->name);
- kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
- ret = snd_ctl_add(codec->card, kcontrol);
- if (ret < 0) {
- printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
- return ret;
- }
-
- return ret;
+ return 0;
}
/* reset 'walked' bit for each dapm path */
p->walked = 0;
}
+/* We implement power down on suspend by checking the power state of
+ * the ALSA card - when we are suspending the ALSA state for the card
+ * is set to D3.
+ */
+static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget)
+{
+ int level = snd_power_get_state(widget->codec->card->snd_card);
+
+ switch (level) {
+ case SNDRV_CTL_POWER_D3hot:
+ case SNDRV_CTL_POWER_D3cold:
+ if (widget->ignore_suspend)
+ pr_debug("%s ignoring suspend\n", widget->name);
+ return widget->ignore_suspend;
+ default:
+ return 1;
+ }
+}
+
/*
* Recursively check for a completed path to an active or physically connected
* output widget. Returns number of complete paths.
case snd_soc_dapm_adc:
case snd_soc_dapm_aif_out:
if (widget->active)
- return 1;
+ return snd_soc_dapm_suspend_check(widget);
default:
break;
}
if (widget->connected) {
/* connected pin ? */
if (widget->id == snd_soc_dapm_output && !widget->ext)
- return 1;
+ return snd_soc_dapm_suspend_check(widget);
/* connected jack or spk ? */
if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk ||
(widget->id == snd_soc_dapm_line && !list_empty(&widget->sources)))
- return 1;
+ return snd_soc_dapm_suspend_check(widget);
}
list_for_each_entry(path, &widget->sinks, list_source) {
case snd_soc_dapm_dac:
case snd_soc_dapm_aif_in:
if (widget->active)
- return 1;
+ return snd_soc_dapm_suspend_check(widget);
default:
break;
}
if (widget->connected) {
/* connected pin ? */
if (widget->id == snd_soc_dapm_input && !widget->ext)
- return 1;
+ return snd_soc_dapm_suspend_check(widget);
/* connected VMID/Bias for lower pops */
if (widget->id == snd_soc_dapm_vmid)
- return 1;
+ return snd_soc_dapm_suspend_check(widget);
/* connected jack ? */
if (widget->id == snd_soc_dapm_mic ||
(widget->id == snd_soc_dapm_line && !list_empty(&widget->sinks)))
- return 1;
+ return snd_soc_dapm_suspend_check(widget);
}
list_for_each_entry(path, &widget->sources, list_sink) {
return ret;
}
- /* Lower PGA volume to reduce pops */
- if (w->id == snd_soc_dapm_pga && !w->power)
- dapm_set_pga(w, w->power);
-
dapm_update_bits(w);
- /* Raise PGA volume to reduce pops */
- if (w->id == snd_soc_dapm_pga && w->power)
- dapm_set_pga(w, w->power);
-
/* power up post event */
if (w->power && w->event &&
(w->event_flags & SND_SOC_DAPM_POST_PMU)) {
pr_err("%s: pre event failed: %d\n",
w->name, ret);
}
-
- /* Lower PGA volume to reduce pops */
- if (w->id == snd_soc_dapm_pga && !w->power)
- dapm_set_pga(w, w->power);
}
if (reg >= 0) {
}
list_for_each_entry(w, pending, power_list) {
- /* Raise PGA volume to reduce pops */
- if (w->id == snd_soc_dapm_pga && w->power)
- dapm_set_pga(w, w->power);
-
/* power up post event */
if (w->power && w->event &&
(w->event_flags & SND_SOC_DAPM_POST_PMU)) {
*/
static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
{
- struct snd_soc_device *socdev = codec->socdev;
+ struct snd_soc_card *card = codec->card;
struct snd_soc_dapm_widget *w;
LIST_HEAD(up_list);
LIST_HEAD(down_list);
if (!w->power_check)
continue;
- /* If we're suspending then pull down all the
- * power. */
- switch (event) {
- case SND_SOC_DAPM_STREAM_SUSPEND:
- power = 0;
- break;
-
- default:
+ if (!w->force)
power = w->power_check(w);
- if (power)
- sys_power = 1;
- break;
- }
+ else
+ power = 1;
+ if (power)
+ sys_power = 1;
if (w->power == power)
continue;
}
if (sys_power && codec->bias_level == SND_SOC_BIAS_OFF) {
- ret = snd_soc_dapm_set_bias_level(socdev,
+ ret = snd_soc_dapm_set_bias_level(card, codec,
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 && 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);
+ ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_PREPARE);
if (ret != 0)
pr_err("Failed to prepare bias: %d\n", ret);
}
/* 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);
+ ret = snd_soc_dapm_set_bias_level(card, codec, 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 (codec->bias_level == SND_SOC_BIAS_STANDBY &&
codec->idle_bias_off) {
- ret = snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_OFF);
+ ret = snd_soc_dapm_set_bias_level(card, codec, 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 (codec->bias_level == SND_SOC_BIAS_PREPARE && sys_power) {
- ret = snd_soc_dapm_set_bias_level(socdev,
- SND_SOC_BIAS_ON);
+ ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_ON);
if (ret != 0)
pr_err("Failed to apply active bias: %d\n", ret);
}
pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n",
codec->pop_time);
+ pop_wait(codec->pop_time);
return 0;
}
static ssize_t dapm_widget_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct snd_soc_device *devdata = dev_get_drvdata(dev);
- struct snd_soc_codec *codec = devdata->card->codec;
+ struct snd_soc_pcm_runtime *rtd =
+ container_of(dev, struct snd_soc_pcm_runtime, dev);
+ struct snd_soc_codec *codec =rtd->codec;
struct snd_soc_dapm_widget *w;
int count = 0;
char *state = "not set";
if (!strcmp(w->name, pin)) {
pr_debug("dapm: %s: pin %s\n", codec->name, pin);
w->connected = status;
+ /* Allow disabling of forced pins */
+ if (status == 0)
+ w->force = 0;
return 0;
}
}
unsigned int invert = mc->invert;
unsigned int mask = (1 << fls(max)) - 1;
- /* return the saved value if we are powered down */
- if (widget->id == snd_soc_dapm_pga && !widget->power) {
- ucontrol->value.integer.value[0] = widget->saved_value;
- return 0;
- }
-
ucontrol->value.integer.value[0] =
(snd_soc_read(widget->codec, reg) >> shift) & mask;
if (shift != rshift)
mutex_lock(&widget->codec->mutex);
widget->value = val;
- /* save volume value if the widget is powered down */
- if (widget->id == snd_soc_dapm_pga && !widget->power) {
- widget->saved_value = val;
- mutex_unlock(&widget->codec->mutex);
- return 1;
- }
-
if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) {
if (val)
/* new connection */
*
* Returns 0 for success else error.
*/
-int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
- char *stream, int event)
+int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
+ const char *stream, int event)
{
+ struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_widget *w;
if (stream == NULL)
w->active = 0;
break;
case SND_SOC_DAPM_STREAM_SUSPEND:
- if (w->active)
- w->suspend = 1;
- w->active = 0;
- break;
case SND_SOC_DAPM_STREAM_RESUME:
- if (w->suspend) {
- w->active = 1;
- w->suspend = 0;
- }
- break;
case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
- break;
case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
break;
}
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
+/**
+ * snd_soc_dapm_force_enable_pin - force a pin to be enabled
+ * @codec: SoC codec
+ * @pin: pin name
+ *
+ * Enables input/output pin regardless of any other state. This is
+ * intended for use with microphone bias supplies used in microphone
+ * jack detection.
+ *
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_force_enable_pin(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)) {
+ pr_debug("dapm: %s: pin %s\n", codec->name, pin);
+ w->connected = 1;
+ w->force = 1;
+ return 0;
+ }
+ }
+
+ pr_err("dapm: %s: configuring unknown pin %s\n", codec->name, pin);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin);
+
/**
* snd_soc_dapm_disable_pin - disable pin.
* @codec: SoC codec
}
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
+ * @card: SoC device
*
* Free all dapm widgets and resources.
*/
-void snd_soc_dapm_free(struct snd_soc_device *socdev)
+void snd_soc_dapm_free(struct snd_soc_codec *codec)
{
- struct snd_soc_codec *codec = socdev->card->codec;
-
- snd_soc_dapm_sys_remove(socdev->dev);
+ snd_soc_dapm_sys_remove(codec->dev);
dapm_free_widgets(codec);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
-/*
- * snd_soc_dapm_shutdown - callback for system shutdown
- */
-void snd_soc_dapm_shutdown(struct snd_soc_device *socdev)
+static void soc_dapm_shutdown_codec(struct snd_soc_codec *codec)
{
- struct snd_soc_codec *codec = socdev->card->codec;
struct snd_soc_dapm_widget *w;
LIST_HEAD(down_list);
int powerdown = 0;
* standby.
*/
if (powerdown) {
- snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_PREPARE);
+ snd_soc_dapm_set_bias_level(NULL, codec, SND_SOC_BIAS_PREPARE);
dapm_seq_run(codec, &down_list, 0, dapm_down_seq);
- snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_STANDBY);
+ snd_soc_dapm_set_bias_level(NULL, codec, SND_SOC_BIAS_STANDBY);
}
+}
+
+/*
+ * snd_soc_dapm_shutdown - callback for system shutdown
+ */
+void snd_soc_dapm_shutdown(struct snd_soc_card *card)
+{
+ struct snd_soc_codec *codec;
+
+ list_for_each_entry(codec, &card->codec_dev_list, list)
+ soc_dapm_shutdown_codec(codec);
- snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_OFF);
+ snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_OFF);
}
/* Module information */