ALSA: hda - Fix / clean-up slave digital out codes
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>
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index cb9aae5..94ea6543 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1426,6 +1426,29 @@
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 @@
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 @@
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 @@
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);
- /* 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->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);
+ 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))
+ set_dig_out_convert(codec, nid,
+ codec->spdif_ctls & 0xff, -1);
+}
+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 @@
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 @@
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 @@
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 @@
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 @@
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);