[alsa-devel] [RFC 4/4] ASoC: hdac_hdmi: Setup and start infoframe
Vinod Koul
vinod.koul at intel.com
Fri Oct 9 14:28:49 CEST 2015
From: "Subhransu S. Prusty" <subhransu.s.prusty at intel.com>
Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty at intel.com>
Signed-off-by: Vinod Koul <vinod.koul at intel.com>
---
sound/soc/codecs/hdac_hdmi.c | 133 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 133 insertions(+)
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 309d84122c72..2aca9ce6f423 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -63,6 +63,39 @@ struct hdac_hdmi_priv {
struct hdac_hdmi_dai_pin_map dai_map[3];
};
+struct hdmi_audio_infoframe {
+ u8 type; /* 0x84 */
+ u8 ver; /* 0x01 */
+ u8 len; /* 0x0a */
+
+ u8 checksum;
+
+ u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
+ u8 SS01_SF24;
+ u8 CXT04;
+ u8 CA;
+ u8 LFEPBL01_LSV36_DM_INH7;
+};
+
+struct dp_audio_infoframe {
+ u8 type; /* 0x84 */
+ u8 len; /* 0x1b */
+ u8 ver; /* 0x11 << 2 */
+
+ u8 CC02_CT47; /* match with HDMI infoframe from this on */
+ u8 SS01_SF24;
+ u8 CXT04;
+ u8 CA;
+ u8 LFEPBL01_LSV36_DM_INH7;
+};
+
+union audio_infoframe {
+ struct hdmi_audio_infoframe hdmi;
+ struct dp_audio_infoframe dp;
+ u8 bytes[0];
+};
+
+
static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
{
struct hdac_device *hdac = container_of(dev, struct hdac_device, dev);
@@ -85,6 +118,104 @@ static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac, hda_nid_t cvt_ni
return 0;
}
+static void hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
+ int packet_index, int byte_index)
+{
+ int val;
+
+ val = (packet_index << 5) | (byte_index & 0x1f);
+
+ snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+}
+
+static void hdac_hdmi_write_dip_byte(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
+ unsigned char val)
+{
+ snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+}
+
+static void hdac_hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai)
+{
+ u8 *bytes = (u8 *)hdmi_ai;
+ u8 sum = 0;
+ int i;
+
+ hdmi_ai->checksum = 0;
+
+ for (i = 0; i < sizeof(*hdmi_ai); i++)
+ sum += bytes[i];
+
+ hdmi_ai->checksum = -sum;
+}
+
+static void hdac_hdmi_fill_audio_infoframe(struct hdac_ext_device *hdac,
+ hda_nid_t pin_nid,
+ u8 *dip, int size)
+{
+ int i;
+
+ hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
+ for (i = 0; i < size; i++)
+ hdac_hdmi_write_dip_byte(hdac, pin_nid, dip[i]);
+}
+
+/*
+ * Enable Audio InfoFrame Transmission
+ */
+static void hdac_hdmi_start_infoframe_trans(struct hdac_ext_device *hdac,
+ hda_nid_t pin_nid)
+{
+ hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
+ snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+ AC_DIPXMIT_BEST);
+}
+
+/*
+ * Disable Audio InfoFrame Transmission
+ */
+static void hdac_hdmi_stop_infoframe_trans(struct hdac_ext_device *hdac,
+ hda_nid_t pin_nid)
+{
+ hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
+ snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+ AC_DIPXMIT_DISABLE);
+}
+
+static void hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
+ hda_nid_t cvt_nid, hda_nid_t pin_nid)
+{
+ union audio_infoframe ai;
+ int ca = 0; /* Default stereo for now */
+ int channels = 2;
+ struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
+
+ /* setup channel count */
+ snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
+ AC_VERB_SET_CVT_CHAN_COUNT, channels - 1);
+
+ /* Set up infoframe */
+ memset(&ai, 0, sizeof(ai));
+
+ /* Only HDMI for now */
+ hdmi_ai->type = 0x84;
+ hdmi_ai->ver = 0x01;
+ hdmi_ai->len = 0x0a;
+ hdmi_ai->CC02_CT47 = channels - 1;
+ hdmi_ai->CA = ca;
+ hdac_hdmi_checksum_audio_infoframe(hdmi_ai);
+
+ /*
+ * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
+ * sizeof(*dp_ai) to avoid partial match/update problems when
+ * the user switches between HDMI/DP monitors.
+ */
+ dev_dbg(&hdac->hdac.dev, "hdmi_pin_setup_infoframe: pin=%d channels=%d ca=0x%02x\n",
+ pin_nid, channels, ca);
+ hdac_hdmi_stop_infoframe_trans(hdac, pin_nid);
+ hdac_hdmi_fill_audio_infoframe(hdac, pin_nid, ai.bytes, sizeof(ai));
+ hdac_hdmi_start_infoframe_trans(hdac, pin_nid);
+}
+
static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state)
{
@@ -118,6 +249,8 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
dd->stream_tag, dd->format);
+ hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt.nid, dai_map->pin.nid);
+
return hdac_hdmi_setup_stream(hdac, dai_map->cvt.nid, dai_map->pin.nid,
dd->stream_tag, dd->format);
}
--
2.4.3
More information about the Alsa-devel
mailing list