summaryrefslogtreecommitdiff
path: root/sound/pci/hda/hda_codec.c
diff options
context:
space:
mode:
authorStephen Warren <swarren@nvidia.com>2011-06-01 11:14:18 -0600
committerTakashi Iwai <tiwai@suse.de>2011-06-06 12:51:59 +0200
commit74b654c957e901e7596ebc7b9f5a1bea62b20509 (patch)
tree2ae316f1b481c0ad4a84c1caff9b99ca57fe63ba /sound/pci/hda/hda_codec.c
parent7c9359762797ba7a70bbaa6364aaecc16786ac83 (diff)
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>
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r--sound/pci/hda/hda_codec.c62
1 files changed, 46 insertions, 16 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index e17e2998d333..c63a06703de3 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -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
*/