From 989c3187156ad197ae473fa9d9d506eef9624f12 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Mon, 19 Nov 2012 14:14:58 +0100
Subject: [PATCH] ALSA: hda - Fix recursive suspend/resume call
Git-commit: 989c3187156ad197ae473fa9d9d506eef9624f12
Patch-mainline: v3.7-rc7
References: FATE#313754

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 |   11 +++++++++--
 sound/pci/hda/hda_codec.h |    1 +
 2 files changed, 10 insertions(+), 2 deletions(-)

--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -227,7 +227,7 @@ static int codec_exec_verb(struct hda_co
 	}
 	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");
@@ -237,7 +237,7 @@ static int codec_exec_verb(struct hda_co
 		goto again;
 	}
 	/* clear reset-flag when the communication gets recovered */
-	if (!err)
+	if (!err || codec->in_pm)
 		bus->response_reset = 0;
 	return err;
 }
@@ -3647,6 +3647,8 @@ static unsigned int hda_call_codec_suspe
 {
 	unsigned int state;
 
+	codec->in_pm = 1;
+
 	if (codec->patch_ops.suspend)
 		codec->patch_ops.suspend(codec);
 	hda_cleanup_all_streams(codec);
@@ -3661,6 +3663,7 @@ static unsigned int hda_call_codec_suspe
 	codec->power_transition = 0;
 	codec->power_jiffies = jiffies;
 	spin_unlock(&codec->power_lock);
+	codec->in_pm = 0;
 	return state;
 }
 
@@ -3669,6 +3672,8 @@ static unsigned int hda_call_codec_suspe
  */
 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
 	 */
@@ -3687,6 +3692,8 @@ static void hda_call_codec_resume(struct
 		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 */
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -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 */
