ALSA: hda - Fix recursive suspend/resume call
authorTakashi Iwai <tiwai@suse.de>
Mon, 19 Nov 2012 13:14:58 +0000 (14:14 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 19 Nov 2012 13:14:58 +0000 (14:14 +0100)
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>
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h

index 70d4848b5cd0ad25ce272877f9c759ed010dfc58..cebe2dfdd9842907923ae9d16e25ca165d49c6d9 100644 (file)
@@ -228,7 +228,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
        }
        mutex_unlock(&bus->cmd_mutex);
        snd_hda_power_down(codec);
-       if (res && *res == -1 && bus->rirb_error) {
+       if (!codec->in_pm && res && *res == -1 && bus->rirb_error) {
                if (bus->response_reset) {
                        snd_printd("hda_codec: resetting BUS due to "
                                   "fatal communication error\n");
@@ -238,7 +238,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
                goto again;
        }
        /* clear reset-flag when the communication gets recovered */
-       if (!err)
+       if (!err || codec->in_pm)
                bus->response_reset = 0;
        return err;
 }
@@ -3616,6 +3616,8 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
 {
        unsigned int state;
 
+       codec->in_pm = 1;
+
        if (codec->patch_ops.suspend)
                codec->patch_ops.suspend(codec);
        hda_cleanup_all_streams(codec);
@@ -3630,6 +3632,7 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
        codec->power_transition = 0;
        codec->power_jiffies = jiffies;
        spin_unlock(&codec->power_lock);
+       codec->in_pm = 0;
        return state;
 }
 
@@ -3638,6 +3641,8 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
  */
 static void hda_call_codec_resume(struct hda_codec *codec)
 {
+       codec->in_pm = 1;
+
        /* set as if powered on for avoiding re-entering the resume
         * in the resume / power-save sequence
         */
@@ -3656,6 +3661,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
                snd_hda_codec_resume_cache(codec);
        }
        snd_hda_jack_report_sync(codec);
+
+       codec->in_pm = 0;
        snd_hda_power_down(codec); /* flag down before returning */
 }
 #endif /* CONFIG_PM */
index 507fe8a917b69b09993193ef460f4fbc3f3af31a..4f4e545c0f4b2ded6029d21851b3443973ea7d23 100644 (file)
@@ -869,6 +869,7 @@ struct hda_codec {
        unsigned int power_on :1;       /* current (global) power-state */
        unsigned int d3_stop_clk:1;     /* support D3 operation without BCLK */
        unsigned int pm_down_notified:1; /* PM notified to controller */
+       unsigned int in_pm:1;           /* suspend/resume being performed */
        int power_transition;   /* power-state in transition */
        int power_count;        /* current (global) power refcount */
        struct delayed_work power_work; /* delayed task for powerdown */