ASoC: Do DAPM power checks only for widgets changed since last run
In order to reduce the number of DAPM power checks we run keep a list of
widgets which have been changed since the last DAPM run and iterate over
that rather than the full widget list. Whenever we change the power state
for a widget we add all the source and sink widgets it has to the dirty
list, ensuring that all widgets in the path are checked.
This covers more widgets than we need to as some of the neighbour widgets
won't be connected but it's simpler as a first step. On one system I tried
this gave:
Power Path Neighbour
Before: 207 1939 2461
After: 114 1066 1327
which seems useful.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index cb00918..9d6bb33 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -119,6 +119,17 @@
kfree(buf);
}
+static bool dapm_dirty_widget(struct snd_soc_dapm_widget *w)
+{
+ return !list_empty(&w->dirty);
+}
+
+static void dapm_mark_dirty(struct snd_soc_dapm_widget *w)
+{
+ if (!dapm_dirty_widget(w))
+ list_add_tail(&w->dirty, &w->dapm->card->dapm_dirty);
+}
+
/* create a new dapm widget */
static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
const struct snd_soc_dapm_widget *_widget)
@@ -1208,11 +1219,30 @@
struct list_head *up_list,
struct list_head *down_list)
{
+ struct snd_soc_dapm_path *path;
+
if (w->power == power)
return;
trace_snd_soc_dapm_widget_power(w, power);
+ /* If we changed our power state perhaps our neigbours changed
+ * also. We're not yet smart enough to update relevant
+ * neighbours when we change the state of a widget, this acts
+ * as a proxy for that. It will notify more neighbours than
+ * is ideal.
+ */
+ list_for_each_entry(path, &w->sources, list_sink) {
+ if (path->source) {
+ dapm_mark_dirty(path->source);
+ }
+ }
+ list_for_each_entry(path, &w->sinks, list_source) {
+ if (path->sink) {
+ dapm_mark_dirty(path->sink);
+ }
+ }
+
if (power)
dapm_seq_insert(w, up_list, true);
else
@@ -1276,13 +1306,18 @@
memset(&card->dapm_stats, 0, sizeof(card->dapm_stats));
/* Check which widgets we need to power and store them in
- * lists indicating if they should be powered up or down.
+ * lists indicating if they should be powered up or down. We
+ * only check widgets that have been flagged as dirty but note
+ * that new widgets may be added to the dirty list while we
+ * iterate.
*/
- list_for_each_entry(w, &card->widgets, list) {
+ list_for_each_entry(w, &card->dapm_dirty, dirty) {
dapm_power_one_widget(w, &up_list, &down_list);
}
list_for_each_entry(w, &card->widgets, list) {
+ list_del_init(&w->dirty);
+
if (w->power) {
d = w->dapm;
@@ -1573,14 +1608,20 @@
found = 1;
/* we now need to match the string in the enum to the path */
- if (!(strcmp(path->name, e->texts[mux])))
+ if (!(strcmp(path->name, e->texts[mux]))) {
path->connect = 1; /* new connection */
- else
+ dapm_mark_dirty(path->source);
+ } else {
+ if (path->connect)
+ dapm_mark_dirty(path->source);
path->connect = 0; /* old connection must be powered down */
+ }
}
- if (found)
+ if (found) {
+ dapm_mark_dirty(widget);
dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
+ }
return 0;
}
@@ -1605,10 +1646,13 @@
/* found, now check type */
found = 1;
path->connect = connect;
+ dapm_mark_dirty(path->source);
}
- if (found)
+ if (found) {
+ dapm_mark_dirty(widget);
dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
+ }
return 0;
}
@@ -1752,6 +1796,7 @@
w->connected = status;
if (status == 0)
w->force = 0;
+ dapm_mark_dirty(w);
return 0;
}
@@ -2107,6 +2152,7 @@
w->new = 1;
+ list_add(&w->dirty, &(w->dapm->card->dapm_dirty));
dapm_debugfs_add_widget(w);
}
@@ -2588,6 +2634,7 @@
INIT_LIST_HEAD(&w->sources);
INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list);
+ INIT_LIST_HEAD(&w->dirty);
list_add(&w->list, &dapm->card->widgets);
/* machine layer set ups unconnected pins and insertions */
@@ -2638,6 +2685,7 @@
dev_vdbg(w->dapm->dev, "widget %s\n %s stream %s event %d\n",
w->name, w->sname, stream, event);
if (strstr(w->sname, stream)) {
+ dapm_mark_dirty(w);
switch(event) {
case SND_SOC_DAPM_STREAM_START:
w->active = 1;
@@ -2727,6 +2775,7 @@
dev_dbg(w->dapm->dev, "dapm: force enable pin %s\n", pin);
w->connected = 1;
w->force = 1;
+ dapm_mark_dirty(w);
return 0;
}