diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index e768187465e3074fd6655c82e0252675f236e383..2c2fcdc72fcfd264c860455287151d5c45af1311 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1114,10 +1114,14 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, struct sn
 	change = codec->spdif_ctls != val;
 	if (change || codec->in_resume) {
 		codec->spdif_ctls = val;
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff);
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				    AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT |
-				    AC_AMP_SET_OUTPUT | ((val & 1) ? 0 : 0x80));
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+				    val & 0xff);
+		if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT |
+					    AC_AMP_SET_OUTPUT |
+					    ((val & 1) ? 0 : 0x80));
 	}
 	mutex_unlock(&codec->spdif_mutex);
 	return change;
@@ -1886,6 +1890,21 @@ int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *i
  * Multi-channel / digital-out PCM helper functions
  */
 
+/* setup SPDIF output stream */
+static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
+				 unsigned int stream_tag, unsigned int format)
+{
+	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+	if (codec->spdif_ctls & AC_DIG1_ENABLE)
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+				    codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+	/* turn on again (if needed) */
+	if (codec->spdif_ctls & AC_DIG1_ENABLE)
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
+				    codec->spdif_ctls & 0xff);
+}
+
 /*
  * open the digital out in the exclusive mode
  */
@@ -1901,6 +1920,18 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mo
 	return 0;
 }
 
+int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
+				  struct hda_multi_out *mout,
+				  unsigned int stream_tag,
+				  unsigned int format,
+				  struct snd_pcm_substream *substream)
+{
+	mutex_lock(&codec->spdif_mutex);
+	setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format);
+	mutex_unlock(&codec->spdif_mutex);
+	return 0;
+}
+
 /*
  * release the digital out
  */
@@ -1942,9 +1973,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_o
 		    snd_hda_is_supported_format(codec, mout->dig_out_nid, format) &&
 		    ! (codec->spdif_status & IEC958_AES0_NONAUDIO)) {
 			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
-			/* setup digital receiver */
-			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
-						   stream_tag, 0, format);
+			setup_dig_out_stream(codec, mout->dig_out_nid,
+					     stream_tag, format);
 		} else {
 			mout->dig_out_used = 0;
 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 39718d6cdadd381435a149ecc6eb0ff62e28c8fc..3505a670995ff0a429dbe69714df84ffaedd9acd 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -148,6 +148,11 @@ struct hda_multi_out {
 
 int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout);
 int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout);
+int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
+				  struct hda_multi_out *mout,
+				  unsigned int stream_tag,
+				  unsigned int format,
+				  struct snd_pcm_substream *substream);
 int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout,
 				  struct snd_pcm_substream *substream);
 int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout,
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 9c241ccf690795ddb44ddcf9fed3329dbb9b24f2..fa194f21282f0448eb8b6303dadfdf7e27c25364 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -192,6 +192,17 @@ static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
+static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					   struct hda_codec *codec,
+					   unsigned int stream_tag,
+					   unsigned int format,
+					   struct snd_pcm_substream *substream)
+{
+	struct ad198x_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+					     format, substream);
+}
+
 /*
  * Analog capture
  */
@@ -250,7 +261,8 @@ static struct hda_pcm_stream ad198x_pcm_digital_playback = {
 	.nid = 0, /* fill later */
 	.ops = {
 		.open = ad198x_dig_playback_pcm_open,
-		.close = ad198x_dig_playback_pcm_close
+		.close = ad198x_dig_playback_pcm_close,
+		.prepare = ad198x_dig_playback_pcm_prepare
 	},
 };
 
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
index 831469d3a923db7dd64bd857b287d8379ba4b1ed..b89db1be4a0f83a8467d34d829c3d71104659854 100644
--- a/sound/pci/hda/patch_atihdmi.c
+++ b/sound/pci/hda/patch_atihdmi.c
@@ -94,6 +94,17 @@ static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
+static int atihdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					    struct hda_codec *codec,
+					    unsigned int stream_tag,
+					    unsigned int format,
+					    struct snd_pcm_substream *substream)
+{
+	struct atihdmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+					     format, substream);
+}
+
 static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
 	.substreams = 1,
 	.channels_min = 2,
@@ -101,7 +112,8 @@ static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
 	.nid = 0x2, /* NID to query formats and rates and setup streams */
 	.ops = {
 		.open = atihdmi_dig_playback_pcm_open,
-		.close = atihdmi_dig_playback_pcm_close
+		.close = atihdmi_dig_playback_pcm_close,
+		.prepare = atihdmi_dig_playback_pcm_prepare
 	},
 };
 
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 5b9d3a31a1ae8f261c527985abe363f57b86f4d4..3c722e667bc8cf4d4686d571f7953017c72474c6 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -497,6 +497,17 @@ static int cmi9880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
+static int cmi9880_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					    struct hda_codec *codec,
+					    unsigned int stream_tag,
+					    unsigned int format,
+					    struct snd_pcm_substream *substream)
+{
+	struct cmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+					     format, substream);
+}
+
 /*
  * Analog capture
  */
@@ -556,7 +567,8 @@ static struct hda_pcm_stream cmi9880_pcm_digital_playback = {
 	/* NID is set in cmi9880_build_pcms */
 	.ops = {
 		.open = cmi9880_dig_playback_pcm_open,
-		.close = cmi9880_dig_playback_pcm_close
+		.close = cmi9880_dig_playback_pcm_close,
+		.prepare = cmi9880_dig_playback_pcm_prepare
 	},
 };
 
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index efb95dc2d6dbdf11ef435d637b1112fa88a53362..2349b5eb5aaa7664e9b998aef6e139e7e2d70968 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -136,6 +136,18 @@ static int conexant_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
+static int conexant_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					 struct hda_codec *codec,
+					 unsigned int stream_tag,
+					 unsigned int format,
+					 struct snd_pcm_substream *substream)
+{
+	struct conexant_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+					     stream_tag,
+					     format, substream);
+}
+
 /*
  * Analog capture
  */
@@ -194,7 +206,8 @@ static struct hda_pcm_stream conexant_pcm_digital_playback = {
 	.nid = 0, /* fill later */
 	.ops = {
 		.open = conexant_dig_playback_pcm_open,
-		.close = conexant_dig_playback_pcm_close
+		.close = conexant_dig_playback_pcm_close,
+		.prepare = conexant_dig_playback_pcm_prepare
 	},
 };
 
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 4243c6b491fc1801dd6b2012a027e2c1a1265b94..d3f7a3dab1c42f7765f5bb6d7c35d98e49e668d5 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -1916,6 +1916,17 @@ static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
 }
 
+static int alc880_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					   struct hda_codec *codec,
+					   unsigned int stream_tag,
+					   unsigned int format,
+					   struct snd_pcm_substream *substream)
+{
+	struct alc_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+					     stream_tag, format, substream);
+}
+
 static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
 					 struct hda_codec *codec,
 					 struct snd_pcm_substream *substream)
@@ -1984,7 +1995,8 @@ static struct hda_pcm_stream alc880_pcm_digital_playback = {
 	/* NID is set in alc_build_pcms */
 	.ops = {
 		.open = alc880_dig_playback_pcm_open,
-		.close = alc880_dig_playback_pcm_close
+		.close = alc880_dig_playback_pcm_close,
+		.prepare = alc880_dig_playback_pcm_prepare
 	},
 };
 
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 6dd4822dbec0e8b3f38cb4cb85da76429e2042ae..612d355b9e0160aaaed3323a2fee4baac67c9489 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -814,6 +814,17 @@ static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
+static int stac92xx_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					 struct hda_codec *codec,
+					 unsigned int stream_tag,
+					 unsigned int format,
+					 struct snd_pcm_substream *substream)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+					     stream_tag, format, substream);
+}
+
 
 /*
  * Analog capture callbacks
@@ -848,7 +859,8 @@ static struct hda_pcm_stream stac92xx_pcm_digital_playback = {
 	/* NID is set in stac92xx_build_pcms */
 	.ops = {
 		.open = stac92xx_dig_playback_pcm_open,
-		.close = stac92xx_dig_playback_pcm_close
+		.close = stac92xx_dig_playback_pcm_close,
+		.prepare = stac92xx_dig_playback_pcm_prepare
 	},
 };
 
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 2b11ac8689b99690ff14112f2f5ba7cd2a77320b..ba32d1e52cb84d52b4f3a0b9087ab2ae2588a55d 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -377,6 +377,17 @@ static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
+static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					unsigned int stream_tag,
+					unsigned int format,
+					struct snd_pcm_substream *substream)
+{
+	struct via_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+					     stream_tag, format, substream);
+}
+
 /*
  * Analog capture
  */
@@ -433,7 +444,8 @@ static struct hda_pcm_stream vt1708_pcm_digital_playback = {
 	/* NID is set in via_build_pcms */
 	.ops = {
 		.open = via_dig_playback_pcm_open,
-		.close = via_dig_playback_pcm_close
+		.close = via_dig_playback_pcm_close,
+		.prepare = via_dig_playback_pcm_prepare
 	},
 };