Commit 0e09b67e authored by Mark Brown's avatar Mark Brown

Merge branch 'dapm' into for-2.6.32

parents fa44c077 291f3bbc
...@@ -369,8 +369,6 @@ struct snd_soc_codec { ...@@ -369,8 +369,6 @@ struct snd_soc_codec {
enum snd_soc_bias_level bias_level; enum snd_soc_bias_level bias_level;
enum snd_soc_bias_level suspend_bias_level; enum snd_soc_bias_level suspend_bias_level;
struct delayed_work delayed_work; struct delayed_work delayed_work;
struct list_head up_list;
struct list_head down_list;
/* codec DAI's */ /* codec DAI's */
struct snd_soc_dai *dai; struct snd_soc_dai *dai;
......
...@@ -52,19 +52,37 @@ ...@@ -52,19 +52,37 @@
/* dapm power sequences - make this per codec in the future */ /* dapm power sequences - make this per codec in the future */
static int dapm_up_seq[] = { static int dapm_up_seq[] = {
snd_soc_dapm_pre, snd_soc_dapm_supply, snd_soc_dapm_micbias, [snd_soc_dapm_pre] = 0,
snd_soc_dapm_mic, snd_soc_dapm_mux, snd_soc_dapm_value_mux, [snd_soc_dapm_supply] = 1,
snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, [snd_soc_dapm_micbias] = 2,
snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, [snd_soc_dapm_mic] = 3,
snd_soc_dapm_post [snd_soc_dapm_mux] = 4,
[snd_soc_dapm_value_mux] = 4,
[snd_soc_dapm_dac] = 5,
[snd_soc_dapm_mixer] = 6,
[snd_soc_dapm_mixer_named_ctl] = 6,
[snd_soc_dapm_pga] = 7,
[snd_soc_dapm_adc] = 8,
[snd_soc_dapm_hp] = 9,
[snd_soc_dapm_spk] = 10,
[snd_soc_dapm_post] = 11,
}; };
static int dapm_down_seq[] = { static int dapm_down_seq[] = {
snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, [snd_soc_dapm_pre] = 0,
snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer, [snd_soc_dapm_adc] = 1,
snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias, [snd_soc_dapm_hp] = 2,
snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_supply, [snd_soc_dapm_spk] = 3,
snd_soc_dapm_post [snd_soc_dapm_pga] = 4,
[snd_soc_dapm_mixer_named_ctl] = 5,
[snd_soc_dapm_mixer] = 5,
[snd_soc_dapm_dac] = 6,
[snd_soc_dapm_mic] = 7,
[snd_soc_dapm_micbias] = 8,
[snd_soc_dapm_mux] = 9,
[snd_soc_dapm_value_mux] = 9,
[snd_soc_dapm_supply] = 10,
[snd_soc_dapm_post] = 11,
}; };
static void pop_wait(u32 pop_time) static void pop_wait(u32 pop_time)
...@@ -689,53 +707,211 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) ...@@ -689,53 +707,211 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
return power; return power;
} }
/* static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
* Scan a single DAPM widget for a complete audio path and update the struct snd_soc_dapm_widget *b,
* power status appropriately. int sort[])
*/
static int dapm_power_widget(struct snd_soc_codec *codec, int event,
struct snd_soc_dapm_widget *w)
{ {
int ret; if (sort[a->id] != sort[b->id])
return sort[a->id] - sort[b->id];
if (a->reg != b->reg)
return a->reg - b->reg;
switch (w->id) { return 0;
case snd_soc_dapm_pre: }
if (!w->event)
return 0;
if (event == SND_SOC_DAPM_STREAM_START) { /* Insert a widget in order into a DAPM power sequence. */
ret = w->event(w, static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,
NULL, SND_SOC_DAPM_PRE_PMU); struct list_head *list,
int sort[])
{
struct snd_soc_dapm_widget *w;
list_for_each_entry(w, list, power_list)
if (dapm_seq_compare(new_widget, w, sort) < 0) {
list_add_tail(&new_widget->power_list, &w->power_list);
return;
}
list_add_tail(&new_widget->power_list, list);
}
/* Apply the coalesced changes from a DAPM sequence */
static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
struct list_head *pending)
{
struct snd_soc_dapm_widget *w;
int reg, power, ret;
unsigned int value = 0;
unsigned int mask = 0;
unsigned int cur_mask;
reg = list_first_entry(pending, struct snd_soc_dapm_widget,
power_list)->reg;
list_for_each_entry(w, pending, power_list) {
cur_mask = 1 << w->shift;
BUG_ON(reg != w->reg);
if (w->invert)
power = !w->power;
else
power = w->power;
mask |= cur_mask;
if (power)
value |= 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) if (ret < 0)
return ret; pr_err("%s: pre event failed: %d\n",
} else if (event == SND_SOC_DAPM_STREAM_STOP) { w->name, ret);
ret = w->event(w, }
NULL, SND_SOC_DAPM_PRE_PMD);
/* 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) if (ret < 0)
return ret; pr_err("%s: pre event failed: %d\n",
w->name, ret);
} }
return 0;
case snd_soc_dapm_post: /* Lower PGA volume to reduce pops */
if (!w->event) if (w->id == snd_soc_dapm_pga && !w->power)
return 0; dapm_set_pga(w, w->power);
}
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);
if (event == SND_SOC_DAPM_STREAM_START) { /* 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, ret = w->event(w,
NULL, SND_SOC_DAPM_POST_PMU); NULL, SND_SOC_DAPM_POST_PMU);
if (ret < 0) if (ret < 0)
return ret; pr_err("%s: post event failed: %d\n",
} else if (event == SND_SOC_DAPM_STREAM_STOP) { w->name, ret);
ret = w->event(w, }
NULL, SND_SOC_DAPM_POST_PMD);
/* 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) if (ret < 0)
return ret; pr_err("%s: post event failed: %d\n",
w->name, ret);
} }
return 0; }
}
default: /* Apply a DAPM power sequence.
return dapm_generic_apply_power(w); *
* We walk over a pre-sorted list of widgets to apply power to. In
* order to minimise the number of writes to the device required
* multiple widgets will be updated in a single write where possible.
* Currently anything that requires more than a single write is not
* handled.
*/
static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
int event, int sort[])
{
struct snd_soc_dapm_widget *w, *n;
LIST_HEAD(pending);
int cur_sort = -1;
int cur_reg = SND_SOC_NOPM;
int ret;
list_for_each_entry_safe(w, n, list, power_list) {
ret = 0;
/* Do we need to apply any queued changes? */
if (sort[w->id] != cur_sort || w->reg != cur_reg) {
if (!list_empty(&pending))
dapm_seq_run_coalesced(codec, &pending);
INIT_LIST_HEAD(&pending);
cur_sort = -1;
cur_reg = SND_SOC_NOPM;
}
switch (w->id) {
case snd_soc_dapm_pre:
if (!w->event)
list_for_each_entry_safe_continue(w, n, list,
power_list);
if (event == SND_SOC_DAPM_STREAM_START)
ret = w->event(w,
NULL, SND_SOC_DAPM_PRE_PMU);
else if (event == SND_SOC_DAPM_STREAM_STOP)
ret = w->event(w,
NULL, SND_SOC_DAPM_PRE_PMD);
break;
case snd_soc_dapm_post:
if (!w->event)
list_for_each_entry_safe_continue(w, n, list,
power_list);
if (event == SND_SOC_DAPM_STREAM_START)
ret = w->event(w,
NULL, SND_SOC_DAPM_POST_PMU);
else if (event == SND_SOC_DAPM_STREAM_STOP)
ret = w->event(w,
NULL, SND_SOC_DAPM_POST_PMD);
break;
case snd_soc_dapm_input:
case snd_soc_dapm_output:
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line:
case snd_soc_dapm_spk:
/* No register support currently */
ret = dapm_generic_apply_power(w);
break;
default:
/* Queue it up for application */
cur_sort = sort[w->id];
cur_reg = w->reg;
list_move(&w->power_list, &pending);
break;
}
if (ret < 0)
pr_err("Failed to apply widget power: %d\n",
ret);
} }
if (!list_empty(&pending))
dapm_seq_run_coalesced(codec, &pending);
} }
/* /*
...@@ -751,23 +927,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) ...@@ -751,23 +927,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
{ {
struct snd_soc_device *socdev = codec->socdev; struct snd_soc_device *socdev = codec->socdev;
struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *w;
LIST_HEAD(up_list);
LIST_HEAD(down_list);
int ret = 0; int ret = 0;
int i, power; int power;
int sys_power = 0; 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 /* Check which widgets we need to power and store them in
* lists indicating if they should be powered up or down. * lists indicating if they should be powered up or down.
*/ */
list_for_each_entry(w, &codec->dapm_widgets, list) { list_for_each_entry(w, &codec->dapm_widgets, list) {
switch (w->id) { switch (w->id) {
case snd_soc_dapm_pre: case snd_soc_dapm_pre:
list_add_tail(&codec->down_list, &w->power_list); dapm_seq_insert(w, &down_list, dapm_down_seq);
break; break;
case snd_soc_dapm_post: case snd_soc_dapm_post:
list_add_tail(&codec->up_list, &w->power_list); dapm_seq_insert(w, &up_list, dapm_up_seq);
break; break;
default: default:
...@@ -782,10 +957,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) ...@@ -782,10 +957,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
continue; continue;
if (power) if (power)
list_add_tail(&w->power_list, &codec->up_list); dapm_seq_insert(w, &up_list, dapm_up_seq);
else else
list_add_tail(&w->power_list, dapm_seq_insert(w, &down_list, dapm_down_seq);
&codec->down_list);
w->power = power; w->power = power;
break; break;
...@@ -802,32 +976,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) ...@@ -802,32 +976,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
} }
/* 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++) { dapm_seq_run(codec, &down_list, event, dapm_down_seq);
list_for_each_entry(w, &codec->down_list, power_list) {
/* is widget in stream order */
if (w->id != dapm_down_seq[i])
continue;
ret = dapm_power_widget(codec, event, w);
if (ret != 0)
pr_err("Failed to power down %s: %d\n",
w->name, ret);
}
}
/* Now power up. */ /* Now power up. */
for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) { dapm_seq_run(codec, &up_list, event, dapm_up_seq);
list_for_each_entry(w, &codec->up_list, power_list) {
/* is widget in stream order */
if (w->id != dapm_up_seq[i])
continue;
ret = dapm_power_widget(codec, event, w);
if (ret != 0)
pr_err("Failed to power up %s: %d\n",
w->name, ret);
}
}
/* If we just powered the last thing off drop to standby bias */ /* If we just powered the last thing off drop to standby bias */
if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) { if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
......
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