2022-05-13 10:07:13 +02:00
|
|
|
#include <assert.h>
|
2022-03-24 17:54:29 +01:00
|
|
|
#include <inttypes.h>
|
2022-03-24 17:20:13 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include <libdisplay-info/edid.h>
|
|
|
|
#include <libdisplay-info/info.h>
|
|
|
|
|
2022-05-24 09:05:14 +02:00
|
|
|
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;
|
2022-05-24 09:38:40 +02:00
|
|
|
const char *tag_name, *str;
|
2022-05-24 09:05:14 +02:00
|
|
|
|
|
|
|
tag = di_edid_display_descriptor_get_tag(desc);
|
|
|
|
tag_name = display_desc_tag_name(tag);
|
|
|
|
|
2022-05-24 09:38:40 +02:00
|
|
|
printf(" %s:", tag_name);
|
|
|
|
|
|
|
|
switch (tag) {
|
|
|
|
case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_SERIAL:
|
|
|
|
case DI_EDID_DISPLAY_DESCRIPTOR_DATA_STRING:
|
|
|
|
case DI_EDID_DISPLAY_DESCRIPTOR_PRODUCT_NAME:
|
|
|
|
str = di_edid_display_descriptor_get_string(desc);
|
|
|
|
printf(" '%s'", str);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break; /* TODO: print other tags */
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\n");
|
2022-05-24 09:05:14 +02:00
|
|
|
}
|
|
|
|
|
2022-05-03 10:35:25 +02:00
|
|
|
static const char *
|
|
|
|
ext_tag_name(enum di_edid_ext_tag tag)
|
|
|
|
{
|
|
|
|
static char name[256];
|
|
|
|
switch (tag) {
|
|
|
|
case DI_EDID_EXT_CEA:
|
|
|
|
return "CTA-861 Extension Block";
|
|
|
|
case DI_EDID_EXT_VTB:
|
|
|
|
return "Video Timing Extension Block";
|
|
|
|
case DI_EDID_EXT_DI:
|
|
|
|
return "Display Information Extension Block";
|
|
|
|
case DI_EDID_EXT_LS:
|
|
|
|
return "Localized String Extension Block";
|
|
|
|
case DI_EDID_EXT_BLOCK_MAP:
|
|
|
|
return "Block Map Extension Block";
|
|
|
|
case DI_EDID_EXT_VENDOR:
|
|
|
|
return "Manufacturer-Specific Extension Block";
|
|
|
|
default:
|
|
|
|
snprintf(name, sizeof(name),
|
|
|
|
"Unknown EDID Extension Block 0x%02x", tag);
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-10 12:20:55 +02:00
|
|
|
static const char *
|
|
|
|
digital_interface_name(enum di_edid_video_input_digital_interface interface)
|
|
|
|
{
|
|
|
|
static char name[256];
|
|
|
|
switch (interface) {
|
|
|
|
case DI_EDID_VIDEO_INPUT_DIGITAL_UNDEFINED:
|
|
|
|
return "Digital interface is not defined";
|
|
|
|
case DI_EDID_VIDEO_INPUT_DIGITAL_DVI:
|
|
|
|
return "DVI interface";
|
|
|
|
case DI_EDID_VIDEO_INPUT_DIGITAL_HDMI_A:
|
|
|
|
return "HDMI-a interface";
|
|
|
|
case DI_EDID_VIDEO_INPUT_DIGITAL_HDMI_B:
|
|
|
|
return "HDMI-b interface";
|
|
|
|
case DI_EDID_VIDEO_INPUT_DIGITAL_MDDI:
|
|
|
|
return "MDDI interface";
|
|
|
|
case DI_EDID_VIDEO_INPUT_DIGITAL_DISPLAYPORT:
|
|
|
|
return "DisplayPort interface";
|
|
|
|
default:
|
|
|
|
snprintf(name, sizeof(name), "Unknown interface: 0x%02x",
|
|
|
|
interface);
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-03 10:35:25 +02:00
|
|
|
static void
|
|
|
|
print_ext(const struct di_edid_ext *ext, size_t ext_index)
|
|
|
|
{
|
|
|
|
const char *tag_name = ext_tag_name(di_edid_ext_get_tag(ext));
|
|
|
|
|
|
|
|
printf("\n----------------\n\n");
|
|
|
|
printf("Block %zu, %s:\n", ext_index + 1, tag_name);
|
|
|
|
}
|
|
|
|
|
2022-06-02 19:24:02 +02:00
|
|
|
static size_t
|
|
|
|
edid_checksum_index(size_t block_index)
|
|
|
|
{
|
|
|
|
return 128 * (block_index + 1) - 1;
|
|
|
|
}
|
|
|
|
|
2022-03-24 17:20:13 +01:00
|
|
|
int
|
2022-06-10 15:02:16 +02:00
|
|
|
main(int argc, char *argv[])
|
2022-03-24 17:20:13 +01:00
|
|
|
{
|
2022-06-10 15:02:16 +02:00
|
|
|
FILE *in;
|
2022-06-02 19:24:02 +02:00
|
|
|
static uint8_t raw[32 * 1024];
|
2022-03-24 17:20:13 +01:00
|
|
|
size_t size = 0;
|
|
|
|
const struct di_edid *edid;
|
|
|
|
struct di_info *info;
|
2022-03-24 17:54:29 +01:00
|
|
|
const struct di_edid_vendor_product *vendor_product;
|
2022-05-10 12:20:55 +02:00
|
|
|
const struct di_edid_video_input_digital *video_input_digital;
|
2022-05-10 12:53:52 +02:00
|
|
|
const struct di_edid_screen_size *screen_size;
|
2022-05-12 11:15:26 +02:00
|
|
|
float gamma;
|
2022-05-12 11:30:54 +02:00
|
|
|
const struct di_edid_dpms *dpms;
|
2022-05-13 10:07:13 +02:00
|
|
|
const struct di_edid_color_encoding_formats *color_encoding_formats;
|
2022-05-13 10:42:59 +02:00
|
|
|
const struct di_edid_misc_features *misc_features;
|
2022-05-24 09:05:14 +02:00
|
|
|
const struct di_edid_display_descriptor *const *display_descs;
|
2022-05-03 10:35:25 +02:00
|
|
|
const struct di_edid_ext *const *exts;
|
|
|
|
size_t i;
|
2022-03-24 17:20:13 +01:00
|
|
|
|
2022-06-10 15:02:16 +02:00
|
|
|
in = stdin;
|
|
|
|
if (argc > 1) {
|
|
|
|
in = fopen(argv[1], "r");
|
|
|
|
if (!in) {
|
|
|
|
perror("failed to open input file");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!feof(in)) {
|
|
|
|
size += fread(&raw[size], 1, sizeof(raw) - size, in);
|
|
|
|
if (ferror(in)) {
|
2022-03-24 17:20:13 +01:00
|
|
|
perror("fread failed");
|
|
|
|
return 1;
|
2022-06-10 14:15:05 +02:00
|
|
|
} else if (size >= sizeof(raw)) {
|
|
|
|
fprintf(stderr, "input too large\n");
|
|
|
|
return 1;
|
2022-03-24 17:20:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-10 15:02:16 +02:00
|
|
|
fclose(in);
|
|
|
|
|
2022-03-24 17:20:13 +01:00
|
|
|
info = di_info_parse_edid(raw, size);
|
|
|
|
if (!info) {
|
|
|
|
perror("di_edid_parse failed");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
edid = di_info_get_edid(info);
|
|
|
|
|
|
|
|
printf("Block 0, Base EDID:\n");
|
|
|
|
printf(" EDID Structure Version & Revision: %d.%d\n",
|
|
|
|
di_edid_get_version(edid), di_edid_get_revision(edid));
|
|
|
|
|
2022-03-24 17:54:29 +01:00
|
|
|
vendor_product = di_edid_get_vendor_product(edid);
|
|
|
|
printf(" Vendor & Product Identification:\n");
|
|
|
|
printf(" Manufacturer: %.3s\n", vendor_product->manufacturer);
|
|
|
|
printf(" Model: %" PRIu16 "\n", vendor_product->product);
|
2022-06-06 17:36:34 +02:00
|
|
|
if (vendor_product->serial != 0) {
|
|
|
|
printf(" Serial Number: %" PRIu32 "\n", vendor_product->serial);
|
|
|
|
}
|
2022-03-24 17:54:29 +01:00
|
|
|
if (vendor_product->model_year != 0) {
|
|
|
|
printf(" Model year: %d\n", vendor_product->model_year);
|
|
|
|
} else {
|
|
|
|
printf(" Made in: week %d of %d\n",
|
|
|
|
vendor_product->manufacture_week,
|
|
|
|
vendor_product->manufacture_year);
|
|
|
|
}
|
|
|
|
|
2022-05-10 12:20:55 +02:00
|
|
|
printf(" Basic Display Parameters & Features:\n");
|
|
|
|
video_input_digital = di_edid_get_video_input_digital(edid);
|
|
|
|
if (video_input_digital) {
|
|
|
|
printf(" Digital display\n");
|
|
|
|
if (di_edid_get_revision(edid) >= 4) {
|
|
|
|
if (video_input_digital->color_bit_depth == 0) {
|
|
|
|
printf(" Color depth is undefined\n");
|
|
|
|
} else {
|
|
|
|
printf(" Bits per primary color channel: %d\n",
|
|
|
|
video_input_digital->color_bit_depth);
|
|
|
|
}
|
|
|
|
printf(" %s\n",
|
|
|
|
digital_interface_name(video_input_digital->interface));
|
|
|
|
}
|
|
|
|
}
|
2022-05-10 12:53:52 +02:00
|
|
|
screen_size = di_edid_get_screen_size(edid);
|
|
|
|
if (screen_size->width_cm > 0) {
|
|
|
|
printf(" Maximum image size: %d cm x %d cm\n",
|
|
|
|
screen_size->width_cm, screen_size->height_cm);
|
|
|
|
} else if (screen_size->landscape_aspect_ratio > 0) {
|
|
|
|
printf(" Aspect ratio: %.2f (landscape)\n",
|
|
|
|
screen_size->landscape_aspect_ratio);
|
|
|
|
} else if (screen_size->portait_aspect_ratio > 0) {
|
|
|
|
printf(" Aspect ratio: %.2f (portrait)\n",
|
|
|
|
screen_size->portait_aspect_ratio);
|
|
|
|
} else {
|
|
|
|
printf(" Image size is variable\n");
|
|
|
|
}
|
2022-05-10 12:20:55 +02:00
|
|
|
|
2022-05-12 11:15:26 +02:00
|
|
|
gamma = di_edid_get_basic_gamma(edid);
|
|
|
|
if (gamma != 0) {
|
|
|
|
printf(" Gamma: %.2f\n", gamma);
|
|
|
|
} else {
|
|
|
|
printf(" Gamma is defined in an extension block\n");
|
|
|
|
}
|
|
|
|
|
2022-05-12 11:30:54 +02:00
|
|
|
dpms = di_edid_get_dpms(edid);
|
|
|
|
if (dpms->standby || dpms->suspend || dpms->off) {
|
|
|
|
printf(" DPMS levels:");
|
|
|
|
if (dpms->standby) {
|
|
|
|
printf(" Standby");
|
|
|
|
}
|
|
|
|
if (dpms->suspend) {
|
|
|
|
printf(" Suspend");
|
|
|
|
}
|
|
|
|
if (dpms->off) {
|
|
|
|
printf(" Off");
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2022-05-13 10:07:13 +02:00
|
|
|
color_encoding_formats = di_edid_get_color_encoding_formats(edid);
|
|
|
|
if (color_encoding_formats) {
|
|
|
|
assert(color_encoding_formats->rgb444);
|
|
|
|
printf(" Supported color formats: RGB 4:4:4");
|
|
|
|
if (color_encoding_formats->ycrcb444) {
|
|
|
|
printf(", YCrCb 4:4:4");
|
|
|
|
}
|
|
|
|
if (color_encoding_formats->ycrcb422) {
|
|
|
|
printf(", YCrCb 4:2:2");
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2022-05-13 10:42:59 +02:00
|
|
|
misc_features = di_edid_get_misc_features(edid);
|
|
|
|
if (misc_features->srgb_is_primary) {
|
|
|
|
printf(" Default (sRGB) color space is primary color space\n");
|
|
|
|
}
|
|
|
|
if (di_edid_get_revision(edid) >= 4) {
|
|
|
|
assert(misc_features->has_preferred_timing);
|
|
|
|
if (misc_features->preferred_timing_is_native) {
|
|
|
|
printf(" First detailed timing includes the native "
|
|
|
|
"pixel format and preferred refresh rate\n");
|
|
|
|
} else {
|
|
|
|
printf(" First detailed timing does not include the "
|
|
|
|
"native pixel format and preferred refresh rate\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (misc_features->has_preferred_timing) {
|
|
|
|
printf(" First detailed timing is the preferred timing\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (misc_features->continuous_freq) {
|
|
|
|
printf(" Display is continuous frequency\n");
|
|
|
|
}
|
|
|
|
if (misc_features->default_gtf) {
|
|
|
|
printf(" Supports GTF timings within operating range\n");
|
|
|
|
}
|
|
|
|
|
2022-05-24 09:05:14 +02:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2022-05-03 10:35:25 +02:00
|
|
|
exts = di_edid_get_extensions(edid);
|
2022-05-25 17:32:52 +02:00
|
|
|
|
|
|
|
for (i = 0; exts[i] != NULL; i++);
|
|
|
|
if (i > 0) {
|
|
|
|
printf(" Extension blocks: %zu\n", i);
|
|
|
|
}
|
2022-06-02 19:24:02 +02:00
|
|
|
printf("Checksum: 0x%02hhx\n", raw[edid_checksum_index(0)]);
|
2022-05-25 17:32:52 +02:00
|
|
|
|
2022-05-03 10:35:25 +02:00
|
|
|
for (i = 0; exts[i] != NULL; i++) {
|
|
|
|
print_ext(exts[i], i);
|
2022-06-02 19:24:02 +02:00
|
|
|
printf("Checksum: 0x%02hhx\n", raw[edid_checksum_index(i + 1)]);
|
2022-05-03 10:35:25 +02:00
|
|
|
}
|
|
|
|
|
2022-03-24 17:20:13 +01:00
|
|
|
di_info_destroy(info);
|
|
|
|
return 0;
|
|
|
|
}
|