aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/patch_hdmi.c
diff options
context:
space:
mode:
authorAnssi Hannula <anssi.hannula@iki.fi>2013-10-24 14:10:35 -0400
committerTakashi Iwai <tiwai@suse.de>2013-10-24 16:53:43 -0400
commit5a61358433b1f89b500f2c365746c73cb7a27e2f (patch)
tree8d3953897407d5d7ab5205a149d8bcbe4dc5948b /sound/pci/hda/patch_hdmi.c
parent307229d2ac5f60447cc655ebbce5d9c0baa18bbc (diff)
ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support
ATI/AMD codecs do not support all the standard HDA HDMI/DP functions, instead various vendor-specific verbs are provided. This commit addresses these missing functions: - standard channel mapping support - standard infoframe configuration support ATI/AMD provides their own verbs that allow the following: - setting CA for infoframe - setting down-mix information for infoframe - channel pair remapping - individual channel remapping (revision ID 3+, 0x100300+) The documentation for the verbs has now been released by AMD: http://www.x.org/docs/AMD/AMD_HDA_verbs_v2.pdf Add support for the ATI/AMD specific verbs and use them instead of the generic methods on ATI/AMD codecs. This allows multi-channel PCM audio to work. Channel remapping is restricted to pairwise mapping on codecs with revision ID 2 (0x100200 as reported by procfs codec#X) or lower. This means cards up to Radeon HD7670 as far as I know. This will not affect standard multi-channel modes since these codecs support automatic FC-LFE swapping for HDMI. ATI/AMD codecs do not advertise all of their supported rates, formats and channel counts, therefore that information is forced accordingly so that all HDMI 1.x PCM parameters are marked as supported. Support for multiple ports is also added to patch_atihdmi so that 0x1002aa01 codecs with multiple ports will work properly when switched back to that patch. v2: splitted ELD emulation to a separate patch, tlv fixes v3: adapted to the new hdmi_ops infrastructure, fixed rev3+ vendor id Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi> Tested-by: Peter Frühberger <fritsch@xbmc.org> # v2 Tested-by: Olivier Langlois <olivier@trillion01.com> # v2+rev3fix Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_hdmi.c')
-rw-r--r--sound/pci/hda/patch_hdmi.c302
1 files changed, 273 insertions, 29 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index acc00fe01b71..1114be018e5a 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -6,6 +6,7 @@
6 * Copyright (c) 2006 ATI Technologies Inc. 6 * Copyright (c) 2006 ATI Technologies Inc.
7 * Copyright (c) 2008 NVIDIA Corp. All rights reserved. 7 * Copyright (c) 2008 NVIDIA Corp. All rights reserved.
8 * Copyright (c) 2008 Wei Ni <wni@nvidia.com> 8 * Copyright (c) 2008 Wei Ni <wni@nvidia.com>
9 * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
9 * 10 *
10 * Authors: 11 * Authors:
11 * Wu Fengguang <wfg@linux.intel.com> 12 * Wu Fengguang <wfg@linux.intel.com>
@@ -128,7 +129,7 @@ struct hdmi_spec {
128 struct hdmi_eld temp_eld; 129 struct hdmi_eld temp_eld;
129 struct hdmi_ops ops; 130 struct hdmi_ops ops;
130 /* 131 /*
131 * Non-generic ATI/NVIDIA specific 132 * Non-generic VIA/NVIDIA specific
132 */ 133 */
133 struct hda_multi_out multiout; 134 struct hda_multi_out multiout;
134 struct hda_pcm_stream pcm_playback; 135 struct hda_pcm_stream pcm_playback;
@@ -2784,49 +2785,292 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
2784} 2785}
2785 2786
2786/* 2787/*
2787 * ATI-specific implementations 2788 * ATI/AMD-specific implementations
2788 *
2789 * FIXME: we may omit the whole this and use the generic code once after
2790 * it's confirmed to work.
2791 */ 2789 */
2792 2790
2793#define ATIHDMI_CVT_NID 0x02 /* audio converter */ 2791#define is_amdhdmi_rev3_or_later(codec) \
2794#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */ 2792 ((codec)->vendor_id == 0x1002aa01 && ((codec)->revision_id & 0xff00) >= 0x0300)
2793#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec)
2794
2795/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */
2796#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771
2797#define ATI_VERB_SET_DOWNMIX_INFO 0x772
2798#define ATI_VERB_SET_MULTICHANNEL_01 0x777
2799#define ATI_VERB_SET_MULTICHANNEL_23 0x778
2800#define ATI_VERB_SET_MULTICHANNEL_45 0x779
2801#define ATI_VERB_SET_MULTICHANNEL_67 0x77a
2802#define ATI_VERB_SET_MULTICHANNEL_1 0x785
2803#define ATI_VERB_SET_MULTICHANNEL_3 0x786
2804#define ATI_VERB_SET_MULTICHANNEL_5 0x787
2805#define ATI_VERB_SET_MULTICHANNEL_7 0x788
2806#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789
2807#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71
2808#define ATI_VERB_GET_DOWNMIX_INFO 0xf72
2809#define ATI_VERB_GET_MULTICHANNEL_01 0xf77
2810#define ATI_VERB_GET_MULTICHANNEL_23 0xf78
2811#define ATI_VERB_GET_MULTICHANNEL_45 0xf79
2812#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a
2813#define ATI_VERB_GET_MULTICHANNEL_1 0xf85
2814#define ATI_VERB_GET_MULTICHANNEL_3 0xf86
2815#define ATI_VERB_GET_MULTICHANNEL_5 0xf87
2816#define ATI_VERB_GET_MULTICHANNEL_7 0xf88
2817#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89
2818
2819#define ATI_OUT_ENABLE 0x1
2820
2821#define ATI_MULTICHANNEL_MODE_PAIRED 0
2822#define ATI_MULTICHANNEL_MODE_SINGLE 1
2823
2824static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, int ca,
2825 int active_channels, int conn_type)
2826{
2827 snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
2828}
2829
2830static int atihdmi_paired_swap_fc_lfe(int pos)
2831{
2832 /*
2833 * ATI/AMD have automatic FC/LFE swap built-in
2834 * when in pairwise mapping mode.
2835 */
2836
2837 switch (pos) {
2838 /* see channel_allocations[].speakers[] */
2839 case 2: return 3;
2840 case 3: return 2;
2841 default: break;
2842 }
2843
2844 return pos;
2845}
2846
2847static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
2848{
2849 struct cea_channel_speaker_allocation *cap;
2850 int i, j;
2851
2852 /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
2853
2854 cap = &channel_allocations[get_channel_allocation_order(ca)];
2855 for (i = 0; i < chs; ++i) {
2856 int mask = to_spk_mask(map[i]);
2857 bool ok = false;
2858 bool companion_ok = false;
2859
2860 if (!mask)
2861 continue;
2862
2863 for (j = 0 + i % 2; j < 8; j += 2) {
2864 int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j);
2865 if (cap->speakers[chan_idx] == mask) {
2866 /* channel is in a supported position */
2867 ok = true;
2868
2869 if (i % 2 == 0 && i + 1 < chs) {
2870 /* even channel, check the odd companion */
2871 int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1);
2872 int comp_mask_req = to_spk_mask(map[i+1]);
2873 int comp_mask_act = cap->speakers[comp_chan_idx];
2874
2875 if (comp_mask_req == comp_mask_act)
2876 companion_ok = true;
2877 else
2878 return -EINVAL;
2879 }
2880 break;
2881 }
2882 }
2883
2884 if (!ok)
2885 return -EINVAL;
2886
2887 if (companion_ok)
2888 i++; /* companion channel already checked */
2889 }
2890
2891 return 0;
2892}
2893
2894static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
2895 int hdmi_slot, int stream_channel)
2896{
2897 int verb;
2898 int ati_channel_setup = 0;
2899
2900 if (hdmi_slot > 7)
2901 return -EINVAL;
2902
2903 if (!has_amd_full_remap_support(codec)) {
2904 hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot);
2905
2906 /* In case this is an odd slot but without stream channel, do not
2907 * disable the slot since the corresponding even slot could have a
2908 * channel. In case neither have a channel, the slot pair will be
2909 * disabled when this function is called for the even slot. */
2910 if (hdmi_slot % 2 != 0 && stream_channel == 0xf)
2911 return 0;
2912
2913 hdmi_slot -= hdmi_slot % 2;
2914
2915 if (stream_channel != 0xf)
2916 stream_channel -= stream_channel % 2;
2917 }
2918
2919 verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e;
2920
2921 /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */
2922
2923 if (stream_channel != 0xf)
2924 ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE;
2925
2926 return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
2927}
2928
2929static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
2930 int asp_slot)
2931{
2932 bool was_odd = false;
2933 int ati_asp_slot = asp_slot;
2934 int verb;
2935 int ati_channel_setup;
2936
2937 if (asp_slot > 7)
2938 return -EINVAL;
2939
2940 if (!has_amd_full_remap_support(codec)) {
2941 ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot);
2942 if (ati_asp_slot % 2 != 0) {
2943 ati_asp_slot -= 1;
2944 was_odd = true;
2945 }
2946 }
2947
2948 verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e;
2949
2950 ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0);
2951
2952 if (!(ati_channel_setup & ATI_OUT_ENABLE))
2953 return 0xf;
2954
2955 return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd;
2956}
2795 2957
2796static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 2958static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
2797 struct hda_codec *codec, 2959 int channels)
2798 unsigned int stream_tag, 2960{
2799 unsigned int format, 2961 int c;
2800 struct snd_pcm_substream *substream) 2962
2963 /*
2964 * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so
2965 * we need to take that into account (a single channel may take 2
2966 * channel slots if we need to carry a silent channel next to it).
2967 * On Rev3+ AMD codecs this function is not used.
2968 */
2969 int chanpairs = 0;
2970
2971 /* We only produce even-numbered channel count TLVs */
2972 if ((channels % 2) != 0)
2973 return -1;
2974
2975 for (c = 0; c < 7; c += 2) {
2976 if (cap->speakers[c] || cap->speakers[c+1])
2977 chanpairs++;
2978 }
2979
2980 if (chanpairs * 2 != channels)
2981 return -1;
2982
2983 return SNDRV_CTL_TLVT_CHMAP_PAIRED;
2984}
2985
2986static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
2987 unsigned int *chmap, int channels)
2988{
2989 /* produce paired maps for pre-rev3 ATI/AMD codecs */
2990 int count = 0;
2991 int c;
2992
2993 for (c = 7; c >= 0; c--) {
2994 int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c);
2995 int spk = cap->speakers[chan];
2996 if (!spk) {
2997 /* add N/A channel if the companion channel is occupied */
2998 if (cap->speakers[chan + (chan % 2 ? -1 : 1)])
2999 chmap[count++] = SNDRV_CHMAP_NA;
3000
3001 continue;
3002 }
3003
3004 chmap[count++] = spk_to_chmap(spk);
3005 }
3006
3007 WARN_ON(count != channels);
3008}
3009
3010static int atihdmi_init(struct hda_codec *codec)
2801{ 3011{
2802 struct hdmi_spec *spec = codec->spec; 3012 struct hdmi_spec *spec = codec->spec;
2803 struct hdmi_spec_per_cvt *per_cvt = get_cvt(spec, 0); 3013 int pin_idx, err;
2804 int chans = substream->runtime->channels;
2805 int i, err;
2806 3014
2807 err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format, 3015 err = generic_hdmi_init(codec);
2808 substream); 3016
2809 if (err < 0) 3017 if (err)
2810 return err; 3018 return err;
2811 snd_hda_codec_write(codec, per_cvt->cvt_nid, 0, 3019
2812 AC_VERB_SET_CVT_CHAN_COUNT, chans - 1); 3020 for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
2813 /* FIXME: XXX */ 3021 struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
2814 for (i = 0; i < chans; i++) { 3022
2815 snd_hda_codec_write(codec, per_cvt->cvt_nid, 0, 3023 /* make sure downmix information in infoframe is zero */
2816 AC_VERB_SET_HDMI_CHAN_SLOT, 3024 snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0);
2817 (i << 4) | i); 3025
3026 /* enable channel-wise remap mode if supported */
3027 if (has_amd_full_remap_support(codec))
3028 snd_hda_codec_write(codec, per_pin->pin_nid, 0,
3029 ATI_VERB_SET_MULTICHANNEL_MODE,
3030 ATI_MULTICHANNEL_MODE_SINGLE);
2818 } 3031 }
3032
2819 return 0; 3033 return 0;
2820} 3034}
2821 3035
2822static int patch_atihdmi(struct hda_codec *codec) 3036static int patch_atihdmi(struct hda_codec *codec)
2823{ 3037{
2824 struct hdmi_spec *spec; 3038 struct hdmi_spec *spec;
2825 int err = patch_simple_hdmi(codec, ATIHDMI_CVT_NID, ATIHDMI_PIN_NID); 3039 struct hdmi_spec_per_cvt *per_cvt;
2826 if (err < 0) 3040 int err, cvt_idx;
3041
3042 err = patch_generic_hdmi(codec);
3043
3044 if (err)
2827 return err; 3045 return err;
3046
3047 codec->patch_ops.init = atihdmi_init;
3048
2828 spec = codec->spec; 3049 spec = codec->spec;
2829 spec->pcm_playback.ops.prepare = atihdmi_playback_pcm_prepare; 3050
3051 spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel;
3052 spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel;
3053 spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe;
3054
3055 if (!has_amd_full_remap_support(codec)) {
3056 /* override to ATI/AMD-specific versions with pairwise mapping */
3057 spec->ops.chmap_cea_alloc_validate_get_type =
3058 atihdmi_paired_chmap_cea_alloc_validate_get_type;
3059 spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap;
3060 spec->ops.chmap_validate = atihdmi_paired_chmap_validate;
3061 }
3062
3063 /* ATI/AMD converters do not advertise all of their capabilities */
3064 for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
3065 per_cvt = get_cvt(spec, cvt_idx);
3066 per_cvt->channels_max = max(per_cvt->channels_max, 8u);
3067 per_cvt->rates |= SUPPORTED_RATES;
3068 per_cvt->formats |= SUPPORTED_FORMATS;
3069 per_cvt->maxbps = max(per_cvt->maxbps, 24u);
3070 }
3071
3072 spec->channels_max = max(spec->channels_max, 8u);
3073
2830 return 0; 3074 return 0;
2831} 3075}
2832 3076
@@ -2846,7 +3090,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
2846{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi }, 3090{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
2847{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi }, 3091{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
2848{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi }, 3092{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
2849{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_generic_hdmi }, 3093{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
2850{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi }, 3094{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
2851{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi }, 3095{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
2852{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi }, 3096{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },