ALSA: hda: Virtualize SPDIF out controls
authorStephen Warren <swarren@nvidia.com>
Wed, 1 Jun 2011 17:14:18 +0000 (11:14 -0600)
committerTakashi Iwai <tiwai@suse.de>
Mon, 6 Jun 2011 10:51:59 +0000 (12:51 +0200)
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>
12 files changed:
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_local.h
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_ca0110.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c

index e17e2998d333c7a692963c82c362b24cb3fedd24..c63a06703de319d43302a77e12e7de9b5944b45b 100644 (file)
@@ -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
  */
index 1d21c0624e03ca937dc91ff3686167eab670e422..96c35cab57bf3de6cfd3241632f9bfc44084eca8 100644 (file)
@@ -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
index 08ec073444e269a770d094e21f2076faa3581d9d..8b88c92826a14abf425235ff74896a4b826e9019 100644 (file)
@@ -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);
 
 /*
index d694e9d4921d85eef34f6588386c0235c9913e10..0f7b8951440f165c1ebd6472ea2ee5f837f7a574 100644 (file)
@@ -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,
index 61b92634b161aabc12d9c96b310aecc734c14e78..6b406840846e640401044823efdab4dc64ed81de 100644 (file)
@@ -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);
index 26a1521045bb58c31ca921d9a90af1d2f6ac59ee..c7b5ca28fa775ead21c4149c3ebb4e7e3d127a7d 100644 (file)
@@ -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);
index ab3308daa960eb031b4c6033811135ac1cf6a06e..9eaf99b01aecdf5172336dd5f642e463b2e66154 100644 (file)
@@ -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,
index 3e6b9a8539c2ff4bb900b62bddaa2f5d4b008a2d..217ca9e13425137fc7b35f1de705e5d896a50b6f 100644 (file)
@@ -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;
index 86b35a071a83a263d67c27573b993b78636a290b..13ee4449718f2f5af4d48ae4049624b61aa4583a 100644 (file)
@@ -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;
        }
index 7a4e10002f56dcb5b2bf299c81bcc72b57a77cd2..5c8a4ea75cd7476dd033b0e7880f2ee3df5e19c9 100644 (file)
@@ -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;
index 7f81cc2274f32ce75a1bee8f0ffaa2b51093486c..7407095cbc78c0894794748661bb15916621fd5f 100644 (file)
@@ -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,
index 8304c748dfb7afb2ff37a12eeaa6c2a73c591e1f..89a0f2a3d26990e11a863b5fea93ee45dbdd3e07 100644 (file)
@@ -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;