提交 9a08160b 编写于 作者: T Takashi Iwai

[ALSA] hda-codec - Add "IEC958 Default PCM" switch

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: NTakashi Iwai <tiwai@suse.de>
上级 f6c7e546
...@@ -1532,6 +1532,43 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) ...@@ -1532,6 +1532,43 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
return 0; 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 * SPDIF input
*/ */
...@@ -2557,9 +2594,36 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec, ...@@ -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, int snd_hda_multi_out_analog_open(struct hda_codec *codec,
struct hda_multi_out *mout, 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, return snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, 2); SNDRV_PCM_HW_PARAM_CHANNELS, 2);
} }
...@@ -2579,7 +2643,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, ...@@ -2579,7 +2643,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
int i; int i;
mutex_lock(&codec->spdif_mutex); 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 && if (chs == 2 &&
snd_hda_is_supported_format(codec, mout->dig_out_nid, snd_hda_is_supported_format(codec, mout->dig_out_nid,
format) && format) &&
......
...@@ -228,8 +228,18 @@ struct hda_multi_out { ...@@ -228,8 +228,18 @@ struct hda_multi_out {
int max_channels; /* currently supported analog channels */ int max_channels; /* currently supported analog channels */
int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */ 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 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, int snd_hda_multi_out_dig_open(struct hda_codec *codec,
struct hda_multi_out *mout); struct hda_multi_out *mout);
int snd_hda_multi_out_dig_close(struct hda_codec *codec, 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, ...@@ -241,7 +251,8 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
struct snd_pcm_substream *substream); struct snd_pcm_substream *substream);
int snd_hda_multi_out_analog_open(struct hda_codec *codec, int snd_hda_multi_out_analog_open(struct hda_codec *codec,
struct hda_multi_out *mout, 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, int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
struct hda_multi_out *mout, struct hda_multi_out *mout,
unsigned int stream_tag, unsigned int stream_tag,
......
...@@ -171,6 +171,11 @@ static int ad198x_build_controls(struct hda_codec *codec) ...@@ -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); err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
if (err < 0) if (err < 0)
return err; 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) { if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, 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, ...@@ -217,7 +222,8 @@ static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
struct ad198x_spec *spec = codec->spec; 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, static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
......
...@@ -329,6 +329,11 @@ static int cmi9880_build_controls(struct hda_codec *codec) ...@@ -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); err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
if (err < 0) if (err < 0)
return err; 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) { if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, 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, ...@@ -432,7 +437,8 @@ static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
struct cmi_spec *spec = codec->spec; 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, static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
......
...@@ -98,7 +98,8 @@ static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, ...@@ -98,7 +98,8 @@ static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
struct conexant_spec *spec = codec->spec; 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, static int conexant_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
...@@ -372,6 +373,11 @@ static int conexant_build_controls(struct hda_codec *codec) ...@@ -372,6 +373,11 @@ static int conexant_build_controls(struct hda_codec *codec)
spec->multiout.dig_out_nid); spec->multiout.dig_out_nid);
if (err < 0) if (err < 0)
return err; 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) { if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid); err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid);
......
...@@ -1520,6 +1520,11 @@ static int alc_build_controls(struct hda_codec *codec) ...@@ -1520,6 +1520,11 @@ static int alc_build_controls(struct hda_codec *codec)
spec->multiout.dig_out_nid); spec->multiout.dig_out_nid);
if (err < 0) if (err < 0)
return err; 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) { if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, 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, ...@@ -2325,7 +2330,8 @@ static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
struct alc_spec *spec = codec->spec; 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, static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
......
...@@ -916,6 +916,11 @@ static int stac92xx_build_controls(struct hda_codec *codec) ...@@ -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); err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
if (err < 0) if (err < 0)
return err; 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) { if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, 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, ...@@ -1748,7 +1753,8 @@ static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
struct sigmatel_spec *spec = codec->spec; 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, static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
......
...@@ -357,7 +357,8 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, ...@@ -357,7 +357,8 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream) struct snd_pcm_substream *substream)
{ {
struct via_spec *spec = codec->spec; 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, static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
...@@ -493,6 +494,11 @@ static int via_build_controls(struct hda_codec *codec) ...@@ -493,6 +494,11 @@ static int via_build_controls(struct hda_codec *codec)
spec->multiout.dig_out_nid); spec->multiout.dig_out_nid);
if (err < 0) if (err < 0)
return err; 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) { if (spec->dig_in_nid) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册