alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support
@ 2013-09-22 17:50 Anssi Hannula
  2013-09-22 21:14 ` Anssi Hannula
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Anssi Hannula @ 2013-09-22 17:50 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Rafał Miłecki, Peter Frühberger

Request for comments and testing.

AMD just released the documentation needed for multi-channel HDMI
support, so here is the initial patch adding such support.

This has been tested on codec 0x1002aa01, revision 0x100200 with the
exception of custom channel maps, which I intend to test later.

I'm especially interested in testers with:
- Older codecs other than 0x1002aa01. My best guess is that the new code
  should work on them as well, but I'm not really 100% sure.
- Codec 0x1002aa01 revisions 0x100300 (Rev ID 3) or later (see
  /proc/asound/cardX/codec#Y). These support individual channel
  remapping and that code is entirely untested (even for regular
  5.1/7.1).

I decided it was best to get this out there early even though major
parts of it are still untested, for comments and for other people to
test :)

Note that this recent commit is probably required for multi-channel to
work with the open-source radeon driver:
ALSA: hda - hdmi: Fallback to ALSA allocation when selecting CA
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/sound/pci/hda/patch_hdmi.c?id=18e391862cceaf43ddb8eb5cca05e1a83abdebaa

Easiest way to test basic multichannel is by speaker-test, e.g.
speaker-test -D hdmi:CARD=Generic,DEV=0 -c8 -r192000 -F S16_LE
for 7.1 16-bit 192kHz


Detailed discussion below:

ATI/AMD codecs do not support all the standard HDA HDMI/DP functions. In
particular, these are missing:
- ELD support
- standard channel mapping support
- standard infoframe configuration support

In place of them, custom HDA pin verbs are included, that allow:
- getting speaker mask from EDID
- getting SADs from EDID
- getting connection type from EDID (HDMI, DisplayPort)
- setting CA for infoframe
- setting down-mix information for infoframe
- channel pair remapping
- individual channel remapping (revision ID 3+)

The documentation for the verbs has now been released by AMD:
http://www.x.org/docs/AMD/AMD_HDA_verbs.pdf

Also, converted nodes do not have all their supported rates, formats and
channel counts listed.

Add support for the ATI/AMD specific verbs and use them instead of the
generic methods on ATI/AMD codecs. This allows multi-channel audio to
work.
Is the way it is done OK, or should something more generic be put into
place (custom callbacks and/or flags in struct hdmi_spec maybe)?

On the older ATI/AMD codecs that do not support individual remapping,
channel remapping is restricted to pair remapping. This does not affect
the standard multi-channel configurations.

Since there is no ELD support (unless out-of-band communication with the
graphics driver is added so that they can provide it to us directly),
ELD is currently emulated with only the basic information populated
(speaker mask, SADs, connection type). There are some bits missing on
the radeon driver side (the SADs etc are not populated) so currently
the ELD emulation only works on fglrx. ELD emulation is not needed for
multi-channel playback.

Rafał, are you interested in implementing the missing bits in the radeon
driver, as I see you have looked into this before?

The ELD emulation was quite quickly hacked together and hasn't been
cleaned up yet, since I'm not sure how we want to ultimately handle
this. Is ELD emulation the way to go? If so, where does the code belong
(the ATI specific code is in patch_hdmi.c, but the ELD parsing/defines
etc are in hda_eld.c)?

Codec 0x1002aa01 is switched back to patch_atihdmi to support the
ATI/AMD specific features.

HBR mode (needed for DTS-HD/TrueHD passthrough) is not working yet, and
the released documentation does not mention it.

Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi>
Tested-by: Peter Frühberger <fritsch@xbmc.org>
Cc: Rafał Miłecki <zajec5@gmail.com>
---
 sound/pci/hda/patch_hdmi.c | 418 ++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 374 insertions(+), 44 deletions(-)

diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 3d8cd044..741d75e 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.
  *  Copyright (c) 2008 Wei Ni <wni@nvidia.com>
+ *  Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
  *
  *  Authors:
  *			Wu Fengguang <wfg@linux.intel.com>
@@ -45,6 +46,9 @@ module_param(static_hdmi_pcm, bool, 0644);
 MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
 
 #define is_haswell(codec)  ((codec)->vendor_id == 0x80862807)
+#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)
 
 struct hdmi_spec_per_cvt {
 	hda_nid_t cvt_nid;
@@ -89,7 +93,7 @@ struct hdmi_spec {
 
 	struct hdmi_eld temp_eld;
 	/*
-	 * Non-generic ATI/NVIDIA specific
+	 * Non-generic VIA/NVIDIA specific
 	 */
 	struct hda_multi_out multiout;
 	struct hda_pcm_stream pcm_playback;
@@ -573,6 +577,20 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels)
 	return ca;
 }
 
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+static int atihdmi_get_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid, int asp_slot);
+
+static int hdmi_get_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid, int asp_slot)
+{
+	if (is_atihdmi(codec))
+		return atihdmi_get_chan_slot(codec, pin_nid, asp_slot);
+
+	return snd_hda_codec_read(codec, pin_nid, 0,
+				  AC_VERB_GET_HDMI_CHAN_SLOT,
+				  asp_slot);
+}
+#endif
+
 static void hdmi_debug_channel_mapping(struct hda_codec *codec,
 				       hda_nid_t pin_nid)
 {
@@ -581,14 +599,26 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec,
 	int slot;
 
 	for (i = 0; i < 8; i++) {
-		slot = snd_hda_codec_read(codec, pin_nid, 0,
-						AC_VERB_GET_HDMI_CHAN_SLOT, i);
+		slot = hdmi_get_chan_slot(codec, pin_nid, i);
 		printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
 						slot >> 4, slot & 0xf);
 	}
 #endif
 }
 
+static int atihdmi_set_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid,
+				  int chanslot_setup);
+
+static int hdmi_set_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid,
+			       int chanslot_setup)
+{
+	if (is_atihdmi(codec))
+		return atihdmi_set_chan_slot(codec, pin_nid, chanslot_setup);
+
+	return snd_hda_codec_write(codec, pin_nid, 0,
+				   AC_VERB_SET_HDMI_CHAN_SLOT,
+				   chanslot_setup);
+}
 
 static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
 				       hda_nid_t pin_nid,
@@ -617,9 +647,8 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
 	}
 
 	for (i = 0; i < 8; i++) {
-		err = snd_hda_codec_write(codec, pin_nid, 0,
-					  AC_VERB_SET_HDMI_CHAN_SLOT,
-					  non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]);
+		err = hdmi_set_chan_slot(codec, pin_nid,
+					 non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]);
 		if (err) {
 			snd_printdd(KERN_NOTICE
 				    "HDMI: channel mapping failed\n");
@@ -728,8 +757,7 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
 		else
 			val = 0xf;
 		val |= (i << 4);
-		err = snd_hda_codec_write(codec, pin_nid, 0,
-					  AC_VERB_SET_HDMI_CHAN_SLOT, val);
+		err = hdmi_set_chan_slot(codec, pin_nid, val);
 		if (err)
 			return -EINVAL;
 	}
@@ -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;
 
+	if (is_atihdmi(codec)) {
+		/* for ATI/AMD we just want to map channels and set ca */
+		hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
+					   channels, per_pin->chmap,
+					   per_pin->chmap_set);
+		atihdmi_set_ca(codec, pin_nid, ca);
+		per_pin->non_pcm = non_pcm;
+		return;
+	}
+
 	memset(&ai, 0, sizeof(ai));
 	if (eld->info.conn_type == 0) { /* HDMI */
 		struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
@@ -1274,6 +1314,9 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
 	return 0;
 }
 
+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;
@@ -1303,8 +1346,12 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
 		"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 {
@@ -1603,6 +1650,8 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
+static int atihdmi_swap_fc_lfe(int pos);
+
 static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 			      unsigned int size, unsigned int __user *tlv)
 {
@@ -1613,6 +1662,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;
+	int tlv_type = SNDRV_CTL_TLVT_CHMAP_VAR;
+
+	if (is_atihdmi(codec) && !has_amd_full_remap_support(codec))
+		tlv_type = SNDRV_CTL_TLVT_CHMAP_PAIRED;
 
 	if (size < 8)
 		return -ENOMEM;
@@ -1620,19 +1673,33 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 		return -EFAULT;
 	size -= 8;
 	dst = tlv + 2;
-	for (chs = 2; chs <= spec->channels_max; chs++) {
+	for (chs = 2; chs <= spec->channels_max;
+	     chs += (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED) ? 2 : 1) {
 		int i, c;
 		struct cea_channel_speaker_allocation *cap;
 		cap = channel_allocations;
 		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++) {
+					if (cap->speakers[c] || cap->speakers[c+1])
+						chanpairs++;
+				}
+				if (chanpairs * 2 != chs)
+					continue;
+
+			} else if (cap->channels != chs)
 				continue;
+
 			if (cap->spk_mask & ~valid_mask)
 				continue;
 			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;
@@ -1643,10 +1710,25 @@ 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--) {
-				int spk = cap->speakers[c];
-				if (!spk)
-					continue;
-				if (put_user(spk_to_chmap(spk), dst))
+				int spk;
+				int chan = c;
+				int chpos;
+
+				if (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED)
+					chan = 7 - atihdmi_swap_fc_lfe(7 - chan);
+
+				spk = cap->speakers[chan];
+				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)
+						chpos = SNDRV_CHMAP_NA;
+					else
+						continue;
+				}
+
+				if (put_user(chpos, dst))
 					return -EFAULT;
 				dst++;
 			}
@@ -1672,6 +1754,18 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
+static int atihdmi_pairwise_chmap_check_order(struct hda_codec *codec, int ca,
+					      int chs, unsigned char *map);
+
+static int hdmi_chmap_check_order(struct hda_codec *codec, int ca,
+				  int chs, unsigned char *map)
+{
+	if (is_atihdmi(codec) && !has_amd_full_remap_support(codec))
+		return atihdmi_pairwise_chmap_check_order(codec, ca, chs, map);
+
+	return 0; /* anything can be remapped as needed */
+}
+
 static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
 			      struct snd_ctl_elem_value *ucontrol)
 {
@@ -1683,7 +1777,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
 	unsigned int ctl_idx;
 	struct snd_pcm_substream *substream;
 	unsigned char chmap[8];
-	int i, ca, prepared = 0;
+	int i, err, ca, prepared = 0;
 
 	ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	substream = snd_pcm_chmap_substream(info, ctl_idx);
@@ -1707,6 +1801,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;
+	err = hdmi_chmap_check_order(codec, ca, ARRAY_SIZE(chmap), chmap);
+	if (err < 0)
+		return -EINVAL;
 	per_pin->chmap_set = true;
 	memcpy(per_pin->chmap, chmap, sizeof(chmap));
 	if (prepared)
@@ -2551,48 +2648,281 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
 
 /*
  * ATI-specific implementations
- *
- * FIXME: we may omit the whole this and use the generic code once after
- * it's confirmed to work.
  */
 
-#define ATIHDMI_CVT_NID		0x02	/* audio converter */
-#define ATIHDMI_PIN_NID		0x03	/* HDMI output pin */
+/* 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_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_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
+
+#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;
+}
+
+static void atihdmi_set_ca(struct hda_codec *codec, hda_nid_t pin_nid, int ca)
+{
+	printk("ATI: setting ca %d\n", ca);
+	snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
+}
+
+static int atihdmi_swap_fc_lfe(int pos)
+{
+	/*
+	 * Older ATI/AMD without channel-wise mapping
+	 * have automatic FC/LFE swap built-in.
+	 */
+
+	switch (pos) {
+		/* see channel_allocations[].speakers[] */
+		case 2: return 3;
+		case 3: return 2;
+		default: break;
+	}
+
+	return pos;
+}
+
+static int atihdmi_pairwise_chmap_check_order(struct hda_codec *codec, int ca,
+					      int chs, unsigned char *map)
+{
+	struct cea_channel_speaker_allocation *cap;
+	int i, j;
+
+	/* check that only channel pairs need to be remapped on old ATI/AMD */
+
+	cap = &channel_allocations[get_channel_allocation_order(ca)];
+	for (i = 0; i < chs; ++i) {
+		int mask = to_spk_mask(map[i]);
+		bool ok = false;
+		bool companion_ok = false;
+
+		if (!mask)
+			continue;
+
+		for (j = 0 + i % 2; j < 8; j += 2) {
+			int chan_idx = 7 - atihdmi_swap_fc_lfe(j);
+			if (cap->speakers[chan_idx] == mask) {
+				/* channel is in a supported position */
+				ok = true;
+
+				if (i % 2 == 0 && i + 1 < chs) {
+					/* even channel, check the odd companion */
+					int comp_chan_idx = 7 - atihdmi_swap_fc_lfe(j + 1);
+					int comp_mask_req = to_spk_mask(map[i+1]);
+					int comp_mask_act = cap->speakers[comp_chan_idx];
+
+					if (comp_mask_req == comp_mask_act)
+						companion_ok = true;
+					else
+						return -EINVAL;
+				}
+				break;
+			}
+		}
+
+		if (!ok)
+			return -EINVAL;
+
+		if (companion_ok)
+			i++; /* companion channel already checked */
+	}
+
+	return 0;
+}
+
+static int atihdmi_set_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid,
+				 int chanslot_setup)
+{
+	int hdmi_slot = chanslot_setup & 0xf;
+	int stream_channel = chanslot_setup >> 4;
+	int verb;
+	int ati_channel_setup = 0;
+
+	if (!has_amd_full_remap_support(codec)) {
+		hdmi_slot = atihdmi_swap_fc_lfe(hdmi_slot);
+
+		/* In case this is an odd slot but without stream channel, do not
+		 * disable the slot since the corresponding even slot could have a
+		 * channel. In case neither have a channel, the slot pair will be
+		 * disabled when this function is called for the even slot. */
+		if (hdmi_slot % 2 != 0 && stream_channel == 0xf)
+			return 0;
+
+		hdmi_slot -= hdmi_slot % 2;
+
+		if (stream_channel != 0xf)
+			stream_channel -= stream_channel % 2;
+	}
+
+	verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e;
+
+	/* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */
+
+	if (stream_channel != 0xf)
+		ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE;
+
+	return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
+}
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+static int atihdmi_get_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid, int asp_slot)
+{
+	bool was_odd = false;
+	int ati_asp_slot = asp_slot;
+	int verb;
+	int ati_channel_setup;
+
+	/* emulate AC_VERB_GET_HDMI_CHAN_SLOT */
+
+	if (!has_amd_full_remap_support(codec)) {
+		ati_asp_slot = atihdmi_swap_fc_lfe(asp_slot);
+		if (ati_asp_slot % 2 != 0) {
+			ati_asp_slot -= 1;
+			was_odd = true;
+		}
+	}
 
-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)
+	verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e;
+
+	ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0);
+
+	if (!(ati_channel_setup & ATI_OUT_ENABLE))
+		return (0xf << 4) | asp_slot;
+
+	return ((ati_channel_setup & 0xf0) + ((!!was_odd) << 4)) | asp_slot;
+}
+#endif
+
+static int atihdmi_init(struct hda_codec *codec)
 {
 	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;
 
-	err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
-					  substream);
-	if (err < 0)
+	err = generic_hdmi_init(codec);
+
+	if (err)
 		return err;
-	snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
-			    AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);
-	/* FIXME: XXX */
-	for (i = 0; i < chans; i++) {
-		snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
-				    AC_VERB_SET_HDMI_CHAN_SLOT,
-				    (i << 4) | i);
+
+	for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+		struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+		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;
 }
 
 static int patch_atihdmi(struct hda_codec *codec)
 {
 	struct hdmi_spec *spec;
-	int err = patch_simple_hdmi(codec, ATIHDMI_CVT_NID, ATIHDMI_PIN_NID);
-	if (err < 0)
+	struct hdmi_spec_per_cvt *per_cvt;
+	int err, cvt_idx;
+
+	err = patch_generic_hdmi(codec);
+
+	if (err)
 		return err;
+
+	codec->patch_ops.init = atihdmi_init;
+
+	/* ATI/AMD converters do not advertise all of their capabilities */
 	spec = codec->spec;
-	spec->pcm_playback.ops.prepare = atihdmi_playback_pcm_prepare;
+	for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+		per_cvt = get_cvt(spec, cvt_idx);
+		per_cvt->channels_max = max(per_cvt->channels_max, 8u);
+		per_cvt->rates |= SUPPORTED_RATES;
+		per_cvt->formats |= SUPPORTED_FORMATS;
+		per_cvt->maxbps = max(per_cvt->maxbps, 24u);
+	}
+
 	return 0;
 }
 
@@ -2612,7 +2942,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 },
-{ .id = 0x1002aa01, .name = "R6xx HDMI",	.patch = patch_generic_hdmi },
+{ .id = 0x1002aa01, .name = "R6xx HDMI",	.patch = patch_atihdmi },
 { .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 },
-- 
1.8.1.5

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support
  2013-09-22 17:50 [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support Anssi Hannula
@ 2013-09-22 21:14 ` Anssi Hannula
  2013-09-25  4:22 ` Olivier Langlois
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Anssi Hannula @ 2013-09-22 21:14 UTC (permalink / raw)
  To: alsa-devel; +Cc: Takashi Iwai, Rafał Miłecki, Peter Frühberger

Just a small update on a few things.

22.09.2013 20:50, Anssi Hannula kirjoitti:
[...]
> Since there is no ELD support (unless out-of-band communication with the
> graphics driver is added so that they can provide it to us directly),
> ELD is currently emulated with only the basic information populated
> (speaker mask, SADs, connection type). There are some bits missing on
> the radeon driver side (the SADs etc are not populated) so currently
> the ELD emulation only works on fglrx. ELD emulation is not needed for
> multi-channel playback.

Actually, it seems this support is already there in radeon open-source
driver. It was added in 3.11 for some chips and 3.12 for others. I
haven't tested yet, though.

[...]
> HBR mode (needed for DTS-HD/TrueHD passthrough) is not working yet, and
> the released documentation does not mention it.

I'm hearing we may get HBR docs soon. If so, I'll update the patch
accordingly at that time.

[...]
> +				/* 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++) {
> +					if (cap->speakers[c] || cap->speakers[c+1])
> +						chanpairs++;
> +				}

s/c++/c += 2/

That part is still untested though.

-- 
Anssi Hannula

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support
  2013-09-22 17:50 [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support Anssi Hannula
  2013-09-22 21:14 ` Anssi Hannula
@ 2013-09-25  4:22 ` Olivier Langlois
  2013-09-25 14:10   ` Anssi Hannula
  2013-09-26  9:59 ` Takashi Iwai
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Olivier Langlois @ 2013-09-25  4:22 UTC (permalink / raw)
  To: Anssi Hannula
  Cc: Takashi Iwai, alsa-devel, Rafał Miłecki, Peter Frühberger

Hi,

This is just to let you know that I have applied your patch on my linux
3.11 setup and

speaker-test -D hdmi:CARD=Generic,DEV=0 -c8 -r192000 -F S16_LE

did work as expected on my 7.1 receiver with:

lano1106@whippet2 /proc/asound/card0 :( $ cat codec#0 
Codec: ATI R6xx HDMI
Address: 0
AFG Function Id: 0x1 (unsol 0)
Vendor Id: 0x1002aa01
Subsystem Id: 0x00aa0100
Revision Id: 0x100300
No Modem Function Group found
Default PCM:
    rates [0x70]: 32000 44100 48000
    bits [0x2]: 16
    formats [0x5]: PCM AC3

From dmesg:

lano1106@whippet2 /etc $ dmesg | grep hdmi
[   56.836416] ALSA sound/pci/hda/patch_hdmi.c:1345 HDMI status: Codec=0
Pin=3 Presence_Detect=0 ELD_Valid=0
[   56.836446] ALSA sound/pci/hda/patch_hdmi.c:1345 HDMI status: Codec=0
Pin=5 Presence_Detect=0 ELD_Valid=0
[   56.836474] ALSA sound/pci/hda/patch_hdmi.c:1345 HDMI status: Codec=0
Pin=7 Presence_Detect=0 ELD_Valid=0
[   56.836502] ALSA sound/pci/hda/patch_hdmi.c:1345 HDMI status: Codec=0
Pin=9 Presence_Detect=0 ELD_Valid=0
[   56.836532] ALSA sound/pci/hda/patch_hdmi.c:1345 HDMI status: Codec=0
Pin=11 Presence_Detect=0 ELD_Valid=0
[   56.836563] ALSA sound/pci/hda/patch_hdmi.c:1345 HDMI status: Codec=0
Pin=13 Presence_Detect=0 ELD_Valid=0
[   79.977754] ALSA sound/pci/hda/patch_hdmi.c:1013 HDMI hot plug event:
Codec=0 Pin=3 Presence_Detect=1 ELD_Valid=0
[   79.977773] ALSA sound/pci/hda/patch_hdmi.c:1345 HDMI status: Codec=0
Pin=3 Presence_Detect=1 ELD_Valid=1
[   79.978112] ALSA sound/pci/hda/patch_hdmi.c:1013 HDMI hot plug event:
Codec=0 Pin=3 Presence_Detect=0 ELD_Valid=1
[   79.978125] ALSA sound/pci/hda/patch_hdmi.c:1345 HDMI status: Codec=0
Pin=3 Presence_Detect=1 ELD_Valid=1
[  300.375444] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[  821.665641] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1857.928517] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0xb
for 6-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1901.741094] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0xb
for 6-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1905.391150] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0xb
for 6-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1909.039165] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0xb
for 6-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1912.689156] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0xb
for 6-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1916.337149] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0xb
for 6-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1919.987152] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0xb
for 6-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1923.635135] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0xb
for 6-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1927.285133] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0xb
for 6-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1930.933134] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0xb
for 6-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1937.298396] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1940.038137] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1942.776132] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1945.514135] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1948.252139] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1950.993136] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1953.731135] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1956.470138] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1959.209138] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1961.950134] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1964.690139] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1967.428135] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC
[ 1978.703563] ALSA sound/pci/hda/patch_hdmi.c:561 HDMI: select CA 0x13
for 8-channel allocation:  FL/FR LFE FC RL/RR RLC/RRC


Correct me if I am wrong but this codec should be able to output 24 bits
PCM. How one could go to test that?

Good work Anssi!

On Sun, 2013-09-22 at 20:50 +0300, Anssi Hannula wrote:
> Request for comments and testing.
> 
> AMD just released the documentation needed for multi-channel HDMI
> support, so here is the initial patch adding such support.
> 
> This has been tested on codec 0x1002aa01, revision 0x100200 with the
> exception of custom channel maps, which I intend to test later.
> 
> I'm especially interested in testers with:
> - Older codecs other than 0x1002aa01. My best guess is that the new code
>   should work on them as well, but I'm not really 100% sure.
> - Codec 0x1002aa01 revisions 0x100300 (Rev ID 3) or later (see
>   /proc/asound/cardX/codec#Y). These support individual channel
>   remapping and that code is entirely untested (even for regular
>   5.1/7.1).
> 
> I decided it was best to get this out there early even though major
> parts of it are still untested, for comments and for other people to
> test :)
> 
> Note that this recent commit is probably required for multi-channel to
> work with the open-source radeon driver:
> ALSA: hda - hdmi: Fallback to ALSA allocation when selecting CA
> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/sound/pci/hda/patch_hdmi.c?id=18e391862cceaf43ddb8eb5cca05e1a83abdebaa
> 
> Easiest way to test basic multichannel is by speaker-test, e.g.
> speaker-test -D hdmi:CARD=Generic,DEV=0 -c8 -r192000 -F S16_LE
> for 7.1 16-bit 192kHz
> 
> 
> Detailed discussion below:
> 
> ATI/AMD codecs do not support all the standard HDA HDMI/DP functions. In
> particular, these are missing:
> - ELD support
> - standard channel mapping support
> - standard infoframe configuration support
> 
> In place of them, custom HDA pin verbs are included, that allow:
> - getting speaker mask from EDID
> - getting SADs from EDID
> - getting connection type from EDID (HDMI, DisplayPort)
> - setting CA for infoframe
> - setting down-mix information for infoframe
> - channel pair remapping
> - individual channel remapping (revision ID 3+)
> 
> The documentation for the verbs has now been released by AMD:
> http://www.x.org/docs/AMD/AMD_HDA_verbs.pdf
> 
> Also, converted nodes do not have all their supported rates, formats and
> channel counts listed.
> 
> Add support for the ATI/AMD specific verbs and use them instead of the
> generic methods on ATI/AMD codecs. This allows multi-channel audio to
> work.
> Is the way it is done OK, or should something more generic be put into
> place (custom callbacks and/or flags in struct hdmi_spec maybe)?
> 
> On the older ATI/AMD codecs that do not support individual remapping,
> channel remapping is restricted to pair remapping. This does not affect
> the standard multi-channel configurations.
> 
> Since there is no ELD support (unless out-of-band communication with the
> graphics driver is added so that they can provide it to us directly),
> ELD is currently emulated with only the basic information populated
> (speaker mask, SADs, connection type). There are some bits missing on
> the radeon driver side (the SADs etc are not populated) so currently
> the ELD emulation only works on fglrx. ELD emulation is not needed for
> multi-channel playback.
> 
> Rafał, are you interested in implementing the missing bits in the radeon
> driver, as I see you have looked into this before?
> 
> The ELD emulation was quite quickly hacked together and hasn't been
> cleaned up yet, since I'm not sure how we want to ultimately handle
> this. Is ELD emulation the way to go? If so, where does the code belong
> (the ATI specific code is in patch_hdmi.c, but the ELD parsing/defines
> etc are in hda_eld.c)?
> 
> Codec 0x1002aa01 is switched back to patch_atihdmi to support the
> ATI/AMD specific features.
> 
> HBR mode (needed for DTS-HD/TrueHD passthrough) is not working yet, and
> the released documentation does not mention it.
> 
> Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi>
> Tested-by: Peter Frühberger <fritsch@xbmc.org>
> Cc: Rafał Miłecki <zajec5@gmail.com>
> ---
>  sound/pci/hda/patch_hdmi.c | 418 ++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 374 insertions(+), 44 deletions(-)
> 
> diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
> index 3d8cd044..741d75e 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.
>   *  Copyright (c) 2008 Wei Ni <wni@nvidia.com>
> + *  Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
>   *
>   *  Authors:
>   *			Wu Fengguang <wfg@linux.intel.com>
> @@ -45,6 +46,9 @@ module_param(static_hdmi_pcm, bool, 0644);
>  MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
>  
>  #define is_haswell(codec)  ((codec)->vendor_id == 0x80862807)
> +#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)
>  
>  struct hdmi_spec_per_cvt {
>  	hda_nid_t cvt_nid;
> @@ -89,7 +93,7 @@ struct hdmi_spec {
>  
>  	struct hdmi_eld temp_eld;
>  	/*
> -	 * Non-generic ATI/NVIDIA specific
> +	 * Non-generic VIA/NVIDIA specific
>  	 */
>  	struct hda_multi_out multiout;
>  	struct hda_pcm_stream pcm_playback;
> @@ -573,6 +577,20 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels)
>  	return ca;
>  }
>  
> +#ifdef CONFIG_SND_DEBUG_VERBOSE
> +static int atihdmi_get_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid, int asp_slot);
> +
> +static int hdmi_get_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid, int asp_slot)
> +{
> +	if (is_atihdmi(codec))
> +		return atihdmi_get_chan_slot(codec, pin_nid, asp_slot);
> +
> +	return snd_hda_codec_read(codec, pin_nid, 0,
> +				  AC_VERB_GET_HDMI_CHAN_SLOT,
> +				  asp_slot);
> +}
> +#endif
> +
>  static void hdmi_debug_channel_mapping(struct hda_codec *codec,
>  				       hda_nid_t pin_nid)
>  {
> @@ -581,14 +599,26 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec,
>  	int slot;
>  
>  	for (i = 0; i < 8; i++) {
> -		slot = snd_hda_codec_read(codec, pin_nid, 0,
> -						AC_VERB_GET_HDMI_CHAN_SLOT, i);
> +		slot = hdmi_get_chan_slot(codec, pin_nid, i);
>  		printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
>  						slot >> 4, slot & 0xf);
>  	}
>  #endif
>  }
>  
> +static int atihdmi_set_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid,
> +				  int chanslot_setup);
> +
> +static int hdmi_set_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid,
> +			       int chanslot_setup)
> +{
> +	if (is_atihdmi(codec))
> +		return atihdmi_set_chan_slot(codec, pin_nid, chanslot_setup);
> +
> +	return snd_hda_codec_write(codec, pin_nid, 0,
> +				   AC_VERB_SET_HDMI_CHAN_SLOT,
> +				   chanslot_setup);
> +}
>  
>  static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
>  				       hda_nid_t pin_nid,
> @@ -617,9 +647,8 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
>  	}
>  
>  	for (i = 0; i < 8; i++) {
> -		err = snd_hda_codec_write(codec, pin_nid, 0,
> -					  AC_VERB_SET_HDMI_CHAN_SLOT,
> -					  non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]);
> +		err = hdmi_set_chan_slot(codec, pin_nid,
> +					 non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]);
>  		if (err) {
>  			snd_printdd(KERN_NOTICE
>  				    "HDMI: channel mapping failed\n");
> @@ -728,8 +757,7 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
>  		else
>  			val = 0xf;
>  		val |= (i << 4);
> -		err = snd_hda_codec_write(codec, pin_nid, 0,
> -					  AC_VERB_SET_HDMI_CHAN_SLOT, val);
> +		err = hdmi_set_chan_slot(codec, pin_nid, val);
>  		if (err)
>  			return -EINVAL;
>  	}
> @@ -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;
>  
> +	if (is_atihdmi(codec)) {
> +		/* for ATI/AMD we just want to map channels and set ca */
> +		hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
> +					   channels, per_pin->chmap,
> +					   per_pin->chmap_set);
> +		atihdmi_set_ca(codec, pin_nid, ca);
> +		per_pin->non_pcm = non_pcm;
> +		return;
> +	}
> +
>  	memset(&ai, 0, sizeof(ai));
>  	if (eld->info.conn_type == 0) { /* HDMI */
>  		struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
> @@ -1274,6 +1314,9 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
>  	return 0;
>  }
>  
> +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;
> @@ -1303,8 +1346,12 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
>  		"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 {
> @@ -1603,6 +1650,8 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
>  	return 0;
>  }
>  
> +static int atihdmi_swap_fc_lfe(int pos);
> +
>  static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
>  			      unsigned int size, unsigned int __user *tlv)
>  {
> @@ -1613,6 +1662,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;
> +	int tlv_type = SNDRV_CTL_TLVT_CHMAP_VAR;
> +
> +	if (is_atihdmi(codec) && !has_amd_full_remap_support(codec))
> +		tlv_type = SNDRV_CTL_TLVT_CHMAP_PAIRED;
>  
>  	if (size < 8)
>  		return -ENOMEM;
> @@ -1620,19 +1673,33 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
>  		return -EFAULT;
>  	size -= 8;
>  	dst = tlv + 2;
> -	for (chs = 2; chs <= spec->channels_max; chs++) {
> +	for (chs = 2; chs <= spec->channels_max;
> +	     chs += (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED) ? 2 : 1) {
>  		int i, c;
>  		struct cea_channel_speaker_allocation *cap;
>  		cap = channel_allocations;
>  		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++) {
> +					if (cap->speakers[c] || cap->speakers[c+1])
> +						chanpairs++;
> +				}
> +				if (chanpairs * 2 != chs)
> +					continue;
> +
> +			} else if (cap->channels != chs)
>  				continue;
> +
>  			if (cap->spk_mask & ~valid_mask)
>  				continue;
>  			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;
> @@ -1643,10 +1710,25 @@ 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--) {
> -				int spk = cap->speakers[c];
> -				if (!spk)
> -					continue;
> -				if (put_user(spk_to_chmap(spk), dst))
> +				int spk;
> +				int chan = c;
> +				int chpos;
> +
> +				if (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED)
> +					chan = 7 - atihdmi_swap_fc_lfe(7 - chan);
> +
> +				spk = cap->speakers[chan];
> +				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)
> +						chpos = SNDRV_CHMAP_NA;
> +					else
> +						continue;
> +				}
> +
> +				if (put_user(chpos, dst))
>  					return -EFAULT;
>  				dst++;
>  			}
> @@ -1672,6 +1754,18 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
>  	return 0;
>  }
>  
> +static int atihdmi_pairwise_chmap_check_order(struct hda_codec *codec, int ca,
> +					      int chs, unsigned char *map);
> +
> +static int hdmi_chmap_check_order(struct hda_codec *codec, int ca,
> +				  int chs, unsigned char *map)
> +{
> +	if (is_atihdmi(codec) && !has_amd_full_remap_support(codec))
> +		return atihdmi_pairwise_chmap_check_order(codec, ca, chs, map);
> +
> +	return 0; /* anything can be remapped as needed */
> +}
> +
>  static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
>  			      struct snd_ctl_elem_value *ucontrol)
>  {
> @@ -1683,7 +1777,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
>  	unsigned int ctl_idx;
>  	struct snd_pcm_substream *substream;
>  	unsigned char chmap[8];
> -	int i, ca, prepared = 0;
> +	int i, err, ca, prepared = 0;
>  
>  	ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
>  	substream = snd_pcm_chmap_substream(info, ctl_idx);
> @@ -1707,6 +1801,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;
> +	err = hdmi_chmap_check_order(codec, ca, ARRAY_SIZE(chmap), chmap);
> +	if (err < 0)
> +		return -EINVAL;
>  	per_pin->chmap_set = true;
>  	memcpy(per_pin->chmap, chmap, sizeof(chmap));
>  	if (prepared)
> @@ -2551,48 +2648,281 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
>  
>  /*
>   * ATI-specific implementations
> - *
> - * FIXME: we may omit the whole this and use the generic code once after
> - * it's confirmed to work.
>   */
>  
> -#define ATIHDMI_CVT_NID		0x02	/* audio converter */
> -#define ATIHDMI_PIN_NID		0x03	/* HDMI output pin */
> +/* 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_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_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
> +
> +#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;
> +}
> +
> +static void atihdmi_set_ca(struct hda_codec *codec, hda_nid_t pin_nid, int ca)
> +{
> +	printk("ATI: setting ca %d\n", ca);
> +	snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
> +}
> +
> +static int atihdmi_swap_fc_lfe(int pos)
> +{
> +	/*
> +	 * Older ATI/AMD without channel-wise mapping
> +	 * have automatic FC/LFE swap built-in.
> +	 */
> +
> +	switch (pos) {
> +		/* see channel_allocations[].speakers[] */
> +		case 2: return 3;
> +		case 3: return 2;
> +		default: break;
> +	}
> +
> +	return pos;
> +}
> +
> +static int atihdmi_pairwise_chmap_check_order(struct hda_codec *codec, int ca,
> +					      int chs, unsigned char *map)
> +{
> +	struct cea_channel_speaker_allocation *cap;
> +	int i, j;
> +
> +	/* check that only channel pairs need to be remapped on old ATI/AMD */
> +
> +	cap = &channel_allocations[get_channel_allocation_order(ca)];
> +	for (i = 0; i < chs; ++i) {
> +		int mask = to_spk_mask(map[i]);
> +		bool ok = false;
> +		bool companion_ok = false;
> +
> +		if (!mask)
> +			continue;
> +
> +		for (j = 0 + i % 2; j < 8; j += 2) {
> +			int chan_idx = 7 - atihdmi_swap_fc_lfe(j);
> +			if (cap->speakers[chan_idx] == mask) {
> +				/* channel is in a supported position */
> +				ok = true;
> +
> +				if (i % 2 == 0 && i + 1 < chs) {
> +					/* even channel, check the odd companion */
> +					int comp_chan_idx = 7 - atihdmi_swap_fc_lfe(j + 1);
> +					int comp_mask_req = to_spk_mask(map[i+1]);
> +					int comp_mask_act = cap->speakers[comp_chan_idx];
> +
> +					if (comp_mask_req == comp_mask_act)
> +						companion_ok = true;
> +					else
> +						return -EINVAL;
> +				}
> +				break;
> +			}
> +		}
> +
> +		if (!ok)
> +			return -EINVAL;
> +
> +		if (companion_ok)
> +			i++; /* companion channel already checked */
> +	}
> +
> +	return 0;
> +}
> +
> +static int atihdmi_set_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid,
> +				 int chanslot_setup)
> +{
> +	int hdmi_slot = chanslot_setup & 0xf;
> +	int stream_channel = chanslot_setup >> 4;
> +	int verb;
> +	int ati_channel_setup = 0;
> +
> +	if (!has_amd_full_remap_support(codec)) {
> +		hdmi_slot = atihdmi_swap_fc_lfe(hdmi_slot);
> +
> +		/* In case this is an odd slot but without stream channel, do not
> +		 * disable the slot since the corresponding even slot could have a
> +		 * channel. In case neither have a channel, the slot pair will be
> +		 * disabled when this function is called for the even slot. */
> +		if (hdmi_slot % 2 != 0 && stream_channel == 0xf)
> +			return 0;
> +
> +		hdmi_slot -= hdmi_slot % 2;
> +
> +		if (stream_channel != 0xf)
> +			stream_channel -= stream_channel % 2;
> +	}
> +
> +	verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e;
> +
> +	/* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */
> +
> +	if (stream_channel != 0xf)
> +		ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE;
> +
> +	return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
> +}
> +
> +#ifdef CONFIG_SND_DEBUG_VERBOSE
> +static int atihdmi_get_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid, int asp_slot)
> +{
> +	bool was_odd = false;
> +	int ati_asp_slot = asp_slot;
> +	int verb;
> +	int ati_channel_setup;
> +
> +	/* emulate AC_VERB_GET_HDMI_CHAN_SLOT */
> +
> +	if (!has_amd_full_remap_support(codec)) {
> +		ati_asp_slot = atihdmi_swap_fc_lfe(asp_slot);
> +		if (ati_asp_slot % 2 != 0) {
> +			ati_asp_slot -= 1;
> +			was_odd = true;
> +		}
> +	}
>  
> -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)
> +	verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e;
> +
> +	ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0);
> +
> +	if (!(ati_channel_setup & ATI_OUT_ENABLE))
> +		return (0xf << 4) | asp_slot;
> +
> +	return ((ati_channel_setup & 0xf0) + ((!!was_odd) << 4)) | asp_slot;
> +}
> +#endif
> +
> +static int atihdmi_init(struct hda_codec *codec)
>  {
>  	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;
>  
> -	err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
> -					  substream);
> -	if (err < 0)
> +	err = generic_hdmi_init(codec);
> +
> +	if (err)
>  		return err;
> -	snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
> -			    AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);
> -	/* FIXME: XXX */
> -	for (i = 0; i < chans; i++) {
> -		snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
> -				    AC_VERB_SET_HDMI_CHAN_SLOT,
> -				    (i << 4) | i);
> +
> +	for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
> +		struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
> +
> +		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;
>  }
>  
>  static int patch_atihdmi(struct hda_codec *codec)
>  {
>  	struct hdmi_spec *spec;
> -	int err = patch_simple_hdmi(codec, ATIHDMI_CVT_NID, ATIHDMI_PIN_NID);
> -	if (err < 0)
> +	struct hdmi_spec_per_cvt *per_cvt;
> +	int err, cvt_idx;
> +
> +	err = patch_generic_hdmi(codec);
> +
> +	if (err)
>  		return err;
> +
> +	codec->patch_ops.init = atihdmi_init;
> +
> +	/* ATI/AMD converters do not advertise all of their capabilities */
>  	spec = codec->spec;
> -	spec->pcm_playback.ops.prepare = atihdmi_playback_pcm_prepare;
> +	for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
> +		per_cvt = get_cvt(spec, cvt_idx);
> +		per_cvt->channels_max = max(per_cvt->channels_max, 8u);
> +		per_cvt->rates |= SUPPORTED_RATES;
> +		per_cvt->formats |= SUPPORTED_FORMATS;
> +		per_cvt->maxbps = max(per_cvt->maxbps, 24u);
> +	}
> +
>  	return 0;
>  }
>  
> @@ -2612,7 +2942,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 },
> -{ .id = 0x1002aa01, .name = "R6xx HDMI",	.patch = patch_generic_hdmi },
> +{ .id = 0x1002aa01, .name = "R6xx HDMI",	.patch = patch_atihdmi },
>  { .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 },


_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support
  2013-09-25  4:22 ` Olivier Langlois
@ 2013-09-25 14:10   ` Anssi Hannula
  2013-09-25 15:17     ` LANGLOIS Olivier PIS -EXT
  0 siblings, 1 reply; 11+ messages in thread
From: Anssi Hannula @ 2013-09-25 14:10 UTC (permalink / raw)
  To: Olivier Langlois
  Cc: Takashi Iwai, alsa-devel, Rafał Miłecki, Peter Frühberger

25.09.2013 07:22, Olivier Langlois kirjoitti:
> Hi,

Hi!

> This is just to let you know that I have applied your patch on my linux
> 3.11 setup and
> 
> speaker-test -D hdmi:CARD=Generic,DEV=0 -c8 -r192000 -F S16_LE
> 
> did work as expected on my 7.1 receiver with:
> 
> lano1106@whippet2 /proc/asound/card0 :( $ cat codec#0 
> Codec: ATI R6xx HDMI
> Address: 0
> AFG Function Id: 0x1 (unsol 0)
> Vendor Id: 0x1002aa01
> Subsystem Id: 0x00aa0100
> Revision Id: 0x100300
> No Modem Function Group found
> Default PCM:
>     rates [0x70]: 32000 44100 48000
>     bits [0x2]: 16
>     formats [0x5]: PCM AC3

Great to hear that, thanks. So Revision Id 3+ works :)

I forgot to ask for it explicitely, but I guess you checked that
channels seemed to mapped correctly (at least for each speaker you
actually have)?

Also, could you paste the whole /proc/asound/card0/codec#0, for the record?

> From dmesg:
> 
> lano1106@whippet2 /etc $ dmesg | grep hdmi
[...]
> 
> Correct me if I am wrong but this codec should be able to output 24 bits
> PCM. How one could go to test that?

Just use -F S32_LE for speaker-test.


Why S32 and not S24? This is because there are three formats for 24-bit
audio:

If each sample has exactly 24 bits and next sample follows immediately,
it is S24_3LE (/BE).
If each sample has a space of 32 bits but only 24 bits are used, there
are two ways to place those 24 bits to 32 bits. Either the most
significant 24 bits are used, or the least significant 24 bits are used.

If the most significant bits are used (and 8 least significant bits not
used), the format is actually exactly the same as 32 bit (except that
the least significant bits are simply ignored), and hence the format is
S32_LE (/BE). The other format (least significant bits used) is S24_LE
(/BE).


> Good work Anssi!
> 
> On Sun, 2013-09-22 at 20:50 +0300, Anssi Hannula wrote:
>> Request for comments and testing.
>>
>> AMD just released the documentation needed for multi-channel HDMI
>> support, so here is the initial patch adding such support.
>>
>> This has been tested on codec 0x1002aa01, revision 0x100200 with the
>> exception of custom channel maps, which I intend to test later.
>>
>> I'm especially interested in testers with:
>> - Older codecs other than 0x1002aa01. My best guess is that the new code
>>   should work on them as well, but I'm not really 100% sure.
>> - Codec 0x1002aa01 revisions 0x100300 (Rev ID 3) or later (see
>>   /proc/asound/cardX/codec#Y). These support individual channel
>>   remapping and that code is entirely untested (even for regular
>>   5.1/7.1).
>>
>> I decided it was best to get this out there early even though major
>> parts of it are still untested, for comments and for other people to
>> test :)
>>
>> Note that this recent commit is probably required for multi-channel to
>> work with the open-source radeon driver:
>> ALSA: hda - hdmi: Fallback to ALSA allocation when selecting CA
>> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/sound/pci/hda/patch_hdmi.c?id=18e391862cceaf43ddb8eb5cca05e1a83abdebaa
>>
>> Easiest way to test basic multichannel is by speaker-test, e.g.
>> speaker-test -D hdmi:CARD=Generic,DEV=0 -c8 -r192000 -F S16_LE
>> for 7.1 16-bit 192kHz
>>
>>
>> Detailed discussion below:
>>
>> ATI/AMD codecs do not support all the standard HDA HDMI/DP functions. In
>> particular, these are missing:
>> - ELD support
>> - standard channel mapping support
>> - standard infoframe configuration support
>>
>> In place of them, custom HDA pin verbs are included, that allow:
>> - getting speaker mask from EDID
>> - getting SADs from EDID
>> - getting connection type from EDID (HDMI, DisplayPort)
>> - setting CA for infoframe
>> - setting down-mix information for infoframe
>> - channel pair remapping
>> - individual channel remapping (revision ID 3+)
>>
>> The documentation for the verbs has now been released by AMD:
>> http://www.x.org/docs/AMD/AMD_HDA_verbs.pdf
>>
>> Also, converted nodes do not have all their supported rates, formats and
>> channel counts listed.
>>
>> Add support for the ATI/AMD specific verbs and use them instead of the
>> generic methods on ATI/AMD codecs. This allows multi-channel audio to
>> work.
>> Is the way it is done OK, or should something more generic be put into
>> place (custom callbacks and/or flags in struct hdmi_spec maybe)?
>>
>> On the older ATI/AMD codecs that do not support individual remapping,
>> channel remapping is restricted to pair remapping. This does not affect
>> the standard multi-channel configurations.
>>
>> Since there is no ELD support (unless out-of-band communication with the
>> graphics driver is added so that they can provide it to us directly),
>> ELD is currently emulated with only the basic information populated
>> (speaker mask, SADs, connection type). There are some bits missing on
>> the radeon driver side (the SADs etc are not populated) so currently
>> the ELD emulation only works on fglrx. ELD emulation is not needed for
>> multi-channel playback.
>>
>> Rafał, are you interested in implementing the missing bits in the radeon
>> driver, as I see you have looked into this before?
>>
>> The ELD emulation was quite quickly hacked together and hasn't been
>> cleaned up yet, since I'm not sure how we want to ultimately handle
>> this. Is ELD emulation the way to go? If so, where does the code belong
>> (the ATI specific code is in patch_hdmi.c, but the ELD parsing/defines
>> etc are in hda_eld.c)?
>>
>> Codec 0x1002aa01 is switched back to patch_atihdmi to support the
>> ATI/AMD specific features.
>>
>> HBR mode (needed for DTS-HD/TrueHD passthrough) is not working yet, and
>> the released documentation does not mention it.
>>
>> Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi>
>> Tested-by: Peter Frühberger <fritsch@xbmc.org>
>> Cc: Rafał Miłecki <zajec5@gmail.com>
>> ---
>>  sound/pci/hda/patch_hdmi.c | 418 ++++++++++++++++++++++++++++++++++++++++-----
>>  1 file changed, 374 insertions(+), 44 deletions(-)
>>

[...]
-- 
Anssi Hannula
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support
  2013-09-25 14:10   ` Anssi Hannula
@ 2013-09-25 15:17     ` LANGLOIS Olivier PIS -EXT
  2013-09-25 22:21       ` Anssi Hannula
  0 siblings, 1 reply; 11+ messages in thread
From: LANGLOIS Olivier PIS -EXT @ 2013-09-25 15:17 UTC (permalink / raw)
  To: Anssi Hannula, Olivier Langlois
  Cc: Takashi Iwai, alsa-devel, Rafał Miłecki, Peter Frühberger

Anssi,

>
> Great to hear that, thanks. So Revision Id 3+ works :)
>
> I forgot to ask for it explicitely, but I guess you checked that channels
> seemed to mapped correctly (at least for each speaker you actually have)?

Absolutely. I haven't been explicit about it but I would have mention it if something was wrong in that regard. I have 8 speakers.

I have used speaker-test to test stereo, 5.1 and 7.1 mapping and the mapping is perfect.

One small detail that I have noticed is that there seem to be a small delay as I add more and more channels. With 8 channels, there might be up to 1 sec between the time speaker-test prints a channel name and the sound from previous chan stops and I start to hear noise from the new one.

I have concluded that this may come from my receiver that seem to reinitialize itself during each channel switch but I'm not totally sure.
>
> Also, could you paste the whole /proc/asound/card0/codec#0, for the
> record?

I'll provide that info to you when I'm back home later. For now, from what I remember, as it is the first thing that I have checked after having reloaded the driver with the patch, there is nothing printed there that reflect the new multi channel capability of the codec. but maybe it is me that does not know what to look for.

Also, if you want me to test functionality only available with my hardware revision, just prepare me small test cases. I'll be happy to run them. No chance that I run on 'advanced' features on my own as my ALSA usage is very simple.

Greetings,
Olivier


________________________________
CONFIDENTIALITY : This e-mail and any attachments are confidential and may be privileged. If you are not a named recipient, please notify the sender immediately and do not disclose the contents to another person, use it for any purpose or store or copy the information in any medium.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support
  2013-09-25 15:17     ` LANGLOIS Olivier PIS -EXT
@ 2013-09-25 22:21       ` Anssi Hannula
  2013-09-26  5:54         ` Olivier Langlois
  0 siblings, 1 reply; 11+ messages in thread
From: Anssi Hannula @ 2013-09-25 22:21 UTC (permalink / raw)
  To: LANGLOIS Olivier PIS -EXT
  Cc: Takashi Iwai, Olivier Langlois, alsa-devel,
	Rafał Miłecki, Peter Frühberger

25.09.2013 18:17, LANGLOIS Olivier PIS -EXT kirjoitti:
> Anssi,
> 
>>
>> Great to hear that, thanks. So Revision Id 3+ works :)
>>
>> I forgot to ask for it explicitely, but I guess you checked that channels
>> seemed to mapped correctly (at least for each speaker you actually have)?
> 
> Absolutely. I haven't been explicit about it but I would have mention it if something was wrong in that regard. I have 8 speakers.
> 
> I have used speaker-test to test stereo, 5.1 and 7.1 mapping and the mapping is perfect.
> 
> One small detail that I have noticed is that there seem to be a small delay as I add more and more channels. With 8 channels, there might be up to 1 sec between the time speaker-test prints a channel name and the sound from previous chan stops and I start to hear noise from the new one.

This happens with other hw as well, I think speaker-test simply does not
account for PCM playback latency when displaying the channel names.

> I have concluded that this may come from my receiver that seem to reinitialize itself during each channel switch but I'm not totally sure.
>>
>> Also, could you paste the whole /proc/asound/card0/codec#0, for the
>> record?
> 
> I'll provide that info to you when I'm back home later. For now, from what I remember, as it is the first thing that I have checked after having reloaded the driver with the patch, there is nothing printed there that reflect the new multi channel capability of the codec. but maybe it is me that does not know what to look for.

That is expected, I'm just generally interested in the contents (pin
connections etc).

> Also, if you want me to test functionality only available with my hardware revision, just prepare me small test cases. I'll be happy to run them. No chance that I run on 'advanced' features on my own as my ALSA usage is very simple.

Nothing more special to test on your hw at this time, I think :)
I will get back to you if something comes up.

-- 
Anssi Hannula

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support
  2013-09-25 22:21       ` Anssi Hannula
@ 2013-09-26  5:54         ` Olivier Langlois
  0 siblings, 0 replies; 11+ messages in thread
From: Olivier Langlois @ 2013-09-26  5:54 UTC (permalink / raw)
  To: Anssi Hannula
  Cc: LANGLOIS Olivier PIS -EXT, Takashi Iwai, alsa-devel,
	Rafał Miłecki, Peter Frühberger

Anssi,

On Thu, 2013-09-26 at 01:21 +0300, Anssi Hannula wrote:
> That is expected, I'm just generally interested in the contents (pin
> connections etc).

Here you go, enjoy!:

lano1106@whippet2 /proc/asound/card0 $ cat codec#0 
Codec: ATI R6xx HDMI
Address: 0
AFG Function Id: 0x1 (unsol 0)
Vendor Id: 0x1002aa01
Subsystem Id: 0x00aa0100
Revision Id: 0x100300
No Modem Function Group found
Default PCM:
    rates [0x70]: 32000 44100 48000
    bits [0x2]: 16
    formats [0x5]: PCM AC3
Default Amp-In caps: N/A
Default Amp-Out caps: N/A
State of AFG node 0x01:
  Power states:  D0 D3 CLKSTOP EPSS
  Power: setting=D0, actual=D0, Clock-stop-OK
GPIO: io=0, o=0, i=0, unsolicited=0, wake=0
Node 0x02 [Audio Output] wcaps 0x221: Stereo Digital Stripe
  Converter: stream=0, channel=0
  Digital:
  Digital category: 0x0
  IEC Coding Type: 0x0
Node 0x03 [Pin Complex] wcaps 0x400381: Stereo Digital
  Control: name="HDMI/DP,pcm=3 Jack", index=0, device=0
  Control: name="IEC958 Playback Con Mask", index=0, device=0
  Control: name="IEC958 Playback Pro Mask", index=0, device=0
  Control: name="IEC958 Playback Default", index=0, device=0
  Control: name="IEC958 Playback Switch", index=0, device=0
  Control: name="ELD", index=0, device=3
  Pincap 0x00000094: OUT Detect HDMI
  Pin Default 0x185600f0: [Jack] Digital Out at Int HDMI
    Conn = Digital, Color = Unknown
    DefAssociation = 0xf, Sequence = 0x0
  Pin-ctls: 0x40: OUT
  Unsolicited: tag=01, enabled=1
  Connection: 1
     0x02
Node 0x04 [Audio Output] wcaps 0x221: Stereo Digital Stripe
  Converter: stream=0, channel=0
  Digital:
  Digital category: 0x0
  IEC Coding Type: 0x0
Node 0x05 [Pin Complex] wcaps 0x400381: Stereo Digital
  Control: name="HDMI/DP,pcm=7 Jack", index=0, device=0
  Control: name="IEC958 Playback Con Mask", index=1, device=0
  Control: name="IEC958 Playback Pro Mask", index=1, device=0
  Control: name="IEC958 Playback Default", index=1, device=0
  Control: name="IEC958 Playback Switch", index=1, device=0
  Control: name="ELD", index=0, device=7
  Pincap 0x00000094: OUT Detect HDMI
  Pin Default 0x185600f0: [Jack] Digital Out at Int HDMI
    Conn = Digital, Color = Unknown
    DefAssociation = 0xf, Sequence = 0x0
  Pin-ctls: 0x40: OUT
  Unsolicited: tag=02, enabled=1
  Connection: 1
     0x04
Node 0x06 [Audio Output] wcaps 0x221: Stereo Digital Stripe
  Converter: stream=0, channel=0
  Digital:
  Digital category: 0x0
  IEC Coding Type: 0x0
Node 0x07 [Pin Complex] wcaps 0x400381: Stereo Digital
  Control: name="HDMI/DP,pcm=8 Jack", index=0, device=0
  Control: name="IEC958 Playback Con Mask", index=2, device=0
  Control: name="IEC958 Playback Pro Mask", index=2, device=0
  Control: name="IEC958 Playback Default", index=2, device=0
  Control: name="IEC958 Playback Switch", index=2, device=0
  Control: name="ELD", index=0, device=8
  Pincap 0x00000094: OUT Detect HDMI
  Pin Default 0x185600f0: [Jack] Digital Out at Int HDMI
    Conn = Digital, Color = Unknown
    DefAssociation = 0xf, Sequence = 0x0
  Pin-ctls: 0x40: OUT
  Unsolicited: tag=03, enabled=1
  Connection: 1
     0x06
Node 0x08 [Audio Output] wcaps 0x221: Stereo Digital Stripe
  Converter: stream=0, channel=0
  Digital:
  Digital category: 0x0
  IEC Coding Type: 0x0
Node 0x09 [Pin Complex] wcaps 0x400381: Stereo Digital
  Control: name="HDMI/DP,pcm=9 Jack", index=0, device=0
  Control: name="IEC958 Playback Con Mask", index=3, device=0
  Control: name="IEC958 Playback Pro Mask", index=3, device=0
  Control: name="IEC958 Playback Default", index=3, device=0
  Control: name="IEC958 Playback Switch", index=3, device=0
  Control: name="ELD", index=0, device=9
  Pincap 0x00000094: OUT Detect HDMI
  Pin Default 0x185600f0: [Jack] Digital Out at Int HDMI
    Conn = Digital, Color = Unknown
    DefAssociation = 0xf, Sequence = 0x0
  Pin-ctls: 0x40: OUT
  Unsolicited: tag=04, enabled=1
  Connection: 1
     0x08
Node 0x0a [Audio Output] wcaps 0x221: Stereo Digital Stripe
  Converter: stream=0, channel=0
  Digital:
  Digital category: 0x0
  IEC Coding Type: 0x0
Node 0x0b [Pin Complex] wcaps 0x400381: Stereo Digital
  Control: name="HDMI/DP,pcm=10 Jack", index=0, device=0
  Control: name="IEC958 Playback Con Mask", index=4, device=0
  Control: name="IEC958 Playback Pro Mask", index=4, device=0
  Control: name="IEC958 Playback Default", index=4, device=0
  Control: name="IEC958 Playback Switch", index=4, device=0
  Control: name="ELD", index=0, device=10
  Pincap 0x00000094: OUT Detect HDMI
  Pin Default 0x185600f0: [Jack] Digital Out at Int HDMI
    Conn = Digital, Color = Unknown
    DefAssociation = 0xf, Sequence = 0x0
  Pin-ctls: 0x40: OUT
  Unsolicited: tag=05, enabled=1
  Connection: 1
     0x0a
Node 0x0c [Audio Output] wcaps 0x221: Stereo Digital Stripe
  Converter: stream=0, channel=0
  Digital:
  Digital category: 0x0
  IEC Coding Type: 0x0
Node 0x0d [Pin Complex] wcaps 0x400381: Stereo Digital
  Control: name="HDMI/DP,pcm=11 Jack", index=0, device=0
  Control: name="IEC958 Playback Con Mask", index=5, device=0
  Control: name="IEC958 Playback Pro Mask", index=5, device=0
  Control: name="IEC958 Playback Default", index=5, device=0
  Control: name="IEC958 Playback Switch", index=5, device=0
  Control: name="ELD", index=0, device=11
  Pincap 0x00000094: OUT Detect HDMI
  Pin Default 0x185600f0: [Jack] Digital Out at Int HDMI
    Conn = Digital, Color = Unknown
    DefAssociation = 0xf, Sequence = 0x0
  Pin-ctls: 0x40: OUT
  Unsolicited: tag=06, enabled=1
  Connection: 1
     0x0c

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support
  2013-09-22 17:50 [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support Anssi Hannula
  2013-09-22 21:14 ` Anssi Hannula
  2013-09-25  4:22 ` Olivier Langlois
@ 2013-09-26  9:59 ` Takashi Iwai
  2013-09-27  9:27 ` Rafał Miłecki
  2013-09-27 10:19 ` Rafał Miłecki
  4 siblings, 0 replies; 11+ messages in thread
From: Takashi Iwai @ 2013-09-26  9:59 UTC (permalink / raw)
  To: Anssi Hannula; +Cc: alsa-devel, Rafał Miłecki, Peter Frühberger

At Sun, 22 Sep 2013 20:50:33 +0300,
Anssi Hannula wrote:
> 
> Request for comments and testing.
> 
> AMD just released the documentation needed for multi-channel HDMI
> support, so here is the initial patch adding such support.
> 
> This has been tested on codec 0x1002aa01, revision 0x100200 with the
> exception of custom channel maps, which I intend to test later.
> 
> I'm especially interested in testers with:
> - Older codecs other than 0x1002aa01. My best guess is that the new code
>   should work on them as well, but I'm not really 100% sure.
> - Codec 0x1002aa01 revisions 0x100300 (Rev ID 3) or later (see
>   /proc/asound/cardX/codec#Y). These support individual channel
>   remapping and that code is entirely untested (even for regular
>   5.1/7.1).
> 
> I decided it was best to get this out there early even though major
> parts of it are still untested, for comments and for other people to
> test :)
> 
> Note that this recent commit is probably required for multi-channel to
> work with the open-source radeon driver:
> ALSA: hda - hdmi: Fallback to ALSA allocation when selecting CA
> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/sound/pci/hda/patch_hdmi.c?id=18e391862cceaf43ddb8eb5cca05e1a83abdebaa
> 
> Easiest way to test basic multichannel is by speaker-test, e.g.
> speaker-test -D hdmi:CARD=Generic,DEV=0 -c8 -r192000 -F S16_LE
> for 7.1 16-bit 192kHz
> 
> 
> Detailed discussion below:
> 
> ATI/AMD codecs do not support all the standard HDA HDMI/DP functions. In
> particular, these are missing:
> - ELD support
> - standard channel mapping support
> - standard infoframe configuration support
> 
> In place of them, custom HDA pin verbs are included, that allow:
> - getting speaker mask from EDID
> - getting SADs from EDID
> - getting connection type from EDID (HDMI, DisplayPort)
> - setting CA for infoframe
> - setting down-mix information for infoframe
> - channel pair remapping
> - individual channel remapping (revision ID 3+)
> 
> The documentation for the verbs has now been released by AMD:
> http://www.x.org/docs/AMD/AMD_HDA_verbs.pdf
> 
> Also, converted nodes do not have all their supported rates, formats and
> channel counts listed.
> 
> Add support for the ATI/AMD specific verbs and use them instead of the
> generic methods on ATI/AMD codecs. This allows multi-channel audio to
> work.
> Is the way it is done OK, or should something more generic be put into
> place (custom callbacks and/or flags in struct hdmi_spec maybe)?
> 
> On the older ATI/AMD codecs that do not support individual remapping,
> channel remapping is restricted to pair remapping. This does not affect
> the standard multi-channel configurations.
> 
> Since there is no ELD support (unless out-of-band communication with the
> graphics driver is added so that they can provide it to us directly),
> ELD is currently emulated with only the basic information populated
> (speaker mask, SADs, connection type). There are some bits missing on
> the radeon driver side (the SADs etc are not populated) so currently
> the ELD emulation only works on fglrx. ELD emulation is not needed for
> multi-channel playback.
> 
> Rafał, are you interested in implementing the missing bits in the radeon
> driver, as I see you have looked into this before?
> 
> The ELD emulation was quite quickly hacked together and hasn't been
> cleaned up yet, since I'm not sure how we want to ultimately handle
> this. Is ELD emulation the way to go? If so, where does the code belong
> (the ATI specific code is in patch_hdmi.c, but the ELD parsing/defines
> etc are in hda_eld.c)?

Good question.  I suppose ELD emulation is the best we can get for
now.  It's generic and make user-space tools compatible.

Overall the patch looks like a nice fix.  Thank you for your work!

Though, the change is a bit too big (and too late) to merge to 3.12,
I'm afraid.  So we have enough time and let's cook it up well in a
good form.


thanks,

Takashi

> 
> Codec 0x1002aa01 is switched back to patch_atihdmi to support the
> ATI/AMD specific features.
> 
> HBR mode (needed for DTS-HD/TrueHD passthrough) is not working yet, and
> the released documentation does not mention it.
> 
> Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi>
> Tested-by: Peter Frühberger <fritsch@xbmc.org>
> Cc: Rafał Miłecki <zajec5@gmail.com>
> ---
>  sound/pci/hda/patch_hdmi.c | 418 ++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 374 insertions(+), 44 deletions(-)
> 
> diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
> index 3d8cd044..741d75e 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.
>   *  Copyright (c) 2008 Wei Ni <wni@nvidia.com>
> + *  Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
>   *
>   *  Authors:
>   *			Wu Fengguang <wfg@linux.intel.com>
> @@ -45,6 +46,9 @@ module_param(static_hdmi_pcm, bool, 0644);
>  MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
>  
>  #define is_haswell(codec)  ((codec)->vendor_id == 0x80862807)
> +#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)
>  
>  struct hdmi_spec_per_cvt {
>  	hda_nid_t cvt_nid;
> @@ -89,7 +93,7 @@ struct hdmi_spec {
>  
>  	struct hdmi_eld temp_eld;
>  	/*
> -	 * Non-generic ATI/NVIDIA specific
> +	 * Non-generic VIA/NVIDIA specific
>  	 */
>  	struct hda_multi_out multiout;
>  	struct hda_pcm_stream pcm_playback;
> @@ -573,6 +577,20 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels)
>  	return ca;
>  }
>  
> +#ifdef CONFIG_SND_DEBUG_VERBOSE
> +static int atihdmi_get_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid, int asp_slot);
> +
> +static int hdmi_get_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid, int asp_slot)
> +{
> +	if (is_atihdmi(codec))
> +		return atihdmi_get_chan_slot(codec, pin_nid, asp_slot);
> +
> +	return snd_hda_codec_read(codec, pin_nid, 0,
> +				  AC_VERB_GET_HDMI_CHAN_SLOT,
> +				  asp_slot);
> +}
> +#endif
> +
>  static void hdmi_debug_channel_mapping(struct hda_codec *codec,
>  				       hda_nid_t pin_nid)
>  {
> @@ -581,14 +599,26 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec,
>  	int slot;
>  
>  	for (i = 0; i < 8; i++) {
> -		slot = snd_hda_codec_read(codec, pin_nid, 0,
> -						AC_VERB_GET_HDMI_CHAN_SLOT, i);
> +		slot = hdmi_get_chan_slot(codec, pin_nid, i);
>  		printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
>  						slot >> 4, slot & 0xf);
>  	}
>  #endif
>  }
>  
> +static int atihdmi_set_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid,
> +				  int chanslot_setup);
> +
> +static int hdmi_set_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid,
> +			       int chanslot_setup)
> +{
> +	if (is_atihdmi(codec))
> +		return atihdmi_set_chan_slot(codec, pin_nid, chanslot_setup);
> +
> +	return snd_hda_codec_write(codec, pin_nid, 0,
> +				   AC_VERB_SET_HDMI_CHAN_SLOT,
> +				   chanslot_setup);
> +}
>  
>  static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
>  				       hda_nid_t pin_nid,
> @@ -617,9 +647,8 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
>  	}
>  
>  	for (i = 0; i < 8; i++) {
> -		err = snd_hda_codec_write(codec, pin_nid, 0,
> -					  AC_VERB_SET_HDMI_CHAN_SLOT,
> -					  non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]);
> +		err = hdmi_set_chan_slot(codec, pin_nid,
> +					 non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]);
>  		if (err) {
>  			snd_printdd(KERN_NOTICE
>  				    "HDMI: channel mapping failed\n");
> @@ -728,8 +757,7 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
>  		else
>  			val = 0xf;
>  		val |= (i << 4);
> -		err = snd_hda_codec_write(codec, pin_nid, 0,
> -					  AC_VERB_SET_HDMI_CHAN_SLOT, val);
> +		err = hdmi_set_chan_slot(codec, pin_nid, val);
>  		if (err)
>  			return -EINVAL;
>  	}
> @@ -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;
>  
> +	if (is_atihdmi(codec)) {
> +		/* for ATI/AMD we just want to map channels and set ca */
> +		hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
> +					   channels, per_pin->chmap,
> +					   per_pin->chmap_set);
> +		atihdmi_set_ca(codec, pin_nid, ca);
> +		per_pin->non_pcm = non_pcm;
> +		return;
> +	}
> +
>  	memset(&ai, 0, sizeof(ai));
>  	if (eld->info.conn_type == 0) { /* HDMI */
>  		struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
> @@ -1274,6 +1314,9 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
>  	return 0;
>  }
>  
> +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;
> @@ -1303,8 +1346,12 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
>  		"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 {
> @@ -1603,6 +1650,8 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
>  	return 0;
>  }
>  
> +static int atihdmi_swap_fc_lfe(int pos);
> +
>  static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
>  			      unsigned int size, unsigned int __user *tlv)
>  {
> @@ -1613,6 +1662,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;
> +	int tlv_type = SNDRV_CTL_TLVT_CHMAP_VAR;
> +
> +	if (is_atihdmi(codec) && !has_amd_full_remap_support(codec))
> +		tlv_type = SNDRV_CTL_TLVT_CHMAP_PAIRED;
>  
>  	if (size < 8)
>  		return -ENOMEM;
> @@ -1620,19 +1673,33 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
>  		return -EFAULT;
>  	size -= 8;
>  	dst = tlv + 2;
> -	for (chs = 2; chs <= spec->channels_max; chs++) {
> +	for (chs = 2; chs <= spec->channels_max;
> +	     chs += (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED) ? 2 : 1) {
>  		int i, c;
>  		struct cea_channel_speaker_allocation *cap;
>  		cap = channel_allocations;
>  		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++) {
> +					if (cap->speakers[c] || cap->speakers[c+1])
> +						chanpairs++;
> +				}
> +				if (chanpairs * 2 != chs)
> +					continue;
> +
> +			} else if (cap->channels != chs)
>  				continue;
> +
>  			if (cap->spk_mask & ~valid_mask)
>  				continue;
>  			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;
> @@ -1643,10 +1710,25 @@ 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--) {
> -				int spk = cap->speakers[c];
> -				if (!spk)
> -					continue;
> -				if (put_user(spk_to_chmap(spk), dst))
> +				int spk;
> +				int chan = c;
> +				int chpos;
> +
> +				if (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED)
> +					chan = 7 - atihdmi_swap_fc_lfe(7 - chan);
> +
> +				spk = cap->speakers[chan];
> +				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)
> +						chpos = SNDRV_CHMAP_NA;
> +					else
> +						continue;
> +				}
> +
> +				if (put_user(chpos, dst))
>  					return -EFAULT;
>  				dst++;
>  			}
> @@ -1672,6 +1754,18 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
>  	return 0;
>  }
>  
> +static int atihdmi_pairwise_chmap_check_order(struct hda_codec *codec, int ca,
> +					      int chs, unsigned char *map);
> +
> +static int hdmi_chmap_check_order(struct hda_codec *codec, int ca,
> +				  int chs, unsigned char *map)
> +{
> +	if (is_atihdmi(codec) && !has_amd_full_remap_support(codec))
> +		return atihdmi_pairwise_chmap_check_order(codec, ca, chs, map);
> +
> +	return 0; /* anything can be remapped as needed */
> +}
> +
>  static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
>  			      struct snd_ctl_elem_value *ucontrol)
>  {
> @@ -1683,7 +1777,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
>  	unsigned int ctl_idx;
>  	struct snd_pcm_substream *substream;
>  	unsigned char chmap[8];
> -	int i, ca, prepared = 0;
> +	int i, err, ca, prepared = 0;
>  
>  	ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
>  	substream = snd_pcm_chmap_substream(info, ctl_idx);
> @@ -1707,6 +1801,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;
> +	err = hdmi_chmap_check_order(codec, ca, ARRAY_SIZE(chmap), chmap);
> +	if (err < 0)
> +		return -EINVAL;
>  	per_pin->chmap_set = true;
>  	memcpy(per_pin->chmap, chmap, sizeof(chmap));
>  	if (prepared)
> @@ -2551,48 +2648,281 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
>  
>  /*
>   * ATI-specific implementations
> - *
> - * FIXME: we may omit the whole this and use the generic code once after
> - * it's confirmed to work.
>   */
>  
> -#define ATIHDMI_CVT_NID		0x02	/* audio converter */
> -#define ATIHDMI_PIN_NID		0x03	/* HDMI output pin */
> +/* 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_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_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
> +
> +#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;
> +}
> +
> +static void atihdmi_set_ca(struct hda_codec *codec, hda_nid_t pin_nid, int ca)
> +{
> +	printk("ATI: setting ca %d\n", ca);
> +	snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
> +}
> +
> +static int atihdmi_swap_fc_lfe(int pos)
> +{
> +	/*
> +	 * Older ATI/AMD without channel-wise mapping
> +	 * have automatic FC/LFE swap built-in.
> +	 */
> +
> +	switch (pos) {
> +		/* see channel_allocations[].speakers[] */
> +		case 2: return 3;
> +		case 3: return 2;
> +		default: break;
> +	}
> +
> +	return pos;
> +}
> +
> +static int atihdmi_pairwise_chmap_check_order(struct hda_codec *codec, int ca,
> +					      int chs, unsigned char *map)
> +{
> +	struct cea_channel_speaker_allocation *cap;
> +	int i, j;
> +
> +	/* check that only channel pairs need to be remapped on old ATI/AMD */
> +
> +	cap = &channel_allocations[get_channel_allocation_order(ca)];
> +	for (i = 0; i < chs; ++i) {
> +		int mask = to_spk_mask(map[i]);
> +		bool ok = false;
> +		bool companion_ok = false;
> +
> +		if (!mask)
> +			continue;
> +
> +		for (j = 0 + i % 2; j < 8; j += 2) {
> +			int chan_idx = 7 - atihdmi_swap_fc_lfe(j);
> +			if (cap->speakers[chan_idx] == mask) {
> +				/* channel is in a supported position */
> +				ok = true;
> +
> +				if (i % 2 == 0 && i + 1 < chs) {
> +					/* even channel, check the odd companion */
> +					int comp_chan_idx = 7 - atihdmi_swap_fc_lfe(j + 1);
> +					int comp_mask_req = to_spk_mask(map[i+1]);
> +					int comp_mask_act = cap->speakers[comp_chan_idx];
> +
> +					if (comp_mask_req == comp_mask_act)
> +						companion_ok = true;
> +					else
> +						return -EINVAL;
> +				}
> +				break;
> +			}
> +		}
> +
> +		if (!ok)
> +			return -EINVAL;
> +
> +		if (companion_ok)
> +			i++; /* companion channel already checked */
> +	}
> +
> +	return 0;
> +}
> +
> +static int atihdmi_set_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid,
> +				 int chanslot_setup)
> +{
> +	int hdmi_slot = chanslot_setup & 0xf;
> +	int stream_channel = chanslot_setup >> 4;
> +	int verb;
> +	int ati_channel_setup = 0;
> +
> +	if (!has_amd_full_remap_support(codec)) {
> +		hdmi_slot = atihdmi_swap_fc_lfe(hdmi_slot);
> +
> +		/* In case this is an odd slot but without stream channel, do not
> +		 * disable the slot since the corresponding even slot could have a
> +		 * channel. In case neither have a channel, the slot pair will be
> +		 * disabled when this function is called for the even slot. */
> +		if (hdmi_slot % 2 != 0 && stream_channel == 0xf)
> +			return 0;
> +
> +		hdmi_slot -= hdmi_slot % 2;
> +
> +		if (stream_channel != 0xf)
> +			stream_channel -= stream_channel % 2;
> +	}
> +
> +	verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e;
> +
> +	/* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */
> +
> +	if (stream_channel != 0xf)
> +		ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE;
> +
> +	return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
> +}
> +
> +#ifdef CONFIG_SND_DEBUG_VERBOSE
> +static int atihdmi_get_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid, int asp_slot)
> +{
> +	bool was_odd = false;
> +	int ati_asp_slot = asp_slot;
> +	int verb;
> +	int ati_channel_setup;
> +
> +	/* emulate AC_VERB_GET_HDMI_CHAN_SLOT */
> +
> +	if (!has_amd_full_remap_support(codec)) {
> +		ati_asp_slot = atihdmi_swap_fc_lfe(asp_slot);
> +		if (ati_asp_slot % 2 != 0) {
> +			ati_asp_slot -= 1;
> +			was_odd = true;
> +		}
> +	}
>  
> -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)
> +	verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e;
> +
> +	ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0);
> +
> +	if (!(ati_channel_setup & ATI_OUT_ENABLE))
> +		return (0xf << 4) | asp_slot;
> +
> +	return ((ati_channel_setup & 0xf0) + ((!!was_odd) << 4)) | asp_slot;
> +}
> +#endif
> +
> +static int atihdmi_init(struct hda_codec *codec)
>  {
>  	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;
>  
> -	err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
> -					  substream);
> -	if (err < 0)
> +	err = generic_hdmi_init(codec);
> +
> +	if (err)
>  		return err;
> -	snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
> -			    AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);
> -	/* FIXME: XXX */
> -	for (i = 0; i < chans; i++) {
> -		snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
> -				    AC_VERB_SET_HDMI_CHAN_SLOT,
> -				    (i << 4) | i);
> +
> +	for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
> +		struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
> +
> +		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;
>  }
>  
>  static int patch_atihdmi(struct hda_codec *codec)
>  {
>  	struct hdmi_spec *spec;
> -	int err = patch_simple_hdmi(codec, ATIHDMI_CVT_NID, ATIHDMI_PIN_NID);
> -	if (err < 0)
> +	struct hdmi_spec_per_cvt *per_cvt;
> +	int err, cvt_idx;
> +
> +	err = patch_generic_hdmi(codec);
> +
> +	if (err)
>  		return err;
> +
> +	codec->patch_ops.init = atihdmi_init;
> +
> +	/* ATI/AMD converters do not advertise all of their capabilities */
>  	spec = codec->spec;
> -	spec->pcm_playback.ops.prepare = atihdmi_playback_pcm_prepare;
> +	for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
> +		per_cvt = get_cvt(spec, cvt_idx);
> +		per_cvt->channels_max = max(per_cvt->channels_max, 8u);
> +		per_cvt->rates |= SUPPORTED_RATES;
> +		per_cvt->formats |= SUPPORTED_FORMATS;
> +		per_cvt->maxbps = max(per_cvt->maxbps, 24u);
> +	}
> +
>  	return 0;
>  }
>  
> @@ -2612,7 +2942,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 },
> -{ .id = 0x1002aa01, .name = "R6xx HDMI",	.patch = patch_generic_hdmi },
> +{ .id = 0x1002aa01, .name = "R6xx HDMI",	.patch = patch_atihdmi },
>  { .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 },
> -- 
> 1.8.1.5
> 
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support
  2013-09-22 17:50 [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support Anssi Hannula
                   ` (2 preceding siblings ...)
  2013-09-26  9:59 ` Takashi Iwai
@ 2013-09-27  9:27 ` Rafał Miłecki
  2013-09-27 10:19 ` Rafał Miłecki
  4 siblings, 0 replies; 11+ messages in thread
From: Rafał Miłecki @ 2013-09-27  9:27 UTC (permalink / raw)
  To: Anssi Hannula; +Cc: Takashi Iwai, alsa-devel, Peter Frühberger

2013/9/22 Anssi Hannula <anssi.hannula@iki.fi>:
> AMD just released the documentation needed for multi-channel HDMI
> support, so here is the initial patch adding such support.
>
> This has been tested on codec 0x1002aa01, revision 0x100200 with the
> exception of custom channel maps, which I intend to test later.
>
> I'm especially interested in testers with:
> - Older codecs other than 0x1002aa01. My best guess is that the new code
>   should work on them as well, but I'm not really 100% sure.
> - Codec 0x1002aa01 revisions 0x100300 (Rev ID 3) or later (see
>   /proc/asound/cardX/codec#Y). These support individual channel
>   remapping and that code is entirely untested (even for regular
>   5.1/7.1).

Wow, that's a nice (and quick) work :) I was on vacations for the last
week, and only noticed the docs release. Planned to work on that, but
you were much faster :)

I'll test it with my cards, but it'll take me some time (env setup,
cards switching...).

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support
  2013-09-22 17:50 [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support Anssi Hannula
                   ` (3 preceding siblings ...)
  2013-09-27  9:27 ` Rafał Miłecki
@ 2013-09-27 10:19 ` Rafał Miłecki
  2013-09-27 13:10   ` Anssi Hannula
  4 siblings, 1 reply; 11+ messages in thread
From: Rafał Miłecki @ 2013-09-27 10:19 UTC (permalink / raw)
  To: Anssi Hannula; +Cc: Takashi Iwai, alsa-devel, Peter Frühberger

2013/9/22 Anssi Hannula <anssi.hannula@iki.fi>:
> +       pos = 20;
> +       for (i = 1; i <= 14; i++) {

What about using HDMI_AUDIO_CODING_TYPE_PCM and HDMI_AUDIO_CODING_TYPE_WMA_PRO?


> +               if (i == 13) /* not handled by ATI/AMD */
> +                       continue;

Replace 13 with HDMI_AUDIO_CODING_TYPE_DST and add
HDMI_AUDIO_CODING_TYPE_DSD (it's not used/reserved too).


> +               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);

Now I understand why I couldn't RE that. I didn't know I have to write
something to the ATI_VERB_SET_AUDIO_DESCRIPTOR.


> +               if (i == 1 && ati_sad & 0xff000000 && (ati_sad & 0xff00) != (ati_sad & 0xff000000) >> 16) {

Don't you get a warning about lacking brackets around
ati_sad & 0xff000000
?
Please replace magic "1" with HDMI_AUDIO_CODING_TYPE_PCM.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support
  2013-09-27 10:19 ` Rafał Miłecki
@ 2013-09-27 13:10   ` Anssi Hannula
  0 siblings, 0 replies; 11+ messages in thread
From: Anssi Hannula @ 2013-09-27 13:10 UTC (permalink / raw)
  To: Rafał Miłecki; +Cc: Takashi Iwai, alsa-devel, Peter Frühberger

27.09.2013 13:19, Rafał Miłecki kirjoitti:
> 2013/9/22 Anssi Hannula <anssi.hannula@iki.fi>:
>> +       pos = 20;
>> +       for (i = 1; i <= 14; i++) {
> 
> What about using HDMI_AUDIO_CODING_TYPE_PCM and HDMI_AUDIO_CODING_TYPE_WMA_PRO?

Well, this is what I meant when I said I didn't clean that function up
yet :)

The magic numbers are there in this version because patch_hdmi.c doesn't
have those defines. So either the defines need to go to a common header
for hda_eld.c and patch_hdmi.c, or the function needs to go to hda_eld.c
(and needed ati/amd defines moved accordingly).

> 
>> +               if (i == 13) /* not handled by ATI/AMD */
>> +                       continue;
> 
> Replace 13 with HDMI_AUDIO_CODING_TYPE_DST and add
> HDMI_AUDIO_CODING_TYPE_DSD (it's not used/reserved too).

Ah, nice catch, I missed that reservation :)

> 
>> +               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);
> 
> Now I understand why I couldn't RE that. I didn't know I have to write
> something to the ATI_VERB_SET_AUDIO_DESCRIPTOR.

Yep.

> 
>> +               if (i == 1 && ati_sad & 0xff000000 && (ati_sad & 0xff00) != (ati_sad & 0xff000000) >> 16) {
> 
> Don't you get a warning about lacking brackets around
> ati_sad & 0xff000000
> ?

Not sure, but will change of course.

> Please replace magic "1" with HDMI_AUDIO_CODING_TYPE_PCM.
> 

-- 
Anssi Hannula

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2013-09-27 13:10 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-09-22 17:50 [RFC/RFT PATCH] ALSA: hda - hdmi: Add ATI/AMD multi-channel audio support Anssi Hannula
2013-09-22 21:14 ` Anssi Hannula
2013-09-25  4:22 ` Olivier Langlois
2013-09-25 14:10   ` Anssi Hannula
2013-09-25 15:17     ` LANGLOIS Olivier PIS -EXT
2013-09-25 22:21       ` Anssi Hannula
2013-09-26  5:54         ` Olivier Langlois
2013-09-26  9:59 ` Takashi Iwai
2013-09-27  9:27 ` Rafał Miłecki
2013-09-27 10:19 ` Rafał Miłecki
2013-09-27 13:10   ` Anssi Hannula

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).