[igt-dev] [PATCH i-g-t 1/7] lib/igt_edid: add support for Short Audio Descriptors

Simon Ser
2019-04-26T16:21:13Z

Short Audio Descriptors (SADs) can be wrapped in an EDID extension to advertise
audio support.

The EDID structure is as follows:
- The 128-byte EDID block contains a field with the number of 128-byte
extension blocks that follow.
- Each extension block has a tag which specifies its type. The tag we're
interested in is CEA-861.
- The CEA block has a few flags, including one that indicates whether basic
audio is supported. The CEA block contains several sub-blocks of variable
size. There are two types of CEA sub-blocks:
- Detailed Timing Descriptors (DTDs): additional video timings
- Data Block Collection: these can detail video, audio, speaker placement and
other pieces of information about the display.
We're interested in audio blocks.
- Audio blocks contain one or more Short Audio Descriptors (SADs). A SAD is a
3-byte record describing a supported format.
- SADs can describe support for the PCM format, including sampling rate,
sample size and channels.

The igt_edid library intentionally exposes all of this complexity because it
would be nice to generate all kind of valid EDIDs and test the kernel handles
them correctly (e.g. multiple SADs in different CEA blocks, or any EDID we find
in the wild really).

Signed-off-by: Simon Ser <simon.ser@intel.com>
---
lib/igt_edid.c | 71 +++++++++++++++++++++++++++++++++++++-----
lib/igt_edid.h | 84 ++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 145 insertions(+), 10 deletions(-)

diff --git a/lib/igt_edid.c b/lib/igt_edid.c
index 9d604b13..d01defb0 100644
--- a/lib/igt_edid.c
+++ b/lib/igt_edid.c
@@ -252,19 +252,74 @@ void edid_init_with_mode(struct edid *edid, drmModeModeInfo *mode)
EDID_DETAIL_MONITOR_NAME, "IGT");
}

+static uint8_t compute_checksum(const uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint8_t sum = 0;
+
+ assert(size > 0);
+ for (i = 0; i < size - 1; i++) {
+ sum += buf[i];
+ }
+
+ return 256 - sum;
+}
+
/**
* edid_update_checksum: compute and update the EDID checksum
*/
void edid_update_checksum(struct edid *edid)
{
- size_t i;
- const uint8_t *buf = (const uint8_t *) edid;
- uint8_t sum = 0;
+ edid->checksum = compute_checksum((uint8_t *) edid,
+ sizeof(struct edid));
+}

- /* calculate checksum */
- for (i = 0; i < sizeof(struct edid) - 1; i++) {
- sum = sum + buf[i];
- }
+/**
+ * cea_sad_init_pcm:
+ * @channels: the number of supported channels (max. 8)
+ * @sampling_rates: bitfield of enum cea_sad_sampling_rate
+ * @sample_sizes: bitfield of enum cea_sad_pcm_sample_size
+ *
+ * Initialize a Short Audio Descriptor to advertise PCM support.
+ */
+void cea_sad_init_pcm(struct cea_sad *sad, int channels,
+ uint8_t sampling_rates, uint8_t sample_sizes)
+{
+ assert(channels <= 8);
+ sad->format_channels = CEA_SAD_FORMAT_PCM << 3 | (channels - 1);
+ sad->sampling_rates = sampling_rates;
+ sad->bitrate = sample_sizes;
+}
+
+/**
+ * edid_ext_set_cea_sad: set an extension block to be CEA SAD
+ */
+void edid_ext_set_cea_sad(struct edid_ext *ext, const struct cea_sad *sads,
+ size_t sads_len)
+{
+ struct edid_cea *cea = &ext->data.cea;
+ struct edid_cea_data_block *data_block;
+ size_t sads_size, data_block_size;

- edid->checksum = 256 - sum;
+ memset(ext, 0, sizeof(struct edid_ext));
+
+ sads_size = sizeof(struct cea_sad) * sads_len;
+ data_block_size = sizeof(struct edid_cea_data_block) + sads_size;
+
+ ext->tag = EDID_EXT_CEA;
+
+ cea->revision = 3;
+ cea->dtd_start = 4 + data_block_size;
+ cea->misc = 1 << 6; /* basic audio, no DTD */
+
+ assert(sads_size <= 0xFF);
+ data_block = (struct edid_cea_data_block *) cea->data;
+ data_block->type_len = EDID_CEA_DATA_AUDIO << 5 | sads_size;
+ memcpy(data_block->data.sads, sads, sads_size);
+}
+
+void edid_ext_update_cea_checksum(struct edid_ext *ext)
+{
+ ext->data.cea.checksum = compute_checksum((uint8_t *) ext,
+ sizeof(struct edid_ext));
}
diff --git a/lib/igt_edid.h b/lib/igt_edid.h
index d0963033..860ed929 100644
--- a/lib/igt_edid.h
+++ b/lib/igt_edid.h
@@ -148,6 +148,80 @@ struct detailed_timing {
} data;
} __attribute__((packed));

+enum cea_sad_format {
+ CEA_SAD_FORMAT_PCM = 1,
+ CEA_SAD_FORMAT_AC3 = 2,
+ CEA_SAD_FORMAT_MPEG1 = 3, /* Layers 1 & 2 */
+ CEA_SAD_FORMAT_MP3 = 4,
+ CEA_SAD_FORMAT_MPEG2 = 5,
+ CEA_SAD_FORMAT_AAC = 6,
+ CEA_SAD_FORMAT_DTS = 7,
+ CEA_SAD_FORMAT_ATRAC = 8,
+ CEA_SAD_FORMAT_SACD = 9, /* One-bit audio */
+ CEA_SAD_FORMAT_DD_PLUS = 10,
+ CEA_SAD_FORMAT_DTS_HD = 11,
+ CEA_SAD_FORMAT_DOLBY = 12, /* MLP/Dolby TrueHD */
+ CEA_SAD_FORMAT_DST = 13,
+ CEA_SAD_FORMAT_WMA = 14, /* Microsoft WMA Pro */
+};
+
+enum cea_sad_sampling_rate {
+ CEA_SAD_SAMPLING_RATE_32KHZ = 1 << 0,
+ CEA_SAD_SAMPLING_RATE_44KHZ = 1 << 1,
+ CEA_SAD_SAMPLING_RATE_48KHZ = 1 << 2,
+ CEA_SAD_SAMPLING_RATE_88KHZ = 1 << 3,
+ CEA_SAD_SAMPLING_RATE_96KHZ = 1 << 4,
+ CEA_SAD_SAMPLING_RATE_176KHZ = 1 << 5,
+ CEA_SAD_SAMPLING_RATE_192KHZ = 1 << 6,
+};
+
+/* for PCM only */
+enum cea_sad_pcm_sample_size {
+ CEA_SAD_SAMPLE_SIZE_16 = 1 << 0,
+ CEA_SAD_SAMPLE_SIZE_20 = 1 << 1,
+ CEA_SAD_SAMPLE_SIZE_24 = 1 << 2,
+};
+
+/* Short Audio Descriptor */
+struct cea_sad {
+ uint8_t format_channels;
+ uint8_t sampling_rates;
+ uint8_t bitrate;
+} __attribute__((packed));
+
+enum edid_cea_data_type {
+ EDID_CEA_DATA_AUDIO = 1,
+ EDID_CEA_DATA_VIDEO = 2,
+ EDID_CEA_DATA_VENDOR_SPECIFIC = 3,
+ EDID_CEA_DATA_SPEAKER_ALLOC = 4,
+};
+
+struct edid_cea_data_block {
+ uint8_t type_len; /* type is from enum edid_cea_data_type */
+ union {
+ struct cea_sad sads[0];
+ } data;
+} __attribute__((packed));
+
+struct edid_cea {
+ uint8_t revision;
+ uint8_t dtd_start;
+ uint8_t misc;
+ char data[123]; /* DBC & DTD collection, padded with zeros */
+ uint8_t checksum;
+} __attribute__((packed));
+
+enum edid_ext_tag {
+ EDID_EXT_CEA = 0x02,
+};
+
+struct edid_ext {
+ uint8_t tag; /* enum edid_ext_tag */
+ union {
+ struct edid_cea cea;
+ } data;
+} __attribute__((packed));
+
struct edid {
char header[8];
/* Vendor & product info */
@@ -183,9 +257,9 @@ struct edid {
/* Detailing timings 1-4 */
struct detailed_timing detailed_timings[DETAILED_TIMINGS_LEN];
/* Number of 128 byte ext. blocks */
- uint8_t extensions;
- /* Checksum */
+ uint8_t extensions_len;
uint8_t checksum;
+ struct edid_ext extensions[];
} __attribute__((packed));

void edid_init(struct edid *edid);
@@ -199,4 +273,10 @@ void detailed_timing_set_string(struct detailed_timing *dt,
enum detailed_non_pixel_type type,
const char *str);

+void cea_sad_init_pcm(struct cea_sad *sad, int channels,
+ uint8_t sampling_rates, uint8_t sample_sizes);
+void edid_ext_set_cea_sad(struct edid_ext *ext, const struct cea_sad *sads,
+ size_t sads_len);
+void edid_ext_update_cea_checksum(struct edid_ext *ext);
+
#endif