All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mark Brown <broonie@opensource.wolfsonmicro.com>
To: alsa-devel@alsa-project.org
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Subject: [PATCH 3/7] ASoC: Coalesce register writes for DAPM sequences
Date: Mon,  8 Jun 2009 15:28:49 +0100	[thread overview]
Message-ID: <1244471333-6485-3-git-send-email-broonie@opensource.wolfsonmicro.com> (raw)
In-Reply-To: <20090608142734.GA6137@rakim.wolfsonmicro.main>

Reduce the number of register writes we need to set the power state for
a CODEC by coalescing updates to widgets with the same sequence order and
same register into a single write.

This can be a noticable performance improvement with slow or heavily
contended control buses, such as I2C controllers with a low clock
frequency, and is particularly noticable when resuming. It can also
reduce the noticability of and pops and clicks by ensuring that left
and right channels are powered simultaneously if they are in the same
register.

Currently widgets that have events are not coalesced, including PGAs
which may use the volume ramping control.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 sound/soc/soc-dapm.c |  136 ++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 104 insertions(+), 32 deletions(-)

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 257d4f1..66f07cd 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -713,6 +713,8 @@ static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
 {
 	if (sort[a->id] != sort[b->id])
 		return sort[a->id] - sort[b->id];
+	if (a->reg != b->reg)
+		return a->reg - b->reg;
 
 	return 0;
 }
@@ -733,63 +735,133 @@ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,
 	list_add_tail(&new_widget->power_list, list);
 }
 
-/* Apply a DAPM power sequence */
-static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
-			 int event)
+/* Apply the coalesced changes from a DAPM sequence */
+static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
+				   struct list_head *pending)
 {
 	struct snd_soc_dapm_widget *w;
+	int reg, power;
+	unsigned int value = 0;
+	unsigned int mask = 0;
+	unsigned int cur_mask;
+
+	reg = list_first_entry(pending, struct snd_soc_dapm_widget,
+			       power_list)->reg;
+
+	list_for_each_entry(w, pending, power_list) {
+		cur_mask = 1 << w->shift;
+		BUG_ON(reg != w->reg);
+
+		if (w->invert)
+			power = !w->power;
+		else
+			power = w->power;
+
+		mask |= cur_mask;
+		if (power)
+			value |= cur_mask;
+
+		pop_dbg(codec->pop_time,
+			"pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
+			w->name, reg, value, mask);
+	}
+
+	pop_dbg(codec->pop_time,
+		"pop test : Applying 0x%x/0x%x to %x in %dms\n",
+		value, mask, reg, codec->pop_time);
+	pop_wait(codec->pop_time);
+	snd_soc_update_bits(codec, reg, mask, value);
+}
+
+/* Apply a DAPM power sequence.
+ *
+ * We walk over a pre-sorted list of widgets to apply power to.  In
+ * order to minimise the number of writes to the device required
+ * multiple widgets will be updated in a single write where possible.
+ * Currently anything that requires more than a single write is not
+ * handled.
+ */
+static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
+			 int event, int sort[])
+{
+	struct snd_soc_dapm_widget *w, *n;
+	LIST_HEAD(pending);
+	int cur_sort = -1;
+	int cur_reg = SND_SOC_NOPM;
 	int ret;
 
-	list_for_each_entry(w, list, power_list) {
+	list_for_each_entry_safe(w, n, list, power_list) {
+		ret = 0;
+
+		/* Do we need to apply any queued changes? */
+		if (sort[w->id] != cur_sort || w->reg != cur_reg) {
+			if (!list_empty(&pending))
+				dapm_seq_run_coalesced(codec, &pending);
+
+			INIT_LIST_HEAD(&pending);
+			cur_sort = -1;
+			cur_reg = SND_SOC_NOPM;
+		}
+
 		switch (w->id) {
 		case snd_soc_dapm_pre:
 			if (!w->event)
-				list_for_each_entry_continue(w, list,
-							     power_list);
+				list_for_each_entry_safe_continue(w, n, list,
+								  power_list);
 
-			if (event == SND_SOC_DAPM_STREAM_START) {
+			if (event == SND_SOC_DAPM_STREAM_START)
 				ret = w->event(w,
 					       NULL, SND_SOC_DAPM_PRE_PMU);
-				if (ret < 0)
-					pr_err("PRE widget failed: %d\n",
-					       ret);
-			} else if (event == SND_SOC_DAPM_STREAM_STOP) {
+			else if (event == SND_SOC_DAPM_STREAM_STOP)
 				ret = w->event(w,
 					       NULL, SND_SOC_DAPM_PRE_PMD);
-				if (ret < 0)
-					pr_err("PRE widget failed: %d\n",
-					       ret);
-			}
 			break;
 
 		case snd_soc_dapm_post:
 			if (!w->event)
-				list_for_each_entry_continue(w, list,
-							     power_list);
+				list_for_each_entry_safe_continue(w, n, list,
+								  power_list);
 
-			if (event == SND_SOC_DAPM_STREAM_START) {
+			if (event == SND_SOC_DAPM_STREAM_START)
 				ret = w->event(w,
 					       NULL, SND_SOC_DAPM_POST_PMU);
-				if (ret < 0)
-					pr_err("POST widget failed: %d\n",
-					       ret);
-			} else if (event == SND_SOC_DAPM_STREAM_STOP) {
+			else if (event == SND_SOC_DAPM_STREAM_STOP)
 				ret = w->event(w,
 					       NULL, SND_SOC_DAPM_POST_PMD);
-				if (ret < 0)
-					pr_err("POST widget failed: %d\n",
-					       ret);
-			}
 			break;
 
-		default:
+		case snd_soc_dapm_input:
+		case snd_soc_dapm_output:
+		case snd_soc_dapm_hp:
+		case snd_soc_dapm_mic:
+		case snd_soc_dapm_line:
+		case snd_soc_dapm_spk:
+			/* No register support currently */
+		case snd_soc_dapm_pga:
+			/* Don't coalsece these yet due to gain ramping */
 			ret = dapm_generic_apply_power(w);
-			if (ret < 0)
-				pr_err("Failed to apply widget power: %d\n",
-				       ret);
 			break;
+
+		default:
+			/* If there's an event or an invalid register
+			 * then run immediately, otherwise store the
+			 * updates so that we can coalesce. */
+			if (w->reg >= 0 && !w->event) {
+				cur_sort = sort[w->id];
+				cur_reg = w->reg;
+				list_move(&w->power_list, &pending);
+			} else {
+				ret = dapm_generic_apply_power(w);
+			}
 		}
+
+		if (ret < 0)
+			pr_err("Failed to apply widget power: %d\n",
+			       ret);
 	}
+
+	if (!list_empty(&pending))
+		dapm_seq_run_coalesced(codec, &pending);
 }
 
 /*
@@ -857,10 +929,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 	}
 
 	/* Power down widgets first; try to avoid amplifying pops. */
-	dapm_seq_run(codec, &codec->down_list, event);
+	dapm_seq_run(codec, &codec->down_list, event, dapm_down_seq);
 
 	/* Now power up. */
-	dapm_seq_run(codec, &codec->up_list, event);
+	dapm_seq_run(codec, &codec->up_list, event, dapm_up_seq);
 
 	/* If we just powered the last thing off drop to standby bias */
 	if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
-- 
1.6.3.1

  parent reply	other threads:[~2009-06-08 14:28 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-06-08 14:27 [PATCH 0/7] DAPM power sequencing changes Mark Brown
2009-06-08 14:28 ` [PATCH 1/7] ASoC: Sort DAPM power sequences while building lists Mark Brown
2009-06-08 14:28 ` [PATCH 2/7] ASoC: Factor out DAPM sequence execution Mark Brown
2009-06-08 14:28 ` Mark Brown [this message]
2009-06-08 14:28 ` [PATCH 4/7] ASoC: Sort specialised mixers and muxes together Mark Brown
2009-06-08 14:28 ` [PATCH 5/7] ASoC: Coalesce power updates for DAPM widgets with events Mark Brown
2009-06-08 14:28 ` [PATCH 6/7] ASoC: Coalesce power updates for PGAs Mark Brown
2009-06-08 14:28 ` [PATCH 7/7] ASoC: Make DAPM power sequence lists local variables Mark Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1244471333-6485-3-git-send-email-broonie@opensource.wolfsonmicro.com \
    --to=broonie@opensource.wolfsonmicro.com \
    --cc=alsa-devel@alsa-project.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.