aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Green <andy@openmoko.com>2008-06-13 11:24:05 -0400
committerJaroslav Kysela <perex@perex.cz>2008-06-16 03:32:31 -0400
commit6ed2597883b1b03ca94f62f0cfe908314cba6d6b (patch)
treed19f086cdd31ca77ea1583cc147b19dfd8458190
parentabb68c26ba15f8e84e580a40c0b1bc349cb534b0 (diff)
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: Andy Green <andy@openmoko.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--include/sound/soc.h1
-rw-r--r--sound/soc/soc-core.c46
2 files changed, 44 insertions, 3 deletions
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 1f5c62181002..340223a8f24c 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -510,6 +510,7 @@ struct snd_soc_device {
510 struct snd_soc_codec *codec; 510 struct snd_soc_codec *codec;
511 struct snd_soc_codec_device *codec_dev; 511 struct snd_soc_codec_device *codec_dev;
512 struct delayed_work delayed_work; 512 struct delayed_work delayed_work;
513 struct work_struct deferred_resume_work;
513 void *codec_data; 514 void *codec_data;
514}; 515};
515 516
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index c96a6184d66e..b931039632c5 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -639,6 +639,16 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
639 struct snd_soc_codec *codec = socdev->codec; 639 struct snd_soc_codec *codec = socdev->codec;
640 int i; 640 int i;
641 641
642 /* Due to the resume being scheduled into a workqueue we could
643 * suspend before that's finished - wait for it to complete.
644 */
645 snd_power_lock(codec->card);
646 snd_power_wait(codec->card, SNDRV_CTL_POWER_D0);
647 snd_power_unlock(codec->card);
648
649 /* we're going to block userspace touching us until resume completes */
650 snd_power_change_state(codec->card, SNDRV_CTL_POWER_D3hot);
651
642 /* mute any active DAC's */ 652 /* mute any active DAC's */
643 for (i = 0; i < machine->num_links; i++) { 653 for (i = 0; i < machine->num_links; i++) {
644 struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai; 654 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 return 0; 701 return 0;
692} 702}
693 703
694/* powers up audio subsystem after a suspend */ 704/* deferred resume work, so resume can complete before we finished
695static int soc_resume(struct platform_device *pdev) 705 * setting our codec back up, which can be very slow on I2C
706 */
707static void soc_resume_deferred(struct work_struct *work)
696{ 708{
697 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 709 struct snd_soc_device *socdev = container_of(work,
710 struct snd_soc_device,
711 deferred_resume_work);
698 struct snd_soc_machine *machine = socdev->machine; 712 struct snd_soc_machine *machine = socdev->machine;
699 struct snd_soc_platform *platform = socdev->platform; 713 struct snd_soc_platform *platform = socdev->platform;
700 struct snd_soc_codec_device *codec_dev = socdev->codec_dev; 714 struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
701 struct snd_soc_codec *codec = socdev->codec; 715 struct snd_soc_codec *codec = socdev->codec;
716 struct platform_device *pdev = to_platform_device(socdev->dev);
702 int i; 717 int i;
703 718
719 /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
720 * so userspace apps are blocked from touching us
721 */
722
723 dev_info(socdev->dev, "starting resume work\n");
724
704 if (machine->resume_pre) 725 if (machine->resume_pre)
705 machine->resume_pre(pdev); 726 machine->resume_pre(pdev);
706 727
@@ -742,6 +763,22 @@ static int soc_resume(struct platform_device *pdev)
742 if (machine->resume_post) 763 if (machine->resume_post)
743 machine->resume_post(pdev); 764 machine->resume_post(pdev);
744 765
766 dev_info(socdev->dev, "resume work completed\n");
767
768 /* userspace can access us now we are back as we were before */
769 snd_power_change_state(codec->card, SNDRV_CTL_POWER_D0);
770}
771
772/* powers up audio subsystem after a suspend */
773static int soc_resume(struct platform_device *pdev)
774{
775 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
776
777 dev_info(socdev->dev, "scheduling resume work\n");
778
779 if (!schedule_work(&socdev->deferred_resume_work))
780 dev_err(socdev->dev, "work item may be lost\n");
781
745 return 0; 782 return 0;
746} 783}
747 784
@@ -788,6 +825,9 @@ static int soc_probe(struct platform_device *pdev)
788 825
789 /* DAPM stream work */ 826 /* DAPM stream work */
790 INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work); 827 INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work);
828 /* deferred resume work */
829 INIT_WORK(&socdev->deferred_resume_work, soc_resume_deferred);
830
791 return 0; 831 return 0;
792 832
793platform_err: 833platform_err: