aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-dapm.c
diff options
context:
space:
mode:
authorJarkko Nikula <jhnikula@gmail.com>2010-12-14 05:18:32 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-12-15 13:01:34 -0500
commit7be31be880ee00c6f8d38184368e8a834923b469 (patch)
tree04b4337378d63ff8051ec2ffd3323680ad8a4225 /sound/soc/soc-dapm.c
parent97c866defc0fc6e18b49603ac19f732f53e79c46 (diff)
ASoC: Extend DAPM to handle power changes on cross-device paths
Power change event like stream start/stop or kcontrol change in a cross-device path originates from one device but codec bias and widget power changes must be populated to another devices on that path as well. This patch modifies the dapm_power_widgets so that all the widgets on a sound card are checked for a power change, not just those that are specific to originating device. Also bias management is extended to check all the devices. Only exception in bias management are widgetless codecs whose bias state is changed only if power change is originating from that context. DAPM context test is added to dapm_seq_run to take care of if power sequence extends to an another device which requires separate register writes. Signed-off-by: Jarkko Nikula <jhnikula@gmail.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r--sound/soc/soc-dapm.c109
1 files changed, 63 insertions, 46 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 8731e89646ae..f362d1de78f3 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -847,19 +847,22 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
847 LIST_HEAD(pending); 847 LIST_HEAD(pending);
848 int cur_sort = -1; 848 int cur_sort = -1;
849 int cur_reg = SND_SOC_NOPM; 849 int cur_reg = SND_SOC_NOPM;
850 struct snd_soc_dapm_context *cur_dapm = NULL;
850 int ret; 851 int ret;
851 852
852 list_for_each_entry_safe(w, n, list, power_list) { 853 list_for_each_entry_safe(w, n, list, power_list) {
853 ret = 0; 854 ret = 0;
854 855
855 /* Do we need to apply any queued changes? */ 856 /* Do we need to apply any queued changes? */
856 if (sort[w->id] != cur_sort || w->reg != cur_reg) { 857 if (sort[w->id] != cur_sort || w->reg != cur_reg ||
858 w->dapm != cur_dapm) {
857 if (!list_empty(&pending)) 859 if (!list_empty(&pending))
858 dapm_seq_run_coalesced(dapm, &pending); 860 dapm_seq_run_coalesced(cur_dapm, &pending);
859 861
860 INIT_LIST_HEAD(&pending); 862 INIT_LIST_HEAD(&pending);
861 cur_sort = -1; 863 cur_sort = -1;
862 cur_reg = SND_SOC_NOPM; 864 cur_reg = SND_SOC_NOPM;
865 cur_dapm = NULL;
863 } 866 }
864 867
865 switch (w->id) { 868 switch (w->id) {
@@ -903,6 +906,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
903 /* Queue it up for application */ 906 /* Queue it up for application */
904 cur_sort = sort[w->id]; 907 cur_sort = sort[w->id];
905 cur_reg = w->reg; 908 cur_reg = w->reg;
909 cur_dapm = w->dapm;
906 list_move(&w->power_list, &pending); 910 list_move(&w->power_list, &pending);
907 break; 911 break;
908 } 912 }
@@ -929,20 +933,22 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
929{ 933{
930 struct snd_soc_card *card = dapm->codec->card; 934 struct snd_soc_card *card = dapm->codec->card;
931 struct snd_soc_dapm_widget *w; 935 struct snd_soc_dapm_widget *w;
936 struct snd_soc_dapm_context *d;
932 LIST_HEAD(up_list); 937 LIST_HEAD(up_list);
933 LIST_HEAD(down_list); 938 LIST_HEAD(down_list);
934 int ret = 0; 939 int ret = 0;
935 int power; 940 int power;
936 int sys_power = 0;
937 941
938 trace_snd_soc_dapm_start(card); 942 trace_snd_soc_dapm_start(card);
939 943
944 list_for_each_entry(d, &card->dapm_list, list)
945 if (d->n_widgets)
946 d->dev_power = 0;
947
940 /* Check which widgets we need to power and store them in 948 /* Check which widgets we need to power and store them in
941 * lists indicating if they should be powered up or down. 949 * lists indicating if they should be powered up or down.
942 */ 950 */
943 list_for_each_entry(w, &card->widgets, list) { 951 list_for_each_entry(w, &card->widgets, list) {
944 if (w->dapm != dapm)
945 continue;
946 switch (w->id) { 952 switch (w->id) {
947 case snd_soc_dapm_pre: 953 case snd_soc_dapm_pre:
948 dapm_seq_insert(w, &down_list, dapm_down_seq); 954 dapm_seq_insert(w, &down_list, dapm_down_seq);
@@ -960,7 +966,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
960 else 966 else
961 power = 1; 967 power = 1;
962 if (power) 968 if (power)
963 sys_power = 1; 969 w->dapm->dev_power = 1;
964 970
965 if (w->power == power) 971 if (w->power == power)
966 continue; 972 continue;
@@ -984,22 +990,22 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
984 switch (event) { 990 switch (event) {
985 case SND_SOC_DAPM_STREAM_START: 991 case SND_SOC_DAPM_STREAM_START:
986 case SND_SOC_DAPM_STREAM_RESUME: 992 case SND_SOC_DAPM_STREAM_RESUME:
987 sys_power = 1; 993 dapm->dev_power = 1;
988 break; 994 break;
989 case SND_SOC_DAPM_STREAM_STOP: 995 case SND_SOC_DAPM_STREAM_STOP:
990 sys_power = !!dapm->codec->active; 996 dapm->dev_power = !!dapm->codec->active;
991 break; 997 break;
992 case SND_SOC_DAPM_STREAM_SUSPEND: 998 case SND_SOC_DAPM_STREAM_SUSPEND:
993 sys_power = 0; 999 dapm->dev_power = 0;
994 break; 1000 break;
995 case SND_SOC_DAPM_STREAM_NOP: 1001 case SND_SOC_DAPM_STREAM_NOP:
996 switch (dapm->bias_level) { 1002 switch (dapm->bias_level) {
997 case SND_SOC_BIAS_STANDBY: 1003 case SND_SOC_BIAS_STANDBY:
998 case SND_SOC_BIAS_OFF: 1004 case SND_SOC_BIAS_OFF:
999 sys_power = 0; 1005 dapm->dev_power = 0;
1000 break; 1006 break;
1001 default: 1007 default:
1002 sys_power = 1; 1008 dapm->dev_power = 1;
1003 break; 1009 break;
1004 } 1010 }
1005 break; 1011 break;
@@ -1008,21 +1014,24 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
1008 } 1014 }
1009 } 1015 }
1010 1016
1011 if (sys_power && dapm->bias_level == SND_SOC_BIAS_OFF) { 1017 list_for_each_entry(d, &dapm->card->dapm_list, list) {
1012 ret = snd_soc_dapm_set_bias_level(card, dapm, 1018 if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) {
1013 SND_SOC_BIAS_STANDBY); 1019 ret = snd_soc_dapm_set_bias_level(card, d,
1014 if (ret != 0) 1020 SND_SOC_BIAS_STANDBY);
1015 dev_err(dapm->dev, 1021 if (ret != 0)
1016 "Failed to turn on bias: %d\n", ret); 1022 dev_err(d->dev,
1017 } 1023 "Failed to turn on bias: %d\n", ret);
1024 }
1018 1025
1019 /* If we're changing to all on or all off then prepare */ 1026 /* If we're changing to all on or all off then prepare */
1020 if ((sys_power && dapm->bias_level == SND_SOC_BIAS_STANDBY) || 1027 if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) ||
1021 (!sys_power && dapm->bias_level == SND_SOC_BIAS_ON)) { 1028 (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) {
1022 ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_PREPARE); 1029 ret = snd_soc_dapm_set_bias_level(card, d,
1023 if (ret != 0) 1030 SND_SOC_BIAS_PREPARE);
1024 dev_err(dapm->dev, 1031 if (ret != 0)
1025 "Failed to prepare bias: %d\n", ret); 1032 dev_err(d->dev,
1033 "Failed to prepare bias: %d\n", ret);
1034 }
1026 } 1035 }
1027 1036
1028 /* Power down widgets first; try to avoid amplifying pops. */ 1037 /* Power down widgets first; try to avoid amplifying pops. */
@@ -1031,29 +1040,36 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
1031 /* Now power up. */ 1040 /* Now power up. */
1032 dapm_seq_run(dapm, &up_list, event, dapm_up_seq); 1041 dapm_seq_run(dapm, &up_list, event, dapm_up_seq);
1033 1042
1034 /* If we just powered the last thing off drop to standby bias */ 1043 list_for_each_entry(d, &dapm->card->dapm_list, list) {
1035 if (dapm->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) { 1044 /* If we just powered the last thing off drop to standby bias */
1036 ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_STANDBY); 1045 if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) {
1037 if (ret != 0) 1046 ret = snd_soc_dapm_set_bias_level(card, d,
1038 dev_err(dapm->dev, 1047 SND_SOC_BIAS_STANDBY);
1039 "Failed to apply standby bias: %d\n", ret); 1048 if (ret != 0)
1040 } 1049 dev_err(d->dev,
1050 "Failed to apply standby bias: %d\n",
1051 ret);
1052 }
1041 1053
1042 /* If we're in standby and can support bias off then do that */ 1054 /* If we're in standby and can support bias off then do that */
1043 if (dapm->bias_level == SND_SOC_BIAS_STANDBY && 1055 if (d->bias_level == SND_SOC_BIAS_STANDBY &&
1044 dapm->idle_bias_off) { 1056 d->idle_bias_off) {
1045 ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_OFF); 1057 ret = snd_soc_dapm_set_bias_level(card, d,
1046 if (ret != 0) 1058 SND_SOC_BIAS_OFF);
1047 dev_err(dapm->dev, 1059 if (ret != 0)
1048 "Failed to turn off bias: %d\n", ret); 1060 dev_err(d->dev,
1049 } 1061 "Failed to turn off bias: %d\n", ret);
1062 }
1050 1063
1051 /* If we just powered up then move to active bias */ 1064 /* If we just powered up then move to active bias */
1052 if (dapm->bias_level == SND_SOC_BIAS_PREPARE && sys_power) { 1065 if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) {
1053 ret = snd_soc_dapm_set_bias_level(card, dapm, SND_SOC_BIAS_ON); 1066 ret = snd_soc_dapm_set_bias_level(card, d,
1054 if (ret != 0) 1067 SND_SOC_BIAS_ON);
1055 dev_err(dapm->dev, 1068 if (ret != 0)
1056 "Failed to apply active bias: %d\n", ret); 1069 dev_err(d->dev,
1070 "Failed to apply active bias: %d\n",
1071 ret);
1072 }
1057 } 1073 }
1058 1074
1059 pop_dbg(dapm->dev, card->pop_time, 1075 pop_dbg(dapm->dev, card->pop_time,
@@ -2309,6 +2325,7 @@ void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm)
2309{ 2325{
2310 snd_soc_dapm_sys_remove(dapm->dev); 2326 snd_soc_dapm_sys_remove(dapm->dev);
2311 dapm_free_widgets(dapm); 2327 dapm_free_widgets(dapm);
2328 list_del(&dapm->list);
2312} 2329}
2313EXPORT_SYMBOL_GPL(snd_soc_dapm_free); 2330EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
2314 2331