diff options
author | Stephen Warren <swarren@nvidia.com> | 2011-06-01 13:14:18 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-06-06 06:51:59 -0400 |
commit | 74b654c957e901e7596ebc7b9f5a1bea62b20509 (patch) | |
tree | 2ae316f1b481c0ad4a84c1caff9b99ca57fe63ba | |
parent | 7c9359762797ba7a70bbaa6364aaecc16786ac83 (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>
-rw-r--r-- | sound/pci/hda/hda_codec.c | 62 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 2 | ||||
-rw-r--r-- | sound/pci/hda/hda_local.h | 4 | ||||
-rw-r--r-- | sound/pci/hda/patch_analog.c | 4 | ||||
-rw-r--r-- | sound/pci/hda/patch_ca0110.c | 3 | ||||
-rw-r--r-- | sound/pci/hda/patch_cirrus.c | 3 | ||||
-rw-r--r-- | sound/pci/hda/patch_cmedia.c | 4 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 1 | ||||
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 3 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 1 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 4 | ||||
-rw-r--r-- | sound/pci/hda/patch_via.c | 1 |
12 files changed, 69 insertions, 23 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, | |||
2663 | val |= spdif->ctls & 1; | 2663 | val |= spdif->ctls & 1; |
2664 | change = spdif->ctls != val; | 2664 | change = spdif->ctls != val; |
2665 | spdif->ctls = val; | 2665 | spdif->ctls = val; |
2666 | 2666 | if (change && nid != (u16)-1) | |
2667 | if (change) | ||
2668 | set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff); | 2667 | set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff); |
2669 | |||
2670 | mutex_unlock(&codec->spdif_mutex); | 2668 | mutex_unlock(&codec->spdif_mutex); |
2671 | return change; | 2669 | return change; |
2672 | } | 2670 | } |
@@ -2684,6 +2682,17 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, | |||
2684 | return 0; | 2682 | return 0; |
2685 | } | 2683 | } |
2686 | 2684 | ||
2685 | static inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid, | ||
2686 | int dig1, int dig2) | ||
2687 | { | ||
2688 | set_dig_out_convert(codec, nid, dig1, dig2); | ||
2689 | /* unmute amp switch (if any) */ | ||
2690 | if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && | ||
2691 | (dig1 & AC_DIG1_ENABLE)) | ||
2692 | snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, | ||
2693 | HDA_AMP_MUTE, 0); | ||
2694 | } | ||
2695 | |||
2687 | static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, | 2696 | static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, |
2688 | struct snd_ctl_elem_value *ucontrol) | 2697 | struct snd_ctl_elem_value *ucontrol) |
2689 | { | 2698 | { |
@@ -2699,15 +2708,9 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, | |||
2699 | if (ucontrol->value.integer.value[0]) | 2708 | if (ucontrol->value.integer.value[0]) |
2700 | val |= AC_DIG1_ENABLE; | 2709 | val |= AC_DIG1_ENABLE; |
2701 | change = spdif->ctls != val; | 2710 | change = spdif->ctls != val; |
2702 | if (change) { | 2711 | spdif->ctls = val; |
2703 | spdif->ctls = val; | 2712 | if (change && nid != (u16)-1) |
2704 | set_dig_out_convert(codec, nid, val & 0xff, -1); | 2713 | set_spdif_ctls(codec, nid, val & 0xff, -1); |
2705 | /* unmute amp switch (if any) */ | ||
2706 | if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && | ||
2707 | (val & AC_DIG1_ENABLE)) | ||
2708 | snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, | ||
2709 | HDA_AMP_MUTE, 0); | ||
2710 | } | ||
2711 | mutex_unlock(&codec->spdif_mutex); | 2714 | mutex_unlock(&codec->spdif_mutex); |
2712 | return change; | 2715 | return change; |
2713 | } | 2716 | } |
@@ -2754,7 +2757,9 @@ static struct snd_kcontrol_new dig_mixes[] = { | |||
2754 | * | 2757 | * |
2755 | * Returns 0 if successful, or a negative error code. | 2758 | * Returns 0 if successful, or a negative error code. |
2756 | */ | 2759 | */ |
2757 | int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) | 2760 | int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, |
2761 | hda_nid_t associated_nid, | ||
2762 | hda_nid_t cvt_nid) | ||
2758 | { | 2763 | { |
2759 | int err; | 2764 | int err; |
2760 | struct snd_kcontrol *kctl; | 2765 | struct snd_kcontrol *kctl; |
@@ -2774,12 +2779,12 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) | |||
2774 | return -ENOMEM; | 2779 | return -ENOMEM; |
2775 | kctl->id.index = idx; | 2780 | kctl->id.index = idx; |
2776 | kctl->private_value = codec->spdif_out.used - 1; | 2781 | kctl->private_value = codec->spdif_out.used - 1; |
2777 | err = snd_hda_ctl_add(codec, nid, kctl); | 2782 | err = snd_hda_ctl_add(codec, associated_nid, kctl); |
2778 | if (err < 0) | 2783 | if (err < 0) |
2779 | return err; | 2784 | return err; |
2780 | } | 2785 | } |
2781 | spdif->nid = nid; | 2786 | spdif->nid = cvt_nid; |
2782 | spdif->ctls = snd_hda_codec_read(codec, nid, 0, | 2787 | spdif->ctls = snd_hda_codec_read(codec, cvt_nid, 0, |
2783 | AC_VERB_GET_DIGI_CONVERT_1, 0); | 2788 | AC_VERB_GET_DIGI_CONVERT_1, 0); |
2784 | spdif->status = convert_to_spdif_status(spdif->ctls); | 2789 | spdif->status = convert_to_spdif_status(spdif->ctls); |
2785 | return 0; | 2790 | return 0; |
@@ -2800,6 +2805,31 @@ struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, | |||
2800 | } | 2805 | } |
2801 | EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid); | 2806 | EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid); |
2802 | 2807 | ||
2808 | void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) | ||
2809 | { | ||
2810 | struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); | ||
2811 | |||
2812 | mutex_lock(&codec->spdif_mutex); | ||
2813 | spdif->nid = (u16)-1; | ||
2814 | mutex_unlock(&codec->spdif_mutex); | ||
2815 | } | ||
2816 | EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_unassign); | ||
2817 | |||
2818 | void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid) | ||
2819 | { | ||
2820 | struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); | ||
2821 | unsigned short val; | ||
2822 | |||
2823 | mutex_lock(&codec->spdif_mutex); | ||
2824 | if (spdif->nid != nid) { | ||
2825 | spdif->nid = nid; | ||
2826 | val = spdif->ctls; | ||
2827 | set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff); | ||
2828 | } | ||
2829 | mutex_unlock(&codec->spdif_mutex); | ||
2830 | } | ||
2831 | EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_assign); | ||
2832 | |||
2803 | /* | 2833 | /* |
2804 | * SPDIF sharing with analog output | 2834 | * SPDIF sharing with analog output |
2805 | */ | 2835 | */ |
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 1d21c0624e03..96c35cab57bf 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h | |||
@@ -954,6 +954,8 @@ struct hda_spdif_out { | |||
954 | }; | 954 | }; |
955 | struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, | 955 | struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, |
956 | hda_nid_t nid); | 956 | hda_nid_t nid); |
957 | void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx); | ||
958 | void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid); | ||
957 | 959 | ||
958 | /* | 960 | /* |
959 | * Mixer | 961 | * Mixer |
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 08ec073444e2..8b88c92826a1 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h | |||
@@ -212,7 +212,9 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, | |||
212 | /* | 212 | /* |
213 | * SPDIF I/O | 213 | * SPDIF I/O |
214 | */ | 214 | */ |
215 | int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid); | 215 | int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, |
216 | hda_nid_t associated_nid, | ||
217 | hda_nid_t cvt_nid); | ||
216 | int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid); | 218 | int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid); |
217 | 219 | ||
218 | /* | 220 | /* |
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index d694e9d4921d..0f7b8951440f 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c | |||
@@ -213,7 +213,9 @@ static int ad198x_build_controls(struct hda_codec *codec) | |||
213 | return err; | 213 | return err; |
214 | } | 214 | } |
215 | if (spec->multiout.dig_out_nid) { | 215 | if (spec->multiout.dig_out_nid) { |
216 | err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); | 216 | err = snd_hda_create_spdif_out_ctls(codec, |
217 | spec->multiout.dig_out_nid, | ||
218 | spec->multiout.dig_out_nid); | ||
217 | if (err < 0) | 219 | if (err < 0) |
218 | return err; | 220 | return err; |
219 | err = snd_hda_create_spdif_share_sw(codec, | 221 | err = snd_hda_create_spdif_share_sw(codec, |
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 61b92634b161..6b406840846e 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c | |||
@@ -240,7 +240,8 @@ static int ca0110_build_controls(struct hda_codec *codec) | |||
240 | } | 240 | } |
241 | 241 | ||
242 | if (spec->dig_out) { | 242 | if (spec->dig_out) { |
243 | err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out); | 243 | err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, |
244 | spec->dig_out); | ||
244 | if (err < 0) | 245 | if (err < 0) |
245 | return err; | 246 | return err; |
246 | err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); | 247 | err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); |
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 26a1521045bb..c7b5ca28fa77 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c | |||
@@ -821,7 +821,8 @@ static int build_digital_output(struct hda_codec *codec) | |||
821 | if (!spec->multiout.dig_out_nid) | 821 | if (!spec->multiout.dig_out_nid) |
822 | return 0; | 822 | return 0; |
823 | 823 | ||
824 | err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); | 824 | err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid, |
825 | spec->multiout.dig_out_nid); | ||
825 | if (err < 0) | 826 | if (err < 0) |
826 | return err; | 827 | return err; |
827 | err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); | 828 | err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); |
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index ab3308daa960..9eaf99b01aec 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c | |||
@@ -327,7 +327,9 @@ static int cmi9880_build_controls(struct hda_codec *codec) | |||
327 | return err; | 327 | return err; |
328 | } | 328 | } |
329 | if (spec->multiout.dig_out_nid) { | 329 | if (spec->multiout.dig_out_nid) { |
330 | err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); | 330 | err = snd_hda_create_spdif_out_ctls(codec, |
331 | spec->multiout.dig_out_nid, | ||
332 | spec->multiout.dig_out_nid); | ||
331 | if (err < 0) | 333 | if (err < 0) |
332 | return err; | 334 | return err; |
333 | err = snd_hda_create_spdif_share_sw(codec, | 335 | err = snd_hda_create_spdif_share_sw(codec, |
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 3e6b9a8539c2..217ca9e13425 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c | |||
@@ -510,6 +510,7 @@ static int conexant_build_controls(struct hda_codec *codec) | |||
510 | } | 510 | } |
511 | if (spec->multiout.dig_out_nid) { | 511 | if (spec->multiout.dig_out_nid) { |
512 | err = snd_hda_create_spdif_out_ctls(codec, | 512 | err = snd_hda_create_spdif_out_ctls(codec, |
513 | spec->multiout.dig_out_nid, | ||
513 | spec->multiout.dig_out_nid); | 514 | spec->multiout.dig_out_nid); |
514 | if (err < 0) | 515 | if (err < 0) |
515 | return err; | 516 | return err; |
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 86b35a071a83..13ee4449718f 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c | |||
@@ -1095,7 +1095,8 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) | |||
1095 | int i; | 1095 | int i; |
1096 | 1096 | ||
1097 | for (i = 0; i < codec->num_pcms; i++) { | 1097 | for (i = 0; i < codec->num_pcms; i++) { |
1098 | err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]); | 1098 | err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i], |
1099 | spec->cvt[i]); | ||
1099 | if (err < 0) | 1100 | if (err < 0) |
1100 | return err; | 1101 | return err; |
1101 | } | 1102 | } |
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7a4e10002f56..5c8a4ea75cd7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c | |||
@@ -3217,6 +3217,7 @@ static int alc_build_controls(struct hda_codec *codec) | |||
3217 | } | 3217 | } |
3218 | if (spec->multiout.dig_out_nid) { | 3218 | if (spec->multiout.dig_out_nid) { |
3219 | err = snd_hda_create_spdif_out_ctls(codec, | 3219 | err = snd_hda_create_spdif_out_ctls(codec, |
3220 | spec->multiout.dig_out_nid, | ||
3220 | spec->multiout.dig_out_nid); | 3221 | spec->multiout.dig_out_nid); |
3221 | if (err < 0) | 3222 | if (err < 0) |
3222 | return err; | 3223 | return err; |
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 7f81cc2274f3..7407095cbc78 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c | |||
@@ -1112,7 +1112,9 @@ static int stac92xx_build_controls(struct hda_codec *codec) | |||
1112 | } | 1112 | } |
1113 | 1113 | ||
1114 | if (spec->multiout.dig_out_nid) { | 1114 | if (spec->multiout.dig_out_nid) { |
1115 | err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); | 1115 | err = snd_hda_create_spdif_out_ctls(codec, |
1116 | spec->multiout.dig_out_nid, | ||
1117 | spec->multiout.dig_out_nid); | ||
1116 | if (err < 0) | 1118 | if (err < 0) |
1117 | return err; | 1119 | return err; |
1118 | err = snd_hda_create_spdif_share_sw(codec, | 1120 | err = snd_hda_create_spdif_share_sw(codec, |
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 8304c748dfb7..89a0f2a3d269 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c | |||
@@ -1497,6 +1497,7 @@ static int via_build_controls(struct hda_codec *codec) | |||
1497 | 1497 | ||
1498 | if (spec->multiout.dig_out_nid) { | 1498 | if (spec->multiout.dig_out_nid) { |
1499 | err = snd_hda_create_spdif_out_ctls(codec, | 1499 | err = snd_hda_create_spdif_out_ctls(codec, |
1500 | spec->multiout.dig_out_nid, | ||
1500 | spec->multiout.dig_out_nid); | 1501 | spec->multiout.dig_out_nid); |
1501 | if (err < 0) | 1502 | if (err < 0) |
1502 | return err; | 1503 | return err; |