Loading include/sound/soc.h +0 −2 Original line number Diff line number Diff line Loading @@ -369,8 +369,6 @@ struct snd_soc_codec { enum snd_soc_bias_level bias_level; enum snd_soc_bias_level suspend_bias_level; struct delayed_work delayed_work; struct list_head up_list; struct list_head down_list; /* codec DAI's */ struct snd_soc_dai *dai; Loading sound/soc/soc-dapm.c +227 −75 Original line number Diff line number Diff line Loading @@ -52,19 +52,37 @@ /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { snd_soc_dapm_pre, snd_soc_dapm_supply, snd_soc_dapm_micbias, snd_soc_dapm_mic, snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post [snd_soc_dapm_pre] = 0, [snd_soc_dapm_supply] = 1, [snd_soc_dapm_micbias] = 2, [snd_soc_dapm_mic] = 3, [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[] = { snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_supply, snd_soc_dapm_post [snd_soc_dapm_pre] = 0, [snd_soc_dapm_adc] = 1, [snd_soc_dapm_hp] = 2, [snd_soc_dapm_spk] = 3, [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) Loading Loading @@ -689,53 +707,211 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) return power; } /* * Scan a single DAPM widget for a complete audio path and update the * power status appropriately. static int dapm_seq_compare(struct snd_soc_dapm_widget *a, struct snd_soc_dapm_widget *b, int sort[]) { if (sort[a->id] != sort[b->id]) return sort[a->id] - sort[b->id]; if (a->reg != b->reg) return a->reg - b->reg; return 0; } /* Insert a widget in order into a DAPM power sequence. */ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, 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) 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); } 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. * * 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 int dapm_power_widget(struct snd_soc_codec *codec, int event, struct snd_soc_dapm_widget *w) 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) return 0; list_for_each_entry_safe_continue(w, n, list, power_list); if (event == SND_SOC_DAPM_STREAM_START) { if (event == SND_SOC_DAPM_STREAM_START) ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU); if (ret < 0) return ret; } else if (event == SND_SOC_DAPM_STREAM_STOP) { else if (event == SND_SOC_DAPM_STREAM_STOP) ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD); if (ret < 0) return ret; } return 0; break; case snd_soc_dapm_post: if (!w->event) return 0; list_for_each_entry_safe_continue(w, n, list, power_list); if (event == SND_SOC_DAPM_STREAM_START) { if (event == SND_SOC_DAPM_STREAM_START) ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMU); if (ret < 0) return ret; } else if (event == SND_SOC_DAPM_STREAM_STOP) { else if (event == SND_SOC_DAPM_STREAM_STOP) ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD); if (ret < 0) return ret; } return 0; 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: return 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) pr_err("Failed to apply widget power: %d\n", ret); } if (!list_empty(&pending)) dapm_seq_run_coalesced(codec, &pending); } /* Loading @@ -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_dapm_widget *w; LIST_HEAD(up_list); LIST_HEAD(down_list); int ret = 0; int i, power; 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: list_add_tail(&codec->down_list, &w->power_list); dapm_seq_insert(w, &down_list, dapm_down_seq); break; case snd_soc_dapm_post: list_add_tail(&codec->up_list, &w->power_list); dapm_seq_insert(w, &up_list, dapm_up_seq); break; default: Loading @@ -782,10 +957,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) continue; if (power) list_add_tail(&w->power_list, &codec->up_list); dapm_seq_insert(w, &up_list, dapm_up_seq); else list_add_tail(&w->power_list, &codec->down_list); dapm_seq_insert(w, &down_list, dapm_down_seq); w->power = power; break; Loading @@ -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. */ for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) { 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); } } dapm_seq_run(codec, &down_list, event, dapm_down_seq); /* Now power up. */ for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) { 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); } } 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) { Loading Loading
include/sound/soc.h +0 −2 Original line number Diff line number Diff line Loading @@ -369,8 +369,6 @@ struct snd_soc_codec { enum snd_soc_bias_level bias_level; enum snd_soc_bias_level suspend_bias_level; struct delayed_work delayed_work; struct list_head up_list; struct list_head down_list; /* codec DAI's */ struct snd_soc_dai *dai; Loading
sound/soc/soc-dapm.c +227 −75 Original line number Diff line number Diff line Loading @@ -52,19 +52,37 @@ /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { snd_soc_dapm_pre, snd_soc_dapm_supply, snd_soc_dapm_micbias, snd_soc_dapm_mic, snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post [snd_soc_dapm_pre] = 0, [snd_soc_dapm_supply] = 1, [snd_soc_dapm_micbias] = 2, [snd_soc_dapm_mic] = 3, [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[] = { snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_supply, snd_soc_dapm_post [snd_soc_dapm_pre] = 0, [snd_soc_dapm_adc] = 1, [snd_soc_dapm_hp] = 2, [snd_soc_dapm_spk] = 3, [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) Loading Loading @@ -689,53 +707,211 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) return power; } /* * Scan a single DAPM widget for a complete audio path and update the * power status appropriately. static int dapm_seq_compare(struct snd_soc_dapm_widget *a, struct snd_soc_dapm_widget *b, int sort[]) { if (sort[a->id] != sort[b->id]) return sort[a->id] - sort[b->id]; if (a->reg != b->reg) return a->reg - b->reg; return 0; } /* Insert a widget in order into a DAPM power sequence. */ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, 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) 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); } 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. * * 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 int dapm_power_widget(struct snd_soc_codec *codec, int event, struct snd_soc_dapm_widget *w) 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) return 0; list_for_each_entry_safe_continue(w, n, list, power_list); if (event == SND_SOC_DAPM_STREAM_START) { if (event == SND_SOC_DAPM_STREAM_START) ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU); if (ret < 0) return ret; } else if (event == SND_SOC_DAPM_STREAM_STOP) { else if (event == SND_SOC_DAPM_STREAM_STOP) ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD); if (ret < 0) return ret; } return 0; break; case snd_soc_dapm_post: if (!w->event) return 0; list_for_each_entry_safe_continue(w, n, list, power_list); if (event == SND_SOC_DAPM_STREAM_START) { if (event == SND_SOC_DAPM_STREAM_START) ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMU); if (ret < 0) return ret; } else if (event == SND_SOC_DAPM_STREAM_STOP) { else if (event == SND_SOC_DAPM_STREAM_STOP) ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD); if (ret < 0) return ret; } return 0; 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: return 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) pr_err("Failed to apply widget power: %d\n", ret); } if (!list_empty(&pending)) dapm_seq_run_coalesced(codec, &pending); } /* Loading @@ -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_dapm_widget *w; LIST_HEAD(up_list); LIST_HEAD(down_list); int ret = 0; int i, power; 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: list_add_tail(&codec->down_list, &w->power_list); dapm_seq_insert(w, &down_list, dapm_down_seq); break; case snd_soc_dapm_post: list_add_tail(&codec->up_list, &w->power_list); dapm_seq_insert(w, &up_list, dapm_up_seq); break; default: Loading @@ -782,10 +957,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) continue; if (power) list_add_tail(&w->power_list, &codec->up_list); dapm_seq_insert(w, &up_list, dapm_up_seq); else list_add_tail(&w->power_list, &codec->down_list); dapm_seq_insert(w, &down_list, dapm_down_seq); w->power = power; break; Loading @@ -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. */ for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) { 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); } } dapm_seq_run(codec, &down_list, event, dapm_down_seq); /* Now power up. */ for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) { 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); } } 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) { Loading