ALSA: hda - Fix / clean-up slave digital out codes
authorTakashi Iwai <tiwai@suse.de>
Thu, 25 Sep 2008 14:32:41 +0000 (16:32 +0200)
committerJaroslav Kysela <perex@perex.cz>
Fri, 10 Oct 2008 11:41:34 +0000 (13:41 +0200)
The recent slave_dig_out addition has some rooms to clean up.
Also it doesn't call snd_hda_cleanup_stream() properly for slaves
at closing.  The patch fixes both issues.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
sound/pci/hda/hda_codec.c

index cb9aae5..94ea654 100644 (file)
@@ -1426,6 +1426,29 @@ static unsigned int convert_to_spdif_status(unsigned short val)
        return sbits;
 }
 
+/* set digital convert verbs both for the given NID and its slaves */
+static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
+                       int verb, int val)
+{
+       hda_nid_t *d;
+
+       snd_hda_codec_write(codec, nid, 0, verb, val);
+       d = codec->slave_dig_outs;
+       if (!d)
+               return;
+       for (; *d; d++)
+               snd_hda_codec_write(codec, *d, 0, verb, val);
+}
+
+static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid,
+                                      int dig1, int dig2)
+{
+       if (dig1 != -1)
+               set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1);
+       if (dig2 != -1)
+               set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, dig2);
+}
+
 static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
                                     struct snd_ctl_elem_value *ucontrol)
 {
@@ -1444,25 +1467,8 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
        change = codec->spdif_ctls != val;
        codec->spdif_ctls = val;
 
-       if (change) {
-               hda_nid_t *d;
-               snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_DIGI_CONVERT_1,
-                                         val & 0xff);
-               snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_DIGI_CONVERT_2,
-                                         val >> 8);
-
-               if (codec->slave_dig_outs)
-                       for (d = codec->slave_dig_outs; *d; d++) {
-                               snd_hda_codec_write_cache(codec, *d, 0,
-                                         AC_VERB_SET_DIGI_CONVERT_1,
-                                         val & 0xff);
-                               snd_hda_codec_write_cache(codec, *d, 0,
-                                         AC_VERB_SET_DIGI_CONVERT_2,
-                                         val >> 8);
-                       }
-       }
+       if (change)
+               set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
 
        mutex_unlock(&codec->spdif_mutex);
        return change;
@@ -1493,17 +1499,8 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
                val |= AC_DIG1_ENABLE;
        change = codec->spdif_ctls != val;
        if (change) {
-               hda_nid_t *d;
                codec->spdif_ctls = val;
-               snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_DIGI_CONVERT_1,
-                                         val & 0xff);
-
-               if (codec->slave_dig_outs)
-                       for (d = codec->slave_dig_outs; *d; d++)
-                               snd_hda_codec_write_cache(codec, *d, 0,
-                                         AC_VERB_SET_DIGI_CONVERT_1,
-                                         val & 0xff);
+               set_dig_out_convert(codec, nid, val & 0xff, -1);
                /* unmute amp switch (if any) */
                if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
                    (val & AC_DIG1_ENABLE))
@@ -2598,32 +2595,32 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
 static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
                                 unsigned int stream_tag, unsigned int format)
 {
-       hda_nid_t *d;
-
        /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
-       if (codec->spdif_status_reset && (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);
-
-               if (codec->slave_dig_outs)
-                       for (d = codec->slave_dig_outs; *d; d++)
-                               snd_hda_codec_write(codec, *d, 0,
-                                   AC_VERB_SET_DIGI_CONVERT_1,
-                                   codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
-       }
+       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+               set_dig_out_convert(codec, nid, 
+                                   codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
+                                   -1);
        snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+       if (codec->slave_dig_outs) {
+               hda_nid_t *d;
+               for (d = codec->slave_dig_outs; *d; d++)
+                       snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
+                                                  format);
+       }
        /* turn on again (if needed) */
-       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-                                   codec->spdif_ctls & 0xff);
+       if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+               set_dig_out_convert(codec, nid,
+                                   codec->spdif_ctls & 0xff, -1);
+}
 
-               if (codec->slave_dig_outs)
-                       for (d = codec->slave_dig_outs; *d; d++)
-                               snd_hda_codec_write(codec, *d, 0,
-                                   AC_VERB_SET_DIGI_CONVERT_1,
-                                   codec->spdif_ctls & 0xff);
+static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+       snd_hda_codec_cleanup_stream(codec, nid);
+       if (codec->slave_dig_outs) {
+               hda_nid_t *d;
+               for (d = codec->slave_dig_outs; *d; d++)
+                       snd_hda_codec_cleanup_stream(codec, *d);
        }
-
 }
 
 /*
@@ -2635,7 +2632,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
        mutex_lock(&codec->spdif_mutex);
        if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)
                /* already opened as analog dup; reset it once */
-               snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
+               cleanup_dig_out_stream(codec, mout->dig_out_nid);
        mout->dig_out_used = HDA_DIG_EXCLUSIVE;
        mutex_unlock(&codec->spdif_mutex);
        return 0;
@@ -2647,12 +2644,8 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
                                  unsigned int format,
                                  struct snd_pcm_substream *substream)
 {
-       hda_nid_t *nid;
        mutex_lock(&codec->spdif_mutex);
        setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format);
-       if (codec->slave_dig_outs)
-               for (nid = codec->slave_dig_outs; *nid; nid++)
-                       setup_dig_out_stream(codec, *nid, stream_tag, format);
        mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
@@ -2719,7 +2712,6 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
                                     struct snd_pcm_substream *substream)
 {
        hda_nid_t *nids = mout->dac_nids;
-       hda_nid_t *d;
        int chs = substream->runtime->channels;
        int i;
 
@@ -2733,16 +2725,9 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
                        mout->dig_out_used = HDA_DIG_ANALOG_DUP;
                        setup_dig_out_stream(codec, mout->dig_out_nid,
                                             stream_tag, format);
-                       if (codec->slave_dig_outs)
-                               for (d = codec->slave_dig_outs; *d; d++)
-                                       setup_dig_out_stream(codec, *d,
-                                               stream_tag, format);
                } else {
                        mout->dig_out_used = 0;
-                       snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
-                       if (codec->slave_dig_outs)
-                               for (d = codec->slave_dig_outs; *d; d++)
-                                       snd_hda_codec_cleanup_stream(codec, *d);
+                       cleanup_dig_out_stream(codec, mout->dig_out_nid);
                }
        }
        mutex_unlock(&codec->spdif_mutex);
@@ -2793,7 +2778,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
                                                     mout->extra_out_nid[i]);
        mutex_lock(&codec->spdif_mutex);
        if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
-               snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
+               cleanup_dig_out_stream(codec, mout->dig_out_nid);
                mout->dig_out_used = 0;
        }
        mutex_unlock(&codec->spdif_mutex);