diff --git a/packages/linux/patches/3.11.3/linux-990.11-ALSA-AMD-HD-Audio.patch b/packages/linux/patches/3.11.3/linux-990.11-ALSA-AMD-HD-Audio.patch index f6652c07d1..53c4fda797 100644 --- a/packages/linux/patches/3.11.3/linux-990.11-ALSA-AMD-HD-Audio.patch +++ b/packages/linux/patches/3.11.3/linux-990.11-ALSA-AMD-HD-Audio.patch @@ -1,5 +1,214 @@ ---- a/sound/pci/hda/patch_hdmi.c.orig 2013-09-22 20:55:14.696690357 +0200 -+++ b/sound/pci/hda/patch_hdmi.c 2013-09-22 21:07:17.924703934 +0200 +Combined patch for ATI/AMD HDMI. + +Actual patchset has been posted to ALSA-devel. + +-- +Anssi Hannula + +diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c +index d0d7ac1e..750841e 100644 +--- a/sound/pci/hda/hda_eld.c ++++ b/sound/pci/hda/hda_eld.c +@@ -2,6 +2,7 @@ + * Generic routines and proc interface for ELD(EDID Like Data) information + * + * Copyright(c) 2008 Intel Corporation. ++ * Copyright (c) 2013 Anssi Hannula + * + * Authors: + * Wu Fengguang +@@ -316,6 +317,9 @@ int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) + AC_DIPSIZE_ELD_BUF); + } + ++static int atihdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, ++ unsigned char *buf, int *eld_size); ++ + int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size) + { +@@ -323,6 +327,9 @@ int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, + int ret = 0; + int size; + ++ if (is_atihdmi(codec)) ++ return atihdmi_get_eld(codec, nid, buf, eld_size); ++ + /* + * ELD size is initialized to zero in caller function. If no errors and + * ELD is valid, actual eld_size is assigned. +@@ -671,3 +678,153 @@ void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, + hinfo->maxbps = min(hinfo->maxbps, maxbps); + hinfo->channels_max = min(hinfo->channels_max, channels_max); + } ++ ++ ++/* ATI/AMD specific stuff (ELD emulation) */ ++ ++#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776 ++#define ATI_VERB_SET_SINK_INFO_INDEX 0x780 ++#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70 ++#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76 ++#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b ++#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80 ++#define ATI_VERB_GET_SINK_INFO_DATA 0xf81 ++ ++#define ATI_SPKALLOC_SPKALLOC 0x007f ++#define ATI_SPKALLOC_TYPE_HDMI 0x0100 ++#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200 ++ ++/* first three bytes are just standard SAD */ ++#define ATI_AUDIODESC_CHANNELS 0x00000007 ++#define ATI_AUDIODESC_RATES 0x0000ff00 ++#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000 ++ ++/* in standard HDMI VSDB format */ ++#define ATI_DELAY_VIDEO_LATENCY 0x000000ff ++#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00 ++ ++enum ati_sink_info_idx { ++ ATI_INFO_IDX_MANUFACTURER_ID = 0, ++ ATI_INFO_IDX_PRODUCT_ID = 1, ++ ATI_INFO_IDX_SINK_DESC_LEN = 2, ++ ATI_INFO_IDX_PORT_ID_LOW = 3, ++ ATI_INFO_IDX_PORT_ID_HIGH = 4, ++ ATI_INFO_IDX_SINK_DESC_FIRST = 5, ++ ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */ ++}; ++ ++static int atihdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, ++ unsigned char *buf, int *eld_size) ++{ ++ int spkalloc, ati_sad, aud_synch; ++ int sink_desc_len = 0; ++ int pos, i; ++ ++ /* ATI/AMD does not have ELD, emulate it */ ++ ++ spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0); ++ ++ if (!spkalloc) { ++ snd_printd(KERN_INFO "HDMI ATI/AMD: no speaker allocation for ELD\n"); ++ return -EINVAL; ++ } ++ ++ memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3); ++ ++ /* version */ ++ buf[0] = ELD_VER_CEA_861D << 3; ++ ++ /* speaker allocation from EDID */ ++ buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC; ++ ++ /* is DisplayPort? */ ++ if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT) ++ buf[5] |= 0x04; ++ ++ pos = ELD_FIXED_BYTES; ++ ++ if (is_amdhdmi_rev3(codec)) { ++ int sink_info; ++ ++ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW); ++ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); ++ put_unaligned_le32(sink_info, buf + 8); ++ ++ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH); ++ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); ++ put_unaligned_le32(sink_info, buf + 12); ++ ++ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID); ++ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); ++ put_unaligned_le16(sink_info, buf + 16); ++ ++ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID); ++ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); ++ put_unaligned_le16(sink_info, buf + 18); ++ ++ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN); ++ sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); ++ ++ if (sink_desc_len > ELD_MAX_MNL) { ++ snd_printd(KERN_INFO "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n", ++ sink_desc_len); ++ sink_desc_len = ELD_MAX_MNL; ++ } ++ ++ buf[4] |= sink_desc_len; ++ ++ for (i = 0; i < sink_desc_len; i++) { ++ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i); ++ buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); ++ } ++ } ++ ++ for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) { ++ if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST) ++ continue; /* not handled by ATI/AMD */ ++ ++ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3); ++ ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0); ++ ++ if (ati_sad & ATI_AUDIODESC_RATES) { ++ /* format is supported, copy SAD as-is */ ++ buf[pos++] = (ati_sad & 0x0000ff) >> 0; ++ buf[pos++] = (ati_sad & 0x00ff00) >> 8; ++ buf[pos++] = (ati_sad & 0xff0000) >> 16; ++ } ++ ++ if (i == AUDIO_CODING_TYPE_LPCM ++ && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) ++ && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) { ++ /* for PCM there is a separate stereo rate mask */ ++ buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1; ++ /* rates from the extra byte */ ++ buf[pos++] = (ati_sad & 0xff000000) >> 24; ++ buf[pos++] = (ati_sad & 0x00ff0000) >> 16; ++ } ++ } ++ ++ if (pos == ELD_FIXED_BYTES + sink_desc_len) { ++ snd_printd(KERN_INFO "HDMI ATI/AMD: no audio descriptors for ELD\n"); ++ return -EINVAL; ++ } ++ ++ aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0); ++ if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) { ++ int video_latency = (aud_synch & ATI_DELAY_VIDEO_LATENCY) - 1; ++ int audio_latency = ((aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8) - 1; ++ ++ if (video_latency > audio_latency) ++ buf[6] = min(video_latency - audio_latency, 0xfa); ++ } ++ ++ /* Baseline length */ ++ buf[2] = pos - 4; ++ ++ /* SAD count */ ++ buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4; ++ ++ *eld_size = pos; ++ ++ return 0; ++} +diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h +index 2e7493e..7c0b89e 100644 +--- a/sound/pci/hda/hda_local.h ++++ b/sound/pci/hda/hda_local.h +@@ -786,4 +786,9 @@ static inline void snd_hda_eld_proc_free(struct hda_codec *codec, + #define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 + void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); + ++/* shared with patch_hdmi.c and hda_eld.c */ ++#define is_atihdmi(codec) (((codec)->vendor_id & 0xffff0000) == 0x10020000) ++#define is_amdhdmi_rev3(codec) \ ++ ((codec)->vendor_id == 0x1002791a && ((codec)->revision_id & 0xff00) >= 0x0300) ++ + #endif /* __SOUND_HDA_LOCAL_H */ +diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c +index 3d8cd044..22f30fe 100644 +--- a/sound/pci/hda/patch_hdmi.c ++++ b/sound/pci/hda/patch_hdmi.c @@ -6,6 +6,7 @@ * Copyright (c) 2006 ATI Technologies Inc. * Copyright (c) 2008 NVIDIA Corp. All rights reserved. @@ -8,19 +217,17 @@ * * Authors: * Wu Fengguang -@@ -43,6 +44,11 @@ - static bool static_hdmi_pcm; - module_param(static_hdmi_pcm, bool, 0644); - MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); -+#define is_atihdmi(codec) (((codec)->vendor_id & 0xffff0000) == 0x10020000) -+#define has_amd_full_remap_support(codec) \ -+ ((codec)->vendor_id == 0x1002791a && ((codec)->revision_id & 0xff00) >= 0x0300) -+ -+static void atihdmi_set_ca(struct hda_codec *codec, hda_nid_t pin_nid, int ca); +@@ -46,6 +47,9 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); + #define is_haswell(codec) ((codec)->vendor_id == 0x80862807) + ++/* is_atihdmi() and is_amdhdmi_rev3() are in hda_local.h */ ++#define has_amd_full_remap_support(codec) is_amdhdmi_rev3(codec) ++ struct hdmi_spec_per_cvt { hda_nid_t cvt_nid; -@@ -85,7 +91,7 @@ + int assigned; +@@ -89,7 +93,7 @@ struct hdmi_spec { struct hdmi_eld temp_eld; /* @@ -29,7 +236,7 @@ */ struct hda_multi_out multiout; struct hda_pcm_stream pcm_playback; -@@ -569,6 +575,20 @@ +@@ -573,6 +577,20 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels) return ca; } @@ -50,7 +257,7 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid) { -@@ -577,14 +597,26 @@ +@@ -581,14 +599,26 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, int slot; for (i = 0; i < 8; i++) { @@ -79,7 +286,7 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid, -@@ -613,9 +645,8 @@ +@@ -617,9 +647,8 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, } for (i = 0; i < 8; i++) { @@ -91,7 +298,7 @@ if (err) { snd_printdd(KERN_NOTICE "HDMI: channel mapping failed\n"); -@@ -724,8 +755,7 @@ +@@ -728,8 +757,7 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, else val = 0xf; val |= (i << 4); @@ -101,7 +308,16 @@ if (err) return -EINVAL; } -@@ -902,6 +932,16 @@ +@@ -883,6 +911,8 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, + return true; + } + ++static void atihdmi_set_ca(struct hda_codec *codec, hda_nid_t pin_nid, int ca); ++ + static void hdmi_setup_audio_infoframe(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool non_pcm) +@@ -912,6 +942,16 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, if (ca < 0) ca = 0; @@ -118,31 +334,16 @@ memset(&ai, 0, sizeof(ai)); if (eld->info.conn_type == 0) { /* HDMI */ struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; -@@ -1282,6 +1322,9 @@ - return 0; - } +@@ -1100,7 +1140,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + new_pinctl); -+static int atihdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, -+ unsigned char *buf, int *eld_size); -+ - static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) - { - struct hda_codec *codec = per_pin->codec; -@@ -1311,8 +1354,12 @@ - "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - codec->addr, pin_nid, pin_eld->monitor_present, eld->eld_valid); - -+ if (pin_eld->monitor_present && is_atihdmi(codec)) -+ eld->eld_valid = (atihdmi_get_eld(codec, pin_nid, eld->eld_buffer, &eld->eld_size) == 0); -+ - if (eld->eld_valid) { -- if (snd_hdmi_get_eld(codec, pin_nid, eld->eld_buffer, -+ if (!is_atihdmi(codec) && -+ snd_hdmi_get_eld(codec, pin_nid, eld->eld_buffer, - &eld->eld_size) < 0) - eld->eld_valid = false; - else { -@@ -1596,6 +1643,8 @@ + } +- if (is_hbr_format(format) && !new_pinctl) { ++ if (is_hbr_format(format) && !new_pinctl && !is_atihdmi(codec)) { + snd_printdd("hdmi_setup_stream: HBR is not supported\n"); + return -EINVAL; + } +@@ -1603,6 +1643,8 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, return 0; } @@ -151,7 +352,7 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { -@@ -1606,6 +1655,10 @@ +@@ -1613,6 +1655,10 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, FL | FR | RL | RR | LFE | FC | RLC | RRC; unsigned int __user *dst; int chs, count = 0; @@ -162,7 +363,7 @@ if (size < 8) return -ENOMEM; -@@ -1613,19 +1666,33 @@ +@@ -1620,19 +1666,35 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -EFAULT; size -= 8; dst = tlv + 2; @@ -175,12 +376,13 @@ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { int chs_bytes = chs * 4; - if (cap->channels != chs) ++ + if (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED) { + int chanpairs = 0; + /* in paired mode we need to take into account + * the occupied channel pairs instead of just the + * channel count */ -+ for (c = 0; c < 8; c++) { ++ for (c = 0; c < 7; c += 2) { + if (cap->speakers[c] || cap->speakers[c+1]) + chanpairs++; + } @@ -195,11 +397,12 @@ if (size < 8) return -ENOMEM; - if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) || ++ + if (put_user(tlv_type, dst) || put_user(chs_bytes, dst + 1)) return -EFAULT; dst += 2; -@@ -1636,10 +1703,25 @@ +@@ -1643,10 +1705,27 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, size -= chs_bytes; count += chs_bytes; for (c = 7; c >= 0; c--) { @@ -218,8 +421,10 @@ + if (spk) + chpos = spk_to_chmap(spk); + else { -+ /* We need to reserve an N/A channel in paired mode */ -+ if (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED) ++ /* We need to reserve an N/A channel in paired mode ++ * if the companion channel is occupied. */ ++ if (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED ++ && cap->speakers[chan + (chan % 2 ? -1 : 1)]) + chpos = SNDRV_CHMAP_NA; + else + continue; @@ -229,7 +434,7 @@ return -EFAULT; dst++; } -@@ -1665,6 +1747,18 @@ +@@ -1672,6 +1751,18 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, return 0; } @@ -248,7 +453,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { -@@ -1676,7 +1770,7 @@ +@@ -1683,7 +1774,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, unsigned int ctl_idx; struct snd_pcm_substream *substream; unsigned char chmap[8]; @@ -257,7 +462,7 @@ ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); substream = snd_pcm_chmap_substream(info, ctl_idx); -@@ -1700,6 +1794,9 @@ +@@ -1707,6 +1798,9 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); if (ca < 0) return -EINVAL; @@ -267,7 +472,7 @@ per_pin->chmap_set = true; memcpy(per_pin->chmap, chmap, sizeof(chmap)); if (prepared) -@@ -2543,48 +2640,281 @@ +@@ -2551,13 +2645,182 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) /* * ATI-specific implementations @@ -281,95 +486,37 @@ +/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */ +#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771 +#define ATI_VERB_SET_DOWNMIX_INFO 0x772 -+#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776 +#define ATI_VERB_SET_MULTICHANNEL_01 0x777 +#define ATI_VERB_SET_MULTICHANNEL_23 0x778 +#define ATI_VERB_SET_MULTICHANNEL_45 0x779 +#define ATI_VERB_SET_MULTICHANNEL_67 0x77a ++#define ATI_VERB_SET_HBR_CONTROL 0x77c +#define ATI_VERB_SET_MULTICHANNEL_1 0x785 +#define ATI_VERB_SET_MULTICHANNEL_3 0x786 +#define ATI_VERB_SET_MULTICHANNEL_5 0x787 +#define ATI_VERB_SET_MULTICHANNEL_7 0x788 +#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789 -+#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70 +#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71 +#define ATI_VERB_GET_DOWNMIX_INFO 0xf72 -+#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76 +#define ATI_VERB_GET_MULTICHANNEL_01 0xf77 +#define ATI_VERB_GET_MULTICHANNEL_23 0xf78 +#define ATI_VERB_GET_MULTICHANNEL_45 0xf79 +#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a ++#define ATI_VERB_GET_HBR_CONTROL 0xf7c +#define ATI_VERB_GET_MULTICHANNEL_1 0xf85 +#define ATI_VERB_GET_MULTICHANNEL_3 0xf86 +#define ATI_VERB_GET_MULTICHANNEL_5 0xf87 +#define ATI_VERB_GET_MULTICHANNEL_7 0xf88 +#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89 + ++/* AMD specific HDA cvt verbs */ ++#define ATI_VERB_SET_RAMP_RATE 0x770 ++#define ATI_VERB_GET_RAMP_RATE 0xf70 ++ +#define ATI_OUT_ENABLE 0x1 + -+/* TODO: cleanup this function */ -+static int atihdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, -+ unsigned char *buf, int *eld_size) -+{ -+ int spkalloc, ati_sad; -+ int pos, i; -+ -+ /* ATI/AMD does not have ELD, emulate it */ -+ -+ spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0); -+ -+ if (!spkalloc) -+ return -EINVAL; -+ -+ memset(buf, 0, 84); -+ -+ /* speaker allocation from EDID */ -+ buf[7] = spkalloc & 0x00007f; -+ -+ /* is DisplayPort? */ -+ if (spkalloc & 0x000200) -+ buf[5] |= 0x04; -+ -+ pos = 20; -+ for (i = 1; i <= 14; i++) { -+ if (i == 13) /* not handled by ATI/AMD */ -+ continue; -+ -+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3); -+ ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0); -+ -+ if (ati_sad & 0xff00) { -+ memcpy(buf + pos, &ati_sad, 3); -+ pos += 3; -+ } -+ -+ if (i == 1 && ati_sad & 0xff000000 && (ati_sad & 0xff00) != (ati_sad & 0xff000000) >> 16) { -+ /* for PCM there is a separate stereo rate mask */ -+ memcpy(buf + pos, &ati_sad, 3); -+ /* max channels to 2 */ -+ buf[pos+0] = (buf[pos+0] & ~0x7) | 0x1; -+ /* rates from extra byte */ -+ buf[pos+1] = (ati_sad & 0xff000000) >> 24; -+ pos += 3; -+ } -+ } -+ -+ if (pos == 20) -+ return -EINVAL; -+ -+ /* version */ -+ buf[0] = 0x10; -+ -+ /* Baseline length */ -+ buf[2] = pos - 4; -+ -+ /* SAD count */ -+ buf[5] |= ((pos - 20) / 3) << 4; -+ -+ *eld_size = pos; -+ -+ return 0; -+} ++#define ATI_HBR_CAPABLE 0x01 ++#define ATI_HBR_ENABLE 0x10 + +static void atihdmi_set_ca(struct hda_codec *codec, hda_nid_t pin_nid, int ca) +{ @@ -393,12 +540,7 @@ + + return pos; +} - --static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, -- struct hda_codec *codec, -- unsigned int stream_tag, -- unsigned int format, -- struct snd_pcm_substream *substream) ++ +static int atihdmi_pairwise_chmap_check_order(struct hda_codec *codec, int ca, + int chs, unsigned char *map) +{ @@ -455,6 +597,9 @@ + int verb; + int ati_channel_setup = 0; + ++ if (hdmi_slot > 7) ++ return -EINVAL; ++ + if (!has_amd_full_remap_support(codec)) { + hdmi_slot = atihdmi_swap_fc_lfe(hdmi_slot); + @@ -491,6 +636,9 @@ + + /* emulate AC_VERB_GET_HDMI_CHAN_SLOT */ + ++ if (asp_slot > 7) ++ return -EINVAL; ++ + if (!has_amd_full_remap_support(codec)) { + ati_asp_slot = atihdmi_swap_fc_lfe(asp_slot); + if (ati_asp_slot % 2 != 0) { @@ -509,19 +657,67 @@ + return ((ati_channel_setup & 0xf0) + ((!!was_odd) << 4)) | asp_slot; +} +#endif -+ -+static int atihdmi_init(struct hda_codec *codec) + + static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, +@@ -2565,34 +2828,117 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + unsigned int format, + struct snd_pcm_substream *substream) { ++ hda_nid_t cvt_nid = hinfo->nid; struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_cvt *per_cvt = get_cvt(spec, 0); - int chans = substream->runtime->channels; - int i, err; -+ int pin_idx, err; ++ int pin_idx = hinfo_to_pin_index(spec, hinfo); ++ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); ++ hda_nid_t pin_nid = per_pin->pin_nid; ++ int hbr_ctl, hbr_ctl_new; - err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format, - substream); - if (err < 0) -+ err = generic_hdmi_init(codec); ++ hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0); ++ if (hbr_ctl & ATI_HBR_CAPABLE) { ++ if (is_hbr_format(format)) ++ hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE; ++ else ++ hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE; ++ ++ snd_printdd("atihdmi_playback_pcm_prepare: " ++ "NID=0x%x, %shbr-ctl=0x%x\n", ++ pin_nid, ++ hbr_ctl == hbr_ctl_new ? "" : "new-", ++ hbr_ctl_new); ++ ++ if (hbr_ctl != hbr_ctl_new) ++ snd_hda_codec_write(codec, pin_nid, 0, ++ ATI_VERB_SET_HBR_CONTROL, ++ hbr_ctl_new); ++ ++ } else if (is_hbr_format(format)) { ++ snd_printdd("atihdmi_playback_pcm_prepare: HBR is not supported\n"); ++ return -EINVAL; ++ } ++ ++ if (is_amdhdmi_rev3(codec)) { ++ int ramp_rate = 180; /* default as per spec */ ++ /* disable ramp-up/down for non-pcm as per spec */ ++ if (format & AC_FMT_TYPE_NON_PCM) ++ ramp_rate = 0; ++ ++ snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate); ++ } ++ ++ return generic_hdmi_playback_pcm_prepare(hinfo, codec, stream_tag, format, substream); ++} ++ ++static int atihdmi_build_pcms(struct hda_codec *codec) ++{ ++ struct hdmi_spec *spec = codec->spec; ++ int err, pin_idx; ++ ++ err = generic_hdmi_build_pcms(codec); + + if (err) return err; @@ -534,14 +730,34 @@ - (i << 4) | i); + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { ++ struct hda_pcm *info = get_pcm_rec(spec, pin_idx); ++ ++ info->stream[SNDRV_PCM_STREAM_PLAYBACK].ops.prepare = atihdmi_playback_pcm_prepare; + } ++ ++ return 0; ++} ++ ++static int atihdmi_init(struct hda_codec *codec) ++{ ++ struct hdmi_spec *spec = codec->spec; ++ int pin_idx, err; ++ ++ err = generic_hdmi_init(codec); ++ ++ if (err) ++ return err; ++ ++ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + ++ /* make sure downmix information in infoframe is zero */ + snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0); + + /* enable channel-wise remap mode if supported */ + if (has_amd_full_remap_support(codec)) + snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_MULTICHANNEL_MODE, 1); - } ++ } + return 0; } @@ -560,6 +776,7 @@ return err; + + codec->patch_ops.init = atihdmi_init; ++ codec->patch_ops.build_pcms = atihdmi_build_pcms; + + /* ATI/AMD converters do not advertise all of their capabilities */ spec = codec->spec; @@ -571,11 +788,13 @@ + per_cvt->formats |= SUPPORTED_FORMATS; + per_cvt->maxbps = max(per_cvt->maxbps, 24u); + } ++ ++ spec->channels_max = max(spec->channels_max, 8u); + return 0; } -@@ -2604,7 +2934,7 @@ +@@ -2612,7 +2958,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi }, @@ -584,4 +803,3 @@ { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi }, { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi }, -