[alsa-devel] [PATCH] Re: iec958 switch uneffective while playing ac3 stream

Takashi Iwai tiwai at suse.de
Thu Apr 5 15:36:34 CEST 2007


At Thu, 05 Apr 2007 12:09:22 +0200,
I wrote:
> 
> Yeah, that'd be better.  I'll fix and commit to the HG tree later.

OK, now it's on HG tree.

While we're at it...  I did a quick hack to add a new mixer element to
choose the SPDIF playback route, either "Analog Duplicate" or
"Exclusive PCM".  (On AD codecs, it has more items for analog
loopbacks.)  This will solve one remaining problem, "SPDIF is blocked
by analog open".

Of course, this has another problem that user may choose an incorrect
value accidentally, but I come to believe it's better than a module
option.


Takashi

diff -r 2c71d018644e pci/hda/hda_codec.c
--- a/pci/hda/hda_codec.c	Thu Apr 05 14:51:48 2007 +0200
+++ b/pci/hda/hda_codec.c	Thu Apr 05 15:26:47 2007 +0200
@@ -1968,7 +1968,8 @@ int snd_hda_multi_out_analog_prepare(str
 	int i;
 
 	mutex_lock(&codec->spdif_mutex);
-	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
+	if (mout->dig_out_nid && !mout->dig_out_exclusive &&
+	    mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
 		if (chs == 2 &&
 		    snd_hda_is_supported_format(codec, mout->dig_out_nid, format) &&
 		    ! (codec->spdif_status & IEC958_AES0_NONAUDIO)) {
diff -r 2c71d018644e pci/hda/hda_local.h
--- a/pci/hda/hda_local.h	Thu Apr 05 14:51:48 2007 +0200
+++ b/pci/hda/hda_local.h	Thu Apr 05 14:56:22 2007 +0200
@@ -144,6 +144,7 @@ struct hda_multi_out {
 	hda_nid_t dig_out_nid;	/* digital out audio widget */
 	int max_channels;	/* currently supported analog channels */
 	int dig_out_used;	/* current usage of digital out (HDA_DIG_XXX) */
+	int dig_out_exclusive;
 };
 
 int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout);
diff -r 2c71d018644e pci/hda/patch_analog.c
--- a/pci/hda/patch_analog.c	Thu Apr 05 14:51:48 2007 +0200
+++ b/pci/hda/patch_analog.c	Thu Apr 05 15:17:01 2007 +0200
@@ -959,20 +959,25 @@ static struct hda_input_mux ad1983_captu
 /*
  * SPDIF playback route
  */
-static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-	static char *texts[] = { "PCM", "ADC" };
+static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = {
+		"Analog Duplicate", "Exclusive PCM", "Analog Loopback"
+	};
 
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
-	uinfo->value.enumerated.items = 2;
-	if (uinfo->value.enumerated.item > 1)
-		uinfo->value.enumerated.item = 1;
-	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	uinfo->value.enumerated.items = 3;
+	if (uinfo->value.enumerated.item > 2)
+		uinfo->value.enumerated.item = 2;
+	strcpy(uinfo->value.enumerated.name,
+	       texts[uinfo->value.enumerated.item]);
 	return 0;
 }
 
-static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct ad198x_spec *spec = codec->spec;
@@ -981,18 +986,21 @@ static int ad1983_spdif_route_get(struct
 	return 0;
 }
 
-static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct ad198x_spec *spec = codec->spec;
 
-	if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
-		spec->spdif_route = ucontrol->value.enumerated.item[0];
-		snd_hda_codec_write(codec, spec->multiout.dig_out_nid, 0,
-				    AC_VERB_SET_CONNECT_SEL, spec->spdif_route);
-		return 1;
-	}
-	return 0;
+	if (spec->spdif_route == ucontrol->value.enumerated.item[0] &&
+	    !codec->in_resume)
+		return 0;
+	spec->spdif_route = ucontrol->value.enumerated.item[0];
+	spec->multiout.dig_out_exclusive = (spec->spdif_route == 1);
+	snd_hda_codec_write(codec, spec->multiout.dig_out_nid, 0,
+			    AC_VERB_SET_CONNECT_SEL,
+			    (spec->spdif_route > 1) ? 1 : 0);
+	return 1;
 }
 
 static struct snd_kcontrol_new ad1983_mixers[] = {
@@ -1020,6 +1028,10 @@ static struct snd_kcontrol_new ad1983_mi
 		.get = ad198x_mux_enum_get,
 		.put = ad198x_mux_enum_put,
 	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new ad1983_spdif_mixers[] = {
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
@@ -1089,8 +1101,9 @@ static int patch_ad1983(struct hda_codec
 	spec->adc_nids = ad1983_adc_nids;
 	spec->capsrc_nids = ad1983_capsrc_nids;
 	spec->input_mux = &ad1983_capture_source;
-	spec->num_mixers = 1;
+	spec->num_mixers = 2;
 	spec->mixers[0] = ad1983_mixers;
+	spec->mixers[1] = ad1983_spdif_mixers;
 	spec->num_init_verbs = 1;
 	spec->init_verbs[0] = ad1983_init_verbs;
 	spec->spdif_route = 0;
@@ -1158,14 +1171,6 @@ static struct snd_kcontrol_new ad1981_mi
 		.info = ad198x_mux_enum_info,
 		.get = ad198x_mux_enum_get,
 		.put = ad198x_mux_enum_put,
-	},
-	/* identical with AD1983 */
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
-		.info = ad1983_spdif_route_info,
-		.get = ad1983_spdif_route_get,
-		.put = ad1983_spdif_route_put,
 	},
 	{ } /* end */
 };
@@ -1423,14 +1428,6 @@ static struct snd_kcontrol_new ad1981_th
 		.get = ad198x_mux_enum_get,
 		.put = ad198x_mux_enum_put,
 	},
-	/* identical with AD1983 */
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
-		.info = ad1983_spdif_route_info,
-		.get = ad1983_spdif_route_get,
-		.put = ad1983_spdif_route_put,
-	},
 	{ } /* end */
 };
 
@@ -1529,6 +1526,9 @@ static int patch_ad1981(struct hda_codec
 		codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
 		break;
 	}
+	if (spec->multiout.dig_out_nid)
+		/* identical with AD1983 */
+		spec->mixers[spec->num_mixers++] = ad1983_spdif_mixers;
 	return 0;
 }
 
@@ -1886,14 +1886,16 @@ static int ad1988_spdif_playback_source_
 					     struct snd_ctl_elem_info *uinfo)
 {
 	static char *texts[] = {
-		"PCM", "ADC1", "ADC2", "ADC3"
+		"Analog Duplicate", "Exclusive PCM",
+		"ADC1", "ADC2", "ADC3"
 	};
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
-	uinfo->value.enumerated.items = 4;
-	if (uinfo->value.enumerated.item >= 4)
-		uinfo->value.enumerated.item = 3;
-	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	uinfo->value.enumerated.items = 5;
+	if (uinfo->value.enumerated.item >= 5)
+		uinfo->value.enumerated.item = 4;
+	strcpy(uinfo->value.enumerated.name,
+	       texts[uinfo->value.enumerated.item]);
 	return 0;
 }
 
@@ -1901,17 +1903,9 @@ static int ad1988_spdif_playback_source_
 					    struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	unsigned int sel;
-
-	sel = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONNECT_SEL, 0);
-	if (sel > 0) {
-		sel = snd_hda_codec_read(codec, 0x0b, 0, AC_VERB_GET_CONNECT_SEL, 0);
-		if (sel <= 3)
-			sel++;
-		else
-			sel = 0;
-	}
-	ucontrol->value.enumerated.item[0] = sel;
+	struct ad198x_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->spdif_route;
 	return 0;
 }
 
@@ -1919,25 +1913,23 @@ static int ad1988_spdif_playback_source_
 					    struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ad198x_spec *spec = codec->spec;
 	unsigned int sel;
-	int change;
-
-	sel = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONNECT_SEL, 0);
-	if (! ucontrol->value.enumerated.item[0]) {
-		change = sel != 0;
-		if (change)
-			snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CONNECT_SEL, 0);
+
+	if (ucontrol->value.enumerated.item[0] != spec->spdif_route &&
+	    !codec->in_resume)
+		return 0;
+
+	spec->spdif_route = ucontrol->value.enumerated.item[0];
+	if (spec->spdif_route < 2) {
+		spec->multiout.dig_out_exclusive = spec->spdif_route;
+		snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CONNECT_SEL, 0);
 	} else {
-		change = sel == 0;
-		if (change)
-			snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CONNECT_SEL, 1);
-		sel = snd_hda_codec_read(codec, 0x0b, 0, AC_VERB_GET_CONNECT_SEL, 0) + 1;
-		change |= sel == ucontrol->value.enumerated.item[0];
-		if (change)
-			snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CONNECT_SEL,
-					    ucontrol->value.enumerated.item[0] - 1);
+		snd_hda_codec_write(codec, 0x02, 0, AC_VERB_SET_CONNECT_SEL, 1);
+		snd_hda_codec_read(codec, 0x0b, 0, AC_VERB_GET_CONNECT_SEL,
+				   spec->spdif_route - 2);
 	}
-	return change;
+	return 1;
 }
 
 static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
diff -r 2c71d018644e pci/hda/patch_realtek.c
--- a/pci/hda/patch_realtek.c	Thu Apr 05 14:51:48 2007 +0200
+++ b/pci/hda/patch_realtek.c	Thu Apr 05 15:29:03 2007 +0200
@@ -1098,6 +1098,62 @@ static struct snd_kcontrol_new alc880_un
 	{ } /* end */
 };
 
+/* additional SPDIF output controls */
+static int spdif_out_route_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = {
+		"Analog Duplicate", "Exclusive PCM"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name,
+	       texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int spdif_out_route_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->multiout.dig_out_exclusive;
+	return 0;
+}
+
+static int spdif_out_route_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	int change;
+
+	mutex_lock(&codec->spdif_mutex);
+	change = (ucontrol->value.enumerated.item[0] !=
+		  spec->multiout.dig_out_exclusive);
+	if (change || codec->in_resume)
+		spec->multiout.dig_out_exclusive =
+			ucontrol->value.enumerated.item[0];
+	mutex_unlock(&codec->spdif_mutex);
+	return change;
+}
+
+static struct snd_kcontrol_new alc_dig_mixes[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		.info = spdif_out_route_info,
+		.get = spdif_out_route_get,
+		.put = spdif_out_route_put,
+	},
+	{ } /* end */
+};
+
 /*
  * build control elements
  */
@@ -1116,6 +1172,9 @@ static int alc_build_controls(struct hda
 	if (spec->multiout.dig_out_nid) {
 		err = snd_hda_create_spdif_out_ctls(codec,
 						    spec->multiout.dig_out_nid);
+		if (err < 0)
+			return err;
+		err = snd_hda_add_new_ctls(codec, alc_dig_mixes);
 		if (err < 0)
 			return err;
 	}
diff -r 2c71d018644e pci/hda/patch_via.c
--- a/pci/hda/patch_via.c	Thu Apr 05 14:51:48 2007 +0200
+++ b/pci/hda/patch_via.c	Thu Apr 05 15:30:24 2007 +0200
@@ -456,6 +456,62 @@ static struct hda_pcm_stream vt1708_pcm_
 	.channels_max = 2,
 };
 
+/* additional SPDIF output controls */
+static int spdif_out_route_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = {
+		"Analog Duplicate", "Exclusive PCM"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item > 1)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name,
+	       texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int spdif_out_route_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct via_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->multiout.dig_out_exclusive;
+	return 0;
+}
+
+static int spdif_out_route_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct via_spec *spec = codec->spec;
+	int change;
+
+	mutex_lock(&codec->spdif_mutex);
+	change = (ucontrol->value.enumerated.item[0] !=
+		  spec->multiout.dig_out_exclusive);
+	if (change || codec->in_resume)
+		spec->multiout.dig_out_exclusive =
+			ucontrol->value.enumerated.item[0];
+	mutex_unlock(&codec->spdif_mutex);
+	return change;
+}
+
+static struct snd_kcontrol_new via_dig_mixes[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		.info = spdif_out_route_info,
+		.get = spdif_out_route_get,
+		.put = spdif_out_route_put,
+	},
+	{ } /* end */
+};
+
 static int via_build_controls(struct hda_codec *codec)
 {
 	struct via_spec *spec = codec->spec;
@@ -471,6 +527,9 @@ static int via_build_controls(struct hda
 	if (spec->multiout.dig_out_nid) {
 		err = snd_hda_create_spdif_out_ctls(codec,
 						    spec->multiout.dig_out_nid);
+		if (err < 0)
+			return err;
+		err = snd_hda_add_new_ctls(codec, via_dig_mixes);
 		if (err < 0)
 			return err;
 	}


More information about the Alsa-devel mailing list