summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorAnssi Hannula <anssi.hannula@iki.fi>2013-10-05 02:25:40 +0300
committerJiri Slaby <jslaby@suse.cz>2014-03-12 13:25:25 +0100
commitb88945f412cf9f7aee57f5537b8758938a2f316a (patch)
tree7a9cb9e03b9477006f64c4e68d082951dd07f082 /sound
parent86bf1122e6e267182919e18ec5fd9f8a286d4da9 (diff)
downloadlwn-b88945f412cf9f7aee57f5537b8758938a2f316a.tar.gz
lwn-b88945f412cf9f7aee57f5537b8758938a2f316a.zip
ALSA: hda - hdmi: Fix programmed active channel count
commit 1df5a06abbaa876ecc01ea84064cdffb4f52a1a1 upstream. Currently the converter channel count is set to the number of actual input channels. The audio infoframe channel count field is set similarly. However, sometimes the used channel map does not map all input channels to outputs. Notably, 3 channel modes (e.g. 2.1) require a dummy input channel so there are 4 input channels. According to the HDA specification, converter channel count should be programmed according to the number of _active_ channels. On Intel HDMI codecs (but not on NVIDIA), setting the converter channel to a higher value than there are actually mapped channels to HDMI slots will cause no audio to be output at all. Note that the effects of this issue are currently partially masked by other bugs that prevent the driver from actually unmapping channels in certain cases. For example, if a 4 channel stream is first created and prepared, it gets a FL,FR,RL,RR mapping (ALSA->HDMI slot mapping 0->0, 1->1, 2->4, 3->5). If one thereafter assigns a FR,FL,FC mapping to it, the driver will remap 2->3 but fail to unmap 2->4 and 3->5, so there are still 4 active channels and the issue will not trigger in this case. These bugs will be fixed separately. Fix the channel counts in the converter channel count field and in the audio infoframe channel count field to match the actual number of active channels. Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/hda/patch_hdmi.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 3b9002e00f30..22f6376f724b 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -63,6 +63,7 @@ struct hdmi_spec_per_pin {
hda_nid_t pin_nid;
int num_mux_nids;
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+ hda_nid_t cvt_nid;
struct hda_codec *codec;
struct hdmi_eld sink_eld;
@@ -913,8 +914,9 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
{
hda_nid_t pin_nid = per_pin->pin_nid;
int channels = per_pin->channels;
+ int active_channels;
struct hdmi_eld *eld;
- int ca;
+ int ca, ordered_ca;
union audio_infoframe ai;
if (!channels)
@@ -936,6 +938,11 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
if (ca < 0)
ca = 0;
+ ordered_ca = get_channel_allocation_order(ca);
+ active_channels = channel_allocations[ordered_ca].channels;
+
+ hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels);
+
memset(&ai, 0, sizeof(ai));
if (eld->info.conn_type == 0) { /* HDMI */
struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
@@ -943,7 +950,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
hdmi_ai->type = 0x84;
hdmi_ai->ver = 0x01;
hdmi_ai->len = 0x0a;
- hdmi_ai->CC02_CT47 = channels - 1;
+ hdmi_ai->CC02_CT47 = active_channels - 1;
hdmi_ai->CA = ca;
hdmi_checksum_audio_infoframe(hdmi_ai);
} else if (eld->info.conn_type == 1) { /* DisplayPort */
@@ -952,7 +959,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
dp_ai->type = 0x84;
dp_ai->len = 0x1b;
dp_ai->ver = 0x11 << 2;
- dp_ai->CC02_CT47 = channels - 1;
+ dp_ai->CC02_CT47 = active_channels - 1;
dp_ai->CA = ca;
} else {
snd_printd("HDMI: unknown connection type at pin %d\n",
@@ -978,7 +985,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
snd_printdd("hdmi_setup_audio_infoframe: "
"pin=%d channels=%d\n",
pin_nid,
- channels);
+ active_channels);
hdmi_stop_infoframe_trans(codec, pin_nid);
hdmi_fill_audio_infoframe(codec, pin_nid,
ai.bytes, sizeof(ai));
@@ -1241,6 +1248,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
per_cvt = get_cvt(spec, cvt_idx);
/* Claim converter */
per_cvt->assigned = 1;
+ per_pin->cvt_nid = per_cvt->cvt_nid;
hinfo->nid = per_cvt->cvt_nid;
snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
@@ -1564,8 +1572,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
per_pin->channels = substream->runtime->channels;
per_pin->setup = true;
- hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels);
-
hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
if (spec->dyn_pin_out) {