-----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: