diff options
-rw-r--r-- | sound/soc/soc-dapm.c | 136 |
1 files changed, 104 insertions, 32 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 257d4f15e00e..66f07cdfb2f7 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -713,6 +713,8 @@ static int dapm_seq_compare(struct snd_soc_dapm_widget *a, | |||
713 | { | 713 | { |
714 | if (sort[a->id] != sort[b->id]) | 714 | if (sort[a->id] != sort[b->id]) |
715 | return sort[a->id] - sort[b->id]; | 715 | return sort[a->id] - sort[b->id]; |
716 | if (a->reg != b->reg) | ||
717 | return a->reg - b->reg; | ||
716 | 718 | ||
717 | return 0; | 719 | return 0; |
718 | } | 720 | } |
@@ -733,63 +735,133 @@ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, | |||
733 | list_add_tail(&new_widget->power_list, list); | 735 | list_add_tail(&new_widget->power_list, list); |
734 | } | 736 | } |
735 | 737 | ||
736 | /* Apply a DAPM power sequence */ | 738 | /* Apply the coalesced changes from a DAPM sequence */ |
737 | static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list, | 739 | static void dapm_seq_run_coalesced(struct snd_soc_codec *codec, |
738 | int event) | 740 | struct list_head *pending) |
739 | { | 741 | { |
740 | struct snd_soc_dapm_widget *w; | 742 | struct snd_soc_dapm_widget *w; |
743 | int reg, power; | ||
744 | unsigned int value = 0; | ||
745 | unsigned int mask = 0; | ||
746 | unsigned int cur_mask; | ||
747 | |||
748 | reg = list_first_entry(pending, struct snd_soc_dapm_widget, | ||
749 | power_list)->reg; | ||
750 | |||
751 | list_for_each_entry(w, pending, power_list) { | ||
752 | cur_mask = 1 << w->shift; | ||
753 | BUG_ON(reg != w->reg); | ||
754 | |||
755 | if (w->invert) | ||
756 | power = !w->power; | ||
757 | else | ||
758 | power = w->power; | ||
759 | |||
760 | mask |= cur_mask; | ||
761 | if (power) | ||
762 | value |= cur_mask; | ||
763 | |||
764 | pop_dbg(codec->pop_time, | ||
765 | "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", | ||
766 | w->name, reg, value, mask); | ||
767 | } | ||
768 | |||
769 | pop_dbg(codec->pop_time, | ||
770 | "pop test : Applying 0x%x/0x%x to %x in %dms\n", | ||
771 | value, mask, reg, codec->pop_time); | ||
772 | pop_wait(codec->pop_time); | ||
773 | snd_soc_update_bits(codec, reg, mask, value); | ||
774 | } | ||
775 | |||
776 | /* Apply a DAPM power sequence. | ||
777 | * | ||
778 | * We walk over a pre-sorted list of widgets to apply power to. In | ||
779 | * order to minimise the number of writes to the device required | ||
780 | * multiple widgets will be updated in a single write where possible. | ||
781 | * Currently anything that requires more than a single write is not | ||
782 | * handled. | ||
783 | */ | ||
784 | static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list, | ||
785 | int event, int sort[]) | ||
786 | { | ||
787 | struct snd_soc_dapm_widget *w, *n; | ||
788 | LIST_HEAD(pending); | ||
789 | int cur_sort = -1; | ||
790 | int cur_reg = SND_SOC_NOPM; | ||
741 | int ret; | 791 | int ret; |
742 | 792 | ||
743 | list_for_each_entry(w, list, power_list) { | 793 | list_for_each_entry_safe(w, n, list, power_list) { |
794 | ret = 0; | ||
795 | |||
796 | /* Do we need to apply any queued changes? */ | ||
797 | if (sort[w->id] != cur_sort || w->reg != cur_reg) { | ||
798 | if (!list_empty(&pending)) | ||
799 | dapm_seq_run_coalesced(codec, &pending); | ||
800 | |||
801 | INIT_LIST_HEAD(&pending); | ||
802 | cur_sort = -1; | ||
803 | cur_reg = SND_SOC_NOPM; | ||
804 | } | ||
805 | |||
744 | switch (w->id) { | 806 | switch (w->id) { |
745 | case snd_soc_dapm_pre: | 807 | case snd_soc_dapm_pre: |
746 | if (!w->event) | 808 | if (!w->event) |
747 | list_for_each_entry_continue(w, list, | 809 | list_for_each_entry_safe_continue(w, n, list, |
748 | power_list); | 810 | power_list); |
749 | 811 | ||
750 | if (event == SND_SOC_DAPM_STREAM_START) { | 812 | if (event == SND_SOC_DAPM_STREAM_START) |
751 | ret = w->event(w, | 813 | ret = w->event(w, |
752 | NULL, SND_SOC_DAPM_PRE_PMU); | 814 | NULL, SND_SOC_DAPM_PRE_PMU); |
753 | if (ret < 0) | 815 | else if (event == SND_SOC_DAPM_STREAM_STOP) |
754 | pr_err("PRE widget failed: %d\n", | ||
755 | ret); | ||
756 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | ||
757 | ret = w->event(w, | 816 | ret = w->event(w, |
758 | NULL, SND_SOC_DAPM_PRE_PMD); | 817 | NULL, SND_SOC_DAPM_PRE_PMD); |
759 | if (ret < 0) | ||
760 | pr_err("PRE widget failed: %d\n", | ||
761 | ret); | ||
762 | } | ||
763 | break; | 818 | break; |
764 | 819 | ||
765 | case snd_soc_dapm_post: | 820 | case snd_soc_dapm_post: |
766 | if (!w->event) | 821 | if (!w->event) |
767 | list_for_each_entry_continue(w, list, | 822 | list_for_each_entry_safe_continue(w, n, list, |
768 | power_list); | 823 | power_list); |
769 | 824 | ||
770 | if (event == SND_SOC_DAPM_STREAM_START) { | 825 | if (event == SND_SOC_DAPM_STREAM_START) |
771 | ret = w->event(w, | 826 | ret = w->event(w, |
772 | NULL, SND_SOC_DAPM_POST_PMU); | 827 | NULL, SND_SOC_DAPM_POST_PMU); |
773 | if (ret < 0) | 828 | else if (event == SND_SOC_DAPM_STREAM_STOP) |
774 | pr_err("POST widget failed: %d\n", | ||
775 | ret); | ||
776 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | ||
777 | ret = w->event(w, | 829 | ret = w->event(w, |
778 | NULL, SND_SOC_DAPM_POST_PMD); | 830 | NULL, SND_SOC_DAPM_POST_PMD); |
779 | if (ret < 0) | ||
780 | pr_err("POST widget failed: %d\n", | ||
781 | ret); | ||
782 | } | ||
783 | break; | 831 | break; |
784 | 832 | ||
785 | default: | 833 | case snd_soc_dapm_input: |
834 | case snd_soc_dapm_output: | ||
835 | case snd_soc_dapm_hp: | ||
836 | case snd_soc_dapm_mic: | ||
837 | case snd_soc_dapm_line: | ||
838 | case snd_soc_dapm_spk: | ||
839 | /* No register support currently */ | ||
840 | case snd_soc_dapm_pga: | ||
841 | /* Don't coalsece these yet due to gain ramping */ | ||
786 | ret = dapm_generic_apply_power(w); | 842 | ret = dapm_generic_apply_power(w); |
787 | if (ret < 0) | ||
788 | pr_err("Failed to apply widget power: %d\n", | ||
789 | ret); | ||
790 | break; | 843 | break; |
844 | |||
845 | default: | ||
846 | /* If there's an event or an invalid register | ||
847 | * then run immediately, otherwise store the | ||
848 | * updates so that we can coalesce. */ | ||
849 | if (w->reg >= 0 && !w->event) { | ||
850 | cur_sort = sort[w->id]; | ||
851 | cur_reg = w->reg; | ||
852 | list_move(&w->power_list, &pending); | ||
853 | } else { | ||
854 | ret = dapm_generic_apply_power(w); | ||
855 | } | ||
791 | } | 856 | } |
857 | |||
858 | if (ret < 0) | ||
859 | pr_err("Failed to apply widget power: %d\n", | ||
860 | ret); | ||
792 | } | 861 | } |
862 | |||
863 | if (!list_empty(&pending)) | ||
864 | dapm_seq_run_coalesced(codec, &pending); | ||
793 | } | 865 | } |
794 | 866 | ||
795 | /* | 867 | /* |
@@ -857,10 +929,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) | |||
857 | } | 929 | } |
858 | 930 | ||
859 | /* Power down widgets first; try to avoid amplifying pops. */ | 931 | /* Power down widgets first; try to avoid amplifying pops. */ |
860 | dapm_seq_run(codec, &codec->down_list, event); | 932 | dapm_seq_run(codec, &codec->down_list, event, dapm_down_seq); |
861 | 933 | ||
862 | /* Now power up. */ | 934 | /* Now power up. */ |
863 | dapm_seq_run(codec, &codec->up_list, event); | 935 | dapm_seq_run(codec, &codec->up_list, event, dapm_up_seq); |
864 | 936 | ||
865 | /* If we just powered the last thing off drop to standby bias */ | 937 | /* If we just powered the last thing off drop to standby bias */ |
866 | if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) { | 938 | if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) { |