forked from mirrors/linux
		
	ALSA: hda/hdmi: extract common interface for ELD handling
Other HDMI-related cards (e.g. hdmi-codec) are also using the ELD. Exrtact common set of interfaces for handling the ELD. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> Signed-off-by: Takashi Iwai <tiwai@suse.de> Link: https://patch.msgid.link/20250124-alsa-hdmi-codec-eld-v1-1-bad045cfaeac@linaro.org
This commit is contained in:
		
							parent
							
								
									e8d04a9248
								
							
						
					
					
						commit
						1b0e9d7f76
					
				
					 6 changed files with 493 additions and 424 deletions
				
			
		|  | @ -2,6 +2,97 @@ | |||
| #ifndef __SOUND_PCM_DRM_ELD_H | ||||
| #define __SOUND_PCM_DRM_ELD_H | ||||
| 
 | ||||
| enum eld_versions { | ||||
| 	ELD_VER_CEA_861D	= 2, | ||||
| 	ELD_VER_PARTIAL		= 31, | ||||
| }; | ||||
| 
 | ||||
| enum cea_audio_coding_types { | ||||
| 	AUDIO_CODING_TYPE_REF_STREAM_HEADER	=  0, | ||||
| 	AUDIO_CODING_TYPE_LPCM			=  1, | ||||
| 	AUDIO_CODING_TYPE_AC3			=  2, | ||||
| 	AUDIO_CODING_TYPE_MPEG1			=  3, | ||||
| 	AUDIO_CODING_TYPE_MP3			=  4, | ||||
| 	AUDIO_CODING_TYPE_MPEG2			=  5, | ||||
| 	AUDIO_CODING_TYPE_AACLC			=  6, | ||||
| 	AUDIO_CODING_TYPE_DTS			=  7, | ||||
| 	AUDIO_CODING_TYPE_ATRAC			=  8, | ||||
| 	AUDIO_CODING_TYPE_SACD			=  9, | ||||
| 	AUDIO_CODING_TYPE_EAC3			= 10, | ||||
| 	AUDIO_CODING_TYPE_DTS_HD		= 11, | ||||
| 	AUDIO_CODING_TYPE_MLP			= 12, | ||||
| 	AUDIO_CODING_TYPE_DST			= 13, | ||||
| 	AUDIO_CODING_TYPE_WMAPRO		= 14, | ||||
| 	AUDIO_CODING_TYPE_REF_CXT		= 15, | ||||
| 	/* also include valid xtypes below */ | ||||
| 	AUDIO_CODING_TYPE_HE_AAC		= 15, | ||||
| 	AUDIO_CODING_TYPE_HE_AAC2		= 16, | ||||
| 	AUDIO_CODING_TYPE_MPEG_SURROUND		= 17, | ||||
| }; | ||||
| 
 | ||||
| enum cea_audio_coding_xtypes { | ||||
| 	AUDIO_CODING_XTYPE_HE_REF_CT		= 0, | ||||
| 	AUDIO_CODING_XTYPE_HE_AAC		= 1, | ||||
| 	AUDIO_CODING_XTYPE_HE_AAC2		= 2, | ||||
| 	AUDIO_CODING_XTYPE_MPEG_SURROUND	= 3, | ||||
| 	AUDIO_CODING_XTYPE_FIRST_RESERVED	= 4, | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * CEA Short Audio Descriptor data | ||||
|  */ | ||||
| struct snd_cea_sad { | ||||
| 	int	channels; | ||||
| 	int	format;		/* (format == 0) indicates invalid SAD */ | ||||
| 	int	rates; | ||||
| 	int	sample_bits;	/* for LPCM */ | ||||
| 	int	max_bitrate;	/* for AC3...ATRAC */ | ||||
| 	int	profile;	/* for WMAPRO */ | ||||
| }; | ||||
| 
 | ||||
| #define ELD_FIXED_BYTES	20 | ||||
| #define ELD_MAX_SIZE    256 | ||||
| #define ELD_MAX_MNL	16 | ||||
| #define ELD_MAX_SAD	16 | ||||
| 
 | ||||
| #define ELD_PCM_BITS_8		BIT(0) | ||||
| #define ELD_PCM_BITS_16		BIT(1) | ||||
| #define ELD_PCM_BITS_20		BIT(2) | ||||
| #define ELD_PCM_BITS_24		BIT(3) | ||||
| #define ELD_PCM_BITS_32		BIT(4) | ||||
| 
 | ||||
| /*
 | ||||
|  * ELD: EDID Like Data | ||||
|  */ | ||||
| struct snd_parsed_hdmi_eld { | ||||
| 	/*
 | ||||
| 	 * all fields will be cleared before updating ELD | ||||
| 	 */ | ||||
| 	int	baseline_len; | ||||
| 	int	eld_ver; | ||||
| 	int	cea_edid_ver; | ||||
| 	char	monitor_name[ELD_MAX_MNL + 1]; | ||||
| 	int	manufacture_id; | ||||
| 	int	product_id; | ||||
| 	u64	port_id; | ||||
| 	int	support_hdcp; | ||||
| 	int	support_ai; | ||||
| 	int	conn_type; | ||||
| 	int	aud_synch_delay; | ||||
| 	int	spk_alloc; | ||||
| 	int	sad_count; | ||||
| 	struct snd_cea_sad sad[ELD_MAX_SAD]; | ||||
| }; | ||||
| 
 | ||||
| int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld); | ||||
| 
 | ||||
| int snd_parse_eld(struct device *dev, struct snd_parsed_hdmi_eld *e, | ||||
| 		  const unsigned char *buf, int size); | ||||
| void snd_show_eld(struct device *dev, struct snd_parsed_hdmi_eld *e); | ||||
| 
 | ||||
| #ifdef CONFIG_SND_PROC_FS | ||||
| void snd_print_eld_info(struct snd_parsed_hdmi_eld *eld, | ||||
| 			struct snd_info_buffer *buffer); | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -5,8 +5,10 @@ | |||
| #include <linux/bitfield.h> | ||||
| #include <linux/export.h> | ||||
| #include <linux/hdmi.h> | ||||
| #include <linux/unaligned.h> | ||||
| #include <drm/drm_edid.h> | ||||
| #include <drm/drm_eld.h> | ||||
| #include <sound/info.h> | ||||
| #include <sound/pcm.h> | ||||
| #include <sound/pcm_drm_eld.h> | ||||
| 
 | ||||
|  | @ -162,3 +164,388 @@ int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld) | |||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld); | ||||
| 
 | ||||
| #define SND_PRINT_RATES_ADVISED_BUFSIZE	80 | ||||
| #define SND_PRINT_BITS_ADVISED_BUFSIZE	16 | ||||
| #define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 | ||||
| 
 | ||||
| static const char * const eld_connection_type_names[4] = { | ||||
| 	"HDMI", | ||||
| 	"DisplayPort", | ||||
| 	"2-reserved", | ||||
| 	"3-reserved" | ||||
| }; | ||||
| 
 | ||||
| static const char * const cea_audio_coding_type_names[] = { | ||||
| 	/*  0 */ "undefined", | ||||
| 	/*  1 */ "LPCM", | ||||
| 	/*  2 */ "AC-3", | ||||
| 	/*  3 */ "MPEG1", | ||||
| 	/*  4 */ "MP3", | ||||
| 	/*  5 */ "MPEG2", | ||||
| 	/*  6 */ "AAC-LC", | ||||
| 	/*  7 */ "DTS", | ||||
| 	/*  8 */ "ATRAC", | ||||
| 	/*  9 */ "DSD (One Bit Audio)", | ||||
| 	/* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)", | ||||
| 	/* 11 */ "DTS-HD", | ||||
| 	/* 12 */ "MLP (Dolby TrueHD)", | ||||
| 	/* 13 */ "DST", | ||||
| 	/* 14 */ "WMAPro", | ||||
| 	/* 15 */ "HE-AAC", | ||||
| 	/* 16 */ "HE-AACv2", | ||||
| 	/* 17 */ "MPEG Surround", | ||||
| }; | ||||
| 
 | ||||
| static const char * const cea_speaker_allocation_names[] = { | ||||
| 	/*  0 */ "FL/FR", | ||||
| 	/*  1 */ "LFE", | ||||
| 	/*  2 */ "FC", | ||||
| 	/*  3 */ "RL/RR", | ||||
| 	/*  4 */ "RC", | ||||
| 	/*  5 */ "FLC/FRC", | ||||
| 	/*  6 */ "RLC/RRC", | ||||
| 	/*  7 */ "FLW/FRW", | ||||
| 	/*  8 */ "FLH/FRH", | ||||
| 	/*  9 */ "TC", | ||||
| 	/* 10 */ "FCH", | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * SS1:SS0 index => sample size | ||||
|  */ | ||||
| static const int cea_sample_sizes[4] = { | ||||
| 	0,			/* 0: Refer to Stream Header */ | ||||
| 	ELD_PCM_BITS_16,	/* 1: 16 bits */ | ||||
| 	ELD_PCM_BITS_20,	/* 2: 20 bits */ | ||||
| 	ELD_PCM_BITS_24,	/* 3: 24 bits */ | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * SF2:SF1:SF0 index => sampling frequency | ||||
|  */ | ||||
| static const int cea_sampling_frequencies[8] = { | ||||
| 	0,			/* 0: Refer to Stream Header */ | ||||
| 	SNDRV_PCM_RATE_32000,	/* 1:  32000Hz */ | ||||
| 	SNDRV_PCM_RATE_44100,	/* 2:  44100Hz */ | ||||
| 	SNDRV_PCM_RATE_48000,	/* 3:  48000Hz */ | ||||
| 	SNDRV_PCM_RATE_88200,	/* 4:  88200Hz */ | ||||
| 	SNDRV_PCM_RATE_96000,	/* 5:  96000Hz */ | ||||
| 	SNDRV_PCM_RATE_176400,	/* 6: 176400Hz */ | ||||
| 	SNDRV_PCM_RATE_192000,	/* 7: 192000Hz */ | ||||
| }; | ||||
| 
 | ||||
| #define GRAB_BITS(buf, byte, lowbit, bits)		\ | ||||
| ({							\ | ||||
| 	BUILD_BUG_ON(lowbit > 7);			\ | ||||
| 	BUILD_BUG_ON(bits > 8);				\ | ||||
| 	BUILD_BUG_ON(bits <= 0);			\ | ||||
| 							\ | ||||
| 	(buf[byte] >> (lowbit)) & ((1 << (bits)) - 1);	\ | ||||
| }) | ||||
| 
 | ||||
| static void hdmi_update_short_audio_desc(struct device *dev, | ||||
| 					 struct snd_cea_sad *a, | ||||
| 					 const unsigned char *buf) | ||||
| { | ||||
| 	int i; | ||||
| 	int val; | ||||
| 
 | ||||
| 	val = GRAB_BITS(buf, 1, 0, 7); | ||||
| 	a->rates = 0; | ||||
| 	for (i = 0; i < 7; i++) | ||||
| 		if (val & (1 << i)) | ||||
| 			a->rates |= cea_sampling_frequencies[i + 1]; | ||||
| 
 | ||||
| 	a->channels = GRAB_BITS(buf, 0, 0, 3); | ||||
| 	a->channels++; | ||||
| 
 | ||||
| 	a->sample_bits = 0; | ||||
| 	a->max_bitrate = 0; | ||||
| 
 | ||||
| 	a->format = GRAB_BITS(buf, 0, 3, 4); | ||||
| 	switch (a->format) { | ||||
| 	case AUDIO_CODING_TYPE_REF_STREAM_HEADER: | ||||
| 		dev_info(dev, "HDMI: audio coding type 0 not expected\n"); | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_LPCM: | ||||
| 		val = GRAB_BITS(buf, 2, 0, 3); | ||||
| 		for (i = 0; i < 3; i++) | ||||
| 			if (val & (1 << i)) | ||||
| 				a->sample_bits |= cea_sample_sizes[i + 1]; | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_AC3: | ||||
| 	case AUDIO_CODING_TYPE_MPEG1: | ||||
| 	case AUDIO_CODING_TYPE_MP3: | ||||
| 	case AUDIO_CODING_TYPE_MPEG2: | ||||
| 	case AUDIO_CODING_TYPE_AACLC: | ||||
| 	case AUDIO_CODING_TYPE_DTS: | ||||
| 	case AUDIO_CODING_TYPE_ATRAC: | ||||
| 		a->max_bitrate = GRAB_BITS(buf, 2, 0, 8); | ||||
| 		a->max_bitrate *= 8000; | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_SACD: | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_EAC3: | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_DTS_HD: | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_MLP: | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_DST: | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_WMAPRO: | ||||
| 		a->profile = GRAB_BITS(buf, 2, 0, 3); | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_REF_CXT: | ||||
| 		a->format = GRAB_BITS(buf, 2, 3, 5); | ||||
| 		if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT || | ||||
| 		    a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) { | ||||
| 			dev_info(dev, | ||||
| 				   "HDMI: audio coding xtype %d not expected\n", | ||||
| 				   a->format); | ||||
| 			a->format = 0; | ||||
| 		} else | ||||
| 			a->format += AUDIO_CODING_TYPE_HE_AAC - | ||||
| 				     AUDIO_CODING_XTYPE_HE_AAC; | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Be careful, ELD buf could be totally rubbish! | ||||
|  */ | ||||
| int snd_parse_eld(struct device *dev, struct snd_parsed_hdmi_eld *e, | ||||
| 		  const unsigned char *buf, int size) | ||||
| { | ||||
| 	int mnl; | ||||
| 	int i; | ||||
| 
 | ||||
| 	memset(e, 0, sizeof(*e)); | ||||
| 	e->eld_ver = GRAB_BITS(buf, 0, 3, 5); | ||||
| 	if (e->eld_ver != ELD_VER_CEA_861D && | ||||
| 	    e->eld_ver != ELD_VER_PARTIAL) { | ||||
| 		dev_info(dev, "HDMI: Unknown ELD version %d\n", e->eld_ver); | ||||
| 		goto out_fail; | ||||
| 	} | ||||
| 
 | ||||
| 	e->baseline_len = GRAB_BITS(buf, 2, 0, 8); | ||||
| 	mnl		= GRAB_BITS(buf, 4, 0, 5); | ||||
| 	e->cea_edid_ver	= GRAB_BITS(buf, 4, 5, 3); | ||||
| 
 | ||||
| 	e->support_hdcp	= GRAB_BITS(buf, 5, 0, 1); | ||||
| 	e->support_ai	= GRAB_BITS(buf, 5, 1, 1); | ||||
| 	e->conn_type	= GRAB_BITS(buf, 5, 2, 2); | ||||
| 	e->sad_count	= GRAB_BITS(buf, 5, 4, 4); | ||||
| 
 | ||||
| 	e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2; | ||||
| 	e->spk_alloc	= GRAB_BITS(buf, 7, 0, 7); | ||||
| 
 | ||||
| 	e->port_id	  = get_unaligned_le64(buf + 8); | ||||
| 
 | ||||
| 	/* not specified, but the spec's tendency is little endian */ | ||||
| 	e->manufacture_id = get_unaligned_le16(buf + 16); | ||||
| 	e->product_id	  = get_unaligned_le16(buf + 18); | ||||
| 
 | ||||
| 	if (mnl > ELD_MAX_MNL) { | ||||
| 		dev_info(dev, "HDMI: MNL is reserved value %d\n", mnl); | ||||
| 		goto out_fail; | ||||
| 	} else if (ELD_FIXED_BYTES + mnl > size) { | ||||
| 		dev_info(dev, "HDMI: out of range MNL %d\n", mnl); | ||||
| 		goto out_fail; | ||||
| 	} else | ||||
| 		strscpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl + 1); | ||||
| 
 | ||||
| 	for (i = 0; i < e->sad_count; i++) { | ||||
| 		if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) { | ||||
| 			dev_info(dev, "HDMI: out of range SAD %d\n", i); | ||||
| 			goto out_fail; | ||||
| 		} | ||||
| 		hdmi_update_short_audio_desc(dev, e->sad + i, | ||||
| 					     buf + ELD_FIXED_BYTES + mnl + 3 * i); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * HDMI sink's ELD info cannot always be retrieved for now, e.g. | ||||
| 	 * in console or for audio devices. Assume the highest speakers | ||||
| 	 * configuration, to _not_ prohibit multi-channel audio playback. | ||||
| 	 */ | ||||
| 	if (!e->spk_alloc) | ||||
| 		e->spk_alloc = 0xffff; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| out_fail: | ||||
| 	return -EINVAL; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_parse_eld); | ||||
| 
 | ||||
| /*
 | ||||
|  * SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with | ||||
|  * hdmi-specific routine. | ||||
|  */ | ||||
| static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen) | ||||
| { | ||||
| 	static const unsigned int alsa_rates[] = { | ||||
| 		5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, | ||||
| 		88200, 96000, 176400, 192000, 384000 | ||||
| 	}; | ||||
| 	int i, j; | ||||
| 
 | ||||
| 	for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++) | ||||
| 		if (pcm & (1 << i)) | ||||
| 			j += scnprintf(buf + j, buflen - j,  " %d", | ||||
| 				alsa_rates[i]); | ||||
| 
 | ||||
| 	buf[j] = '\0'; /* necessary when j == 0 */ | ||||
| } | ||||
| 
 | ||||
| static void eld_print_pcm_bits(int pcm, char *buf, int buflen) | ||||
| { | ||||
| 	static const unsigned int bits[] = { 8, 16, 20, 24, 32 }; | ||||
| 	int i, j; | ||||
| 
 | ||||
| 	for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++) | ||||
| 		if (pcm & (ELD_PCM_BITS_8 << i)) | ||||
| 			j += scnprintf(buf + j, buflen - j,  " %d", bits[i]); | ||||
| 
 | ||||
| 	buf[j] = '\0'; /* necessary when j == 0 */ | ||||
| } | ||||
| 
 | ||||
| static void hdmi_show_short_audio_desc(struct device *dev, | ||||
| 				       struct snd_cea_sad *a) | ||||
| { | ||||
| 	char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; | ||||
| 	char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits ="; | ||||
| 
 | ||||
| 	if (!a->format) | ||||
| 		return; | ||||
| 
 | ||||
| 	hdmi_print_pcm_rates(a->rates, buf, sizeof(buf)); | ||||
| 
 | ||||
| 	if (a->format == AUDIO_CODING_TYPE_LPCM) | ||||
| 		eld_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2) - 8); | ||||
| 	else if (a->max_bitrate) | ||||
| 		snprintf(buf2, sizeof(buf2), | ||||
| 				", max bitrate = %d", a->max_bitrate); | ||||
| 	else | ||||
| 		buf2[0] = '\0'; | ||||
| 
 | ||||
| 	dev_dbg(dev, | ||||
| 		"HDMI: supports coding type %s: channels = %d, rates =%s%s\n", | ||||
| 		cea_audio_coding_type_names[a->format], | ||||
| 		a->channels, buf, buf2); | ||||
| } | ||||
| 
 | ||||
| static void snd_eld_print_channel_allocation(int spk_alloc, char *buf, int buflen) | ||||
| { | ||||
| 	int i, j; | ||||
| 
 | ||||
| 	for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { | ||||
| 		if (spk_alloc & (1 << i)) | ||||
| 			j += scnprintf(buf + j, buflen - j,  " %s", | ||||
| 					cea_speaker_allocation_names[i]); | ||||
| 	} | ||||
| 	buf[j] = '\0';	/* necessary when j == 0 */ | ||||
| } | ||||
| 
 | ||||
| void snd_show_eld(struct device *dev, struct snd_parsed_hdmi_eld *e) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	dev_dbg(dev, "HDMI: detected monitor %s at connection type %s\n", | ||||
| 		e->monitor_name, | ||||
| 		eld_connection_type_names[e->conn_type]); | ||||
| 
 | ||||
| 	if (e->spk_alloc) { | ||||
| 		char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; | ||||
| 
 | ||||
| 		snd_eld_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); | ||||
| 		dev_dbg(dev, "HDMI: available speakers:%s\n", buf); | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < e->sad_count; i++) | ||||
| 		hdmi_show_short_audio_desc(dev, e->sad + i); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_show_eld); | ||||
| 
 | ||||
| #ifdef CONFIG_SND_PROC_FS | ||||
| static void hdmi_print_sad_info(int i, struct snd_cea_sad *a, | ||||
| 				struct snd_info_buffer *buffer) | ||||
| { | ||||
| 	char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; | ||||
| 
 | ||||
| 	snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n", | ||||
| 			i, a->format, cea_audio_coding_type_names[a->format]); | ||||
| 	snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels); | ||||
| 
 | ||||
| 	hdmi_print_pcm_rates(a->rates, buf, sizeof(buf)); | ||||
| 	snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf); | ||||
| 
 | ||||
| 	if (a->format == AUDIO_CODING_TYPE_LPCM) { | ||||
| 		eld_print_pcm_bits(a->sample_bits, buf, sizeof(buf)); | ||||
| 		snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n", | ||||
| 							i, a->sample_bits, buf); | ||||
| 	} | ||||
| 
 | ||||
| 	if (a->max_bitrate) | ||||
| 		snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n", | ||||
| 							i, a->max_bitrate); | ||||
| 
 | ||||
| 	if (a->profile) | ||||
| 		snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile); | ||||
| } | ||||
| 
 | ||||
| void snd_print_eld_info(struct snd_parsed_hdmi_eld *e, | ||||
| 			struct snd_info_buffer *buffer) | ||||
| { | ||||
| 	char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; | ||||
| 	int i; | ||||
| 	static const char * const eld_version_names[32] = { | ||||
| 		"reserved", | ||||
| 		"reserved", | ||||
| 		"CEA-861D or below", | ||||
| 		[3 ... 30] = "reserved", | ||||
| 		[31] = "partial" | ||||
| 	}; | ||||
| 	static const char * const cea_edid_version_names[8] = { | ||||
| 		"no CEA EDID Timing Extension block present", | ||||
| 		"CEA-861", | ||||
| 		"CEA-861-A", | ||||
| 		"CEA-861-B, C or D", | ||||
| 		[4 ... 7] = "reserved" | ||||
| 	}; | ||||
| 
 | ||||
| 	snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); | ||||
| 	snd_iprintf(buffer, "connection_type\t\t%s\n", | ||||
| 				eld_connection_type_names[e->conn_type]); | ||||
| 	snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver, | ||||
| 					eld_version_names[e->eld_ver]); | ||||
| 	snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver, | ||||
| 				cea_edid_version_names[e->cea_edid_ver]); | ||||
| 	snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id); | ||||
| 	snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id); | ||||
| 	snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id); | ||||
| 	snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp); | ||||
| 	snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai); | ||||
| 	snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay); | ||||
| 
 | ||||
| 	snd_eld_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); | ||||
| 	snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf); | ||||
| 
 | ||||
| 	snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count); | ||||
| 
 | ||||
| 	for (i = 0; i < e->sad_count; i++) | ||||
| 		hdmi_print_sad_info(i, e->sad + i, buffer); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(snd_print_eld_info); | ||||
| #endif /* CONFIG_SND_PROC_FS */ | ||||
|  |  | |||
|  | @ -266,6 +266,7 @@ comment "Set to Y if you want auto-loading the codec driver" | |||
| config SND_HDA_CODEC_HDMI | ||||
| 	tristate "Build HDMI/DisplayPort HD-audio codec support" | ||||
| 	select SND_DYNAMIC_MINORS | ||||
| 	select SND_PCM_ELD | ||||
| 	help | ||||
| 	  Say Y or M here to include HDMI and DisplayPort HD-audio codec | ||||
| 	  support in snd-hda-intel driver.  This includes all AMD/ATI, | ||||
|  |  | |||
|  | @ -17,11 +17,6 @@ | |||
| #include <sound/hda_codec.h> | ||||
| #include "hda_local.h" | ||||
| 
 | ||||
| enum eld_versions { | ||||
| 	ELD_VER_CEA_861D	= 2, | ||||
| 	ELD_VER_PARTIAL		= 31, | ||||
| }; | ||||
| 
 | ||||
| enum cea_edid_versions { | ||||
| 	CEA_EDID_VER_NONE	= 0, | ||||
| 	CEA_EDID_VER_CEA861	= 1, | ||||
|  | @ -30,95 +25,12 @@ enum cea_edid_versions { | |||
| 	CEA_EDID_VER_RESERVED	= 4, | ||||
| }; | ||||
| 
 | ||||
| static const char * const eld_connection_type_names[4] = { | ||||
| 	"HDMI", | ||||
| 	"DisplayPort", | ||||
| 	"2-reserved", | ||||
| 	"3-reserved" | ||||
| }; | ||||
| 
 | ||||
| enum cea_audio_coding_types { | ||||
| 	AUDIO_CODING_TYPE_REF_STREAM_HEADER	=  0, | ||||
| 	AUDIO_CODING_TYPE_LPCM			=  1, | ||||
| 	AUDIO_CODING_TYPE_AC3			=  2, | ||||
| 	AUDIO_CODING_TYPE_MPEG1			=  3, | ||||
| 	AUDIO_CODING_TYPE_MP3			=  4, | ||||
| 	AUDIO_CODING_TYPE_MPEG2			=  5, | ||||
| 	AUDIO_CODING_TYPE_AACLC			=  6, | ||||
| 	AUDIO_CODING_TYPE_DTS			=  7, | ||||
| 	AUDIO_CODING_TYPE_ATRAC			=  8, | ||||
| 	AUDIO_CODING_TYPE_SACD			=  9, | ||||
| 	AUDIO_CODING_TYPE_EAC3			= 10, | ||||
| 	AUDIO_CODING_TYPE_DTS_HD		= 11, | ||||
| 	AUDIO_CODING_TYPE_MLP			= 12, | ||||
| 	AUDIO_CODING_TYPE_DST			= 13, | ||||
| 	AUDIO_CODING_TYPE_WMAPRO		= 14, | ||||
| 	AUDIO_CODING_TYPE_REF_CXT		= 15, | ||||
| 	/* also include valid xtypes below */ | ||||
| 	AUDIO_CODING_TYPE_HE_AAC		= 15, | ||||
| 	AUDIO_CODING_TYPE_HE_AAC2		= 16, | ||||
| 	AUDIO_CODING_TYPE_MPEG_SURROUND		= 17, | ||||
| }; | ||||
| 
 | ||||
| enum cea_audio_coding_xtypes { | ||||
| 	AUDIO_CODING_XTYPE_HE_REF_CT		= 0, | ||||
| 	AUDIO_CODING_XTYPE_HE_AAC		= 1, | ||||
| 	AUDIO_CODING_XTYPE_HE_AAC2		= 2, | ||||
| 	AUDIO_CODING_XTYPE_MPEG_SURROUND	= 3, | ||||
| 	AUDIO_CODING_XTYPE_FIRST_RESERVED	= 4, | ||||
| }; | ||||
| 
 | ||||
| static const char * const cea_audio_coding_type_names[] = { | ||||
| 	/*  0 */ "undefined", | ||||
| 	/*  1 */ "LPCM", | ||||
| 	/*  2 */ "AC-3", | ||||
| 	/*  3 */ "MPEG1", | ||||
| 	/*  4 */ "MP3", | ||||
| 	/*  5 */ "MPEG2", | ||||
| 	/*  6 */ "AAC-LC", | ||||
| 	/*  7 */ "DTS", | ||||
| 	/*  8 */ "ATRAC", | ||||
| 	/*  9 */ "DSD (One Bit Audio)", | ||||
| 	/* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)", | ||||
| 	/* 11 */ "DTS-HD", | ||||
| 	/* 12 */ "MLP (Dolby TrueHD)", | ||||
| 	/* 13 */ "DST", | ||||
| 	/* 14 */ "WMAPro", | ||||
| 	/* 15 */ "HE-AAC", | ||||
| 	/* 16 */ "HE-AACv2", | ||||
| 	/* 17 */ "MPEG Surround", | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * The following two lists are shared between | ||||
|  * 	- HDMI audio InfoFrame (source to sink) | ||||
|  * 	- CEA E-EDID Extension (sink to source) | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * SS1:SS0 index => sample size | ||||
|  */ | ||||
| static const int cea_sample_sizes[4] = { | ||||
| 	0,	 		/* 0: Refer to Stream Header */ | ||||
| 	AC_SUPPCM_BITS_16,	/* 1: 16 bits */ | ||||
| 	AC_SUPPCM_BITS_20,	/* 2: 20 bits */ | ||||
| 	AC_SUPPCM_BITS_24,	/* 3: 24 bits */ | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * SF2:SF1:SF0 index => sampling frequency | ||||
|  */ | ||||
| static const int cea_sampling_frequencies[8] = { | ||||
| 	0,			/* 0: Refer to Stream Header */ | ||||
| 	SNDRV_PCM_RATE_32000,	/* 1:  32000Hz */ | ||||
| 	SNDRV_PCM_RATE_44100,	/* 2:  44100Hz */ | ||||
| 	SNDRV_PCM_RATE_48000,	/* 3:  48000Hz */ | ||||
| 	SNDRV_PCM_RATE_88200,	/* 4:  88200Hz */ | ||||
| 	SNDRV_PCM_RATE_96000,	/* 5:  96000Hz */ | ||||
| 	SNDRV_PCM_RATE_176400,	/* 6: 176400Hz */ | ||||
| 	SNDRV_PCM_RATE_192000,	/* 7: 192000Hz */ | ||||
| }; | ||||
| 
 | ||||
| static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid, | ||||
| 					int byte_index) | ||||
| { | ||||
|  | @ -132,159 +44,6 @@ static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid, | |||
| 	return val; | ||||
| } | ||||
| 
 | ||||
| #define GRAB_BITS(buf, byte, lowbit, bits) 		\ | ||||
| ({							\ | ||||
| 	BUILD_BUG_ON(lowbit > 7);			\ | ||||
| 	BUILD_BUG_ON(bits > 8);				\ | ||||
| 	BUILD_BUG_ON(bits <= 0);			\ | ||||
| 							\ | ||||
| 	(buf[byte] >> (lowbit)) & ((1 << (bits)) - 1);	\ | ||||
| }) | ||||
| 
 | ||||
| static void hdmi_update_short_audio_desc(struct hda_codec *codec, | ||||
| 					 struct cea_sad *a, | ||||
| 					 const unsigned char *buf) | ||||
| { | ||||
| 	int i; | ||||
| 	int val; | ||||
| 
 | ||||
| 	val = GRAB_BITS(buf, 1, 0, 7); | ||||
| 	a->rates = 0; | ||||
| 	for (i = 0; i < 7; i++) | ||||
| 		if (val & (1 << i)) | ||||
| 			a->rates |= cea_sampling_frequencies[i + 1]; | ||||
| 
 | ||||
| 	a->channels = GRAB_BITS(buf, 0, 0, 3); | ||||
| 	a->channels++; | ||||
| 
 | ||||
| 	a->sample_bits = 0; | ||||
| 	a->max_bitrate = 0; | ||||
| 
 | ||||
| 	a->format = GRAB_BITS(buf, 0, 3, 4); | ||||
| 	switch (a->format) { | ||||
| 	case AUDIO_CODING_TYPE_REF_STREAM_HEADER: | ||||
| 		codec_info(codec, "HDMI: audio coding type 0 not expected\n"); | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_LPCM: | ||||
| 		val = GRAB_BITS(buf, 2, 0, 3); | ||||
| 		for (i = 0; i < 3; i++) | ||||
| 			if (val & (1 << i)) | ||||
| 				a->sample_bits |= cea_sample_sizes[i + 1]; | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_AC3: | ||||
| 	case AUDIO_CODING_TYPE_MPEG1: | ||||
| 	case AUDIO_CODING_TYPE_MP3: | ||||
| 	case AUDIO_CODING_TYPE_MPEG2: | ||||
| 	case AUDIO_CODING_TYPE_AACLC: | ||||
| 	case AUDIO_CODING_TYPE_DTS: | ||||
| 	case AUDIO_CODING_TYPE_ATRAC: | ||||
| 		a->max_bitrate = GRAB_BITS(buf, 2, 0, 8); | ||||
| 		a->max_bitrate *= 8000; | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_SACD: | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_EAC3: | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_DTS_HD: | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_MLP: | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_DST: | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_WMAPRO: | ||||
| 		a->profile = GRAB_BITS(buf, 2, 0, 3); | ||||
| 		break; | ||||
| 
 | ||||
| 	case AUDIO_CODING_TYPE_REF_CXT: | ||||
| 		a->format = GRAB_BITS(buf, 2, 3, 5); | ||||
| 		if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT || | ||||
| 		    a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) { | ||||
| 			codec_info(codec, | ||||
| 				   "HDMI: audio coding xtype %d not expected\n", | ||||
| 				   a->format); | ||||
| 			a->format = 0; | ||||
| 		} else | ||||
| 			a->format += AUDIO_CODING_TYPE_HE_AAC - | ||||
| 				     AUDIO_CODING_XTYPE_HE_AAC; | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Be careful, ELD buf could be totally rubbish! | ||||
|  */ | ||||
| int snd_hdmi_parse_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e, | ||||
| 			  const unsigned char *buf, int size) | ||||
| { | ||||
| 	int mnl; | ||||
| 	int i; | ||||
| 
 | ||||
| 	memset(e, 0, sizeof(*e)); | ||||
| 	e->eld_ver = GRAB_BITS(buf, 0, 3, 5); | ||||
| 	if (e->eld_ver != ELD_VER_CEA_861D && | ||||
| 	    e->eld_ver != ELD_VER_PARTIAL) { | ||||
| 		codec_info(codec, "HDMI: Unknown ELD version %d\n", e->eld_ver); | ||||
| 		goto out_fail; | ||||
| 	} | ||||
| 
 | ||||
| 	e->baseline_len = GRAB_BITS(buf, 2, 0, 8); | ||||
| 	mnl		= GRAB_BITS(buf, 4, 0, 5); | ||||
| 	e->cea_edid_ver	= GRAB_BITS(buf, 4, 5, 3); | ||||
| 
 | ||||
| 	e->support_hdcp	= GRAB_BITS(buf, 5, 0, 1); | ||||
| 	e->support_ai	= GRAB_BITS(buf, 5, 1, 1); | ||||
| 	e->conn_type	= GRAB_BITS(buf, 5, 2, 2); | ||||
| 	e->sad_count	= GRAB_BITS(buf, 5, 4, 4); | ||||
| 
 | ||||
| 	e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2; | ||||
| 	e->spk_alloc	= GRAB_BITS(buf, 7, 0, 7); | ||||
| 
 | ||||
| 	e->port_id	  = get_unaligned_le64(buf + 8); | ||||
| 
 | ||||
| 	/* not specified, but the spec's tendency is little endian */ | ||||
| 	e->manufacture_id = get_unaligned_le16(buf + 16); | ||||
| 	e->product_id	  = get_unaligned_le16(buf + 18); | ||||
| 
 | ||||
| 	if (mnl > ELD_MAX_MNL) { | ||||
| 		codec_info(codec, "HDMI: MNL is reserved value %d\n", mnl); | ||||
| 		goto out_fail; | ||||
| 	} else if (ELD_FIXED_BYTES + mnl > size) { | ||||
| 		codec_info(codec, "HDMI: out of range MNL %d\n", mnl); | ||||
| 		goto out_fail; | ||||
| 	} else | ||||
| 		strscpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl + 1); | ||||
| 
 | ||||
| 	for (i = 0; i < e->sad_count; i++) { | ||||
| 		if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) { | ||||
| 			codec_info(codec, "HDMI: out of range SAD %d\n", i); | ||||
| 			goto out_fail; | ||||
| 		} | ||||
| 		hdmi_update_short_audio_desc(codec, e->sad + i, | ||||
| 					buf + ELD_FIXED_BYTES + mnl + 3 * i); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * HDMI sink's ELD info cannot always be retrieved for now, e.g. | ||||
| 	 * in console or for audio devices. Assume the highest speakers | ||||
| 	 * configuration, to _not_ prohibit multi-channel audio playback. | ||||
| 	 */ | ||||
| 	if (!e->spk_alloc) | ||||
| 		e->spk_alloc = 0xffff; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| out_fail: | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) | ||||
| { | ||||
| 	return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, | ||||
|  | @ -346,155 +105,27 @@ int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, | |||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with | ||||
|  * hdmi-specific routine. | ||||
|  */ | ||||
| static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen) | ||||
| { | ||||
| 	static const unsigned int alsa_rates[] = { | ||||
| 		5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, | ||||
| 		88200, 96000, 176400, 192000, 384000 | ||||
| 	}; | ||||
| 	int i, j; | ||||
| 
 | ||||
| 	for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++) | ||||
| 		if (pcm & (1 << i)) | ||||
| 			j += scnprintf(buf + j, buflen - j,  " %d", | ||||
| 				alsa_rates[i]); | ||||
| 
 | ||||
| 	buf[j] = '\0'; /* necessary when j == 0 */ | ||||
| } | ||||
| 
 | ||||
| #define SND_PRINT_RATES_ADVISED_BUFSIZE	80 | ||||
| 
 | ||||
| static void hdmi_show_short_audio_desc(struct hda_codec *codec, | ||||
| 				       struct cea_sad *a) | ||||
| { | ||||
| 	char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; | ||||
| 	char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits ="; | ||||
| 
 | ||||
| 	if (!a->format) | ||||
| 		return; | ||||
| 
 | ||||
| 	hdmi_print_pcm_rates(a->rates, buf, sizeof(buf)); | ||||
| 
 | ||||
| 	if (a->format == AUDIO_CODING_TYPE_LPCM) | ||||
| 		snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2) - 8); | ||||
| 	else if (a->max_bitrate) | ||||
| 		snprintf(buf2, sizeof(buf2), | ||||
| 				", max bitrate = %d", a->max_bitrate); | ||||
| 	else | ||||
| 		buf2[0] = '\0'; | ||||
| 
 | ||||
| 	codec_dbg(codec, | ||||
| 		  "HDMI: supports coding type %s: channels = %d, rates =%s%s\n", | ||||
| 		  cea_audio_coding_type_names[a->format], | ||||
| 		  a->channels, buf, buf2); | ||||
| } | ||||
| 
 | ||||
| void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	codec_dbg(codec, "HDMI: detected monitor %s at connection type %s\n", | ||||
| 			e->monitor_name, | ||||
| 			eld_connection_type_names[e->conn_type]); | ||||
| 
 | ||||
| 	if (e->spk_alloc) { | ||||
| 		char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; | ||||
| 		snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); | ||||
| 		codec_dbg(codec, "HDMI: available speakers:%s\n", buf); | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < e->sad_count; i++) | ||||
| 		hdmi_show_short_audio_desc(codec, e->sad + i); | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_SND_PROC_FS | ||||
| 
 | ||||
| static void hdmi_print_sad_info(int i, struct cea_sad *a, | ||||
| 				struct snd_info_buffer *buffer) | ||||
| { | ||||
| 	char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; | ||||
| 
 | ||||
| 	snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n", | ||||
| 			i, a->format, cea_audio_coding_type_names[a->format]); | ||||
| 	snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels); | ||||
| 
 | ||||
| 	hdmi_print_pcm_rates(a->rates, buf, sizeof(buf)); | ||||
| 	snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf); | ||||
| 
 | ||||
| 	if (a->format == AUDIO_CODING_TYPE_LPCM) { | ||||
| 		snd_print_pcm_bits(a->sample_bits, buf, sizeof(buf)); | ||||
| 		snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n", | ||||
| 							i, a->sample_bits, buf); | ||||
| 	} | ||||
| 
 | ||||
| 	if (a->max_bitrate) | ||||
| 		snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n", | ||||
| 							i, a->max_bitrate); | ||||
| 
 | ||||
| 	if (a->profile) | ||||
| 		snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile); | ||||
| } | ||||
| 
 | ||||
| void snd_hdmi_print_eld_info(struct hdmi_eld *eld, | ||||
| 			     struct snd_info_buffer *buffer, | ||||
| 			     hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid) | ||||
| { | ||||
| 	struct parsed_hdmi_eld *e = &eld->info; | ||||
| 	char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; | ||||
| 	int i; | ||||
| 	static const char * const eld_version_names[32] = { | ||||
| 		"reserved", | ||||
| 		"reserved", | ||||
| 		"CEA-861D or below", | ||||
| 		[3 ... 30] = "reserved", | ||||
| 		[31] = "partial" | ||||
| 	}; | ||||
| 	static const char * const cea_edid_version_names[8] = { | ||||
| 		"no CEA EDID Timing Extension block present", | ||||
| 		"CEA-861", | ||||
| 		"CEA-861-A", | ||||
| 		"CEA-861-B, C or D", | ||||
| 		[4 ... 7] = "reserved" | ||||
| 	}; | ||||
| 
 | ||||
| 	snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present); | ||||
| 	snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid); | ||||
| 	snd_iprintf(buffer, "codec_pin_nid\t\t0x%x\n", pin_nid); | ||||
| 	snd_iprintf(buffer, "codec_dev_id\t\t0x%x\n", dev_id); | ||||
| 	snd_iprintf(buffer, "codec_cvt_nid\t\t0x%x\n", cvt_nid); | ||||
| 
 | ||||
| 	if (!eld->eld_valid) | ||||
| 		return; | ||||
| 	snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); | ||||
| 	snd_iprintf(buffer, "connection_type\t\t%s\n", | ||||
| 				eld_connection_type_names[e->conn_type]); | ||||
| 	snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver, | ||||
| 					eld_version_names[e->eld_ver]); | ||||
| 	snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver, | ||||
| 				cea_edid_version_names[e->cea_edid_ver]); | ||||
| 	snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id); | ||||
| 	snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id); | ||||
| 	snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id); | ||||
| 	snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp); | ||||
| 	snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai); | ||||
| 	snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay); | ||||
| 
 | ||||
| 	snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); | ||||
| 	snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf); | ||||
| 
 | ||||
| 	snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count); | ||||
| 
 | ||||
| 	for (i = 0; i < e->sad_count; i++) | ||||
| 		hdmi_print_sad_info(i, e->sad + i, buffer); | ||||
| 	snd_print_eld_info(&eld->info, buffer); | ||||
| } | ||||
| 
 | ||||
| void snd_hdmi_write_eld_info(struct hdmi_eld *eld, | ||||
| 			     struct snd_info_buffer *buffer) | ||||
| { | ||||
| 	struct parsed_hdmi_eld *e = &eld->info; | ||||
| 	struct snd_parsed_hdmi_eld *e = &eld->info; | ||||
| 	char line[64]; | ||||
| 	char name[64]; | ||||
| 	char *sname; | ||||
|  | @ -556,7 +187,7 @@ void snd_hdmi_write_eld_info(struct hdmi_eld *eld, | |||
| #endif /* CONFIG_SND_PROC_FS */ | ||||
| 
 | ||||
| /* update PCM info based on ELD */ | ||||
| void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, | ||||
| void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e, | ||||
| 			      struct hda_pcm_stream *hinfo) | ||||
| { | ||||
| 	u32 rates; | ||||
|  | @ -574,17 +205,17 @@ void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, | |||
| 	maxbps = 16; | ||||
| 	channels_max = 2; | ||||
| 	for (i = 0; i < e->sad_count; i++) { | ||||
| 		struct cea_sad *a = &e->sad[i]; | ||||
| 		struct snd_cea_sad *a = &e->sad[i]; | ||||
| 		rates |= a->rates; | ||||
| 		if (a->channels > channels_max) | ||||
| 			channels_max = a->channels; | ||||
| 		if (a->format == AUDIO_CODING_TYPE_LPCM) { | ||||
| 			if (a->sample_bits & AC_SUPPCM_BITS_20) { | ||||
| 			if (a->sample_bits & ELD_PCM_BITS_20) { | ||||
| 				formats |= SNDRV_PCM_FMTBIT_S32_LE; | ||||
| 				if (maxbps < 20) | ||||
| 					maxbps = 20; | ||||
| 			} | ||||
| 			if (a->sample_bits & AC_SUPPCM_BITS_24) { | ||||
| 			if (a->sample_bits & ELD_PCM_BITS_24) { | ||||
| 				formats |= SNDRV_PCM_FMTBIT_S32_LE; | ||||
| 				if (maxbps < 24) | ||||
| 					maxbps = 24; | ||||
|  |  | |||
|  | @ -10,6 +10,8 @@ | |||
| #ifndef __SOUND_HDA_LOCAL_H | ||||
| #define __SOUND_HDA_LOCAL_H | ||||
| 
 | ||||
| #include <sound/pcm_drm_eld.h> | ||||
| 
 | ||||
| /* We abuse kcontrol_new.subdev field to pass the NID corresponding to
 | ||||
|  * the given new control.  If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG, | ||||
|  * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID. | ||||
|  | @ -675,61 +677,18 @@ int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol, | |||
| #define snd_hda_enum_bool_helper_info(kcontrol, uinfo) \ | ||||
| 	snd_hda_enum_helper_info(kcontrol, uinfo, 0, NULL) | ||||
| 
 | ||||
| /*
 | ||||
|  * CEA Short Audio Descriptor data | ||||
|  */ | ||||
| struct cea_sad { | ||||
| 	int	channels; | ||||
| 	int	format;		/* (format == 0) indicates invalid SAD */ | ||||
| 	int	rates; | ||||
| 	int	sample_bits;	/* for LPCM */ | ||||
| 	int	max_bitrate;	/* for AC3...ATRAC */ | ||||
| 	int	profile;	/* for WMAPRO */ | ||||
| }; | ||||
| 
 | ||||
| #define ELD_FIXED_BYTES	20 | ||||
| #define ELD_MAX_SIZE    256 | ||||
| #define ELD_MAX_MNL	16 | ||||
| #define ELD_MAX_SAD	16 | ||||
| 
 | ||||
| /*
 | ||||
|  * ELD: EDID Like Data | ||||
|  */ | ||||
| struct parsed_hdmi_eld { | ||||
| 	/*
 | ||||
| 	 * all fields will be cleared before updating ELD | ||||
| 	 */ | ||||
| 	int	baseline_len; | ||||
| 	int	eld_ver; | ||||
| 	int	cea_edid_ver; | ||||
| 	char	monitor_name[ELD_MAX_MNL + 1]; | ||||
| 	int	manufacture_id; | ||||
| 	int	product_id; | ||||
| 	u64	port_id; | ||||
| 	int	support_hdcp; | ||||
| 	int	support_ai; | ||||
| 	int	conn_type; | ||||
| 	int	aud_synch_delay; | ||||
| 	int	spk_alloc; | ||||
| 	int	sad_count; | ||||
| 	struct cea_sad sad[ELD_MAX_SAD]; | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_eld { | ||||
| 	bool	monitor_present; | ||||
| 	bool	eld_valid; | ||||
| 	int	eld_size; | ||||
| 	char    eld_buffer[ELD_MAX_SIZE]; | ||||
| 	struct parsed_hdmi_eld info; | ||||
| 	struct snd_parsed_hdmi_eld info; | ||||
| }; | ||||
| 
 | ||||
| int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); | ||||
| int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, | ||||
| 		     unsigned char *buf, int *eld_size); | ||||
| int snd_hdmi_parse_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e, | ||||
| 		       const unsigned char *buf, int size); | ||||
| void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e); | ||||
| void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, | ||||
| void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e, | ||||
| 			      struct hda_pcm_stream *hinfo); | ||||
| 
 | ||||
| int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, | ||||
|  |  | |||
|  | @ -1511,8 +1511,8 @@ static void update_eld(struct hda_codec *codec, | |||
| 
 | ||||
| 	if (eld->eld_valid) { | ||||
| 		if (eld->eld_size <= 0 || | ||||
| 		    snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer, | ||||
| 				       eld->eld_size) < 0) { | ||||
| 		    snd_parse_eld(hda_codec_dev(codec), &eld->info, | ||||
| 				  eld->eld_buffer, eld->eld_size) < 0) { | ||||
| 			eld->eld_valid = false; | ||||
| 			if (repoll) { | ||||
| 				schedule_delayed_work(&per_pin->work, | ||||
|  | @ -1555,7 +1555,7 @@ static void update_eld(struct hda_codec *codec, | |||
| 		pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); | ||||
| 
 | ||||
| 	if (eld->eld_valid) | ||||
| 		snd_hdmi_show_eld(codec, &eld->info); | ||||
| 		snd_show_eld(hda_codec_dev(codec), &eld->info); | ||||
| 
 | ||||
| 	eld_changed = (pin_eld->eld_valid != eld->eld_valid); | ||||
| 	eld_changed |= (pin_eld->monitor_present != eld->monitor_present); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Dmitry Baryshkov
						Dmitry Baryshkov