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;
  * 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
  */
        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
  */
                    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);
 
 
 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,
 
        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
  */
        .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
        },
 };
 
 
        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,
        .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
        },
 };
 
 
        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
  */
        /* 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
        },
 };
 
 
        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
  */
        .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
        },
 };
 
 
        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)
        /* 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
        },
 };
 
 
        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
        /* 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
        },
 };
 
 
        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
  */
        /* 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
        },
 };