ALSA: hda - Add support for HDMI HBR passthrough
Anssi Hannula [Tue, 3 Aug 2010 10:28:58 +0000 (13:28 +0300)]
Passing IEC 61937 encapsulated compressed audio at bitrates over 6.144
Mbps (i.e. more than a single 2-channel 16-bit 192kHz IEC 60958 link)
over HDMI requires the use of HBR Audio Stream Packets instead of Audio
Sample Packets.

Enable HBR mode when the stream has 8 channels and the Non-PCM bit is
set.

If the audio converter is not connected to any HBR-capable pins, return
-EINVAL in prepare().

Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

sound/pci/hda/hda_codec.h
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_intelhdmi.c
sound/pci/hda/patch_nvhdmi.c

index 4797416..48b3367 100644 (file)
@@ -364,6 +364,9 @@ enum {
 #define AC_DIG2_CC                     (0x7f<<0)
 
 /* Pin widget control - 8bit */
+#define AC_PINCTL_EPT                  (0x3<<0)
+#define AC_PINCTL_EPT_NATIVE           0
+#define AC_PINCTL_EPT_HBR              3
 #define AC_PINCTL_VREFEN               (0x7<<0)
 #define AC_PINCTL_VREF_HIZ             0       /* Hi-Z */
 #define AC_PINCTL_VREF_50              1       /* 50% */
index 2fc5396..8534792 100644 (file)
@@ -698,11 +698,48 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
  * Callbacks
  */
 
-static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
                              u32 stream_tag, int format)
 {
+       struct hdmi_spec *spec = codec->spec;
        int tag;
        int fmt;
+       int pinctl;
+       int new_pinctl = 0;
+       int i;
+
+       for (i = 0; i < spec->num_pins; i++) {
+               if (spec->pin_cvt[i] != nid)
+                       continue;
+               if (!(snd_hda_query_pin_caps(codec, spec->pin[i]) & AC_PINCAP_HBR))
+                       continue;
+
+               pinctl = snd_hda_codec_read(codec, spec->pin[i], 0,
+                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+
+               new_pinctl = pinctl & ~AC_PINCTL_EPT;
+               /* Non-PCM, 8 channels */
+               if ((format & 0x8000) && (format & 0x0f) == 7)
+                       new_pinctl |= AC_PINCTL_EPT_HBR;
+               else
+                       new_pinctl |= AC_PINCTL_EPT_NATIVE;
+
+               snd_printdd("hdmi_setup_stream: "
+                           "NID=0x%x, %spinctl=0x%x\n",
+                           spec->pin[i],
+                           pinctl == new_pinctl ? "" : "new-",
+                           new_pinctl);
+
+               if (pinctl != new_pinctl)
+                       snd_hda_codec_write(codec, spec->pin[i], 0,
+                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                           new_pinctl);
+       }
+
+       if ((format & 0x8000) && (format & 0x0f) == 7 && !new_pinctl) {
+               snd_printdd("hdmi_setup_stream: HBR is not supported\n");
+               return -EINVAL;
+       }
 
        tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
        fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
@@ -722,6 +759,7 @@ static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
        if (fmt != format)
                snd_hda_codec_write(codec, nid, 0,
                                    AC_VERB_SET_STREAM_FORMAT, format);
+       return 0;
 }
 
 /*
index b81d23e..5972d5e 100644 (file)
@@ -66,8 +66,7 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 
        hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
 
-       hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-       return 0;
+       return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
 }
 
 static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
index b0652ac..a281836 100644 (file)
@@ -202,8 +202,7 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch_89(struct hda_pcm_stream *hinfo,
 
        hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
 
-       hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-       return 0;
+       return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
 }
 
 static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,