[ALSA] hda-codec - Add "IEC958 Default PCM" switch
authorTakashi Iwai <tiwai@suse.de>
Tue, 12 Feb 2008 17:37:26 +0000 (18:37 +0100)
committerTakashi Iwai <tiwai@suse.de>
Thu, 24 Apr 2008 10:00:10 +0000 (12:00 +0200)
Added a new mixer switch to enable/disable the sharing of the default
PCM stream with analog and SPDIF outputs.  When "IEC958 Default PCM"
switch is on, the PCM stream is routed both to analog and SPDIF outputs.
This is the behavior in the earlier version.

Turning this switch off has a merit for some codecs, though.  Some codec
chips don't support 24bit formats for SPDIF but only for analog outputs.
In this case, you can use 24bit format by disabling this switch.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_local.h
sound/pci/hda/patch_analog.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 af2c894..853e5c7 100644 (file)
@@ -1532,6 +1532,43 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
        return 0;
 }
 
+/*
+ * 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 = "IEC958 Default PCM 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
  */
@@ -2557,9 +2594,36 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
  */
 int snd_hda_multi_out_analog_open(struct hda_codec *codec,
                                  struct hda_multi_out *mout,
-                                 struct snd_pcm_substream *substream)
-{
-       substream->runtime->hw.channels_max = mout->max_channels;
+                                 struct snd_pcm_substream *substream,
+                                 struct hda_pcm_stream *hinfo)
+{
+       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);
 }
@@ -2579,7 +2643,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
        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) &&
index ad0014a..ce2ad42 100644 (file)
@@ -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 hda_codec *codec,
                                  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,
index 266c35e..1f21028 100644 (file)
@@ -171,6 +171,11 @@ static int ad198x_build_controls(struct hda_codec *codec)
                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(struct hda_pcm_stream *hinfo,
                                    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,
index 99ce74b..9794d41 100644 (file)
@@ -329,6 +329,11 @@ static int cmi9880_build_controls(struct hda_codec *codec)
                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(struct hda_pcm_stream *hinfo,
                                     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,
index bb915ed..2bb9a58 100644 (file)
@@ -98,7 +98,8 @@ static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
                                      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(struct hda_codec *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);
index d41eafa..6c60581 100644 (file)
@@ -1520,6 +1520,11 @@ static int alc_build_controls(struct hda_codec *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);
@@ -2325,7 +2330,8 @@ static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo,
                                    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,
index 7f506ef..7901e76 100644 (file)
@@ -916,6 +916,11 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                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(struct hda_pcm_stream *hinfo,
                                      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,
index d9a5c6a..3515a3f 100644 (file)
@@ -357,7 +357,8 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
                                 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_codec *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);