Commit 6ed25978 authored by Andy Green's avatar Andy Green Committed by Jaroslav Kysela

ALSA: ASoC: Don't block system resume

On OpenMoko soc-audio resume is taking 700ms of the whole resume time of
1.3s, dominated by writes to 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.

[Added fix for race with resume to suspend and fixed a couple of nits
from checkpatch -- broonie.]
Signed-off-by: default avatarAndy Green <andy@openmoko.com>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarJaroslav Kysela <perex@perex.cz>
parent abb68c26
...@@ -510,6 +510,7 @@ struct snd_soc_device { ...@@ -510,6 +510,7 @@ struct snd_soc_device {
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
struct snd_soc_codec_device *codec_dev; struct snd_soc_codec_device *codec_dev;
struct delayed_work delayed_work; struct delayed_work delayed_work;
struct work_struct deferred_resume_work;
void *codec_data; void *codec_data;
}; };
......
...@@ -639,6 +639,16 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) ...@@ -639,6 +639,16 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
struct snd_soc_codec *codec = socdev->codec; struct snd_soc_codec *codec = socdev->codec;
int i; int i;
/* Due to the resume being scheduled into a workqueue we could
* suspend before that's finished - wait for it to complete.
*/
snd_power_lock(codec->card);
snd_power_wait(codec->card, SNDRV_CTL_POWER_D0);
snd_power_unlock(codec->card);
/* 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 */ /* mute any active DAC's */
for (i = 0; i < machine->num_links; i++) { for (i = 0; i < machine->num_links; i++) {
struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai; struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
...@@ -691,16 +701,27 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) ...@@ -691,16 +701,27 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
return 0; return 0;
} }
/* powers up audio subsystem after a suspend */ /* deferred resume work, so resume can complete before we finished
static int soc_resume(struct platform_device *pdev) * 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_machine *machine = socdev->machine;
struct snd_soc_platform *platform = socdev->platform; struct snd_soc_platform *platform = socdev->platform;
struct snd_soc_codec_device *codec_dev = socdev->codec_dev; struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
struct snd_soc_codec *codec = socdev->codec; struct snd_soc_codec *codec = socdev->codec;
struct platform_device *pdev = to_platform_device(socdev->dev);
int i; 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) if (machine->resume_pre)
machine->resume_pre(pdev); machine->resume_pre(pdev);
...@@ -742,6 +763,22 @@ static int soc_resume(struct platform_device *pdev) ...@@ -742,6 +763,22 @@ static int soc_resume(struct platform_device *pdev)
if (machine->resume_post) if (machine->resume_post)
machine->resume_post(pdev); 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; return 0;
} }
...@@ -788,6 +825,9 @@ static int soc_probe(struct platform_device *pdev) ...@@ -788,6 +825,9 @@ static int soc_probe(struct platform_device *pdev)
/* DAPM stream work */ /* DAPM stream work */
INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work); INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work);
/* deferred resume work */
INIT_WORK(&socdev->deferred_resume_work, soc_resume_deferred);
return 0; return 0;
platform_err: platform_err:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment