aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-dapm.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-06-11 16:04:04 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-06-11 16:04:04 -0400
commit0e09b67e5886bf549ab14ef73de35d64ac867e2b (patch)
treea0d347f99e5faf766888a040156a17fa296772db /sound/soc/soc-dapm.c
parentfa44c077eb2a17aa6913feceb493d13f45f3fa25 (diff)
parent291f3bbcacf278726911c713e14cedb71c486b16 (diff)
Merge branch 'dapm' into for-2.6.32
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r--sound/soc/soc-dapm.c302
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 */
54static int dapm_up_seq[] = { 54static 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
62static int dapm_down_seq[] = { 71static 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
70static void pop_wait(u32 pop_time) 88static 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/* 710static 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 */
696static 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, 723static 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 */
739static 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 */
841static 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) {