[alsa-devel] Digital-Out (Toslink) on HDA-Intal (AD1986A)?

Takashi Iwai tiwai at suse.de
Fri Feb 8 12:10:39 CET 2008


At Thu, 7 Feb 2008 23:04:00 +0100,
Martin Egge wrote:
> 
> > At Fri, 1 Feb 2008 22:02:51 +0100,
> > Martin Egge wrote:
> > > Am Dienstag, 29. Januar 2008 schrieb Takashi Iwai:
> > > > Could you try HG bisect?  It'd be greatly helpful.
> > >
> > > I've tried HG bisect this evening. It took me some time to discover that
> > > the problem is not within alsa-driver or alsa-kernel but within alsa-lib.
> > > This is the result of HG bisect:
> > >
> > > The first bad revision is:
> > > changeset:   2397:d1e9a2f92e7b
> > > user:        tiwai
> > > date:        Fri Jan 18 13:42:51 2008 +0100
> > > summary:     dmix - Enable auto format detection as default
> >
> > Thanks for checkign this.  Now the thing gets a bit clearer.
> > The problem is explained like this: via "default" PCM, 32bit format is
> > chosen now because the analog audio-out widget supports.  But the
> > SPDIF out widget doesn't support that format.  Thus it's not played
> > properly.
> 
> Oh I see, but why has this behavior changed since 1.0.15?

Because we want to use the best quality of format as default.  In the
older version, it was fixed to S16 although the device supports
32bit.

You can set the following in your ~/.asoundrc to use S16 again
	defaults.pcm.dmix.format S16

> > If my guess is correct, you can propbably get SPDIF output via "spdif"
> > output.  What happens with the below?
> >
> > 	% aplay -Dplug:spdif foo.wav
> 
> You are right. Choosing plug:spdif plays the sound cia the SDIF-Out. Of course 
> this does not play the sound via the internal speakers when no SPDIF is 
> connected.
> 
> But I'd prefer the old behavior from 1.0.15 where the default device was 
> playing sound via SPDIF when connected and via internal speakers when SDIF is 
> not connected.

Try the patch below.  This adds the mixer switch "Analog To SPDIF".
If this is on, the supported format is more limited so that the
default PCM can route to SPDIF.  If this switch is off, dmix will
choose 32bit automatically.


Takashi


diff -r 7c1928ffe1f3 pci/hda/hda_codec.c
--- a/pci/hda/hda_codec.c	Thu Feb 07 17:12:01 2008 +0100
+++ b/pci/hda/hda_codec.c	Fri Feb 08 11:58:12 2008 +0100
@@ -1527,6 +1527,43 @@ int snd_hda_create_spdif_out_ctls(struct
 }
 
 /*
+ * SPDIF sharing with analog output
+ */
+static int spdif_share_sw_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = mout->share_spdif;
+	return 0;
+}
+
+static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
+	mout->share_spdif = !!ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static struct snd_kcontrol_new spdif_share_sw = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Analog To SPDIF Playback Switch",
+	.info = snd_ctl_boolean_mono_info,
+	.get = spdif_share_sw_get,
+	.put = spdif_share_sw_put,
+};
+
+int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
+				  struct hda_multi_out *mout)
+{
+	if (!mout->dig_out_nid)
+		return 0;
+	/* ATTENTION: here mout is passed as private_data, instead of codec */
+	return snd_ctl_add(codec->bus->card,
+			   snd_ctl_new1(&spdif_share_sw, mout));
+}
+
+/*
  * SPDIF input
  */
 
@@ -2551,9 +2588,36 @@ int snd_hda_multi_out_dig_close(struct h
  */
 int snd_hda_multi_out_analog_open(struct hda_codec *codec,
 				  struct hda_multi_out *mout,
-				  struct snd_pcm_substream *substream)
+				  struct snd_pcm_substream *substream,
+				  struct hda_pcm_stream *hinfo)
 {
-	substream->runtime->hw.channels_max = mout->max_channels;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	runtime->hw.channels_max = mout->max_channels;
+	if (mout->dig_out_nid) {
+		if (!mout->analog_rates) {
+			mout->analog_rates = hinfo->rates;
+			mout->analog_formats = hinfo->formats;
+			mout->analog_maxbps = hinfo->maxbps;
+		} else {
+			runtime->hw.rates = mout->analog_rates;
+			runtime->hw.formats = mout->analog_formats;
+			hinfo->maxbps = mout->analog_maxbps;
+		}
+		if (!mout->spdif_rates) {
+			snd_hda_query_supported_pcm(codec, mout->dig_out_nid,
+						    &mout->spdif_rates,
+						    &mout->spdif_formats,
+						    &mout->spdif_maxbps);
+		}
+		mutex_lock(&codec->spdif_mutex);
+		if (mout->share_spdif) {
+			runtime->hw.rates &= mout->spdif_rates;
+			runtime->hw.formats &= mout->spdif_formats;
+			if (mout->spdif_maxbps < hinfo->maxbps)
+				hinfo->maxbps = mout->spdif_maxbps;
+		}
+	}
+	mutex_unlock(&codec->spdif_mutex);
 	return snd_pcm_hw_constraint_step(substream->runtime, 0,
 					  SNDRV_PCM_HW_PARAM_CHANNELS, 2);
 }
@@ -2573,7 +2637,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->share_spdif &&
+	    mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
 		if (chs == 2 &&
 		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
 						format) &&
diff -r 7c1928ffe1f3 pci/hda/hda_local.h
--- a/pci/hda/hda_local.h	Thu Feb 07 17:12:01 2008 +0100
+++ b/pci/hda/hda_local.h	Fri Feb 08 11:58:12 2008 +0100
@@ -228,8 +228,18 @@ struct hda_multi_out {
 	int max_channels;	/* currently supported analog channels */
 	int dig_out_used;	/* current usage of digital out (HDA_DIG_XXX) */
 	int no_share_stream;	/* don't share a stream with multiple pins */
+	int share_spdif;	/* share SPDIF pin */
+	/* PCM information for both analog and SPDIF DACs */
+	unsigned int analog_rates;
+	unsigned int analog_maxbps;
+	u64 analog_formats;
+	unsigned int spdif_rates;
+	unsigned int spdif_maxbps;
+	u64 spdif_formats;
 };
 
+int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
+				  struct hda_multi_out *mout);
 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,
@@ -241,7 +251,8 @@ int snd_hda_multi_out_dig_prepare(struct
 				  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);
+				  struct snd_pcm_substream *substream,
+				  struct hda_pcm_stream *hinfo);
 int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
 				     struct hda_multi_out *mout,
 				     unsigned int stream_tag,
diff -r 7c1928ffe1f3 pci/hda/patch_analog.c
--- a/pci/hda/patch_analog.c	Thu Feb 07 17:12:01 2008 +0100
+++ b/pci/hda/patch_analog.c	Fri Feb 08 11:58:12 2008 +0100
@@ -171,6 +171,11 @@ static int ad198x_build_controls(struct 
 		err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
 		if (err < 0)
 			return err;
+		err = snd_hda_create_spdif_share_sw(codec,
+						    &spec->multiout);
+		if (err < 0)
+			return err;
+		spec->multiout.share_spdif = 1;
 	} 
 	if (spec->dig_in_nid) {
 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -217,7 +222,8 @@ static int ad198x_playback_pcm_open(stru
 				    struct snd_pcm_substream *substream)
 {
 	struct ad198x_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+					     hinfo);
 }
 
 static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
diff -r 7c1928ffe1f3 pci/hda/patch_cmedia.c
--- a/pci/hda/patch_cmedia.c	Thu Feb 07 17:12:01 2008 +0100
+++ b/pci/hda/patch_cmedia.c	Fri Feb 08 11:58:12 2008 +0100
@@ -329,6 +329,11 @@ static int cmi9880_build_controls(struct
 		err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
 		if (err < 0)
 			return err;
+		err = snd_hda_create_spdif_share_sw(codec,
+						    &spec->multiout);
+		if (err < 0)
+			return err;
+		spec->multiout.share_spdif = 1;
 	}
 	if (spec->dig_in_nid) {
 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -432,7 +437,8 @@ static int cmi9880_playback_pcm_open(str
 				     struct snd_pcm_substream *substream)
 {
 	struct cmi_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+					     hinfo);
 }
 
 static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
diff -r 7c1928ffe1f3 pci/hda/patch_conexant.c
--- a/pci/hda/patch_conexant.c	Thu Feb 07 17:12:01 2008 +0100
+++ b/pci/hda/patch_conexant.c	Fri Feb 08 11:58:12 2008 +0100
@@ -98,7 +98,8 @@ static int conexant_playback_pcm_open(st
 				      struct snd_pcm_substream *substream)
 {
 	struct conexant_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+					     hinfo);
 }
 
 static int conexant_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -372,6 +373,11 @@ static int conexant_build_controls(struc
 						    spec->multiout.dig_out_nid);
 		if (err < 0)
 			return err;
+		err = snd_hda_create_spdif_share_sw(codec,
+						    &spec->multiout);
+		if (err < 0)
+			return err;
+		spec->multiout.share_spdif = 1;
 	} 
 	if (spec->dig_in_nid) {
 		err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid);
diff -r 7c1928ffe1f3 pci/hda/patch_realtek.c
--- a/pci/hda/patch_realtek.c	Thu Feb 07 17:12:01 2008 +0100
+++ b/pci/hda/patch_realtek.c	Fri Feb 08 11:58:12 2008 +0100
@@ -1517,6 +1517,11 @@ static int alc_build_controls(struct hda
 						    spec->multiout.dig_out_nid);
 		if (err < 0)
 			return err;
+		err = snd_hda_create_spdif_share_sw(codec,
+						    &spec->multiout);
+		if (err < 0)
+			return err;
+		spec->multiout.share_spdif = 1;
 	}
 	if (spec->dig_in_nid) {
 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -2319,7 +2324,8 @@ static int alc880_playback_pcm_open(stru
 				    struct snd_pcm_substream *substream)
 {
 	struct alc_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+					     hinfo);
 }
 
 static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
diff -r 7c1928ffe1f3 pci/hda/patch_sigmatel.c
--- a/pci/hda/patch_sigmatel.c	Thu Feb 07 17:12:01 2008 +0100
+++ b/pci/hda/patch_sigmatel.c	Fri Feb 08 11:58:12 2008 +0100
@@ -916,6 +916,11 @@ static int stac92xx_build_controls(struc
 		err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
 		if (err < 0)
 			return err;
+		err = snd_hda_create_spdif_share_sw(codec,
+						    &spec->multiout);
+		if (err < 0)
+			return err;
+		spec->multiout.share_spdif = 1;
 	}
 	if (spec->dig_in_nid) {
 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -1748,7 +1753,8 @@ static int stac92xx_playback_pcm_open(st
 				      struct snd_pcm_substream *substream)
 {
 	struct sigmatel_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+					     hinfo);
 }
 
 static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
diff -r 7c1928ffe1f3 pci/hda/patch_via.c
--- a/pci/hda/patch_via.c	Thu Feb 07 17:12:01 2008 +0100
+++ b/pci/hda/patch_via.c	Fri Feb 08 11:58:12 2008 +0100
@@ -357,7 +357,8 @@ static int via_playback_pcm_open(struct 
 				 struct snd_pcm_substream *substream)
 {
 	struct via_spec *spec = codec->spec;
-	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+					     hinfo);
 }
 
 static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -493,6 +494,11 @@ static int via_build_controls(struct hda
 						    spec->multiout.dig_out_nid);
 		if (err < 0)
 			return err;
+		err = snd_hda_create_spdif_share_sw(codec,
+						    &spec->multiout);
+		if (err < 0)
+			return err;
+		spec->multiout.share_spdif = 1;
 	}
 	if (spec->dig_in_nid) {
 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);


More information about the Alsa-devel mailing list