aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-05-16 12:47:29 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-05-18 10:53:14 -0400
commit6d3ddc81f5762d54ce7d1db70eb757c6c12fabbc (patch)
treeb476a394790be86d97d9fc47beff0de6e1186c96
parentb7a755a8a145a7e34e735bda9c452317de7a538a (diff)
ASoC: Split DAPM power checks from sequencing of power changes
DAPM has always applied any changes to the power state of widgets as soon as it has determined that they are required. Instead of doing this store all the changes that are required on lists of widgets to power up and down, then iterate over those lists and apply the changes. This changes the sequence in which changes are implemented, doing all power downs before power ups and always using the up/down sequences (previously they were only used when changes were due to DAC/ADC power events). The error handling is also changed so that we continue attempting to power widgets if some changes fail. The main benefit of this is to allow future changes to do optimisations over the whole power sequence and to reduce the number of walks of the widget graph required to check the power status of widgets. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--include/sound/soc-dapm.h3
-rw-r--r--include/sound/soc.h2
-rw-r--r--sound/soc/soc-dapm.c81
3 files changed, 61 insertions, 25 deletions
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 533f9f256496..b3f789d0cee8 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -385,6 +385,9 @@ struct snd_soc_dapm_widget {
385 /* widget input and outputs */ 385 /* widget input and outputs */
386 struct list_head sources; 386 struct list_head sources;
387 struct list_head sinks; 387 struct list_head sinks;
388
389 /* used during DAPM updates */
390 struct list_head power_list;
388}; 391};
389 392
390#endif 393#endif
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 6ab80bf7abd2..8309ce81cf3b 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -372,6 +372,8 @@ struct snd_soc_codec {
372 enum snd_soc_bias_level bias_level; 372 enum snd_soc_bias_level bias_level;
373 enum snd_soc_bias_level suspend_bias_level; 373 enum snd_soc_bias_level suspend_bias_level;
374 struct delayed_work delayed_work; 374 struct delayed_work delayed_work;
375 struct list_head up_list;
376 struct list_head down_list;
375 377
376 /* codec DAI's */ 378 /* codec DAI's */
377 struct snd_soc_dai *dai; 379 struct snd_soc_dai *dai;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 7847f80e96d1..04ef84106d7c 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -658,7 +658,7 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
658static int dapm_power_widget(struct snd_soc_codec *codec, int event, 658static int dapm_power_widget(struct snd_soc_codec *codec, int event,
659 struct snd_soc_dapm_widget *w) 659 struct snd_soc_dapm_widget *w)
660{ 660{
661 int power, ret; 661 int ret;
662 662
663 switch (w->id) { 663 switch (w->id) {
664 case snd_soc_dapm_pre: 664 case snd_soc_dapm_pre:
@@ -696,18 +696,8 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event,
696 return 0; 696 return 0;
697 697
698 default: 698 default:
699 break; 699 return dapm_generic_apply_power(w);
700 } 700 }
701
702 if (!w->power_check)
703 return 0;
704
705 power = w->power_check(w);
706 if (w->power == power)
707 return 0;
708 w->power = power;
709
710 return dapm_generic_apply_power(w);
711} 701}
712 702
713/* 703/*
@@ -722,27 +712,68 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event,
722static int dapm_power_widgets(struct snd_soc_codec *codec, int event) 712static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
723{ 713{
724 struct snd_soc_dapm_widget *w; 714 struct snd_soc_dapm_widget *w;
725 int i, c = 1, *seq = NULL, ret = 0; 715 int ret = 0;
726 716 int i, power;
727 /* do we have a sequenced stream event */ 717
728 if (event == SND_SOC_DAPM_STREAM_START) { 718 INIT_LIST_HEAD(&codec->up_list);
729 c = ARRAY_SIZE(dapm_up_seq); 719 INIT_LIST_HEAD(&codec->down_list);
730 seq = dapm_up_seq; 720
731 } else if (event == SND_SOC_DAPM_STREAM_STOP) { 721 /* Check which widgets we need to power and store them in
732 c = ARRAY_SIZE(dapm_down_seq); 722 * lists indicating if they should be powered up or down.
733 seq = dapm_down_seq; 723 */
724 list_for_each_entry(w, &codec->dapm_widgets, list) {
725 switch (w->id) {
726 case snd_soc_dapm_pre:
727 list_add_tail(&codec->down_list, &w->power_list);
728 break;
729 case snd_soc_dapm_post:
730 list_add_tail(&codec->up_list, &w->power_list);
731 break;
732
733 default:
734 if (!w->power_check)
735 continue;
736
737 power = w->power_check(w);
738 if (w->power == power)
739 continue;
740
741 if (power)
742 list_add_tail(&w->power_list, &codec->up_list);
743 else
744 list_add_tail(&w->power_list,
745 &codec->down_list);
746
747 w->power = power;
748 break;
749 }
734 } 750 }
735 751
736 for (i = 0; i < c; i++) { 752 /* Power down widgets first; try to avoid amplifying pops. */
737 list_for_each_entry(w, &codec->dapm_widgets, list) { 753 for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) {
754 list_for_each_entry(w, &codec->down_list, power_list) {
755 /* is widget in stream order */
756 if (w->id != dapm_down_seq[i])
757 continue;
758
759 ret = dapm_power_widget(codec, event, w);
760 if (ret != 0)
761 pr_err("Failed to power down %s: %d\n",
762 w->name, ret);
763 }
764 }
738 765
766 /* Now power up. */
767 for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) {
768 list_for_each_entry(w, &codec->up_list, power_list) {
739 /* is widget in stream order */ 769 /* is widget in stream order */
740 if (seq && seq[i] && w->id != seq[i]) 770 if (w->id != dapm_up_seq[i])
741 continue; 771 continue;
742 772
743 ret = dapm_power_widget(codec, event, w); 773 ret = dapm_power_widget(codec, event, w);
744 if (ret != 0) 774 if (ret != 0)
745 return ret; 775 pr_err("Failed to power up %s: %d\n",
776 w->name, ret);
746 } 777 }
747 } 778 }
748 779