aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sound/soc/soc-dapm.c136
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 */
737static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list, 739static 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 */
784static 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) {