Commit 965ac42c authored by Liam Girdwood's avatar Liam Girdwood Committed by Jaroslav Kysela

[ALSA] ASoC force running of delayed PM work at suspend() and remove()

This patch fixes a bug whereby the power management delayed work would
never be run at driver suspend() or module remove(). Delayed work would
be created (after audio had finished) with a long delay (~5 secs) and
was sometimes never queued before flush_scheduled_work() was being
called at suspend or module remove. This caused the delayed work to
queued after the module had been removed or after resume.
This patch forces any delayed work to complete by cancelling it (timer
cannot fire and add it to queue later), scheduling it for now and
waiting on it's completion.
This is something I probably would like to add to workqueue.c in the
next merge window, however it's here atm because it can oops.
Signed-off-by: default avatarLiam Girdwood <lg@opensource.wolfsonmicro.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarJaroslav Kysela <perex@suse.cz>
parent beb60119
...@@ -77,6 +77,25 @@ static int pmdown_time = 5000; ...@@ -77,6 +77,25 @@ static int pmdown_time = 5000;
module_param(pmdown_time, int, 0); module_param(pmdown_time, int, 0);
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
/*
* This function forces any delayed work to be queued and run.
*/
static int run_delayed_work(struct delayed_work *dwork)
{
int ret;
/* cancel any work waiting to be queued. */
ret = cancel_delayed_work(dwork);
/* if there was any work waiting then we run it now and
* wait for it's completion */
if (ret) {
schedule_delayed_work(dwork, 0);
flush_scheduled_work();
}
return ret;
}
#ifdef CONFIG_SND_SOC_AC97_BUS #ifdef CONFIG_SND_SOC_AC97_BUS
/* unregister ac97 codec */ /* unregister ac97 codec */
static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
...@@ -1101,7 +1120,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) ...@@ -1101,7 +1120,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
} }
/* close any waiting streams and save state */ /* close any waiting streams and save state */
flush_scheduled_work(); run_delayed_work(&socdev->delayed_work);
codec->suspend_dapm_state = codec->dapm_state; codec->suspend_dapm_state = codec->dapm_state;
for(i = 0; i < codec->num_dai; i++) { for(i = 0; i < codec->num_dai; i++) {
...@@ -1255,6 +1274,8 @@ static int soc_remove(struct platform_device *pdev) ...@@ -1255,6 +1274,8 @@ static int soc_remove(struct platform_device *pdev)
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;
run_delayed_work(&socdev->delayed_work);
if (platform->remove) if (platform->remove)
platform->remove(pdev); platform->remove(pdev);
......
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