ALSA: hda: Virtualize SPDIF out controls
The SPDIF output controls apply to converter widgets. A future change will create a PCM device per pin widget, and hence a set of SPDIF output controls per pin widget, for certain HDMI codecs. To support this, we need the ability to virtualize the SPDIF output controls. Specifically: * Controls can be "unassigned" from real hardware when a converter is not used for the PCM the control was created for. * Control puts only write to hardware when they are assigned. * Controls can be "assigned" to real hardware when a converter is picked to support output for a particular PCM. * When a converter is assigned, the hardware is updated to the cached configuration. Signed-off-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
7c93597627
commit
74b654c957
@ -2663,10 +2663,8 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
|
||||
val |= spdif->ctls & 1;
|
||||
change = spdif->ctls != val;
|
||||
spdif->ctls = val;
|
||||
|
||||
if (change)
|
||||
if (change && nid != (u16)-1)
|
||||
set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
|
||||
|
||||
mutex_unlock(&codec->spdif_mutex);
|
||||
return change;
|
||||
}
|
||||
@ -2684,6 +2682,17 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid,
|
||||
int dig1, int dig2)
|
||||
{
|
||||
set_dig_out_convert(codec, nid, dig1, dig2);
|
||||
/* unmute amp switch (if any) */
|
||||
if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
|
||||
(dig1 & AC_DIG1_ENABLE))
|
||||
snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
|
||||
HDA_AMP_MUTE, 0);
|
||||
}
|
||||
|
||||
static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
@ -2699,15 +2708,9 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
|
||||
if (ucontrol->value.integer.value[0])
|
||||
val |= AC_DIG1_ENABLE;
|
||||
change = spdif->ctls != val;
|
||||
if (change) {
|
||||
spdif->ctls = val;
|
||||
set_dig_out_convert(codec, nid, val & 0xff, -1);
|
||||
/* unmute amp switch (if any) */
|
||||
if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
|
||||
(val & AC_DIG1_ENABLE))
|
||||
snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
|
||||
HDA_AMP_MUTE, 0);
|
||||
}
|
||||
spdif->ctls = val;
|
||||
if (change && nid != (u16)-1)
|
||||
set_spdif_ctls(codec, nid, val & 0xff, -1);
|
||||
mutex_unlock(&codec->spdif_mutex);
|
||||
return change;
|
||||
}
|
||||
@ -2754,7 +2757,9 @@ static struct snd_kcontrol_new dig_mixes[] = {
|
||||
*
|
||||
* Returns 0 if successful, or a negative error code.
|
||||
*/
|
||||
int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
|
||||
int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
|
||||
hda_nid_t associated_nid,
|
||||
hda_nid_t cvt_nid)
|
||||
{
|
||||
int err;
|
||||
struct snd_kcontrol *kctl;
|
||||
@ -2774,12 +2779,12 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
|
||||
return -ENOMEM;
|
||||
kctl->id.index = idx;
|
||||
kctl->private_value = codec->spdif_out.used - 1;
|
||||
err = snd_hda_ctl_add(codec, nid, kctl);
|
||||
err = snd_hda_ctl_add(codec, associated_nid, kctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
spdif->nid = nid;
|
||||
spdif->ctls = snd_hda_codec_read(codec, nid, 0,
|
||||
spdif->nid = cvt_nid;
|
||||
spdif->ctls = snd_hda_codec_read(codec, cvt_nid, 0,
|
||||
AC_VERB_GET_DIGI_CONVERT_1, 0);
|
||||
spdif->status = convert_to_spdif_status(spdif->ctls);
|
||||
return 0;
|
||||
@ -2800,6 +2805,31 @@ struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid);
|
||||
|
||||
void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
|
||||
{
|
||||
struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
|
||||
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
spdif->nid = (u16)-1;
|
||||
mutex_unlock(&codec->spdif_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_unassign);
|
||||
|
||||
void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
|
||||
{
|
||||
struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
|
||||
unsigned short val;
|
||||
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
if (spdif->nid != nid) {
|
||||
spdif->nid = nid;
|
||||
val = spdif->ctls;
|
||||
set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff);
|
||||
}
|
||||
mutex_unlock(&codec->spdif_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_assign);
|
||||
|
||||
/*
|
||||
* SPDIF sharing with analog output
|
||||
*/
|
||||
|
@ -954,6 +954,8 @@ struct hda_spdif_out {
|
||||
};
|
||||
struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
|
||||
hda_nid_t nid);
|
||||
void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx);
|
||||
void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid);
|
||||
|
||||
/*
|
||||
* Mixer
|
||||
|
@ -212,7 +212,9 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
||||
/*
|
||||
* SPDIF I/O
|
||||
*/
|
||||
int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid);
|
||||
int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
|
||||
hda_nid_t associated_nid,
|
||||
hda_nid_t cvt_nid);
|
||||
int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
|
||||
|
||||
/*
|
||||
|
@ -213,7 +213,9 @@ static int ad198x_build_controls(struct hda_codec *codec)
|
||||
return err;
|
||||
}
|
||||
if (spec->multiout.dig_out_nid) {
|
||||
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,
|
||||
spec->multiout.dig_out_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hda_create_spdif_share_sw(codec,
|
||||
|
@ -240,7 +240,8 @@ static int ca0110_build_controls(struct hda_codec *codec)
|
||||
}
|
||||
|
||||
if (spec->dig_out) {
|
||||
err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out);
|
||||
err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
|
||||
spec->dig_out);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
|
||||
|
@ -821,7 +821,8 @@ static int build_digital_output(struct hda_codec *codec)
|
||||
if (!spec->multiout.dig_out_nid)
|
||||
return 0;
|
||||
|
||||
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,
|
||||
spec->multiout.dig_out_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
|
||||
|
@ -327,7 +327,9 @@ static int cmi9880_build_controls(struct hda_codec *codec)
|
||||
return err;
|
||||
}
|
||||
if (spec->multiout.dig_out_nid) {
|
||||
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,
|
||||
spec->multiout.dig_out_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hda_create_spdif_share_sw(codec,
|
||||
|
@ -510,6 +510,7 @@ static int conexant_build_controls(struct hda_codec *codec)
|
||||
}
|
||||
if (spec->multiout.dig_out_nid) {
|
||||
err = snd_hda_create_spdif_out_ctls(codec,
|
||||
spec->multiout.dig_out_nid,
|
||||
spec->multiout.dig_out_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -1095,7 +1095,8 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < codec->num_pcms; i++) {
|
||||
err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
|
||||
err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i],
|
||||
spec->cvt[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
@ -3217,6 +3217,7 @@ static int alc_build_controls(struct hda_codec *codec)
|
||||
}
|
||||
if (spec->multiout.dig_out_nid) {
|
||||
err = snd_hda_create_spdif_out_ctls(codec,
|
||||
spec->multiout.dig_out_nid,
|
||||
spec->multiout.dig_out_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -1112,7 +1112,9 @@ static int stac92xx_build_controls(struct hda_codec *codec)
|
||||
}
|
||||
|
||||
if (spec->multiout.dig_out_nid) {
|
||||
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,
|
||||
spec->multiout.dig_out_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hda_create_spdif_share_sw(codec,
|
||||
|
@ -1497,6 +1497,7 @@ static int via_build_controls(struct hda_codec *codec)
|
||||
|
||||
if (spec->multiout.dig_out_nid) {
|
||||
err = snd_hda_create_spdif_out_ctls(codec,
|
||||
spec->multiout.dig_out_nid,
|
||||
spec->multiout.dig_out_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
Loading…
Reference in New Issue
Block a user