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
|
@ -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 @@ error:
|
|||
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 New Issue