ASoC: Do DAPM power checks only for widgets changed since last run
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Mon, 3 Oct 2011 20:06:40 +0000 (21:06 +0100)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Tue, 4 Oct 2011 15:50:20 +0000 (16:50 +0100)
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>
include/sound/soc-dapm.h
include/sound/soc.h
sound/soc/soc-core.c
sound/soc/soc-dapm.c

index bb5953219d0b342d6feecd97d0ad76ad25202297..c080635b3c3551d45aa039c5e5bce2230ee1d115 100644 (file)
@@ -492,6 +492,7 @@ struct snd_soc_dapm_widget {
 
        /* used during DAPM updates */
        struct list_head power_list;
+       struct list_head dirty;
 };
 
 struct snd_soc_dapm_update {
index 9d0524a3a7f82d80c3eafc2752d576be8df1aa49..8ab1cfed1067abe35a65ae11a5aef5b744aced37 100644 (file)
@@ -826,6 +826,7 @@ struct snd_soc_card {
        struct list_head widgets;
        struct list_head paths;
        struct list_head dapm_list;
+       struct list_head dapm_dirty;
 
        /* Generic DAPM context for the card */
        struct snd_soc_dapm_context dapm;
index 1ed8093b44e8386e111cae4dae81b96ff1efd21a..778c177b5bfbd8b1a83c9bac0a318e036fc43b41 100644 (file)
@@ -2916,6 +2916,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
                card->rtd[i].dai_link = &card->dai_link[i];
 
        INIT_LIST_HEAD(&card->list);
+       INIT_LIST_HEAD(&card->dapm_dirty);
        card->instantiated = 0;
        mutex_init(&card->mutex);
 
index cb00918b08d620a970c92c41a041be39493c6ba7..9d6bb33e6094d7c9f646ad8d2584ba90927835e0 100644 (file)
@@ -119,6 +119,17 @@ static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...)
        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 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
                                  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 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
        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 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
 
                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 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
                /* 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 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
        w->connected = status;
        if (status == 0)
                w->force = 0;
+       dapm_mark_dirty(w);
 
        return 0;
 }
@@ -2107,6 +2152,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
 
                w->new = 1;
 
+               list_add(&w->dirty, &(w->dapm->card->dapm_dirty));
                dapm_debugfs_add_widget(w);
        }
 
@@ -2588,6 +2634,7 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        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 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
                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 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
        dev_dbg(w->dapm->dev, "dapm: force enable pin %s\n", pin);
        w->connected = 1;
        w->force = 1;
+       dapm_mark_dirty(w);
 
        return 0;
 }