All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7] DAPM power sequencing changes
@ 2009-06-08 14:27 Mark Brown
  2009-06-08 14:28 ` [PATCH 1/7] ASoC: Sort DAPM power sequences while building lists Mark Brown
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Mark Brown @ 2009-06-08 14:27 UTC (permalink / raw)
  To: alsa-devel

I've implemented what I hope are some improvements to the DAPM power
sequence application.  Currently when it is implementing power changes
DAPM will do one register write per widget.  Normally this leads to
multiple writes to the same register since power control tends to be
clustered in a few registers, slowing things down.  This can get
noticable on slow or heavily contended buses, especially around resume.
The overall effect of these changes is that DAPM will try to minimise
the number of register writes it does when implementing power sequences.

As with any substantial change in the power sequencing there is a risk
that there may have been some performance regressions on some systems -
my biggest concern is that some thing were being helped by the delays
introduced by the repeated register writes and may require special
handling to fix.  My hope is that this won't happen if anything it should
fix some problems by ensuring that both left and right channels of
stereo paths get powered up together but without wide testing that's
hard to guarantee.  Any testing that you can do on your systems would be
greatly appreciated.

Obviously this is quite an invasive change so I don't intend to submit
to ALSA until after the 2.6.31 merge window has closed.  The patches can
be found here for the time being:

  git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git dapm

Mark Brown (7):
      ASoC: Sort DAPM power sequences while building lists
      ASoC: Factor out DAPM sequence execution
      ASoC: Coalesce register writes for DAPM sequences
      ASoC: Sort specialised mixers and muxes together
      ASoC: Coalesce power updates for DAPM widgets with events
      ASoC: Coalesce power updates for PGAs
      ASoC: Make DAPM power sequence lists local variables

 include/sound/soc.h  |    2 -
 sound/soc/soc-dapm.c |  302 +++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 227 insertions(+), 77 deletions(-)

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/7] ASoC: Sort DAPM power sequences while building lists
  2009-06-08 14:27 [PATCH 0/7] DAPM power sequencing changes Mark Brown
@ 2009-06-08 14:28 ` Mark Brown
  2009-06-08 14:28 ` [PATCH 2/7] ASoC: Factor out DAPM sequence execution Mark Brown
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Mark Brown @ 2009-06-08 14:28 UTC (permalink / raw)
  To: alsa-devel; +Cc: Mark Brown

In the past the DAPM power sequencing was done by iterating over the list
of widgets once for each widget type and powering widgets of that type.
Instead of doing that do the sorting at the time we insert the widgets
into the lists of widgets to apply power changes to. This reduces the
amount of computation required for seqencing still further, though the
costs are generally dwarfed by the costs of the register writes
implementing them.

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

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 21c6907..1b38e21 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -52,19 +52,37 @@
 
 /* dapm power sequences - make this per codec in the future */
 static int dapm_up_seq[] = {
-	snd_soc_dapm_pre, snd_soc_dapm_supply, snd_soc_dapm_micbias,
-	snd_soc_dapm_mic, snd_soc_dapm_mux, snd_soc_dapm_value_mux,
-	snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl,
-	snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
-	snd_soc_dapm_post
+	[snd_soc_dapm_pre] = 0,
+	[snd_soc_dapm_supply] = 1,
+	[snd_soc_dapm_micbias] = 2,
+	[snd_soc_dapm_mic] = 3,
+	[snd_soc_dapm_mux] = 4,
+	[snd_soc_dapm_value_mux] = 5,
+	[snd_soc_dapm_dac] = 6,
+	[snd_soc_dapm_mixer] = 7,
+	[snd_soc_dapm_mixer_named_ctl] = 8,
+	[snd_soc_dapm_pga] = 9,
+	[snd_soc_dapm_adc] = 10,
+	[snd_soc_dapm_hp] = 11,
+	[snd_soc_dapm_spk] = 12,
+	[snd_soc_dapm_post] = 13,
 };
 
 static int dapm_down_seq[] = {
-	snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
-	snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer,
-	snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias,
-	snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_supply,
-	snd_soc_dapm_post
+	[snd_soc_dapm_pre] = 0,
+	[snd_soc_dapm_adc] = 1,
+	[snd_soc_dapm_hp] = 2,
+	[snd_soc_dapm_spk] = 3,
+	[snd_soc_dapm_pga] = 4,
+	[snd_soc_dapm_mixer_named_ctl] = 5,
+	[snd_soc_dapm_mixer] = 6,
+	[snd_soc_dapm_dac] = 7,
+	[snd_soc_dapm_mic] = 8,
+	[snd_soc_dapm_micbias] = 9,
+	[snd_soc_dapm_mux] = 10,
+	[snd_soc_dapm_value_mux] = 11,
+	[snd_soc_dapm_supply] = 12,
+	[snd_soc_dapm_post] = 13,
 };
 
 static void pop_wait(u32 pop_time)
@@ -738,6 +756,32 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event,
 	}
 }
 
+static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
+			    struct snd_soc_dapm_widget *b,
+			    int sort[])
+{
+	if (sort[a->id] != sort[b->id])
+		return sort[a->id] - sort[b->id];
+
+	return 0;
+}
+
+/* Insert a widget in order into a DAPM power sequence. */
+static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,
+			    struct list_head *list,
+			    int sort[])
+{
+	struct snd_soc_dapm_widget *w;
+
+	list_for_each_entry(w, list, power_list)
+		if (dapm_seq_compare(new_widget, w, sort) < 0) {
+			list_add_tail(&new_widget->power_list, &w->power_list);
+			return;
+		}
+
+	list_add_tail(&new_widget->power_list, list);
+}
+
 /*
  * Scan each dapm widget for complete audio path.
  * A complete path is a route that has valid endpoints i.e.:-
@@ -752,7 +796,7 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 	struct snd_soc_device *socdev = codec->socdev;
 	struct snd_soc_dapm_widget *w;
 	int ret = 0;
-	int i, power;
+	int power;
 	int sys_power = 0;
 
 	INIT_LIST_HEAD(&codec->up_list);
@@ -764,10 +808,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 	list_for_each_entry(w, &codec->dapm_widgets, list) {
 		switch (w->id) {
 		case snd_soc_dapm_pre:
-			list_add_tail(&codec->down_list, &w->power_list);
+			dapm_seq_insert(w, &codec->down_list, dapm_down_seq);
 			break;
 		case snd_soc_dapm_post:
-			list_add_tail(&codec->up_list, &w->power_list);
+			dapm_seq_insert(w, &codec->up_list, dapm_up_seq);
 			break;
 
 		default:
@@ -782,10 +826,11 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 				continue;
 
 			if (power)
-				list_add_tail(&w->power_list, &codec->up_list);
+				dapm_seq_insert(w, &codec->up_list,
+						dapm_up_seq);
 			else
-				list_add_tail(&w->power_list,
-					      &codec->down_list);
+				dapm_seq_insert(w, &codec->down_list,
+						dapm_down_seq);
 
 			w->power = power;
 			break;
@@ -802,31 +847,19 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 	}
 
 	/* Power down widgets first; try to avoid amplifying pops. */
-	for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) {
-		list_for_each_entry(w, &codec->down_list, power_list) {
-			/* is widget in stream order */
-			if (w->id != dapm_down_seq[i])
-				continue;
-
-			ret = dapm_power_widget(codec, event, w);
-			if (ret != 0)
-				pr_err("Failed to power down %s: %d\n",
-				       w->name, ret);
-		}
+	list_for_each_entry(w, &codec->down_list, power_list) {
+		ret = dapm_power_widget(codec, event, w);
+		if (ret != 0)
+			pr_err("Failed to power down %s: %d\n",
+			       w->name, ret);
 	}
 
 	/* Now power up. */
-	for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) {
-		list_for_each_entry(w, &codec->up_list, power_list) {
-			/* is widget in stream order */
-			if (w->id != dapm_up_seq[i])
-				continue;
-
-			ret = dapm_power_widget(codec, event, w);
-			if (ret != 0)
-				pr_err("Failed to power up %s: %d\n",
-				       w->name, ret);
-		}
+	list_for_each_entry(w, &codec->up_list, power_list) {
+		ret = dapm_power_widget(codec, event, w);
+		if (ret != 0)
+			pr_err("Failed to power up %s: %d\n",
+			       w->name, ret);
 	}
 
 	/* If we just powered the last thing off drop to standby bias */
-- 
1.6.3.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/7] ASoC: Factor out DAPM sequence execution
  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 ` Mark Brown
  2009-06-08 14:28 ` [PATCH 3/7] ASoC: Coalesce register writes for DAPM sequences Mark Brown
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Mark Brown @ 2009-06-08 14:28 UTC (permalink / raw)
  To: alsa-devel; +Cc: Mark Brown

Lump the list walk into a single function, and pull in the power
application too so we can do some further refactoring. Pure code
motion.

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

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 1b38e21..257d4f1 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -707,55 +707,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
 	return power;
 }
 
-/*
- * Scan a single DAPM widget for a complete audio path and update the
- * power status appropriately.
- */
-static int dapm_power_widget(struct snd_soc_codec *codec, int event,
-			     struct snd_soc_dapm_widget *w)
-{
-	int ret;
-
-	switch (w->id) {
-	case snd_soc_dapm_pre:
-		if (!w->event)
-			return 0;
-
-		if (event == SND_SOC_DAPM_STREAM_START) {
-			ret = w->event(w,
-				       NULL, SND_SOC_DAPM_PRE_PMU);
-			if (ret < 0)
-				return ret;
-		} else if (event == SND_SOC_DAPM_STREAM_STOP) {
-			ret = w->event(w,
-				       NULL, SND_SOC_DAPM_PRE_PMD);
-			if (ret < 0)
-				return ret;
-		}
-		return 0;
-
-	case snd_soc_dapm_post:
-		if (!w->event)
-			return 0;
-
-		if (event == SND_SOC_DAPM_STREAM_START) {
-			ret = w->event(w,
-				       NULL, SND_SOC_DAPM_POST_PMU);
-			if (ret < 0)
-				return ret;
-		} else if (event == SND_SOC_DAPM_STREAM_STOP) {
-			ret = w->event(w,
-				       NULL, SND_SOC_DAPM_POST_PMD);
-			if (ret < 0)
-				return ret;
-		}
-		return 0;
-
-	default:
-		return dapm_generic_apply_power(w);
-	}
-}
-
 static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
 			    struct snd_soc_dapm_widget *b,
 			    int sort[])
@@ -782,6 +733,65 @@ 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)
+{
+	struct snd_soc_dapm_widget *w;
+	int ret;
+
+	list_for_each_entry(w, list, power_list) {
+		switch (w->id) {
+		case snd_soc_dapm_pre:
+			if (!w->event)
+				list_for_each_entry_continue(w, list,
+							     power_list);
+
+			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) {
+				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);
+
+			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) {
+				ret = w->event(w,
+					       NULL, SND_SOC_DAPM_POST_PMD);
+				if (ret < 0)
+					pr_err("POST widget failed: %d\n",
+					       ret);
+			}
+			break;
+
+		default:
+			ret = dapm_generic_apply_power(w);
+			if (ret < 0)
+				pr_err("Failed to apply widget power: %d\n",
+				       ret);
+			break;
+		}
+	}
+}
+
 /*
  * Scan each dapm widget for complete audio path.
  * A complete path is a route that has valid endpoints i.e.:-
@@ -847,20 +857,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 	}
 
 	/* Power down widgets first; try to avoid amplifying pops. */
-	list_for_each_entry(w, &codec->down_list, power_list) {
-		ret = dapm_power_widget(codec, event, w);
-		if (ret != 0)
-			pr_err("Failed to power down %s: %d\n",
-			       w->name, ret);
-	}
+	dapm_seq_run(codec, &codec->down_list, event);
 
 	/* Now power up. */
-	list_for_each_entry(w, &codec->up_list, power_list) {
-		ret = dapm_power_widget(codec, event, w);
-		if (ret != 0)
-			pr_err("Failed to power up %s: %d\n",
-			       w->name, ret);
-	}
+	dapm_seq_run(codec, &codec->up_list, event);
 
 	/* 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

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/7] ASoC: Coalesce register writes for DAPM sequences
  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
  2009-06-08 14:28 ` [PATCH 4/7] ASoC: Sort specialised mixers and muxes together Mark Brown
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Mark Brown @ 2009-06-08 14:28 UTC (permalink / raw)
  To: alsa-devel; +Cc: Mark Brown

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

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 4/7] ASoC: Sort specialised mixers and muxes together
  2009-06-08 14:27 [PATCH 0/7] DAPM power sequencing changes Mark Brown
                   ` (2 preceding siblings ...)
  2009-06-08 14:28 ` [PATCH 3/7] ASoC: Coalesce register writes for DAPM sequences Mark Brown
@ 2009-06-08 14:28 ` Mark Brown
  2009-06-08 14:28 ` [PATCH 5/7] ASoC: Coalesce power updates for DAPM widgets with events Mark Brown
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Mark Brown @ 2009-06-08 14:28 UTC (permalink / raw)
  To: alsa-devel; +Cc: Mark Brown

The more flexible value muxes and named mixers don't need to be sorted
differently from a power management point of view, they are different
only in terms of the control interface and not in terms of seqencing
behaviour.

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

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 66f07cd..9187db1 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -57,15 +57,15 @@ static int dapm_up_seq[] = {
 	[snd_soc_dapm_micbias] = 2,
 	[snd_soc_dapm_mic] = 3,
 	[snd_soc_dapm_mux] = 4,
-	[snd_soc_dapm_value_mux] = 5,
-	[snd_soc_dapm_dac] = 6,
-	[snd_soc_dapm_mixer] = 7,
-	[snd_soc_dapm_mixer_named_ctl] = 8,
-	[snd_soc_dapm_pga] = 9,
-	[snd_soc_dapm_adc] = 10,
-	[snd_soc_dapm_hp] = 11,
-	[snd_soc_dapm_spk] = 12,
-	[snd_soc_dapm_post] = 13,
+	[snd_soc_dapm_value_mux] = 4,
+	[snd_soc_dapm_dac] = 5,
+	[snd_soc_dapm_mixer] = 6,
+	[snd_soc_dapm_mixer_named_ctl] = 6,
+	[snd_soc_dapm_pga] = 7,
+	[snd_soc_dapm_adc] = 8,
+	[snd_soc_dapm_hp] = 9,
+	[snd_soc_dapm_spk] = 10,
+	[snd_soc_dapm_post] = 11,
 };
 
 static int dapm_down_seq[] = {
@@ -75,14 +75,14 @@ static int dapm_down_seq[] = {
 	[snd_soc_dapm_spk] = 3,
 	[snd_soc_dapm_pga] = 4,
 	[snd_soc_dapm_mixer_named_ctl] = 5,
-	[snd_soc_dapm_mixer] = 6,
-	[snd_soc_dapm_dac] = 7,
-	[snd_soc_dapm_mic] = 8,
-	[snd_soc_dapm_micbias] = 9,
-	[snd_soc_dapm_mux] = 10,
-	[snd_soc_dapm_value_mux] = 11,
-	[snd_soc_dapm_supply] = 12,
-	[snd_soc_dapm_post] = 13,
+	[snd_soc_dapm_mixer] = 5,
+	[snd_soc_dapm_dac] = 6,
+	[snd_soc_dapm_mic] = 7,
+	[snd_soc_dapm_micbias] = 8,
+	[snd_soc_dapm_mux] = 9,
+	[snd_soc_dapm_value_mux] = 9,
+	[snd_soc_dapm_supply] = 10,
+	[snd_soc_dapm_post] = 11,
 };
 
 static void pop_wait(u32 pop_time)
-- 
1.6.3.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 5/7] ASoC: Coalesce power updates for DAPM widgets with events
  2009-06-08 14:27 [PATCH 0/7] DAPM power sequencing changes Mark Brown
                   ` (3 preceding siblings ...)
  2009-06-08 14:28 ` [PATCH 4/7] ASoC: Sort specialised mixers and muxes together Mark Brown
@ 2009-06-08 14:28 ` 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
  6 siblings, 0 replies; 8+ messages in thread
From: Mark Brown @ 2009-06-08 14:28 UTC (permalink / raw)
  To: alsa-devel; +Cc: Mark Brown

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

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 9187db1..3fc791c 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -740,7 +740,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
 				   struct list_head *pending)
 {
 	struct snd_soc_dapm_widget *w;
-	int reg, power;
+	int reg, power, ret;
 	unsigned int value = 0;
 	unsigned int mask = 0;
 	unsigned int cur_mask;
@@ -764,13 +764,62 @@ static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
 		pop_dbg(codec->pop_time,
 			"pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
 			w->name, reg, value, mask);
+
+		/* power up pre event */
+		if (w->power && w->event &&
+		    (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
+			pop_dbg(codec->pop_time, "pop test : %s PRE_PMU\n",
+				w->name);
+			ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
+			if (ret < 0)
+				pr_err("%s: pre event failed: %d\n",
+				       w->name, ret);
+		}
+
+		/* power down pre event */
+		if (!w->power && w->event &&
+		    (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
+			pop_dbg(codec->pop_time, "pop test : %s PRE_PMD\n",
+				w->name);
+			ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
+			if (ret < 0)
+				pr_err("%s: pre event failed: %d\n",
+				       w->name, ret);
+		}
+	}
+
+	if (reg >= 0) {
+		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);
 	}
 
-	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);
+	list_for_each_entry(w, pending, power_list) {
+		/* power up post event */
+		if (w->power && w->event &&
+		    (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
+			pop_dbg(codec->pop_time, "pop test : %s POST_PMU\n",
+				w->name);
+			ret = w->event(w,
+				       NULL, SND_SOC_DAPM_POST_PMU);
+			if (ret < 0)
+				pr_err("%s: post event failed: %d\n",
+				       w->name, ret);
+		}
+
+		/* power down post event */
+		if (!w->power && w->event &&
+		    (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
+			pop_dbg(codec->pop_time, "pop test : %s POST_PMD\n",
+				w->name);
+			ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
+			if (ret < 0)
+				pr_err("%s: post event failed: %d\n",
+				       w->name, ret);
+		}
+	}
 }
 
 /* Apply a DAPM power sequence.
@@ -843,16 +892,11 @@ static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
 			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);
-			}
+			/* Queue it up for application */
+			cur_sort = sort[w->id];
+			cur_reg = w->reg;
+			list_move(&w->power_list, &pending);
+			break;
 		}
 
 		if (ret < 0)
-- 
1.6.3.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 6/7] ASoC: Coalesce power updates for PGAs
  2009-06-08 14:27 [PATCH 0/7] DAPM power sequencing changes Mark Brown
                   ` (4 preceding siblings ...)
  2009-06-08 14:28 ` [PATCH 5/7] ASoC: Coalesce power updates for DAPM widgets with events Mark Brown
@ 2009-06-08 14:28 ` Mark Brown
  2009-06-08 14:28 ` [PATCH 7/7] ASoC: Make DAPM power sequence lists local variables Mark Brown
  6 siblings, 0 replies; 8+ messages in thread
From: Mark Brown @ 2009-06-08 14:28 UTC (permalink / raw)
  To: alsa-devel; +Cc: Mark Brown

Handle gain ramping for PGAs so we can coalesce their power updates too.
This is not ideal since we can't cope properly with gain ramping for
stereo paths but that was the case without coalescing and gain ramping
is relatively infrequently used so the effects are limited.

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

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 3fc791c..7299ce4 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -786,6 +786,10 @@ static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
 				pr_err("%s: pre event failed: %d\n",
 				       w->name, ret);
 		}
+
+		/* Lower PGA volume to reduce pops */
+		if (w->id == snd_soc_dapm_pga && !w->power)
+			dapm_set_pga(w, w->power);
 	}
 
 	if (reg >= 0) {
@@ -797,6 +801,10 @@ static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
 	}
 
 	list_for_each_entry(w, pending, power_list) {
+		/* Raise PGA volume to reduce pops */
+		if (w->id == snd_soc_dapm_pga && w->power)
+			dapm_set_pga(w, w->power);
+
 		/* power up post event */
 		if (w->power && w->event &&
 		    (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
@@ -886,8 +894,6 @@ static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
 		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);
 			break;
 
-- 
1.6.3.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 7/7] ASoC: Make DAPM power sequence lists local variables
  2009-06-08 14:27 [PATCH 0/7] DAPM power sequencing changes Mark Brown
                   ` (5 preceding siblings ...)
  2009-06-08 14:28 ` [PATCH 6/7] ASoC: Coalesce power updates for PGAs Mark Brown
@ 2009-06-08 14:28 ` Mark Brown
  6 siblings, 0 replies; 8+ messages in thread
From: Mark Brown @ 2009-06-08 14:28 UTC (permalink / raw)
  To: alsa-devel; +Cc: Mark Brown

They are now only accessed within dapm_power_widgets() so can be local
to that function.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 include/sound/soc.h  |    2 --
 sound/soc/soc-dapm.c |   19 ++++++++-----------
 2 files changed, 8 insertions(+), 13 deletions(-)

diff --git a/include/sound/soc.h b/include/sound/soc.h
index cf6111d..5964dd6 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -369,8 +369,6 @@ struct snd_soc_codec {
 	enum snd_soc_bias_level bias_level;
 	enum snd_soc_bias_level suspend_bias_level;
 	struct delayed_work delayed_work;
-	struct list_head up_list;
-	struct list_head down_list;
 
 	/* codec DAI's */
 	struct snd_soc_dai *dai;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 7299ce4..1c30da1 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -927,23 +927,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 {
 	struct snd_soc_device *socdev = codec->socdev;
 	struct snd_soc_dapm_widget *w;
+	LIST_HEAD(up_list);
+	LIST_HEAD(down_list);
 	int ret = 0;
 	int power;
 	int sys_power = 0;
 
-	INIT_LIST_HEAD(&codec->up_list);
-	INIT_LIST_HEAD(&codec->down_list);
-
 	/* Check which widgets we need to power and store them in
 	 * lists indicating if they should be powered up or down.
 	 */
 	list_for_each_entry(w, &codec->dapm_widgets, list) {
 		switch (w->id) {
 		case snd_soc_dapm_pre:
-			dapm_seq_insert(w, &codec->down_list, dapm_down_seq);
+			dapm_seq_insert(w, &down_list, dapm_down_seq);
 			break;
 		case snd_soc_dapm_post:
-			dapm_seq_insert(w, &codec->up_list, dapm_up_seq);
+			dapm_seq_insert(w, &up_list, dapm_up_seq);
 			break;
 
 		default:
@@ -958,11 +957,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 				continue;
 
 			if (power)
-				dapm_seq_insert(w, &codec->up_list,
-						dapm_up_seq);
+				dapm_seq_insert(w, &up_list, dapm_up_seq);
 			else
-				dapm_seq_insert(w, &codec->down_list,
-						dapm_down_seq);
+				dapm_seq_insert(w, &down_list, dapm_down_seq);
 
 			w->power = power;
 			break;
@@ -979,10 +976,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_down_seq);
+	dapm_seq_run(codec, &down_list, event, dapm_down_seq);
 
 	/* Now power up. */
-	dapm_seq_run(codec, &codec->up_list, event, dapm_up_seq);
+	dapm_seq_run(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

^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2009-06-08 14:28 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH 3/7] ASoC: Coalesce register writes for DAPM sequences Mark Brown
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

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.