-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Somebody in the thread at some point said:

| All you should need to do to make this work is use the snd_power*() API
| which ought to be a matter of a few lines of code above what you've got
| already.  If the core calls snd_power_change_state() to move into
| SNDRV_CTL_POWER_D3hot as it starts to suspend and then goes back into
| SNDRV_CTL_POWER_D0 once resume is complete then the ALSA core will
| ensure that user space is blocked until the resume is complete.

Excellent!

| Could you give that a spin?  If it works and survives further testing
| it'd be good to push your revised patch out to ALSA for merge in 2.6.27
| since it should be a noticable win for most users.
|
| It also occurs to me that you will be able to save a little time overall
| by making the driver only write back registers that aren't in the
| default state on resume rather than blindly writing them all back as the
| code currently does.  This is probably worth doing regardless of
| anything else, though the saving will depend on the exact configuration
| at suspend time.  Unfortunately this won't be too big a proportion of
| the time consumed - a lot of it will come from the power up sequencing
| writing registers multiple times to minimising pops.

I took your advice on the nits and attach a new version of the patch.
Thanks a lot for your hints.

I had one more realization a bit late in the day, on GTA02 Codec digital
side is "always on", only the analogue power is switched off in suspend.
~ In this scenario, the regs will probably not need any reload on resume
at all.  But I couldn't even see where all this fits into the
mach-gta02.c world, I guess it doesn't matter now if there will be a
general deferred resume reload of them all anyway.

- -Andy
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org

iEYEARECAAYFAkhQH04ACgkQOjLpvpq7dMqjuwCffl0IQhNmtySUG8Uj9T9GzU/L
CkYAniaYRM5b6iVAf8RaK36Yf2KETu6p
=shQD
-----END PGP SIGNATURE-----
improve-soc-audio-resume-in-workqueue.patch

From: Andy Green <[EMAIL PROTECTED]>

soc-audio resume is taking 700ms of the whole resume time
of 1.3s in order to spew registers back into the codec over
i2c.  This patch shunts the resume guts into a workqueue
which then is done asynchronously.

The "card" is locked using the ALSA power state apis as
suggested by Mark Brown.

Signed-off-by: Andy Green <[EMAIL PROTECTED]>
---

 include/sound/soc.h  |    1 +
 sound/soc/soc-core.c |   41 +++++++++++++++++++++++++++++++++++++----
 2 files changed, 38 insertions(+), 4 deletions(-)


diff --git a/include/sound/soc.h b/include/sound/soc.h
index aedb348..2d6c0eb 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -442,6 +442,7 @@ struct snd_soc_device {
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec_device *codec_dev;
 	struct delayed_work delayed_work;
+	struct work_struct deferred_resume_work;
 	void *codec_data;
 };
 
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 9e20333..70e1790 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -632,6 +632,9 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
 	struct snd_soc_codec *codec = socdev->codec;
 	int i;
 
+	/* we're going to block userspace touching us until resume completes */
+	snd_power_change_state(codec->card, SNDRV_CTL_POWER_D3hot);
+
 	/* mute any active DAC's */
 	for(i = 0; i < machine->num_links; i++) {
 		struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
@@ -684,20 +687,32 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
 	return 0;
 }
 
-/* powers up audio subsystem after a suspend */
-static int soc_resume(struct platform_device *pdev)
+/* deferred resume work, so resume can complete before we finished
+ * setting our codec back up, which can be very slow on I2C
+ */
+
+static void soc_resume_deferred(struct work_struct *work)
 {
- 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_device *socdev = container_of(work,
+						     struct snd_soc_device,
+						     deferred_resume_work);
  	struct snd_soc_machine *machine = socdev->machine;
  	struct snd_soc_platform *platform = socdev->platform;
  	struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
 	struct snd_soc_codec *codec = socdev->codec;
+	struct platform_device *pdev = to_platform_device(socdev->dev);
 	int i;
 
+	/* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
+	 * so userspace apps are blocked from touching us
+	 */
+
+	dev_info(socdev->dev, "starting resume work\n");
+
 	if (machine->resume_pre)
 		machine->resume_pre(pdev);
 
-	for(i = 0; i < machine->num_links; i++) {
+	for (i = 0; i < machine->num_links; i++) {
 		struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
 		if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97)
 			cpu_dai->resume(pdev, cpu_dai);
@@ -735,6 +750,21 @@ static int soc_resume(struct platform_device *pdev)
 	if (machine->resume_post)
 		machine->resume_post(pdev);
 
+	dev_info(socdev->dev, "resume work completed\n");
+
+	/* userspace can access us now we are back as we were before */
+	snd_power_change_state(codec->card, SNDRV_CTL_POWER_D0);
+}
+
+/* powers up audio subsystem after a suspend */
+static int soc_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	dev_info(socdev->dev, "scheduling resume work\n");
+	if (!schedule_work(&socdev->deferred_resume_work))
+		dev_err(socdev->dev, "work item may be lost\n");
+
 	return 0;
 }
 
@@ -781,6 +811,9 @@ static int soc_probe(struct platform_device *pdev)
 
 	/* DAPM stream work */
 	INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work);
+	/* deferred resume work */
+	INIT_WORK(&socdev->deferred_resume_work, soc_resume_deferred);
+
 	return 0;
 
 platform_err:

Reply via email to