[alsa-devel] [PATCH] alsa: hdmi - support infoframe for DisplayPort

Takashi Iwai tiwai at suse.de
Tue Sep 21 10:01:41 CEST 2010


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 at intel.com>

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


More information about the Alsa-devel mailing list