diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-11-19 08:14:58 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-11-19 08:14:58 -0500 |
commit | 989c3187156ad197ae473fa9d9d506eef9624f12 (patch) | |
tree | 2f18bbd11a8811729e039ab1cb72cdc21f74254f /sound/pci | |
parent | 0ced14fbda44bf9c4977f1b01ae1077b944b94ab (diff) |
ALSA: hda - Fix recursive suspend/resume call
When the bus reset is performed during the suspend/resume (including
the power-saving too), it calls snd_hda_suspend() and
snd_hda_resume() again, and deadlocks eventually.
For avoiding the recursive call, add a new flag indicating that the PM
is being performed, and don't go to the bus reset mode when it's on.
Reported-and-tested-by: Julian Wollrath <jwollrath@web.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 11 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 1 |
2 files changed, 10 insertions, 2 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 70d4848b5cd0..cebe2dfdd984 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -228,7 +228,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, | |||
228 | } | 228 | } |
229 | mutex_unlock(&bus->cmd_mutex); | 229 | mutex_unlock(&bus->cmd_mutex); |
230 | snd_hda_power_down(codec); | 230 | snd_hda_power_down(codec); |
231 | if (res && *res == -1 && bus->rirb_error) { | 231 | if (!codec->in_pm && res && *res == -1 && bus->rirb_error) { |
232 | if (bus->response_reset) { | 232 | if (bus->response_reset) { |
233 | snd_printd("hda_codec: resetting BUS due to " | 233 | snd_printd("hda_codec: resetting BUS due to " |
234 | "fatal communication error\n"); | 234 | "fatal communication error\n"); |
@@ -238,7 +238,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, | |||
238 | goto again; | 238 | goto again; |
239 | } | 239 | } |
240 | /* clear reset-flag when the communication gets recovered */ | 240 | /* clear reset-flag when the communication gets recovered */ |
241 | if (!err) | 241 | if (!err || codec->in_pm) |
242 | bus->response_reset = 0; | 242 | bus->response_reset = 0; |
243 | return err; | 243 | return err; |
244 | } | 244 | } |
@@ -3616,6 +3616,8 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq) | |||
3616 | { | 3616 | { |
3617 | unsigned int state; | 3617 | unsigned int state; |
3618 | 3618 | ||
3619 | codec->in_pm = 1; | ||
3620 | |||
3619 | if (codec->patch_ops.suspend) | 3621 | if (codec->patch_ops.suspend) |
3620 | codec->patch_ops.suspend(codec); | 3622 | codec->patch_ops.suspend(codec); |
3621 | hda_cleanup_all_streams(codec); | 3623 | hda_cleanup_all_streams(codec); |
@@ -3630,6 +3632,7 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq) | |||
3630 | codec->power_transition = 0; | 3632 | codec->power_transition = 0; |
3631 | codec->power_jiffies = jiffies; | 3633 | codec->power_jiffies = jiffies; |
3632 | spin_unlock(&codec->power_lock); | 3634 | spin_unlock(&codec->power_lock); |
3635 | codec->in_pm = 0; | ||
3633 | return state; | 3636 | return state; |
3634 | } | 3637 | } |
3635 | 3638 | ||
@@ -3638,6 +3641,8 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq) | |||
3638 | */ | 3641 | */ |
3639 | static void hda_call_codec_resume(struct hda_codec *codec) | 3642 | static void hda_call_codec_resume(struct hda_codec *codec) |
3640 | { | 3643 | { |
3644 | codec->in_pm = 1; | ||
3645 | |||
3641 | /* set as if powered on for avoiding re-entering the resume | 3646 | /* set as if powered on for avoiding re-entering the resume |
3642 | * in the resume / power-save sequence | 3647 | * in the resume / power-save sequence |
3643 | */ | 3648 | */ |
@@ -3656,6 +3661,8 @@ static void hda_call_codec_resume(struct hda_codec *codec) | |||
3656 | snd_hda_codec_resume_cache(codec); | 3661 | snd_hda_codec_resume_cache(codec); |
3657 | } | 3662 | } |
3658 | snd_hda_jack_report_sync(codec); | 3663 | snd_hda_jack_report_sync(codec); |
3664 | |||
3665 | codec->in_pm = 0; | ||
3659 | snd_hda_power_down(codec); /* flag down before returning */ | 3666 | snd_hda_power_down(codec); /* flag down before returning */ |
3660 | } | 3667 | } |
3661 | #endif /* CONFIG_PM */ | 3668 | #endif /* CONFIG_PM */ |
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 507fe8a917b6..4f4e545c0f4b 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h | |||
@@ -869,6 +869,7 @@ struct hda_codec { | |||
869 | unsigned int power_on :1; /* current (global) power-state */ | 869 | unsigned int power_on :1; /* current (global) power-state */ |
870 | unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ | 870 | unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ |
871 | unsigned int pm_down_notified:1; /* PM notified to controller */ | 871 | unsigned int pm_down_notified:1; /* PM notified to controller */ |
872 | unsigned int in_pm:1; /* suspend/resume being performed */ | ||
872 | int power_transition; /* power-state in transition */ | 873 | int power_transition; /* power-state in transition */ |
873 | int power_count; /* current (global) power refcount */ | 874 | int power_count; /* current (global) power refcount */ |
874 | struct delayed_work power_work; /* delayed task for powerdown */ | 875 | struct delayed_work power_work; /* delayed task for powerdown */ |