aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-dapm.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-06-07 07:51:26 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-06-08 08:51:04 -0400
commitb22ead2a510fdb30440753f90237e86fdac70fae (patch)
treec82e4d89c55014010ee5ce552cd469a5cab47624 /sound/soc/soc-dapm.c
parent163cac061c97394d4ef9c89efe5921dac937ddb8 (diff)
ASoC: Coalesce register writes for DAPM sequences
Reduce the number of register writes we need to set the power state for a CODEC by coalescing updates to widgets with the same sequence order and same register into a single write. This can be a noticable performance improvement with slow or heavily contended control buses, such as I2C controllers with a low clock frequency, and is particularly noticable when resuming. It can also reduce the noticability of and pops and clicks by ensuring that left and right channels are powered simultaneously if they are in the same register. Currently widgets that have events are not coalesced, including PGAs which may use the volume ramping control. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/soc-dapm.c')
-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) {