X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=sound%2Fsoc%2Fsoc-dapm.c;h=c39146d435e227217ce12cb68376332230863cc1;hb=9b8a83b205bd07b06784028effd94515fe9278c3;hp=7e15914b363362406e868070eb04c347e5fc8c8a;hpb=867f503d580eafbcc342141bae53cf6a27d413b1;p=deliverable%2Flinux.git diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7e15914b3633..c39146d435e2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -48,6 +48,8 @@ #include +#define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++; + /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, @@ -117,6 +119,20 @@ 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, 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) @@ -316,7 +332,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, } } 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: @@ -328,13 +344,13 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, 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; @@ -443,6 +459,11 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) if (path->name != (char *)w->kcontrol_news[i].name) continue; + if (w->kcontrols[i]) { + path->kcontrol = w->kcontrols[i]; + continue; + } + wlistsize = sizeof(struct snd_soc_dapm_widget_list) + sizeof(struct snd_soc_dapm_widget *), wlist = kzalloc(wlistsize, GFP_KERNEL); @@ -579,8 +600,8 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) name + prefix_len, prefix); ret = snd_ctl_add(card, kcontrol); if (ret < 0) { - dev_err(dapm->dev, - "asoc: failed to add kcontrol %s\n", w->name); + dev_err(dapm->dev, "failed to add kcontrol %s: %d\n", + w->name, ret); kfree(wlist); return ret; } @@ -644,6 +665,8 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) struct snd_soc_dapm_path *path; int con = 0; + DAPM_UPDATE_STAT(widget, path_checks); + if (widget->id == snd_soc_dapm_supply) return 0; @@ -668,6 +691,8 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) } list_for_each_entry(path, &widget->sinks, list_source) { + DAPM_UPDATE_STAT(widget, neighbour_checks); + if (path->weak) continue; @@ -692,6 +717,8 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) struct snd_soc_dapm_path *path; int con = 0; + DAPM_UPDATE_STAT(widget, path_checks); + if (widget->id == snd_soc_dapm_supply) return 0; @@ -721,6 +748,8 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) } list_for_each_entry(path, &widget->sources, list_sink) { + DAPM_UPDATE_STAT(widget, neighbour_checks); + if (path->weak) continue; @@ -756,12 +785,29 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w, } EXPORT_SYMBOL_GPL(dapm_reg_event); +static int dapm_widget_power_check(struct snd_soc_dapm_widget *w) +{ + if (w->power_checked) + return w->new_power; + + if (w->force) + w->new_power = 1; + else + 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 int dapm_generic_check_power(struct snd_soc_dapm_widget *w) { int in, out; + DAPM_UPDATE_STAT(w, power_checks); + in = is_connected_input_ep(w); dapm_clear_walk(w->dapm); out = is_connected_output_ep(w); @@ -774,6 +820,8 @@ static int dapm_adc_check_power(struct snd_soc_dapm_widget *w) { int in; + DAPM_UPDATE_STAT(w, power_checks); + if (w->active) { in = is_connected_input_ep(w); dapm_clear_walk(w->dapm); @@ -788,6 +836,8 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w) { int out; + DAPM_UPDATE_STAT(w, power_checks); + if (w->active) { out = is_connected_output_ep(w); dapm_clear_walk(w->dapm); @@ -803,8 +853,12 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) struct snd_soc_dapm_path *path; int power = 0; + DAPM_UPDATE_STAT(w, power_checks); + /* Check if one of our outputs is connected */ list_for_each_entry(path, &w->sinks, list_source) { + DAPM_UPDATE_STAT(w, neighbour_checks); + if (path->weak) continue; @@ -815,13 +869,7 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) if (!path->sink) continue; - if (path->sink->force) { - power = 1; - break; - } - - if (path->sink->power_check && - path->sink->power_check(path->sink)) { + if (dapm_widget_power_check(path->sink)) { power = 1; break; } @@ -832,6 +880,11 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) return power; } +static int dapm_always_on_check_power(struct snd_soc_dapm_widget *w) +{ + return 1; +} + static int dapm_seq_compare(struct snd_soc_dapm_widget *a, struct snd_soc_dapm_widget *b, bool power_up) @@ -1172,6 +1225,78 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie) } } +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 + dapm_seq_insert(w, down_list, false); + + w->power = power; +} + +static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, + struct list_head *up_list, + struct list_head *down_list) +{ + int power; + + switch (w->id) { + case snd_soc_dapm_pre: + dapm_seq_insert(w, down_list, false); + break; + case snd_soc_dapm_post: + dapm_seq_insert(w, up_list, true); + break; + + default: + power = dapm_widget_power_check(w); + + dapm_widget_set_power(w, power, up_list, down_list); + break; + } +} + /* * Scan each dapm widget for complete audio path. * A complete path is a route that has valid endpoints i.e.:- @@ -1190,7 +1315,6 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) LIST_HEAD(down_list); LIST_HEAD(async_domain); enum snd_soc_bias_level bias; - int power; trace_snd_soc_dapm_start(card); @@ -1203,61 +1327,45 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) } } - /* Check which widgets we need to power and store them in - * lists indicating if they should be powered up or down. - */ + memset(&card->dapm_stats, 0, sizeof(card->dapm_stats)); + list_for_each_entry(w, &card->widgets, list) { - switch (w->id) { - case snd_soc_dapm_pre: - dapm_seq_insert(w, &down_list, false); - break; - case snd_soc_dapm_post: - dapm_seq_insert(w, &up_list, true); - break; + w->power_checked = false; + } - default: - if (!w->power_check) - continue; + /* Check which widgets we need to power and store them in + * 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->dapm_dirty, dirty) { + dapm_power_one_widget(w, &up_list, &down_list); + } - if (!w->force) - power = w->power_check(w); - else - power = 1; + list_for_each_entry(w, &card->widgets, list) { + list_del_init(&w->dirty); - if (power) { - d = w->dapm; + if (w->power) { + d = w->dapm; - /* Supplies and micbiases only bring - * the context up to STANDBY as unless - * something else is active and - * passing audio they generally don't - * require full power. - */ - switch (w->id) { - case snd_soc_dapm_supply: - case snd_soc_dapm_micbias: - if (d->target_bias_level < SND_SOC_BIAS_STANDBY) - d->target_bias_level = SND_SOC_BIAS_STANDBY; - break; - default: - d->target_bias_level = SND_SOC_BIAS_ON; - break; - } + /* Supplies and micbiases only bring the + * context up to STANDBY as unless something + * else is active and passing audio they + * generally don't require full power. + */ + switch (w->id) { + case snd_soc_dapm_supply: + case snd_soc_dapm_micbias: + if (d->target_bias_level < SND_SOC_BIAS_STANDBY) + d->target_bias_level = SND_SOC_BIAS_STANDBY; + break; + default: + d->target_bias_level = SND_SOC_BIAS_ON; + break; } - - if (w->power == power) - continue; - - trace_snd_soc_dapm_widget_power(w, power); - - if (power) - dapm_seq_insert(w, &up_list, true); - else - dapm_seq_insert(w, &down_list, false); - - w->power = power; - break; } + } /* If there are no DAPM widgets then try to figure out power from the @@ -1286,14 +1394,18 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) } } - /* Force all contexts in the card to the same bias state */ + /* Force all contexts in the card to the same bias state if + * they're not ground referenced. + */ bias = SND_SOC_BIAS_OFF; list_for_each_entry(d, &card->dapm_list, list) if (d->target_bias_level > bias) bias = d->target_bias_level; list_for_each_entry(d, &card->dapm_list, list) - d->target_bias_level = bias; + if (!d->idle_bias_off) + d->target_bias_level = bias; + trace_snd_soc_dapm_walk_done(card); /* Run all the bias changes in parallel */ list_for_each_entry(d, &dapm->card->dapm_list, list) @@ -1524,14 +1636,21 @@ 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, "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; } @@ -1556,11 +1675,13 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, /* found, now check type */ found = 1; path->connect = connect; - break; + 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; } @@ -1704,6 +1825,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, "pin configuration"); return 0; } @@ -2043,6 +2165,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) break; } + if (!w->power_check) + w->power_check = dapm_always_on_check_power; + /* Read the initial power state from the device */ if (w->reg >= 0) { val = soc_widget_read(w, w->reg); @@ -2056,6 +2181,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); } @@ -2537,6 +2663,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 */ @@ -2584,9 +2711,10 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, { if (!w->sname || w->dapm != dapm) continue; - dev_dbg(w->dapm->dev, "widget %s\n %s stream %s event %d\n", + 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; @@ -2604,6 +2732,10 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, } dapm_power_widgets(dapm, event); + + /* do we need to notify any clients that DAPM stream is complete */ + if (dapm->stream_event) + dapm->stream_event(dapm, event); } /** @@ -2672,6 +2804,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, "force enable"); return 0; } @@ -2763,7 +2896,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); /** * snd_soc_dapm_free - free dapm resources - * @card: SoC device + * @dapm: DAPM context * * Free all dapm widgets and resources. */