At Tue, 21 Sep 2010 14:25:49 +0800, Wu Fengguang wrote: > > DisplayPort works mostly in the same way as HDMI, except that it expects > a slightly different audio infoframe format. > > Citations from "HDA036-A: Display Port Support and HDMI Miscellaneous > Corrections": > > The HDMI specification defines a data island packet with a header of 4 > bytes (3 bytes content + 1 byte ECC) and packet body of 32 bytes (28 > bytes content and 4 bytes ECC). Display Port specification on the other > hand defines a data island packet (secondary data packet) with header of > 4 bytes protected by 4 bytes of parity, and data of theoretically up to > 1024 bytes with each 16 bytes chunk of data protected by 4 bytes of > parity. Note that the ECC or parity bytes are not present in the DIP > content populated by software and are hardware generated. > > It tests DP connection based on the ELD conn_type field, which will be > set by the graphics driver and can be overriden manually by users > through the /proc/asound/card0/eld* interface. > > The DP infoframe is tested OK on Intel SandyBridge/CougarPoint platform. > > Signed-off-by: Wu Fengguang <fengguang.wu@xxxxxxxxx> Thanks, applied now. Takashi > --- > sound/pci/hda/patch_hdmi.c | 110 +++++++++++++++++++++++------------ > 1 file changed, 73 insertions(+), 37 deletions(-) > > Takashi: half of the patch is harmless code refactor. > The real changes happen in the first and last chunks. > > --- sound-2.6.orig/sound/pci/hda/patch_hdmi.c 2010-09-21 09:41:46.000000000 +0800 > +++ sound-2.6/sound/pci/hda/patch_hdmi.c 2010-09-21 11:20:59.000000000 +0800 > @@ -82,17 +82,29 @@ struct hdmi_spec { > struct hdmi_audio_infoframe { > u8 type; /* 0x84 */ > u8 ver; /* 0x01 */ > u8 len; /* 0x0a */ > > - u8 checksum; /* PB0 */ > + 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; > - u8 reserved[5]; /* PB6 - PB10 */ > +}; > + > +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; > }; > > /* > * CEA speaker placement: > * > @@ -192,11 +204,11 @@ static int hdmi_channel_mapping[0x32][8] > > /* > * This is an ordered list! > * > * The preceding ones have better chances to be selected by > - * hdmi_setup_channel_allocation(). > + * hdmi_channel_allocation(). > */ > static struct cea_channel_speaker_allocation channel_allocations[] = { > /* channel: 7 6 5 4 3 2 1 0 */ > { .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, > /* 2.1 */ > @@ -369,18 +381,18 @@ static void init_channel_allocations(voi > * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask > * spk_mask => (channel_allocations[]) => ai->CA > * > * TODO: it could select the wrong CA from multiple candidates. > */ > -static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, > - struct hdmi_audio_infoframe *ai) > +static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid, > + int channels) > { > struct hdmi_spec *spec = codec->spec; > struct hdmi_eld *eld; > int i; > + int ca = 0; > int spk_mask = 0; > - int channels = 1 + (ai->CC02_CT47 & 0x7); > char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; > > /* > * CA defaults to 0 for basic stereo audio > */ > @@ -414,20 +426,20 @@ static int hdmi_setup_channel_allocation > /* search for the first working match in the CA table */ > for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { > if (channels == channel_allocations[i].channels && > (spk_mask & channel_allocations[i].spk_mask) == > channel_allocations[i].spk_mask) { > - ai->CA = channel_allocations[i].ca_index; > + ca = channel_allocations[i].ca_index; > break; > } > } > > snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); > snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n", > - ai->CA, channels, buf); > + ca, channels, buf); > > - return ai->CA; > + return ca; > } > > static void hdmi_debug_channel_mapping(struct hda_codec *codec, > hda_nid_t pin_nid) > { > @@ -445,14 +457,13 @@ static void hdmi_debug_channel_mapping(s > } > > > static void hdmi_setup_channel_mapping(struct hda_codec *codec, > hda_nid_t pin_nid, > - struct hdmi_audio_infoframe *ai) > + int ca) > { > int i; > - int ca = ai->CA; > int err; > > if (hdmi_channel_mapping[ca][1] == 0) { > for (i = 0; i < channel_allocations[ca].channels; i++) > hdmi_channel_mapping[ca][i] = i | (i << 4); > @@ -545,57 +556,53 @@ static void hdmi_clear_dip_buffers(struc > i, size, j); > } > #endif > } > > -static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai) > +static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai) > { > - u8 *bytes = (u8 *)ai; > + u8 *bytes = (u8 *)hdmi_ai; > u8 sum = 0; > int i; > > - ai->checksum = 0; > + hdmi_ai->checksum = 0; > > - for (i = 0; i < sizeof(*ai); i++) > + for (i = 0; i < sizeof(*hdmi_ai); i++) > sum += bytes[i]; > > - ai->checksum = -sum; > + hdmi_ai->checksum = -sum; > } > > static void hdmi_fill_audio_infoframe(struct hda_codec *codec, > hda_nid_t pin_nid, > - struct hdmi_audio_infoframe *ai) > + u8 *dip, int size) > { > - u8 *bytes = (u8 *)ai; > int i; > > hdmi_debug_dip_size(codec, pin_nid); > hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ > > - hdmi_checksum_audio_infoframe(ai); > - > hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); > - for (i = 0; i < sizeof(*ai); i++) > - hdmi_write_dip_byte(codec, pin_nid, bytes[i]); > + for (i = 0; i < size; i++) > + hdmi_write_dip_byte(codec, pin_nid, dip[i]); > } > > static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, > - struct hdmi_audio_infoframe *ai) > + u8 *dip, int size) > { > - u8 *bytes = (u8 *)ai; > u8 val; > int i; > > if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) > != AC_DIPXMIT_BEST) > return false; > > hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); > - for (i = 0; i < sizeof(*ai); i++) { > + for (i = 0; i < size; i++) { > val = snd_hda_codec_read(codec, pin_nid, 0, > AC_VERB_GET_HDMI_DIP_DATA, 0); > - if (val != bytes[i]) > + if (val != dip[i]) > return false; > } > > return true; > } > @@ -603,35 +610,64 @@ static bool hdmi_infoframe_uptodate(stru > static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, > struct snd_pcm_substream *substream) > { > struct hdmi_spec *spec = codec->spec; > hda_nid_t pin_nid; > + int channels = substream->runtime->channels; > + int ca; > int i; > - struct hdmi_audio_infoframe ai = { > - .type = 0x84, > - .ver = 0x01, > - .len = 0x0a, > - .CC02_CT47 = substream->runtime->channels - 1, > - }; > + u8 ai[max(sizeof(struct hdmi_audio_infoframe), > + sizeof(struct dp_audio_infoframe))]; > > - hdmi_setup_channel_allocation(codec, nid, &ai); > + ca = hdmi_channel_allocation(codec, nid, channels); > > for (i = 0; i < spec->num_pins; i++) { > if (spec->pin_cvt[i] != nid) > continue; > if (!spec->sink_eld[i].monitor_present) > continue; > > pin_nid = spec->pin[i]; > - if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) { > + > + memset(ai, 0, sizeof(ai)); > + if (spec->sink_eld[i].conn_type == 0) { /* HDMI */ > + struct hdmi_audio_infoframe *hdmi_ai; > + > + hdmi_ai = (struct hdmi_audio_infoframe *)ai; > + hdmi_ai->type = 0x84; > + hdmi_ai->ver = 0x01; > + hdmi_ai->len = 0x0a; > + hdmi_ai->CC02_CT47 = channels - 1; > + hdmi_checksum_audio_infoframe(hdmi_ai); > + } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */ > + struct dp_audio_infoframe *dp_ai; > + > + dp_ai = (struct dp_audio_infoframe *)ai; > + dp_ai->type = 0x84; > + dp_ai->len = 0x1b; > + dp_ai->ver = 0x11 << 2; > + dp_ai->CC02_CT47 = channels - 1; > + } else { > + snd_printd("HDMI: unknown connection type at pin %d\n", > + pin_nid); > + continue; > + } > + > + /* > + * 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. > + */ > + if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) { > snd_printdd("hdmi_setup_audio_infoframe: " > "cvt=%d pin=%d channels=%d\n", > nid, pin_nid, > - substream->runtime->channels); > - hdmi_setup_channel_mapping(codec, pin_nid, &ai); > + channels); > + hdmi_setup_channel_mapping(codec, pin_nid, ca); > hdmi_stop_infoframe_trans(codec, pin_nid); > - hdmi_fill_audio_infoframe(codec, pin_nid, &ai); > + hdmi_fill_audio_infoframe(codec, pin_nid, > + ai, sizeof(ai)); > hdmi_start_infoframe_trans(codec, pin_nid); > } > } > } > > _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel