diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-06-11 16:04:04 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-06-11 16:04:04 -0400 |
commit | 0e09b67e5886bf549ab14ef73de35d64ac867e2b (patch) | |
tree | a0d347f99e5faf766888a040156a17fa296772db /sound/soc/soc-dapm.c | |
parent | fa44c077eb2a17aa6913feceb493d13f45f3fa25 (diff) | |
parent | 291f3bbcacf278726911c713e14cedb71c486b16 (diff) |
Merge branch 'dapm' into for-2.6.32
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r-- | sound/soc/soc-dapm.c | 302 |
1 files changed, 227 insertions, 75 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7ad8afa8553d..653435930ad8 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -52,19 +52,37 @@ | |||
52 | 52 | ||
53 | /* dapm power sequences - make this per codec in the future */ | 53 | /* dapm power sequences - make this per codec in the future */ |
54 | static int dapm_up_seq[] = { | 54 | static int dapm_up_seq[] = { |
55 | snd_soc_dapm_pre, snd_soc_dapm_supply, snd_soc_dapm_micbias, | 55 | [snd_soc_dapm_pre] = 0, |
56 | snd_soc_dapm_mic, snd_soc_dapm_mux, snd_soc_dapm_value_mux, | 56 | [snd_soc_dapm_supply] = 1, |
57 | snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, | 57 | [snd_soc_dapm_micbias] = 2, |
58 | snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, | 58 | [snd_soc_dapm_mic] = 3, |
59 | snd_soc_dapm_post | 59 | [snd_soc_dapm_mux] = 4, |
60 | [snd_soc_dapm_value_mux] = 4, | ||
61 | [snd_soc_dapm_dac] = 5, | ||
62 | [snd_soc_dapm_mixer] = 6, | ||
63 | [snd_soc_dapm_mixer_named_ctl] = 6, | ||
64 | [snd_soc_dapm_pga] = 7, | ||
65 | [snd_soc_dapm_adc] = 8, | ||
66 | [snd_soc_dapm_hp] = 9, | ||
67 | [snd_soc_dapm_spk] = 10, | ||
68 | [snd_soc_dapm_post] = 11, | ||
60 | }; | 69 | }; |
61 | 70 | ||
62 | static int dapm_down_seq[] = { | 71 | static int dapm_down_seq[] = { |
63 | snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, | 72 | [snd_soc_dapm_pre] = 0, |
64 | snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer, | 73 | [snd_soc_dapm_adc] = 1, |
65 | snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias, | 74 | [snd_soc_dapm_hp] = 2, |
66 | snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_supply, | 75 | [snd_soc_dapm_spk] = 3, |
67 | snd_soc_dapm_post | 76 | [snd_soc_dapm_pga] = 4, |
77 | [snd_soc_dapm_mixer_named_ctl] = 5, | ||
78 | [snd_soc_dapm_mixer] = 5, | ||
79 | [snd_soc_dapm_dac] = 6, | ||
80 | [snd_soc_dapm_mic] = 7, | ||
81 | [snd_soc_dapm_micbias] = 8, | ||
82 | [snd_soc_dapm_mux] = 9, | ||
83 | [snd_soc_dapm_value_mux] = 9, | ||
84 | [snd_soc_dapm_supply] = 10, | ||
85 | [snd_soc_dapm_post] = 11, | ||
68 | }; | 86 | }; |
69 | 87 | ||
70 | static void pop_wait(u32 pop_time) | 88 | static void pop_wait(u32 pop_time) |
@@ -689,53 +707,211 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) | |||
689 | return power; | 707 | return power; |
690 | } | 708 | } |
691 | 709 | ||
692 | /* | 710 | static int dapm_seq_compare(struct snd_soc_dapm_widget *a, |
693 | * Scan a single DAPM widget for a complete audio path and update the | 711 | struct snd_soc_dapm_widget *b, |
694 | * power status appropriately. | 712 | int sort[]) |
695 | */ | ||
696 | static int dapm_power_widget(struct snd_soc_codec *codec, int event, | ||
697 | struct snd_soc_dapm_widget *w) | ||
698 | { | 713 | { |
699 | int ret; | 714 | if (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; | ||
700 | 718 | ||
701 | switch (w->id) { | 719 | return 0; |
702 | case snd_soc_dapm_pre: | 720 | } |
703 | if (!w->event) | ||
704 | return 0; | ||
705 | 721 | ||
706 | if (event == SND_SOC_DAPM_STREAM_START) { | 722 | /* Insert a widget in order into a DAPM power sequence. */ |
707 | ret = w->event(w, | 723 | static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, |
708 | NULL, SND_SOC_DAPM_PRE_PMU); | 724 | struct list_head *list, |
725 | int sort[]) | ||
726 | { | ||
727 | struct snd_soc_dapm_widget *w; | ||
728 | |||
729 | list_for_each_entry(w, list, power_list) | ||
730 | if (dapm_seq_compare(new_widget, w, sort) < 0) { | ||
731 | list_add_tail(&new_widget->power_list, &w->power_list); | ||
732 | return; | ||
733 | } | ||
734 | |||
735 | list_add_tail(&new_widget->power_list, list); | ||
736 | } | ||
737 | |||
738 | /* Apply the coalesced changes from a DAPM sequence */ | ||
739 | static void dapm_seq_run_coalesced(struct snd_soc_codec *codec, | ||
740 | struct list_head *pending) | ||
741 | { | ||
742 | struct snd_soc_dapm_widget *w; | ||
743 | int reg, power, ret; | ||
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 | /* power up pre event */ | ||
769 | if (w->power && w->event && | ||
770 | (w->event_flags & SND_SOC_DAPM_PRE_PMU)) { | ||
771 | pop_dbg(codec->pop_time, "pop test : %s PRE_PMU\n", | ||
772 | w->name); | ||
773 | ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU); | ||
709 | if (ret < 0) | 774 | if (ret < 0) |
710 | return ret; | 775 | pr_err("%s: pre event failed: %d\n", |
711 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | 776 | w->name, ret); |
712 | ret = w->event(w, | 777 | } |
713 | NULL, SND_SOC_DAPM_PRE_PMD); | 778 | |
779 | /* power down pre event */ | ||
780 | if (!w->power && w->event && | ||
781 | (w->event_flags & SND_SOC_DAPM_PRE_PMD)) { | ||
782 | pop_dbg(codec->pop_time, "pop test : %s PRE_PMD\n", | ||
783 | w->name); | ||
784 | ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD); | ||
714 | if (ret < 0) | 785 | if (ret < 0) |
715 | return ret; | 786 | pr_err("%s: pre event failed: %d\n", |
787 | w->name, ret); | ||
716 | } | 788 | } |
717 | return 0; | ||
718 | 789 | ||
719 | case snd_soc_dapm_post: | 790 | /* Lower PGA volume to reduce pops */ |
720 | if (!w->event) | 791 | if (w->id == snd_soc_dapm_pga && !w->power) |
721 | return 0; | 792 | dapm_set_pga(w, w->power); |
793 | } | ||
794 | |||
795 | if (reg >= 0) { | ||
796 | pop_dbg(codec->pop_time, | ||
797 | "pop test : Applying 0x%x/0x%x to %x in %dms\n", | ||
798 | value, mask, reg, codec->pop_time); | ||
799 | pop_wait(codec->pop_time); | ||
800 | snd_soc_update_bits(codec, reg, mask, value); | ||
801 | } | ||
802 | |||
803 | list_for_each_entry(w, pending, power_list) { | ||
804 | /* Raise PGA volume to reduce pops */ | ||
805 | if (w->id == snd_soc_dapm_pga && w->power) | ||
806 | dapm_set_pga(w, w->power); | ||
722 | 807 | ||
723 | if (event == SND_SOC_DAPM_STREAM_START) { | 808 | /* power up post event */ |
809 | if (w->power && w->event && | ||
810 | (w->event_flags & SND_SOC_DAPM_POST_PMU)) { | ||
811 | pop_dbg(codec->pop_time, "pop test : %s POST_PMU\n", | ||
812 | w->name); | ||
724 | ret = w->event(w, | 813 | ret = w->event(w, |
725 | NULL, SND_SOC_DAPM_POST_PMU); | 814 | NULL, SND_SOC_DAPM_POST_PMU); |
726 | if (ret < 0) | 815 | if (ret < 0) |
727 | return ret; | 816 | pr_err("%s: post event failed: %d\n", |
728 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | 817 | w->name, ret); |
729 | ret = w->event(w, | 818 | } |
730 | NULL, SND_SOC_DAPM_POST_PMD); | 819 | |
820 | /* power down post event */ | ||
821 | if (!w->power && w->event && | ||
822 | (w->event_flags & SND_SOC_DAPM_POST_PMD)) { | ||
823 | pop_dbg(codec->pop_time, "pop test : %s POST_PMD\n", | ||
824 | w->name); | ||
825 | ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD); | ||
731 | if (ret < 0) | 826 | if (ret < 0) |
732 | return ret; | 827 | pr_err("%s: post event failed: %d\n", |
828 | w->name, ret); | ||
733 | } | 829 | } |
734 | return 0; | 830 | } |
831 | } | ||
735 | 832 | ||
736 | default: | 833 | /* Apply a DAPM power sequence. |
737 | return dapm_generic_apply_power(w); | 834 | * |
835 | * We walk over a pre-sorted list of widgets to apply power to. In | ||
836 | * order to minimise the number of writes to the device required | ||
837 | * multiple widgets will be updated in a single write where possible. | ||
838 | * Currently anything that requires more than a single write is not | ||
839 | * handled. | ||
840 | */ | ||
841 | static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list, | ||
842 | int event, int sort[]) | ||
843 | { | ||
844 | struct snd_soc_dapm_widget *w, *n; | ||
845 | LIST_HEAD(pending); | ||
846 | int cur_sort = -1; | ||
847 | int cur_reg = SND_SOC_NOPM; | ||
848 | int ret; | ||
849 | |||
850 | list_for_each_entry_safe(w, n, list, power_list) { | ||
851 | ret = 0; | ||
852 | |||
853 | /* Do we need to apply any queued changes? */ | ||
854 | if (sort[w->id] != cur_sort || w->reg != cur_reg) { | ||
855 | if (!list_empty(&pending)) | ||
856 | dapm_seq_run_coalesced(codec, &pending); | ||
857 | |||
858 | INIT_LIST_HEAD(&pending); | ||
859 | cur_sort = -1; | ||
860 | cur_reg = SND_SOC_NOPM; | ||
861 | } | ||
862 | |||
863 | switch (w->id) { | ||
864 | case snd_soc_dapm_pre: | ||
865 | if (!w->event) | ||
866 | list_for_each_entry_safe_continue(w, n, list, | ||
867 | power_list); | ||
868 | |||
869 | if (event == SND_SOC_DAPM_STREAM_START) | ||
870 | ret = w->event(w, | ||
871 | NULL, SND_SOC_DAPM_PRE_PMU); | ||
872 | else if (event == SND_SOC_DAPM_STREAM_STOP) | ||
873 | ret = w->event(w, | ||
874 | NULL, SND_SOC_DAPM_PRE_PMD); | ||
875 | break; | ||
876 | |||
877 | case snd_soc_dapm_post: | ||
878 | if (!w->event) | ||
879 | list_for_each_entry_safe_continue(w, n, list, | ||
880 | power_list); | ||
881 | |||
882 | if (event == SND_SOC_DAPM_STREAM_START) | ||
883 | ret = w->event(w, | ||
884 | NULL, SND_SOC_DAPM_POST_PMU); | ||
885 | else if (event == SND_SOC_DAPM_STREAM_STOP) | ||
886 | ret = w->event(w, | ||
887 | NULL, SND_SOC_DAPM_POST_PMD); | ||
888 | break; | ||
889 | |||
890 | case snd_soc_dapm_input: | ||
891 | case snd_soc_dapm_output: | ||
892 | case snd_soc_dapm_hp: | ||
893 | case snd_soc_dapm_mic: | ||
894 | case snd_soc_dapm_line: | ||
895 | case snd_soc_dapm_spk: | ||
896 | /* No register support currently */ | ||
897 | ret = dapm_generic_apply_power(w); | ||
898 | break; | ||
899 | |||
900 | default: | ||
901 | /* Queue it up for application */ | ||
902 | cur_sort = sort[w->id]; | ||
903 | cur_reg = w->reg; | ||
904 | list_move(&w->power_list, &pending); | ||
905 | break; | ||
906 | } | ||
907 | |||
908 | if (ret < 0) | ||
909 | pr_err("Failed to apply widget power: %d\n", | ||
910 | ret); | ||
738 | } | 911 | } |
912 | |||
913 | if (!list_empty(&pending)) | ||
914 | dapm_seq_run_coalesced(codec, &pending); | ||
739 | } | 915 | } |
740 | 916 | ||
741 | /* | 917 | /* |
@@ -751,23 +927,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) | |||
751 | { | 927 | { |
752 | struct snd_soc_device *socdev = codec->socdev; | 928 | struct snd_soc_device *socdev = codec->socdev; |
753 | struct snd_soc_dapm_widget *w; | 929 | struct snd_soc_dapm_widget *w; |
930 | LIST_HEAD(up_list); | ||
931 | LIST_HEAD(down_list); | ||
754 | int ret = 0; | 932 | int ret = 0; |
755 | int i, power; | 933 | int power; |
756 | int sys_power = 0; | 934 | int sys_power = 0; |
757 | 935 | ||
758 | INIT_LIST_HEAD(&codec->up_list); | ||
759 | INIT_LIST_HEAD(&codec->down_list); | ||
760 | |||
761 | /* Check which widgets we need to power and store them in | 936 | /* Check which widgets we need to power and store them in |
762 | * lists indicating if they should be powered up or down. | 937 | * lists indicating if they should be powered up or down. |
763 | */ | 938 | */ |
764 | list_for_each_entry(w, &codec->dapm_widgets, list) { | 939 | list_for_each_entry(w, &codec->dapm_widgets, list) { |
765 | switch (w->id) { | 940 | switch (w->id) { |
766 | case snd_soc_dapm_pre: | 941 | case snd_soc_dapm_pre: |
767 | list_add_tail(&codec->down_list, &w->power_list); | 942 | dapm_seq_insert(w, &down_list, dapm_down_seq); |
768 | break; | 943 | break; |
769 | case snd_soc_dapm_post: | 944 | case snd_soc_dapm_post: |
770 | list_add_tail(&codec->up_list, &w->power_list); | 945 | dapm_seq_insert(w, &up_list, dapm_up_seq); |
771 | break; | 946 | break; |
772 | 947 | ||
773 | default: | 948 | default: |
@@ -782,10 +957,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) | |||
782 | continue; | 957 | continue; |
783 | 958 | ||
784 | if (power) | 959 | if (power) |
785 | list_add_tail(&w->power_list, &codec->up_list); | 960 | dapm_seq_insert(w, &up_list, dapm_up_seq); |
786 | else | 961 | else |
787 | list_add_tail(&w->power_list, | 962 | dapm_seq_insert(w, &down_list, dapm_down_seq); |
788 | &codec->down_list); | ||
789 | 963 | ||
790 | w->power = power; | 964 | w->power = power; |
791 | break; | 965 | break; |
@@ -802,32 +976,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) | |||
802 | } | 976 | } |
803 | 977 | ||
804 | /* Power down widgets first; try to avoid amplifying pops. */ | 978 | /* Power down widgets first; try to avoid amplifying pops. */ |
805 | for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) { | 979 | dapm_seq_run(codec, &down_list, event, dapm_down_seq); |
806 | list_for_each_entry(w, &codec->down_list, power_list) { | ||
807 | /* is widget in stream order */ | ||
808 | if (w->id != dapm_down_seq[i]) | ||
809 | continue; | ||
810 | |||
811 | ret = dapm_power_widget(codec, event, w); | ||
812 | if (ret != 0) | ||
813 | pr_err("Failed to power down %s: %d\n", | ||
814 | w->name, ret); | ||
815 | } | ||
816 | } | ||
817 | 980 | ||
818 | /* Now power up. */ | 981 | /* Now power up. */ |
819 | for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) { | 982 | dapm_seq_run(codec, &up_list, event, dapm_up_seq); |
820 | list_for_each_entry(w, &codec->up_list, power_list) { | ||
821 | /* is widget in stream order */ | ||
822 | if (w->id != dapm_up_seq[i]) | ||
823 | continue; | ||
824 | |||
825 | ret = dapm_power_widget(codec, event, w); | ||
826 | if (ret != 0) | ||
827 | pr_err("Failed to power up %s: %d\n", | ||
828 | w->name, ret); | ||
829 | } | ||
830 | } | ||
831 | 983 | ||
832 | /* If we just powered the last thing off drop to standby bias */ | 984 | /* If we just powered the last thing off drop to standby bias */ |
833 | if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) { | 985 | if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) { |