aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-dapm.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-02-18 14:49:43 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-02-22 13:40:54 -0500
commit9d0624a740b773d961fe0dc454d748c60d892b71 (patch)
treeab7f2dde596391491d74ae2bc93935dd44b51893 /sound/soc/soc-dapm.c
parented5a4c472346e4bc1de37f84bb8d2aca26e59538 (diff)
ASoC: Run bias level changes for all DAPM contexts in parallel
As bias level changes can be quite time consuming and the bias changes for multiple devices aren't strongly tied to each other (if anything it can be advantageous to bring different devices up together) we can improve the state transition time for multi-component systems by running the bias level changes for all the devices in parallel. This is very simple to achieve using the kernel async functionality so use that to schedule the work. This should have no practical effect for the overwhelming majority of systems which have a single DAPM context - we'll bounce into another thread to do the bias level change but otherwise everything will happen in exactly the same order as it did before. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r--sound/soc/soc-dapm.c118
1 files changed, 67 insertions, 51 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 1a932bbe8251..200ae7cd2f00 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -32,6 +32,7 @@
32#include <linux/module.h> 32#include <linux/module.h>
33#include <linux/moduleparam.h> 33#include <linux/moduleparam.h>
34#include <linux/init.h> 34#include <linux/init.h>
35#include <linux/async.h>
35#include <linux/delay.h> 36#include <linux/delay.h>
36#include <linux/pm.h> 37#include <linux/pm.h>
37#include <linux/bitops.h> 38#include <linux/bitops.h>
@@ -1014,7 +1015,62 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
1014 } 1015 }
1015} 1016}
1016 1017
1018/* Async callback run prior to DAPM sequences - brings to _PREPARE if
1019 * they're changing state.
1020 */
1021static void dapm_pre_sequence_async(void *data, async_cookie_t cookie)
1022{
1023 struct snd_soc_dapm_context *d = data;
1024 int ret;
1025
1026 if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) {
1027 ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
1028 if (ret != 0)
1029 dev_err(d->dev,
1030 "Failed to turn on bias: %d\n", ret);
1031 }
1032
1033 /* If we're changing to all on or all off then prepare */
1034 if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) ||
1035 (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) {
1036 ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE);
1037 if (ret != 0)
1038 dev_err(d->dev,
1039 "Failed to prepare bias: %d\n", ret);
1040 }
1041}
1042
1043/* Async callback run prior to DAPM sequences - brings to their final
1044 * state.
1045 */
1046static void dapm_post_sequence_async(void *data, async_cookie_t cookie)
1047{
1048 struct snd_soc_dapm_context *d = data;
1049 int ret;
1050
1051 /* If we just powered the last thing off drop to standby bias */
1052 if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) {
1053 ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
1054 if (ret != 0)
1055 dev_err(d->dev, "Failed to apply standby bias: %d\n",
1056 ret);
1057 }
1017 1058
1059 /* If we're in standby and can support bias off then do that */
1060 if (d->bias_level == SND_SOC_BIAS_STANDBY && d->idle_bias_off) {
1061 ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF);
1062 if (ret != 0)
1063 dev_err(d->dev, "Failed to turn off bias: %d\n", ret);
1064 }
1065
1066 /* If we just powered up then move to active bias */
1067 if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) {
1068 ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON);
1069 if (ret != 0)
1070 dev_err(d->dev, "Failed to apply active bias: %d\n",
1071 ret);
1072 }
1073}
1018 1074
1019/* 1075/*
1020 * Scan each dapm widget for complete audio path. 1076 * Scan each dapm widget for complete audio path.
@@ -1032,7 +1088,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
1032 struct snd_soc_dapm_context *d; 1088 struct snd_soc_dapm_context *d;
1033 LIST_HEAD(up_list); 1089 LIST_HEAD(up_list);
1034 LIST_HEAD(down_list); 1090 LIST_HEAD(down_list);
1035 int ret = 0; 1091 LIST_HEAD(async_domain);
1036 int power; 1092 int power;
1037 1093
1038 trace_snd_soc_dapm_start(card); 1094 trace_snd_soc_dapm_start(card);
@@ -1110,25 +1166,11 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
1110 } 1166 }
1111 } 1167 }
1112 1168
1113 list_for_each_entry(d, &dapm->card->dapm_list, list) { 1169 /* Run all the bias changes in parallel */
1114 if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { 1170 list_for_each_entry(d, &dapm->card->dapm_list, list)
1115 ret = snd_soc_dapm_set_bias_level(d, 1171 async_schedule_domain(dapm_pre_sequence_async, d,
1116 SND_SOC_BIAS_STANDBY); 1172 &async_domain);
1117 if (ret != 0) 1173 async_synchronize_full_domain(&async_domain);
1118 dev_err(d->dev,
1119 "Failed to turn on bias: %d\n", ret);
1120 }
1121
1122 /* If we're changing to all on or all off then prepare */
1123 if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) ||
1124 (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) {
1125 ret = snd_soc_dapm_set_bias_level(d,
1126 SND_SOC_BIAS_PREPARE);
1127 if (ret != 0)
1128 dev_err(d->dev,
1129 "Failed to prepare bias: %d\n", ret);
1130 }
1131 }
1132 1174
1133 /* Power down widgets first; try to avoid amplifying pops. */ 1175 /* Power down widgets first; try to avoid amplifying pops. */
1134 dapm_seq_run(dapm, &down_list, event, false); 1176 dapm_seq_run(dapm, &down_list, event, false);
@@ -1138,37 +1180,11 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
1138 /* Now power up. */ 1180 /* Now power up. */
1139 dapm_seq_run(dapm, &up_list, event, true); 1181 dapm_seq_run(dapm, &up_list, event, true);
1140 1182
1141 list_for_each_entry(d, &dapm->card->dapm_list, list) { 1183 /* Run all the bias changes in parallel */
1142 /* If we just powered the last thing off drop to standby bias */ 1184 list_for_each_entry(d, &dapm->card->dapm_list, list)
1143 if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) { 1185 async_schedule_domain(dapm_post_sequence_async, d,
1144 ret = snd_soc_dapm_set_bias_level(d, 1186 &async_domain);
1145 SND_SOC_BIAS_STANDBY); 1187 async_synchronize_full_domain(&async_domain);
1146 if (ret != 0)
1147 dev_err(d->dev,
1148 "Failed to apply standby bias: %d\n",
1149 ret);
1150 }
1151
1152 /* If we're in standby and can support bias off then do that */
1153 if (d->bias_level == SND_SOC_BIAS_STANDBY &&
1154 d->idle_bias_off) {
1155 ret = snd_soc_dapm_set_bias_level(d,
1156 SND_SOC_BIAS_OFF);
1157 if (ret != 0)
1158 dev_err(d->dev,
1159 "Failed to turn off bias: %d\n", ret);
1160 }
1161
1162 /* If we just powered up then move to active bias */
1163 if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) {
1164 ret = snd_soc_dapm_set_bias_level(d,
1165 SND_SOC_BIAS_ON);
1166 if (ret != 0)
1167 dev_err(d->dev,
1168 "Failed to apply active bias: %d\n",
1169 ret);
1170 }
1171 }
1172 1188
1173 pop_dbg(dapm->dev, card->pop_time, 1189 pop_dbg(dapm->dev, card->pop_time,
1174 "DAPM sequencing finished, waiting %dms\n", card->pop_time); 1190 "DAPM sequencing finished, waiting %dms\n", card->pop_time);