static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
{
int change, power;
- unsigned short old, new;
+ unsigned int old, new;
struct snd_soc_codec *codec = widget->codec;
/* check for valid widgets */
struct list_head *pending)
{
struct snd_soc_dapm_widget *w;
- int reg, power;
+ int reg, power, ret;
unsigned int value = 0;
unsigned int mask = 0;
unsigned int cur_mask;
pop_dbg(codec->pop_time,
"pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
w->name, reg, value, mask);
+
+ /* power up pre event */
+ if (w->power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
+ pop_dbg(codec->pop_time, "pop test : %s PRE_PMU\n",
+ w->name);
+ ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
+ if (ret < 0)
+ pr_err("%s: pre event failed: %d\n",
+ w->name, ret);
+ }
+
+ /* power down pre event */
+ if (!w->power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
+ pop_dbg(codec->pop_time, "pop test : %s PRE_PMD\n",
+ w->name);
+ ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
+ if (ret < 0)
+ 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);
}
- pop_dbg(codec->pop_time,
- "pop test : Applying 0x%x/0x%x to %x in %dms\n",
- value, mask, reg, codec->pop_time);
- pop_wait(codec->pop_time);
- snd_soc_update_bits(codec, reg, mask, value);
+ if (reg >= 0) {
+ pop_dbg(codec->pop_time,
+ "pop test : Applying 0x%x/0x%x to %x in %dms\n",
+ value, mask, reg, codec->pop_time);
+ pop_wait(codec->pop_time);
+ snd_soc_update_bits(codec, reg, mask, value);
+ }
+
+ 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)) {
+ pop_dbg(codec->pop_time, "pop test : %s POST_PMU\n",
+ w->name);
+ ret = w->event(w,
+ NULL, SND_SOC_DAPM_POST_PMU);
+ if (ret < 0)
+ pr_err("%s: post event failed: %d\n",
+ w->name, ret);
+ }
+
+ /* power down post event */
+ if (!w->power && w->event &&
+ (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
+ pop_dbg(codec->pop_time, "pop test : %s POST_PMD\n",
+ w->name);
+ ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
+ if (ret < 0)
+ pr_err("%s: post event failed: %d\n",
+ w->name, ret);
+ }
+ }
}
/* Apply a DAPM power sequence.
case snd_soc_dapm_line:
case snd_soc_dapm_spk:
/* No register support currently */
- case snd_soc_dapm_pga:
- /* Don't coalsece these yet due to gain ramping */
ret = dapm_generic_apply_power(w);
break;
default:
- /* If there's an event or an invalid register
- * then run immediately, otherwise store the
- * updates so that we can coalesce. */
- if (w->reg >= 0 && !w->event) {
- cur_sort = sort[w->id];
- cur_reg = w->reg;
- list_move(&w->power_list, &pending);
- } else {
- ret = dapm_generic_apply_power(w);
- }
+ /* Queue it up for application */
+ cur_sort = sort[w->id];
+ cur_reg = w->reg;
+ list_move(&w->power_list, &pending);
+ break;
}
if (ret < 0)
{
struct snd_soc_device *socdev = codec->socdev;
struct snd_soc_dapm_widget *w;
+ LIST_HEAD(up_list);
+ LIST_HEAD(down_list);
int ret = 0;
int power;
int sys_power = 0;
- INIT_LIST_HEAD(&codec->up_list);
- INIT_LIST_HEAD(&codec->down_list);
-
/* 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, &codec->dapm_widgets, list) {
switch (w->id) {
case snd_soc_dapm_pre:
- dapm_seq_insert(w, &codec->down_list, dapm_down_seq);
+ dapm_seq_insert(w, &down_list, dapm_down_seq);
break;
case snd_soc_dapm_post:
- dapm_seq_insert(w, &codec->up_list, dapm_up_seq);
+ dapm_seq_insert(w, &up_list, dapm_up_seq);
break;
default:
continue;
if (power)
- dapm_seq_insert(w, &codec->up_list,
- dapm_up_seq);
+ dapm_seq_insert(w, &up_list, dapm_up_seq);
else
- dapm_seq_insert(w, &codec->down_list,
- dapm_down_seq);
+ dapm_seq_insert(w, &down_list, dapm_down_seq);
w->power = power;
break;
}
/* Power down widgets first; try to avoid amplifying pops. */
- dapm_seq_run(codec, &codec->down_list, event, dapm_down_seq);
+ dapm_seq_run(codec, &down_list, event, dapm_down_seq);
/* Now power up. */
- dapm_seq_run(codec, &codec->up_list, event, dapm_up_seq);
+ dapm_seq_run(codec, &up_list, event, dapm_up_seq);
/* If we just powered the last thing off drop to standby bias */
if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
- unsigned short val, val2, val_mask;
+ unsigned int val, val2, val_mask;
int ret;
val = (ucontrol->value.integer.value[0] & mask);
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned short val, bitmask;
+ unsigned int val, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
;
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned short val, mux;
- unsigned short mask, bitmask;
+ unsigned int val, mux;
+ unsigned int mask, bitmask;
int ret = 0;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned short reg_val, val, mux;
+ unsigned int reg_val, val, mux;
reg_val = snd_soc_read(widget->codec, e->reg);
val = (reg_val >> e->shift_l) & e->mask;
{
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned short val, mux;
- unsigned short mask;
+ unsigned int val, mux;
+ unsigned int mask;
int ret = 0;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
}
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)
+{
+ struct snd_soc_codec *codec = socdev->card->codec;
+ struct snd_soc_dapm_widget *w;
+ LIST_HEAD(down_list);
+ int powerdown = 0;
+
+ list_for_each_entry(w, &codec->dapm_widgets, list) {
+ if (w->power) {
+ dapm_seq_insert(w, &down_list, dapm_down_seq);
+ w->power = 0;
+ powerdown = 1;
+ }
+ }
+
+ /* If there were no widgets to power down we're already in
+ * standby.
+ */
+ if (powerdown) {
+ snd_soc_dapm_set_bias_level(socdev, 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(socdev, SND_SOC_BIAS_OFF);
+}
+
/* Module information */
MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");