edid: add basic support for display descriptors

Expose an API to list display descriptors. Detailed timing
descriptors are not parsed.

Signed-off-by: Simon Ser <contact@emersion.fr>
This commit is contained in:
Simon Ser 2022-05-24 09:05:14 +02:00
parent c05fbc875b
commit 2f8f41ea48
6 changed files with 211 additions and 7 deletions

View file

@ -4,6 +4,51 @@
#include <libdisplay-info/edid.h>
#include <libdisplay-info/info.h>
static const char *
display_desc_tag_name(enum di_edid_display_descriptor_tag tag)
{
static char name[256];
switch (tag) {
case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL:
return "Display Product Serial Number";
case DI_EDID_DISPLAY_DESCRIPTOR_DATA_STRING:
return "Alphanumeric Data String";
case DI_EDID_DISPLAY_DESCRIPTOR_RANGE_LIMITS:
return "Display Range Limits";
case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME:
return "Display Product Name";
case DI_EDID_DISPLAY_DESCRIPTOR_COLOR_POINT:
return "Color Point Data";
case DI_EDID_DISPLAY_DESCRIPTOR_STD_TIMING_IDS:
return "Standard Timing Identifications";
case DI_EDID_DISPLAY_DESCRIPTOR_DCM_DATA:
return "Display Color Management Data";
case DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES:
return "CVT 3 Byte Timing Codes";
case DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III:
return "Established timings III";
case DI_EDID_DISPLAY_DESCRIPTOR_DUMMY:
return "Dummy Descriptor";
default:
snprintf(name, sizeof(name), "%s Display Descriptor (0x%02hhx)",
tag <= 0x0F ? "Manufacturer-Specified" : "Unknown",
tag);
return name;
}
}
static void
print_display_desc(const struct di_edid_display_descriptor *desc)
{
enum di_edid_display_descriptor_tag tag;
const char *tag_name;
tag = di_edid_display_descriptor_get_tag(desc);
tag_name = display_desc_tag_name(tag);
printf(" %s:\n", tag_name);
}
static const char *
ext_tag_name(enum di_edid_ext_tag tag)
{
@ -51,6 +96,7 @@ main(void)
const struct di_edid *edid;
struct di_info *info;
const struct di_edid_vendor_product *vendor_product;
const struct di_edid_display_descriptor *const *display_descs;
const struct di_edid_ext *const *exts;
size_t i;
@ -87,6 +133,12 @@ main(void)
vendor_product->manufacture_year);
}
printf(" Detailed Timing Descriptors:\n");
display_descs = di_edid_get_display_descriptors(edid);
for (i = 0; display_descs[i] != NULL; i++) {
print_display_desc(display_descs[i]);
}
exts = di_edid_get_extensions(edid);
for (i = 0; exts[i] != NULL; i++);

94
edid.c
View file

@ -9,6 +9,10 @@
* The size of an EDID block, defined in section 2.2.
*/
#define EDID_BLOCK_SIZE 128
/**
* The size of an EDID byte descriptor, defined in section 3.10.
*/
#define EDID_BYTE_DESCRIPTOR_SIZE 18
/**
* Fixed EDID header, defined in section 3.1.
@ -76,6 +80,68 @@ parse_vendor_product(const uint8_t data[static EDID_BLOCK_SIZE],
}
}
static bool
parse_byte_descriptor(struct di_edid *edid,
const uint8_t data[static EDID_BYTE_DESCRIPTOR_SIZE])
{
struct di_edid_display_descriptor *desc;
uint8_t tag;
if (data[0] || data[1]) {
if (edid->display_descriptors_len > 0) {
/* A detailed timing descriptor is not allowed after a
* display descriptor per note 3 of table 3.20. */
errno = EINVAL;
return false;
}
/* TODO: parse detailed timing descriptor */
return true;
}
/* TODO: check we got at least one detailed timing descriptor, per note
* 4 of table 3.20. */
desc = calloc(1, sizeof(*desc));
if (!desc) {
return false;
}
tag = data[3];
switch (tag) {
case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL:
case DI_EDID_DISPLAY_DESCRIPTOR_DATA_STRING:
case DI_EDID_DISPLAY_DESCRIPTOR_RANGE_LIMITS:
case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME:
case DI_EDID_DISPLAY_DESCRIPTOR_COLOR_POINT:
case DI_EDID_DISPLAY_DESCRIPTOR_STD_TIMING_IDS:
case DI_EDID_DISPLAY_DESCRIPTOR_DCM_DATA:
case DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES:
case DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III:
case DI_EDID_DISPLAY_DESCRIPTOR_DUMMY:
break; /* Ignore */
default:
free(desc);
if (tag <= 0x0F) {
/* Manufacturer-specific */
errno = ENOTSUP;
} else {
/* Reserved */
if (edid->revision > 4) {
errno = ENOTSUP;
} else {
errno = EINVAL;
}
}
return false;
}
desc->tag = tag;
edid->display_descriptors[edid->display_descriptors_len++] = desc;
return true;
}
static bool
parse_ext(struct di_edid *edid, const uint8_t data[static EDID_BLOCK_SIZE])
{
@ -120,7 +186,7 @@ di_edid_parse(const void *data, size_t size)
struct di_edid *edid;
int version, revision;
size_t exts_len, i;
const uint8_t *ext_data;
const uint8_t *byte_desc_data, *ext_data;
if (size < EDID_BLOCK_SIZE ||
size > EDID_MAX_BLOCK_COUNT * EDID_BLOCK_SIZE ||
@ -163,6 +229,16 @@ di_edid_parse(const void *data, size_t size)
parse_vendor_product(data, &edid->vendor_product);
for (i = 0; i < EDID_BYTE_DESCRIPTOR_COUNT; i++) {
byte_desc_data = (const uint8_t *) data
+ 0x36 + i * EDID_BYTE_DESCRIPTOR_SIZE;
if (!parse_byte_descriptor(edid, byte_desc_data)
&& errno != ENOTSUP) {
di_edid_destroy(edid);
return NULL;
}
}
for (i = 0; i < exts_len; i++) {
ext_data = (const uint8_t *) data + (i + 1) * EDID_BLOCK_SIZE;
if (!parse_ext(edid, ext_data) && errno != ENOTSUP) {
@ -179,6 +255,10 @@ di_edid_destroy(struct di_edid *edid)
{
size_t i;
for (i = 0; i < edid->display_descriptors_len; i++) {
free(edid->display_descriptors[i]);
}
for (i = 0; edid->exts[i] != NULL; i++) {
free(edid->exts[i]);
}
@ -204,6 +284,18 @@ di_edid_get_vendor_product(const struct di_edid *edid)
return &edid->vendor_product;
}
const struct di_edid_display_descriptor *const *
di_edid_get_display_descriptors(const struct di_edid *edid)
{
return (const struct di_edid_display_descriptor *const *) &edid->display_descriptors;
}
enum di_edid_display_descriptor_tag
di_edid_display_descriptor_get_tag(const struct di_edid_display_descriptor *desc)
{
return desc->tag;
}
const struct di_edid_ext *const *
di_edid_get_extensions(const struct di_edid *edid)
{

View file

@ -14,15 +14,26 @@
* section 2.2.1.
*/
#define EDID_MAX_BLOCK_COUNT 256
/**
* The number of EDID byte descriptors, defined in section 3.10.
*/
#define EDID_BYTE_DESCRIPTOR_COUNT 4
struct di_edid {
struct di_edid_vendor_product vendor_product;
int version, revision;
/* NULL-terminated */
struct di_edid_display_descriptor *display_descriptors[EDID_BYTE_DESCRIPTOR_COUNT + 1];
size_t display_descriptors_len;
/* NULL-terminated, doesn't include the base block */
struct di_edid_ext *exts[EDID_MAX_BLOCK_COUNT];
size_t exts_len;
};
struct di_edid_display_descriptor {
enum di_edid_display_descriptor_tag tag;
};
struct di_edid_ext {
enum di_edid_ext_tag tag;
};

View file

@ -46,6 +46,51 @@ struct di_edid_vendor_product {
const struct di_edid_vendor_product *
di_edid_get_vendor_product(const struct di_edid *edid);
/**
* EDID display descriptor tag, defined in section 3.10.3.
*/
enum di_edid_display_descriptor_tag {
/* Display Product Serial Number */
DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL = 0xFF,
/* Alphanumeric Data String (ASCII) */
DI_EDID_DISPLAY_DESCRIPTOR_DATA_STRING = 0xFE,
/* Display Range Limits */
DI_EDID_DISPLAY_DESCRIPTOR_RANGE_LIMITS = 0xFD,
/* Display Product Name */
DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME = 0xFC,
/* Color Point Data */
DI_EDID_DISPLAY_DESCRIPTOR_COLOR_POINT = 0xFB,
/* Standard Timing Identifications */
DI_EDID_DISPLAY_DESCRIPTOR_STD_TIMING_IDS = 0xFA,
/* Display Color Management (DCM) Data */
DI_EDID_DISPLAY_DESCRIPTOR_DCM_DATA = 0xF9,
/* CVT 3 Byte Timing Codes */
DI_EDID_DISPLAY_DESCRIPTOR_CVT_TIMING_CODES = 0xF8,
/* Established Timings III */
DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III = 0xF7,
/* Dummy Descriptor */
DI_EDID_DISPLAY_DESCRIPTOR_DUMMY = 0x10,
};
/**
* Get a list of EDID display descriptors.
*
* The returned array is NULL-terminated.
*/
const struct di_edid_display_descriptor *const *
di_edid_get_display_descriptors(const struct di_edid *edid);
/**
* EDID display descriptor.
*/
struct di_edid_display_descriptor;
/**
* Get the tag of an EDID display descriptor.
*/
enum di_edid_display_descriptor_tag
di_edid_display_descriptor_get_tag(const struct di_edid_display_descriptor *desc);
/**
* Get a list of EDID extensions.
*

View file

@ -1,6 +1,6 @@
--- ref
+++ di
@@ -5,39 +5,4 @@
@@ -5,39 +5,8 @@
Model: 41003
Serial Number: 842091859
Made in: week 47 of 2008
@ -31,12 +31,14 @@
- DMT 0x23: 1280x1024 60.019740 Hz 5:4 63.981 kHz 108.000000 MHz
- DMT 0x33: 1600x1200 60.000000 Hz 4:3 75.000 kHz 162.000000 MHz
- DMT 0x15: 1152x864 75.000000 Hz 4:3 67.500 kHz 108.000000 MHz
- Detailed Timing Descriptors:
Detailed Timing Descriptors:
- DTD 1: 1920x1200 59.950171 Hz 8:5 74.038 kHz 154.000000 MHz (519 mm x 320 mm)
- Hfront 48 Hsync 32 Hback 80 Hpol P
- Vfront 3 Vsync 6 Vback 26 Vpol N
- Display Product Serial Number: 'G283H8BI21MS'
- Display Product Name: 'DELL 2408WFP'
- Display Range Limits:
+ Display Product Serial Number:
+ Display Product Name:
Display Range Limits:
- Monitor ranges (Bare Limits): 56-76 Hz V, 30-83 kHz H, max dotclock 170 MHz
Checksum: 0x92

View file

@ -1,6 +1,6 @@
--- ref
+++ di
@@ -5,103 +5,10 @@
@@ -5,103 +5,14 @@
Model: 13847
Serial Number: 16843009
Made in: week 12 of 2019
@ -30,14 +30,16 @@
- DMT 0x2f: 1440x900 59.887445 Hz 16:10 55.935 kHz 106.500000 MHz
- DMT 0x33: 1600x1200 60.000000 Hz 4:3 75.000 kHz 162.000000 MHz
- DMT 0x23: 1280x1024 60.019740 Hz 5:4 63.981 kHz 108.000000 MHz
- Detailed Timing Descriptors:
Detailed Timing Descriptors:
- DTD 1: 2560x1440 59.950550 Hz 16:9 88.787 kHz 241.500000 MHz (597 mm x 339 mm)
- Hfront 48 Hsync 32 Hback 80 Hpol P
- Vfront 3 Vsync 5 Vback 33 Vpol N
- Display Range Limits:
Display Range Limits:
- Monitor ranges (GTF): 46-75 Hz V, 30-112 kHz H, max dotclock 310 MHz
- Display Product Name: 'HP 27 QD'
- Display Product Serial Number: 'CN49120J6N'
+ Display Product Name:
+ Display Product Serial Number:
Extension blocks: 1
Checksum: 0x20