diff options
author | Andy Green <andy@openmoko.com> | 2008-06-13 11:24:05 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2008-06-16 03:32:31 -0400 |
commit | 6ed2597883b1b03ca94f62f0cfe908314cba6d6b (patch) | |
tree | d19f086cdd31ca77ea1583cc147b19dfd8458190 | |
parent | abb68c26ba15f8e84e580a40c0b1bc349cb534b0 (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.h | 1 | ||||
-rw-r--r-- | sound/soc/soc-core.c | 46 |
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 |
695 | static int soc_resume(struct platform_device *pdev) | 705 | * setting our codec back up, which can be very slow on I2C |
706 | */ | ||
707 | static 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 */ | ||
773 | static 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 | ||
793 | platform_err: | 833 | platform_err: |