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
|
#ifndef __SOUND_PCM_DRM_ELD_H
|
||||||
#define __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_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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,10 @@
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/hdmi.h>
|
#include <linux/hdmi.h>
|
||||||
|
#include <linux/unaligned.h>
|
||||||
#include <drm/drm_edid.h>
|
#include <drm/drm_edid.h>
|
||||||
#include <drm/drm_eld.h>
|
#include <drm/drm_eld.h>
|
||||||
|
#include <sound/info.h>
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
#include <sound/pcm_drm_eld.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;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld);
|
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
|
config SND_HDA_CODEC_HDMI
|
||||||
tristate "Build HDMI/DisplayPort HD-audio codec support"
|
tristate "Build HDMI/DisplayPort HD-audio codec support"
|
||||||
select SND_DYNAMIC_MINORS
|
select SND_DYNAMIC_MINORS
|
||||||
|
select SND_PCM_ELD
|
||||||
help
|
help
|
||||||
Say Y or M here to include HDMI and DisplayPort HD-audio codec
|
Say Y or M here to include HDMI and DisplayPort HD-audio codec
|
||||||
support in snd-hda-intel driver. This includes all AMD/ATI,
|
support in snd-hda-intel driver. This includes all AMD/ATI,
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,6 @@
|
||||||
#include <sound/hda_codec.h>
|
#include <sound/hda_codec.h>
|
||||||
#include "hda_local.h"
|
#include "hda_local.h"
|
||||||
|
|
||||||
enum eld_versions {
|
|
||||||
ELD_VER_CEA_861D = 2,
|
|
||||||
ELD_VER_PARTIAL = 31,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum cea_edid_versions {
|
enum cea_edid_versions {
|
||||||
CEA_EDID_VER_NONE = 0,
|
CEA_EDID_VER_NONE = 0,
|
||||||
CEA_EDID_VER_CEA861 = 1,
|
CEA_EDID_VER_CEA861 = 1,
|
||||||
|
|
@ -30,95 +25,12 @@ enum cea_edid_versions {
|
||||||
CEA_EDID_VER_RESERVED = 4,
|
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
|
* The following two lists are shared between
|
||||||
* - HDMI audio InfoFrame (source to sink)
|
* - HDMI audio InfoFrame (source to sink)
|
||||||
* - CEA E-EDID Extension (sink to source)
|
* - 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,
|
static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid,
|
||||||
int byte_index)
|
int byte_index)
|
||||||
{
|
{
|
||||||
|
|
@ -132,159 +44,6 @@ static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid,
|
||||||
return val;
|
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)
|
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,
|
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;
|
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
|
#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,
|
void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
|
||||||
struct snd_info_buffer *buffer,
|
struct snd_info_buffer *buffer,
|
||||||
hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid)
|
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, "monitor_present\t\t%d\n", eld->monitor_present);
|
||||||
snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid);
|
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_pin_nid\t\t0x%x\n", pin_nid);
|
||||||
snd_iprintf(buffer, "codec_dev_id\t\t0x%x\n", dev_id);
|
snd_iprintf(buffer, "codec_dev_id\t\t0x%x\n", dev_id);
|
||||||
snd_iprintf(buffer, "codec_cvt_nid\t\t0x%x\n", cvt_nid);
|
snd_iprintf(buffer, "codec_cvt_nid\t\t0x%x\n", cvt_nid);
|
||||||
|
|
||||||
if (!eld->eld_valid)
|
if (!eld->eld_valid)
|
||||||
return;
|
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_print_eld_info(&eld->info, buffer);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
|
void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
|
||||||
struct snd_info_buffer *buffer)
|
struct snd_info_buffer *buffer)
|
||||||
{
|
{
|
||||||
struct parsed_hdmi_eld *e = &eld->info;
|
struct snd_parsed_hdmi_eld *e = &eld->info;
|
||||||
char line[64];
|
char line[64];
|
||||||
char name[64];
|
char name[64];
|
||||||
char *sname;
|
char *sname;
|
||||||
|
|
@ -556,7 +187,7 @@ void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
|
||||||
#endif /* CONFIG_SND_PROC_FS */
|
#endif /* CONFIG_SND_PROC_FS */
|
||||||
|
|
||||||
/* update PCM info based on ELD */
|
/* 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)
|
struct hda_pcm_stream *hinfo)
|
||||||
{
|
{
|
||||||
u32 rates;
|
u32 rates;
|
||||||
|
|
@ -574,17 +205,17 @@ void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
|
||||||
maxbps = 16;
|
maxbps = 16;
|
||||||
channels_max = 2;
|
channels_max = 2;
|
||||||
for (i = 0; i < e->sad_count; i++) {
|
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;
|
rates |= a->rates;
|
||||||
if (a->channels > channels_max)
|
if (a->channels > channels_max)
|
||||||
channels_max = a->channels;
|
channels_max = a->channels;
|
||||||
if (a->format == AUDIO_CODING_TYPE_LPCM) {
|
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;
|
formats |= SNDRV_PCM_FMTBIT_S32_LE;
|
||||||
if (maxbps < 20)
|
if (maxbps < 20)
|
||||||
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;
|
formats |= SNDRV_PCM_FMTBIT_S32_LE;
|
||||||
if (maxbps < 24)
|
if (maxbps < 24)
|
||||||
maxbps = 24;
|
maxbps = 24;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
#ifndef __SOUND_HDA_LOCAL_H
|
#ifndef __SOUND_HDA_LOCAL_H
|
||||||
#define __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
|
/* 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,
|
* 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.
|
* 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) \
|
#define snd_hda_enum_bool_helper_info(kcontrol, uinfo) \
|
||||||
snd_hda_enum_helper_info(kcontrol, uinfo, 0, NULL)
|
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 {
|
struct hdmi_eld {
|
||||||
bool monitor_present;
|
bool monitor_present;
|
||||||
bool eld_valid;
|
bool eld_valid;
|
||||||
int eld_size;
|
int eld_size;
|
||||||
char eld_buffer[ELD_MAX_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_size(struct hda_codec *codec, hda_nid_t nid);
|
||||||
int snd_hdmi_get_eld(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);
|
unsigned char *buf, int *eld_size);
|
||||||
int snd_hdmi_parse_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e,
|
void snd_hdmi_eld_update_pcm_info(struct snd_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,
|
|
||||||
struct hda_pcm_stream *hinfo);
|
struct hda_pcm_stream *hinfo);
|
||||||
|
|
||||||
int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
|
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_valid) {
|
||||||
if (eld->eld_size <= 0 ||
|
if (eld->eld_size <= 0 ||
|
||||||
snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer,
|
snd_parse_eld(hda_codec_dev(codec), &eld->info,
|
||||||
eld->eld_size) < 0) {
|
eld->eld_buffer, eld->eld_size) < 0) {
|
||||||
eld->eld_valid = false;
|
eld->eld_valid = false;
|
||||||
if (repoll) {
|
if (repoll) {
|
||||||
schedule_delayed_work(&per_pin->work,
|
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);
|
pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
|
||||||
|
|
||||||
if (eld->eld_valid)
|
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->eld_valid != eld->eld_valid);
|
||||||
eld_changed |= (pin_eld->monitor_present != eld->monitor_present);
|
eld_changed |= (pin_eld->monitor_present != eld->monitor_present);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue