-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi Mark -
I noticed today that our resume on defconfig-2.6.24 takes ~1.3s, and
that almost all of that is because of glacial i2c register restoring in
resumes of pcf50633 (450ms) and soc-audio (700ms): together it's 1150ms
of the 1300ms.
The pcf50633 resume was able to be reduced to 255ms by using a one-hit
block i2c transaction instead of individual ones and other
optimizations, but the WM8753 datasheet does not mention that it is able
to cope with autoincrementing register addresses like that.
So I made a little experimental patch that defers the guts of soc-audio
resume action into an immediately-scheduled workqueue that runs in
parallel with the rest of resume. The advantage for us is that resume
to userspace unfrozen is then reduced to ~450ms from 1300ms or 3 times
faster total.
It "works", audio is OK after resume, but it does not have any locking
to stop races with other stuff wanting to touch codec registers while it
is completing. Do you have any ideas how to have it block other access
to codec registers until it completes without making a locking Hell or
lots of invasiveness? It's fine if audio related userspace is going to
block briefly until it is fully resumed, instead of whole of userspace
is frozen until soc-audio completes resuming.
I attach the patch without locking for reference (not because it is safe
to use).
- -Andy
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org
iEYEARECAAYFAkhP7+EACgkQOjLpvpq7dMrY4ACfd5X2cTRS94/JpZhtOZEWsZ73
J5YAn1O7hJQpwA/OxWt7xBYKt00MSfs/
=d9cl
-----END PGP SIGNATURE-----
experimental-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.
Signed-off-by: Andy Green <[EMAIL PROTECTED]>
---
include/sound/soc.h | 2 ++
sound/soc/soc-core.c | 59 +++++++++++++++++++++++++++++++++++++-------------
2 files changed, 46 insertions(+), 15 deletions(-)
diff --git a/include/sound/soc.h b/include/sound/soc.h
index aedb348..f972edd 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -442,6 +442,8 @@ 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;
+ struct platform_device *pdev;
void *codec_data;
};
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 9e20333..6a6ebbc 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -684,27 +684,28 @@ 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;
int i;
- if (machine->resume_pre)
- machine->resume_pre(pdev);
-
- 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);
- }
+ /* unfortunately can't do this when work scheduled since the locks
+ * are accounted for by different pid and it blows warnings
+ */
+ dev_info(socdev->dev, "starting resume work\n");
if (codec_dev->resume)
- codec_dev->resume(pdev);
+ codec_dev->resume(socdev->pdev);
for(i = 0; i < codec->num_dai; i++) {
char* stream = codec->dai[i].playback.stream_name;
@@ -727,13 +728,38 @@ static int soc_resume(struct platform_device *pdev)
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);
+ cpu_dai->resume(socdev->pdev, cpu_dai);
if (platform->resume)
- platform->resume(pdev, cpu_dai);
+ platform->resume(socdev->pdev, cpu_dai);
}
if (machine->resume_post)
- machine->resume_post(pdev);
+ machine->resume_post(socdev->pdev);
+
+ dev_info(socdev->dev, "resume work completed\n");
+}
+
+/* powers up audio subsystem after a suspend */
+static int soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_machine *machine = socdev->machine;
+ int i;
+
+ socdev->pdev = pdev;
+
+ if (machine->resume_pre)
+ machine->resume_pre(pdev);
+
+ 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);
+ }
+
+ 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 +807,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: