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, const char *reason)
+{
+ if (!dapm_dirty_widget(w)) {
+ dev_vdbg(w->dapm->dev, "Marking %s dirty due to %s\n",
+ w->name, reason);
+ 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)
}
}
break;
- /* does not effect routing - always connected */
+ /* does not affect routing - always connected */
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
case snd_soc_dapm_output:
case snd_soc_dapm_supply:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
- p->connect = 1;
- break;
- /* does effect routing - dynamically connected */
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_spk:
case snd_soc_dapm_line:
+ p->connect = 1;
+ break;
+ /* does affect routing - dynamically connected */
case snd_soc_dapm_pre:
case snd_soc_dapm_post:
p->connect = 0;
static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
{
+ if (w->power_checked)
+ return w->new_power;
+
if (w->force)
- return 1;
+ w->new_power = 1;
else
- return w->power_check(w);
+ w->new_power = w->power_check(w);
+
+ w->power_checked = true;
+
+ return w->new_power;
}
/* Generic check to see if a widget should be powered.
}
}
+static void dapm_widget_set_peer_power(struct snd_soc_dapm_widget *peer,
+ bool power, bool connect)
+{
+ /* If a connection is being made or broken then that update
+ * will have marked the peer dirty, otherwise the widgets are
+ * not connected and this update has no impact. */
+ if (!connect)
+ return;
+
+ /* If the peer is already in the state we're moving to then we
+ * won't have an impact on it. */
+ if (power != peer->power)
+ dapm_mark_dirty(peer, "peer state change");
+}
+
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.
+ */
+ list_for_each_entry(path, &w->sources, list_sink) {
+ if (path->source) {
+ dapm_widget_set_peer_power(path->source, power,
+ path->connect);
+ }
+ }
+ list_for_each_entry(path, &w->sinks, list_source) {
+ if (path->sink) {
+ dapm_widget_set_peer_power(path->sink, power,
+ path->connect);
+ }
+ }
+
if (power)
dapm_seq_insert(w, up_list, true);
else
memset(&card->dapm_stats, 0, sizeof(card->dapm_stats));
+ list_for_each_entry(w, &card->widgets, list) {
+ w->power_checked = false;
+ }
+
/* 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;
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, "mux connection");
+ } else {
+ if (path->connect)
+ dapm_mark_dirty(path->source,
+ "mux disconnection");
path->connect = 0; /* old connection must be powered down */
+ }
}
- if (found)
+ if (found) {
+ dapm_mark_dirty(widget, "mux change");
dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
+ }
return 0;
}
/* found, now check type */
found = 1;
path->connect = connect;
+ dapm_mark_dirty(path->source, "mixer connection");
}
- if (found)
+ if (found) {
+ dapm_mark_dirty(widget, "mixer update");
dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
+ }
return 0;
}
w->connected = status;
if (status == 0)
w->force = 0;
+ dapm_mark_dirty(w, "pin configuration");
return 0;
}
w->new = 1;
+ list_add(&w->dirty, &(w->dapm->card->dapm_dirty));
dapm_debugfs_add_widget(w);
}
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 */
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, "stream event");
switch(event) {
case SND_SOC_DAPM_STREAM_START:
w->active = 1;
dev_dbg(w->dapm->dev, "dapm: force enable pin %s\n", pin);
w->connected = 1;
w->force = 1;
+ dapm_mark_dirty(w, "force enable");
return 0;
}