[ALSA] hda-codec - Fix SPDIF output
authorTakashi Iwai <tiwai@suse.de>
Thu, 5 Apr 2007 12:51:48 +0000 (14:51 +0200)
committerJaroslav Kysela <perex@suse.cz>
Fri, 11 May 2007 14:55:56 +0000 (16:55 +0200)
Fix SPDIF output (at least on Realtek codecs).  The DIGI_CONVERT verbs
have to be reset before the PCM stream is set up.  Otherwise the digital
setup is screwed up.
Also, check the AMP capability before setting AMP of the digital out
widget.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_local.h
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_atihdmi.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c

index e768187..2c2fcdc 100644 (file)
@@ -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);
index 39718d6..3505a67 100644 (file)
@@ -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,
index 9c241cc..fa194f2 100644 (file)
@@ -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
        },
 };
 
index 831469d..b89db1b 100644 (file)
@@ -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
        },
 };
 
index 5b9d3a3..3c722e6 100644 (file)
@@ -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
        },
 };
 
index efb95dc..2349b5e 100644 (file)
@@ -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
        },
 };
 
index 4243c6b..d3f7a3d 100644 (file)
@@ -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
        },
 };
 
index 6dd4822..612d355 100644 (file)
@@ -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
        },
 };
 
index 2b11ac8..ba32d1e 100644 (file)
@@ -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
        },
 };