summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sound/pci/hda/hda_codec.c73
-rw-r--r--sound/pci/hda/hda_local.h13
-rw-r--r--sound/pci/hda/patch_analog.c8
-rw-r--r--sound/pci/hda/patch_cmedia.c8
-rw-r--r--sound/pci/hda/patch_conexant.c8
-rw-r--r--sound/pci/hda/patch_realtek.c8
-rw-r--r--sound/pci/hda/patch_sigmatel.c8
-rw-r--r--sound/pci/hda/patch_via.c8
8 files changed, 123 insertions, 11 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index af2c8943b30..853e5c786c3 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1533,6 +1533,43 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
}
/*
+ * 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) &&
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index ad0014ab71f..ce2ad42a8a8 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -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,
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 266c35e32b6..1f2102860fe 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -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,
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 99ce74b4e9f..9794d4166ae 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -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,
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index bb915ede0ce..2bb9a58db9f 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -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);
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index d41eafacd86..6c605813fc6 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -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,
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 7f506ef0acc..7901e76f269 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -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,
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index d9a5c6a2dd9..3515a3fb5d9 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -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);