From de02d0786d4075091f5b1860474cd21d85ff5862 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 20 Sep 2011 21:43:24 +0100 Subject: ASoC: Trace and collect statistics for DAPM graph walking One of the longest standing areas for improvement in ASoC has been the DAPM algorithm - it repeats the same checks many times whenever it is run and makes no effort to limit the areas of the graph it checks meaning we do an awful lot of walks over the full graph. This has never mattered too much as the size of the graph has generally been small in relation to the size of the devices supported and the speed of CPUs but it is annoying. In preparation for work on improving this insert a trace point after the graph walk has been done. This gives us specific timing information for the walk, and in order to give quantifiable (non-benchmark) numbers also count every time we check a link or check the power for a widget and report those numbers. Substantial changes in the algorithm may require tweaks to the stats but they should be useful for simpler things. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 4a440b52dd7a..6a1e13ea996d 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, @@ -649,6 +651,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; @@ -697,6 +701,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; @@ -767,6 +773,8 @@ 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); @@ -779,6 +787,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); @@ -793,6 +803,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); @@ -808,6 +820,8 @@ 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) { if (path->weak) @@ -1208,6 +1222,8 @@ 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. */ @@ -1299,6 +1315,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) list_for_each_entry(d, &card->dapm_list, list) 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) -- cgit v1.2.3 From 7c81beb048b49a9fe73254c6e6396e4b1937cdb9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 20 Sep 2011 22:22:32 +0100 Subject: ASoC: Factor out per-widget DAPM power checks The indentation is getting a little deep. Should be straight code motion, no functional changes. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 112 +++++++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 52 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6a1e13ea996d..84d1d799a0df 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1191,6 +1191,65 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie) } } +static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, + struct list_head *up_list, + struct list_head *down_list) +{ + struct snd_soc_dapm_context *d; + 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: + if (!w->power_check) + break; + + if (!w->force) + power = w->power_check(w); + else + power = 1; + + if (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; + } + } + + if (w->power == power) + break; + + 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; + } +} + /* * Scan each dapm widget for complete audio path. * A complete path is a route that has valid endpoints i.e.:- @@ -1209,7 +1268,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); @@ -1228,57 +1286,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) * lists indicating if they should be powered up or down. */ 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; - - default: - if (!w->power_check) - continue; - - if (!w->force) - power = w->power_check(w); - else - power = 1; - - if (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; - } - } - - 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; - } + dapm_power_one_widget(w, &up_list, &down_list); } /* If there are no DAPM widgets then try to figure out power from the -- cgit v1.2.3 From e56235e099d7290a2331b984a79f75bbe0865fe8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 21 Sep 2011 18:19:14 +0100 Subject: ASoC: Add another DAPM stat for neighbour checks The number of times we look at a potentially connected neighbour is just as important as the number of times we actually recurse into looking at that neighbour so also collect that statistic. Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 + include/trace/events/asoc.h | 7 +++++-- sound/soc/soc-dapm.c | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 0e2d01713cb6..bb5953219d0b 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -540,6 +540,7 @@ struct snd_soc_dapm_widget_list { struct snd_soc_dapm_stats { int power_checks; int path_checks; + int neighbour_checks; }; #endif diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index 2e1adf62e0a8..ab26f8aa3c78 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -226,16 +226,19 @@ TRACE_EVENT(snd_soc_dapm_walk_done, __string( name, card->name ) __field( int, power_checks ) __field( int, path_checks ) + __field( int, neighbour_checks ) ), TP_fast_assign( __assign_str(name, card->name); __entry->power_checks = card->dapm_stats.power_checks; __entry->path_checks = card->dapm_stats.path_checks; + __entry->neighbour_checks = card->dapm_stats.neighbour_checks; ), - TP_printk("%s: %d power checks, %d path checks", __get_str(name), - (int)__entry->power_checks, (int)__entry->path_checks) + TP_printk("%s: checks %d power, %d path, %d neighbour", + __get_str(name), (int)__entry->power_checks, + (int)__entry->path_checks, (int)__entry->neighbour_checks) ); TRACE_EVENT(snd_soc_jack_irq, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 84d1d799a0df..6cac04595ccd 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -677,6 +677,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; @@ -732,6 +734,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; -- cgit v1.2.3 From 85a843c50ffb3597928968250a3f552a45b1b9de Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 21 Sep 2011 21:29:47 +0100 Subject: ASoC: Don't force bias on ground referenced devices Currently we force all devices in the system to be at the same bias level. This is due to concerns about power or pop/click impacts from either ramping VMID or mismatching VMID on the analogue I/O lines between connected devices but does mean we power devices up more often than we really need to. If a device flags idle_bias_off this will usually mean that it's either all digital or ground referenced (in which case the idle and powered bias levels are identical) so this concern does not apply and we can save some power by leaving it off when not needed itself. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6cac04595ccd..2bde6b0c038b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1319,13 +1319,16 @@ 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); -- cgit v1.2.3 From a8fdac83a3703c7f35d4efe37a14e38aa256919b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 28 Sep 2011 18:20:26 +0100 Subject: ASoC: Also count neighbour checks for supplies Missed when the stat was originally added. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 2bde6b0c038b..c277228ec967 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -828,6 +828,8 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) /* 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; -- cgit v1.2.3 From 05623c4314cba3971f8476151aff73126127925f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 28 Sep 2011 17:02:31 +0100 Subject: ASoC: Factor write of widget power out into a separate function Split the decision about what the new power should be out from the implementation of that decision. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c277228ec967..dcbd4687b42f 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1197,6 +1197,23 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie) } } +static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, + struct list_head *up_list, + struct list_head *down_list) +{ + if (w->power == power) + return; + + 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; +} + static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, struct list_head *up_list, struct list_head *down_list) @@ -1241,17 +1258,7 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, } } - if (w->power == power) - break; - - 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; + dapm_widget_set_power(w, power, up_list, down_list); break; } } -- cgit v1.2.3 From f9de6d741d246583a8fdcf212cf14456a1622ce1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 28 Sep 2011 17:19:47 +0100 Subject: ASoC: Move bias level decision into main dapm_power_widgets() Future patches will try to reduce the number of widgets we check on each DAPM run but we're still going to need to look and see if the devices is on at all so we can manage the overall device bias. Move these checks out into the main dapm_power_widgets() function so we don't have to think about them for now. Once we're doing more incremental updates it'll probably be worth using refcounts for each bias level to avoid having to do the sweep over all widgets but that's not going to be where the big performance wins are. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index dcbd4687b42f..12bd01a1863a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1218,7 +1218,6 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, struct list_head *up_list, struct list_head *down_list) { - struct snd_soc_dapm_context *d; int power; switch (w->id) { @@ -1238,26 +1237,6 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, else power = 1; - if (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; - } - } - dapm_widget_set_power(w, power, up_list, down_list); break; } @@ -1302,6 +1281,29 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) dapm_power_one_widget(w, &up_list, &down_list); } + list_for_each_entry(w, &card->widgets, list) { + 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; + } + } + + } + /* If there are no DAPM widgets then try to figure out power from the * event type. */ -- cgit v1.2.3 From 35c64bcad5c8244d973efbf7e58f6e0e09635504 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 28 Sep 2011 18:23:53 +0100 Subject: ASoC: Ensure all DAPM widgets have a power check callback Makes the code simpler. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 12bd01a1863a..8d760449965d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -857,6 +857,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) @@ -1229,9 +1234,6 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, break; default: - if (!w->power_check) - break; - if (!w->force) power = w->power_check(w); else @@ -2090,6 +2092,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); -- cgit v1.2.3 From d805002befc52a7edbfb0ec202a10a767e67515d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 28 Sep 2011 18:28:23 +0100 Subject: ASoC: Factor out widget power check operation We've got the same code in two different places, let's have it in a single place instead. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8d760449965d..c1f3563133bc 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -771,6 +771,14 @@ 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->force) + return 1; + else + return w->power_check(w); +} + /* Generic check to see if a widget should be powered. */ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) @@ -840,13 +848,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; } @@ -1234,10 +1236,7 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, break; default: - if (!w->force) - power = w->power_check(w); - else - power = 1; + power = dapm_widget_power_check(w); dapm_widget_set_power(w, power, up_list, down_list); break; -- cgit v1.2.3 From 565631008f6dd27c3e975c2103141f344d80b84e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 3 Oct 2011 22:41:09 +0100 Subject: ASoC: Mark headphone, mic, speaker and line widgets as always connected We're not actually doing any dynamic power management based on connection and output drivers (which are pretty much the same thing) are marked as unconditionally connected already. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c1f3563133bc..cb00918b08d6 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -318,7 +318,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: @@ -330,13 +330,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; -- cgit v1.2.3 From db432b414e20b7218bbd91654d7be9c524a4337a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 3 Oct 2011 21:06:40 +0100 Subject: 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 --- include/sound/soc-dapm.h | 1 + include/sound/soc.h | 1 + sound/soc/soc-core.c | 1 + sound/soc/soc-dapm.c | 61 +++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 58 insertions(+), 6 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index bb5953219d0b..c080635b3c35 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -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 { diff --git a/include/sound/soc.h b/include/sound/soc.h index 9d0524a3a7f8..8ab1cfed1067 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -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; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1ed8093b44e8..778c177b5bfb 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -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); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index cb00918b08d6..9d6bb33e6094 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -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; } -- cgit v1.2.3 From fe4fda5d8f28d06ae8f1482f4bde8a83be16e44b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 3 Oct 2011 22:36:57 +0100 Subject: ASoC: Reduce the number of neigbours we mark dirty when updating power If two widgets are not currently connected then there is no need to propagate a power state change between them as we mark the affected widgets when we change a connection. Similarly if a neighbour widget is already in the state being set for the current widget then there is no need to recheck. On one system I tested this gave: Power Path Neighbour Before: 114 1066 1327 After: 106 970 1186 which is an improvement, although relatively small. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 9d6bb33e6094..214a709128d2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1215,6 +1215,21 @@ 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); +} + static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, struct list_head *up_list, struct list_head *down_list) @@ -1227,19 +1242,18 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, 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. + * also. */ list_for_each_entry(path, &w->sources, list_sink) { if (path->source) { - dapm_mark_dirty(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_mark_dirty(path->sink); + dapm_widget_set_peer_power(path->sink, power, + path->connect); } } -- cgit v1.2.3 From 75c1f891b4c394c607532fdcea294c2556e410c4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 4 Oct 2011 22:28:08 +0100 Subject: ASoC: Add verbose debugging showing why widgets get marked dirty Help diagnose why we're checking widgets by providing some logging when we first dirty them. This should possibly be a trace point if it's useful but can be absurdly verbose if enabled, we can always change it later if desired. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 214a709128d2..e6a08822227e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -124,10 +124,13 @@ 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) +static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) { - if (!dapm_dirty_widget(w)) + 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 */ @@ -1227,7 +1230,7 @@ static void dapm_widget_set_peer_power(struct snd_soc_dapm_widget *peer, /* 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); + dapm_mark_dirty(peer, "peer state change"); } static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, @@ -1624,16 +1627,17 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, /* we now need to match the string in the enum to the path */ if (!(strcmp(path->name, e->texts[mux]))) { path->connect = 1; /* new connection */ - dapm_mark_dirty(path->source); + dapm_mark_dirty(path->source, "mux connection"); } else { if (path->connect) - dapm_mark_dirty(path->source); + dapm_mark_dirty(path->source, + "mux disconnection"); path->connect = 0; /* old connection must be powered down */ } } if (found) { - dapm_mark_dirty(widget); + dapm_mark_dirty(widget, "mux change"); dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); } @@ -1660,11 +1664,11 @@ 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); + dapm_mark_dirty(path->source, "mixer connection"); } if (found) { - dapm_mark_dirty(widget); + dapm_mark_dirty(widget, "mixer update"); dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); } @@ -1810,7 +1814,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); + dapm_mark_dirty(w, "pin configuration"); return 0; } @@ -2699,7 +2703,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); + dapm_mark_dirty(w, "stream event"); switch(event) { case SND_SOC_DAPM_STREAM_START: w->active = 1; @@ -2789,7 +2793,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); + dapm_mark_dirty(w, "force enable"); return 0; } -- cgit v1.2.3 From 9b8a83b205bd07b06784028effd94515fe9278c3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 4 Oct 2011 22:15:59 +0100 Subject: ASoC: Only run power_check() on a widget once per run Some widgets will get power_check() run on them more than once during a DAPM run, most commonly due to supply widgets checking to see if their consumers are powered up. It's wasteful to do this so cache the result of power_check() during a run. For one system I tested this on I got an improvement of: Power Path Neighbour Before: 106 970 1186 After: 69 727 905 from this. Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 ++ sound/soc/soc-dapm.c | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index c080635b3c35..e2853daf802c 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -473,6 +473,8 @@ struct snd_soc_dapm_widget { unsigned char ext:1; /* has external widgets */ unsigned char force:1; /* force state */ unsigned char ignore_suspend:1; /* kept enabled over suspend */ + unsigned char new_power:1; /* power from this run */ + unsigned char power_checked:1; /* power checked this run */ int subseq; /* sort within widget type */ int (*power_check)(struct snd_soc_dapm_widget *w); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index e6a08822227e..c39146d435e2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -787,10 +787,17 @@ 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) - 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. @@ -1322,6 +1329,10 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) 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. We * only check widgets that have been flagged as dirty but note -- cgit v1.2.3 From f3bf3e456a8be9b359a8f4ff458ae1be4fc4c516 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 4 Oct 2011 22:43:31 +0100 Subject: ASoC: Don't mark the outputs of supplies as dirty on state changes The whole point of supply widgets is that they aren't inputs to their sinks so a state change in a supply should never affect the state of the widget being supplied and we don't need to mark them as dirty. Power Path Neighbour Before: 69 727 905 After: 63 607 731 This is particularly useful where supplies affect large portions of the chip (eg, a bandgap supplying the analogue sections). Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c39146d435e2..cbca1ddb6619 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1260,11 +1260,18 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool 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); + switch (w->id) { + case snd_soc_dapm_supply: + /* Supplies can't affect their outputs, only their inputs */ + break; + default: + list_for_each_entry(path, &w->sinks, list_source) { + if (path->sink) { + dapm_widget_set_peer_power(path->sink, power, + path->connect); + } } + break; } if (power) -- cgit v1.2.3 From f68d7e168785a2e89f615863fb5fab22518c8eb8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 4 Oct 2011 22:57:50 +0100 Subject: ASoC: Stop checking for supplied widgets after we find the first We don't really care how many widgets a supply is supplying, we just care if the number is non-zero. This didn't actually produce any improvement in the test cases I've been using but seems obviously sensible enough that I'm pushing it out anyway. We could do a similar thing for other widgets but this may be unhelpful for further refactorings Liam was working on aiming to allow us to identify connected audio paths. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index cbca1ddb6619..82d93bf3c251 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -851,7 +851,6 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w) 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); @@ -869,15 +868,13 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) if (!path->sink) continue; - if (dapm_widget_power_check(path->sink)) { - power = 1; - break; - } + if (dapm_widget_power_check(path->sink)) + return 1; } dapm_clear_walk(w->dapm); - return power; + return 0; } static int dapm_always_on_check_power(struct snd_soc_dapm_widget *w) -- cgit v1.2.3 From 7508b12a8eb713436feb65893ae7ada57bf165ce Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 5 Oct 2011 12:09:12 +0100 Subject: ASoC: Use dapm_mark_dirty() for new DAPM widgets for consistency Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 82d93bf3c251..8711aab01445 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2185,7 +2185,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_mark_dirty(w, "new widget"); dapm_debugfs_add_widget(w); } -- cgit v1.2.3 From 4f4c0072228785179d35b2bd9e48081ce9fa51f6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 7 Oct 2011 14:29:19 +0100 Subject: ASoC: Suppress early calls to snd_soc_dapm_sync() Ensure we only have one sync during the initial startup of the card by making snd_soc_dapm_sync() a noop on non-instantiated cards. This avoids any bounces due to things like jacks reporting their initial state on partially initialised cards. The callers that don't also get called at runtime should just be removed. Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 1 + sound/soc/soc-dapm.c | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2a2507627520..b65e3d40177c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1498,6 +1498,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) #endif card->instantiated = 1; + snd_soc_dapm_sync(&card->dapm); mutex_unlock(&card->mutex); return; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8711aab01445..e49c56d4951e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1845,6 +1845,13 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, */ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) { + /* + * Suppress early reports (eg, jacks syncing their state) to avoid + * silly DAPM runs during card startup. + */ + if (!dapm->card || !dapm->card->instantiated) + return 0; + return dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); -- cgit v1.2.3 From 7ca3a18b055ac6667f4e7e34eae6637270002402 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 8 Oct 2011 14:04:50 +0100 Subject: ASoC: Assign power_check when we allocate DAPM widgets This ensures none of the rest of the code ever encounters a widget which does not have a power check function. Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 67 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 28 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index e49c56d4951e..22fb7355b134 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2137,48 +2137,21 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) case snd_soc_dapm_switch: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: - w->power_check = dapm_generic_check_power; dapm_new_mixer(w); break; case snd_soc_dapm_mux: case snd_soc_dapm_virt_mux: case snd_soc_dapm_value_mux: - w->power_check = dapm_generic_check_power; dapm_new_mux(w); break; - case snd_soc_dapm_adc: - case snd_soc_dapm_aif_out: - w->power_check = dapm_adc_check_power; - break; - case snd_soc_dapm_dac: - case snd_soc_dapm_aif_in: - w->power_check = dapm_dac_check_power; - break; case snd_soc_dapm_pga: case snd_soc_dapm_out_drv: - w->power_check = dapm_generic_check_power; dapm_new_pga(w); break; - case snd_soc_dapm_input: - case snd_soc_dapm_output: - case snd_soc_dapm_micbias: - case snd_soc_dapm_spk: - case snd_soc_dapm_hp: - case snd_soc_dapm_mic: - case snd_soc_dapm_line: - w->power_check = dapm_generic_check_power; - break; - case snd_soc_dapm_supply: - w->power_check = dapm_supply_check_power; - case snd_soc_dapm_vmid: - case snd_soc_dapm_pre: - case snd_soc_dapm_post: + default: 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); @@ -2667,6 +2640,44 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, else snprintf(w->name, name_len, "%s", widget->name); + switch (w->id) { + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + w->power_check = dapm_generic_check_power; + break; + case snd_soc_dapm_mux: + case snd_soc_dapm_virt_mux: + case snd_soc_dapm_value_mux: + w->power_check = dapm_generic_check_power; + break; + case snd_soc_dapm_adc: + case snd_soc_dapm_aif_out: + w->power_check = dapm_adc_check_power; + break; + case snd_soc_dapm_dac: + case snd_soc_dapm_aif_in: + w->power_check = dapm_dac_check_power; + break; + case snd_soc_dapm_pga: + case snd_soc_dapm_out_drv: + case snd_soc_dapm_input: + case snd_soc_dapm_output: + case snd_soc_dapm_micbias: + case snd_soc_dapm_spk: + case snd_soc_dapm_hp: + case snd_soc_dapm_mic: + case snd_soc_dapm_line: + w->power_check = dapm_generic_check_power; + break; + case snd_soc_dapm_supply: + w->power_check = dapm_supply_check_power; + break; + default: + w->power_check = dapm_always_on_check_power; + break; + } + dapm->n_widgets++; w->dapm = dapm; w->codec = dapm->codec; -- cgit v1.2.3 From 024dc078558e64e4cebc62c096285430a61dd10e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 9 Oct 2011 11:52:05 +0100 Subject: ASoC: Cache connected input and output recursions The number of connected input and output endpoints for a given widgets can't change during a DAPM run so there is no need to redo the recursion through branches of the tree we've already visited. Doing this on one of my test systems gives an improvement of: Power Path Neighbour Before: 63 607 731 After: 63 141 181 which scales up well as more widgets are involved in paths. Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 ++ sound/soc/soc-dapm.c | 60 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 15 deletions(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index e2853daf802c..bfefc16137ea 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -495,6 +495,8 @@ struct snd_soc_dapm_widget { /* used during DAPM updates */ struct list_head power_list; struct list_head dirty; + int inputs; + int outputs; }; struct snd_soc_dapm_update { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 22fb7355b134..258326b031cf 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -665,6 +665,9 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) struct snd_soc_dapm_path *path; int con = 0; + if (widget->outputs >= 0) + return widget->outputs; + DAPM_UPDATE_STAT(widget, path_checks); if (widget->id == snd_soc_dapm_supply) @@ -673,21 +676,29 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) switch (widget->id) { case snd_soc_dapm_adc: case snd_soc_dapm_aif_out: - if (widget->active) - return snd_soc_dapm_suspend_check(widget); + if (widget->active) { + widget->outputs = snd_soc_dapm_suspend_check(widget); + return widget->outputs; + } default: break; } if (widget->connected) { /* connected pin ? */ - if (widget->id == snd_soc_dapm_output && !widget->ext) - return snd_soc_dapm_suspend_check(widget); + if (widget->id == snd_soc_dapm_output && !widget->ext) { + widget->outputs = snd_soc_dapm_suspend_check(widget); + return widget->outputs; + } /* connected jack or spk ? */ - if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk || - (widget->id == snd_soc_dapm_line && !list_empty(&widget->sources))) - return snd_soc_dapm_suspend_check(widget); + if (widget->id == snd_soc_dapm_hp || + widget->id == snd_soc_dapm_spk || + (widget->id == snd_soc_dapm_line && + !list_empty(&widget->sources))) { + widget->outputs = snd_soc_dapm_suspend_check(widget); + return widget->outputs; + } } list_for_each_entry(path, &widget->sinks, list_source) { @@ -705,6 +716,8 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) } } + widget->outputs = con; + return con; } @@ -717,6 +730,9 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) struct snd_soc_dapm_path *path; int con = 0; + if (widget->inputs >= 0) + return widget->inputs; + DAPM_UPDATE_STAT(widget, path_checks); if (widget->id == snd_soc_dapm_supply) @@ -726,25 +742,35 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) switch (widget->id) { case snd_soc_dapm_dac: case snd_soc_dapm_aif_in: - if (widget->active) - return snd_soc_dapm_suspend_check(widget); + if (widget->active) { + widget->inputs = snd_soc_dapm_suspend_check(widget); + return widget->inputs; + } default: break; } if (widget->connected) { /* connected pin ? */ - if (widget->id == snd_soc_dapm_input && !widget->ext) - return snd_soc_dapm_suspend_check(widget); + if (widget->id == snd_soc_dapm_input && !widget->ext) { + widget->inputs = snd_soc_dapm_suspend_check(widget); + return widget->inputs; + } /* connected VMID/Bias for lower pops */ - if (widget->id == snd_soc_dapm_vmid) - return snd_soc_dapm_suspend_check(widget); + if (widget->id == snd_soc_dapm_vmid) { + widget->inputs = snd_soc_dapm_suspend_check(widget); + return widget->inputs; + } /* connected jack ? */ if (widget->id == snd_soc_dapm_mic || - (widget->id == snd_soc_dapm_line && !list_empty(&widget->sinks))) - return snd_soc_dapm_suspend_check(widget); + (widget->id == snd_soc_dapm_line && + !list_empty(&widget->sinks))) { + widget->inputs = snd_soc_dapm_suspend_check(widget); + return widget->inputs; + } + } list_for_each_entry(path, &widget->sources, list_sink) { @@ -762,6 +788,8 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) } } + widget->inputs = con; + return con; } @@ -1335,6 +1363,8 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) list_for_each_entry(w, &card->widgets, list) { w->power_checked = false; + w->inputs = -1; + w->outputs = -1; } /* Check which widgets we need to power and store them in -- cgit v1.2.3 From 25c77c5fae5e0ef43ab6381f89fc41e26d2ca0f4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 8 Oct 2011 13:36:03 +0100 Subject: ASoC: Fix DAPM sync for TLV320AIC3x custom DAPM widget We really should be doing this in the core, not in a driver... Signed-off-by: Mark Brown Tested-by: Jarkko Nikula --- include/sound/soc-dapm.h | 3 +++ sound/soc/codecs/tlv320aic3x.c | 4 ++++ sound/soc/soc-dapm.c | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'sound/soc/soc-dapm.c') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index bfefc16137ea..17a4c17f19f5 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -381,6 +381,9 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin); +/* Mostly internal - should not normally be used */ +void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason); + /* dapm widget types */ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index d877b39b5000..be55b7f36282 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -197,6 +197,10 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, else /* old connection must be powered down */ path->connect = invert ? 1 : 0; + + dapm_mark_dirty(path->source, "tlv320aic3x source"); + dapm_mark_dirty(path->sink, "tlv320aic3x sink"); + break; } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 258326b031cf..f42e8b9fb17d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -124,7 +124,7 @@ 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) +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", @@ -132,6 +132,7 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) list_add_tail(&w->dirty, &w->dapm->card->dapm_dirty); } } +EXPORT_SYMBOL_GPL(dapm_mark_dirty); /* create a new dapm widget */ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( -- cgit v1.2.3