di-edid-decode: split into multiple files

di-edid-decode.c is getting bit and messy.

Use one file per spec.

Signed-off-by: Simon Ser <contact@emersion.fr>
This commit is contained in:
Simon Ser 2022-10-04 18:15:16 +02:00
parent f2d6819f04
commit 97a8f16d1f
6 changed files with 780 additions and 716 deletions

277
di-edid-decode/cta.c Normal file
View file

@ -0,0 +1,277 @@
#include <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <libdisplay-info/cta.h>
#include "di-edid-decode.h"
static void
printf_cta_svds(const struct di_cta_svd *const *svds)
{
size_t i;
const struct di_cta_svd *svd;
for (i = 0; svds[i] != NULL; i++) {
svd = svds[i];
printf(" VIC %3" PRIu8, svd->vic);
if (svd->native)
printf(" (native)");
printf("\n");
// TODO: print detailed mode info
}
}
static uint8_t
encode_max_luminance(float max)
{
if (max == 0)
return 0;
return (uint8_t) (log2f(max / 50) * 32);
}
static uint8_t
encode_min_luminance(float min, float max)
{
if (min == 0)
return 0;
return (uint8_t) (255 * sqrtf(min / max * 100));
}
static void
print_cta_hdr_static_metadata(const struct di_cta_hdr_static_metadata_block *metadata)
{
printf(" Electro optical transfer functions:\n");
if (metadata->eotfs->traditional_sdr)
printf(" Traditional gamma - SDR luminance range\n");
if (metadata->eotfs->traditional_hdr)
printf(" Traditional gamma - HDR luminance range\n");
if (metadata->eotfs->pq)
printf(" SMPTE ST2084\n");
if (metadata->eotfs->hlg)
printf(" Hybrid Log-Gamma\n");
printf(" Supported static metadata descriptors:\n");
if (metadata->descriptors->type1)
printf(" Static metadata type 1\n");
/* TODO: figure out a way to print raw values? */
if (metadata->desired_content_max_luminance != 0)
printf(" Desired content max luminance: %" PRIu8 " (%.3f cd/m^2)\n",
encode_max_luminance(metadata->desired_content_max_luminance),
metadata->desired_content_max_luminance);
if (metadata->desired_content_max_frame_avg_luminance != 0)
printf(" Desired content max frame-average luminance: %" PRIu8 " (%.3f cd/m^2)\n",
encode_max_luminance(metadata->desired_content_max_frame_avg_luminance),
metadata->desired_content_max_frame_avg_luminance);
if (metadata->desired_content_min_luminance != 0)
printf(" Desired content min luminance: %" PRIu8 " (%.3f cd/m^2)\n",
encode_min_luminance(metadata->desired_content_min_luminance,
metadata->desired_content_max_luminance),
metadata->desired_content_min_luminance);
}
static void
print_cta_vesa_transfer_characteristics(const struct di_cta_vesa_transfer_characteristics *tf)
{
size_t i;
switch (tf->usage) {
case DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_WHITE:
printf(" White");
break;
case DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_RED:
printf(" Red");
break;
case DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_GREEN:
printf(" Green");
break;
case DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_BLUE:
printf(" Blue");
break;
}
printf(" transfer characteristics:");
for (i = 0; i < tf->points_len; i++)
printf(" %u", (uint16_t) (tf->points[i] * 1023.0f));
printf("\n");
uncommon_features.cta_transfer_characteristics = true;
}
static const char *
cta_data_block_tag_name(enum di_cta_data_block_tag tag)
{
switch (tag) {
case DI_CTA_DATA_BLOCK_AUDIO:
return "Audio Data Block";
case DI_CTA_DATA_BLOCK_VIDEO:
return "Video Data Block";
case DI_CTA_DATA_BLOCK_SPEAKER_ALLOC:
return "Speaker Allocation Data Block";
case DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC:
return "VESA Display Transfer Characteristic Data Block";
case DI_CTA_DATA_BLOCK_VIDEO_CAP:
return "Video Capability Data Block";
case DI_CTA_DATA_BLOCK_VESA_DISPLAY_DEVICE:
return "VESA Display Device Data Block";
case DI_CTA_DATA_BLOCK_COLORIMETRY:
return "Colorimetry Data Block";
case DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA:
return "HDR Static Metadata Data Block";
case DI_CTA_DATA_BLOCK_HDR_DYNAMIC_METADATA:
return "HDR Dynamic Metadata Data Block";
case DI_CTA_DATA_BLOCK_VIDEO_FORMAT_PREF:
return "Video Format Preference Data Block";
case DI_CTA_DATA_BLOCK_YCBCR420:
return "YCbCr 4:2:0 Video Data Block";
case DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP:
return "YCbCr 4:2:0 Capability Map Data Block";
case DI_CTA_DATA_BLOCK_HDMI_AUDIO:
return "HDMI Audio Data Block";
case DI_CTA_DATA_BLOCK_ROOM_CONFIG:
return "Room Configuration Data Block";
case DI_CTA_DATA_BLOCK_SPEAKER_LOCATION:
return "Speaker Location Data Block";
case DI_CTA_DATA_BLOCK_INFOFRAME:
return "InfoFrame Data Block";
case DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VII:
return "DisplayID Type VII Video Timing Data Block";
case DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VIII:
return "DisplayID Type VIII Video Timing Data Block";
case DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_X:
return "DisplayID Type X Video Timing Data Block";
case DI_CTA_DATA_BLOCK_HDMI_EDID_EXT_OVERRIDE :
return "HDMI Forum EDID Extension Override Data Block";
case DI_CTA_DATA_BLOCK_HDMI_SINK_CAP:
return "HDMI Forum Sink Capability Data Block";
}
return "Unknown CTA-861 Data Block";
}
static const char *
video_cap_over_underscan_name(enum di_cta_video_cap_over_underscan over_underscan,
const char *unknown)
{
switch (over_underscan) {
case DI_CTA_VIDEO_CAP_UNKNOWN_OVER_UNDERSCAN:
return unknown;
case DI_CTA_VIDEO_CAP_ALWAYS_OVERSCAN:
return "Always Overscanned";
case DI_CTA_VIDEO_CAP_ALWAYS_UNDERSCAN:
return "Always Underscanned";
case DI_CTA_VIDEO_CAP_BOTH_OVER_UNDERSCAN:
return "Supports both over- and underscan";
}
abort();
}
void
print_cta(const struct di_edid_cta *cta)
{
const struct di_edid_cta_flags *cta_flags;
const struct di_cta_data_block *const *data_blocks;
const struct di_cta_data_block *data_block;
enum di_cta_data_block_tag data_block_tag;
const struct di_cta_svd *const *svds;
const struct di_cta_video_cap_block *video_cap;
const struct di_cta_colorimetry_block *colorimetry;
const struct di_cta_hdr_static_metadata_block *hdr_static_metadata;
const struct di_cta_vesa_transfer_characteristics *transfer_characteristics;
size_t i;
const struct di_edid_detailed_timing_def *const *detailed_timing_defs;
printf(" Revision: %d\n", di_edid_cta_get_revision(cta));
cta_flags = di_edid_cta_get_flags(cta);
if (cta_flags->it_underscan) {
printf(" Underscans IT Video Formats by default\n");
}
if (cta_flags->basic_audio) {
printf(" Basic audio support\n");
}
if (cta_flags->ycc444) {
printf(" Supports YCbCr 4:4:4\n");
}
if (cta_flags->ycc422) {
printf(" Supports YCbCr 4:2:2\n");
}
printf(" Native detailed modes: %d\n", cta_flags->native_dtds);
data_blocks = di_edid_cta_get_data_blocks(cta);
for (i = 0; data_blocks[i] != NULL; i++) {
data_block = data_blocks[i];
data_block_tag = di_cta_data_block_get_tag(data_block);
printf(" %s:\n", cta_data_block_tag_name(data_block_tag));
switch (data_block_tag) {
case DI_CTA_DATA_BLOCK_VIDEO:
svds = di_cta_data_block_get_svds(data_block);
printf_cta_svds(svds);
break;
case DI_CTA_DATA_BLOCK_VIDEO_CAP:
video_cap = di_cta_data_block_get_video_cap(data_block);
printf(" YCbCr quantization: %s\n",
video_cap->selectable_ycc_quantization_range ?
"Selectable (via AVI YQ)" : "No Data");
printf(" RGB quantization: %s\n",
video_cap->selectable_ycc_quantization_range ?
"Selectable (via AVI Q)" : "No Data");
printf(" PT scan behavior: %s\n",
video_cap_over_underscan_name(video_cap->pt_over_underscan,
"No Data"));
printf(" IT scan behavior: %s\n",
video_cap_over_underscan_name(video_cap->it_over_underscan,
"IT video formats not supported"));
printf(" CE scan behavior: %s\n",
video_cap_over_underscan_name(video_cap->ce_over_underscan,
"CE video formats not supported"));
break;
case DI_CTA_DATA_BLOCK_COLORIMETRY:
colorimetry = di_cta_data_block_get_colorimetry(data_block);
if (colorimetry->xvycc_601)
printf(" xvYCC601\n");
if (colorimetry->xvycc_709)
printf(" xvYCC709\n");
if (colorimetry->sycc_601)
printf(" sYCC601\n");
if (colorimetry->opycc_601)
printf(" opYCC601\n");
if (colorimetry->oprgb)
printf(" opRGB\n");
if (colorimetry->bt2020_cycc)
printf(" BT2020cYCC\n");
if (colorimetry->bt2020_ycc)
printf(" BT2020YCC\n");
if (colorimetry->bt2020_rgb)
printf(" BT2020RGB\n");
if (colorimetry->ictcp)
printf(" ICtCp\n");
if (colorimetry->st2113_rgb)
printf(" ST2113RGB\n");
break;
case DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA:
hdr_static_metadata = di_cta_data_block_get_hdr_static_metadata(data_block);
print_cta_hdr_static_metadata(hdr_static_metadata);
break;
case DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC:
transfer_characteristics = di_cta_data_block_get_vesa_transfer_characteristics(data_block);
print_cta_vesa_transfer_characteristics(transfer_characteristics);
break;
default:
break; /* Ignore */
}
}
detailed_timing_defs = di_edid_cta_get_detailed_timing_defs(cta);
if (detailed_timing_defs[0]) {
printf(" Detailed Timing Descriptors:\n");
}
for (i = 0; detailed_timing_defs[i] != NULL; i++) {
print_detailed_timing_def(detailed_timing_defs[i]);
}
}

276
di-edid-decode/displayid.c Normal file
View file

@ -0,0 +1,276 @@
#include <stdio.h>
#include <stdlib.h>
#include <libdisplay-info/displayid.h>
#include "di-edid-decode.h"
static bool is_displayid_base_block = true;
static void
print_displayid_display_params(const struct di_displayid_display_params *params)
{
printf(" Image size: %.1f mm x %.1f mm\n",
params->horiz_image_mm, params->vert_image_mm);
printf(" Display native pixel format: %dx%d\n",
params->horiz_pixels, params->vert_pixels);
printf(" Feature support flags:\n");
if (params->features->audio)
printf(" Audio support on video interface\n");
if (params->features->separate_audio_inputs)
printf(" Separate audio inputs provided\n");
if (params->features->audio_input_override)
printf(" Audio input override\n");
if (params->features->power_management)
printf(" Power management (DPM)\n");
if (params->features->fixed_timing)
printf(" Fixed timing\n");
if (params->features->fixed_pixel_format)
printf(" Fixed pixel format\n");
if (params->features->ai)
printf(" Support ACP, ISRC1, or ISRC2packets\n");
if (params->features->deinterlacing)
printf(" De-interlacing\n");
if (params->gamma != 0)
printf(" Gamma: %.2f\n", params->gamma);
printf(" Aspect ratio: %.2f\n", params->aspect_ratio);
printf(" Dynamic bpc native: %d\n", params->bits_per_color_native);
printf(" Dynamic bpc overall: %d\n", params->bits_per_color_overall);
}
static void
get_displayid_type_i_timing_aspect_ratio(enum di_displayid_type_i_timing_aspect_ratio ratio,
int *horiz, int *vert)
{
switch (ratio) {
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_1_1:
*horiz = *vert = 1;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_5_4:
*horiz = 5;
*vert = 4;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_4_3:
*horiz = 4;
*vert = 3;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_15_9:
*horiz = 15;
*vert = 9;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_16_9:
*horiz = 16;
*vert = 9;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_16_10:
*horiz = 16;
*vert = 10;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_64_27:
*horiz = 64;
*vert = 27;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_256_135:
*horiz = 256;
*vert = 135;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_UNDEFINED:
*horiz = *vert = 0;
return;
}
abort(); /* Unreachable */
}
static const char *
displayid_type_i_timing_stereo_3d_name(enum di_displayid_type_i_timing_stereo_3d stereo_3d)
{
switch (stereo_3d) {
case DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_NEVER:
return "no 3D stereo";
case DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_ALWAYS:
return "3D stereo";
case DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_USER:
return "3D stereo depends on user action";
}
abort(); /* Unreachable */
}
static const char *
displayid_type_i_timing_sync_polarity_name(enum di_displayid_type_i_timing_sync_polarity pol)
{
switch (pol) {
case DI_DISPLAYID_TYPE_I_TIMING_SYNC_NEGATIVE:
return "N";
case DI_DISPLAYID_TYPE_I_TIMING_SYNC_POSITIVE:
return "P";
}
abort(); /* Unreachable */
}
static void
print_displayid_type_i_timing(const struct di_displayid_type_i_timing *t)
{
int horiz_total, vert_total;
int horiz_back_porch, vert_back_porch;
int horiz_ratio, vert_ratio;
double pixel_clock_hz, refresh, horiz_freq_hz;
get_displayid_type_i_timing_aspect_ratio(t->aspect_ratio,
&horiz_ratio, &vert_ratio);
horiz_total = t->horiz_active + t->horiz_blank;
vert_total = t->vert_active + t->vert_blank;
pixel_clock_hz = t->pixel_clock_mhz * 1000 * 1000;
refresh = pixel_clock_hz / (horiz_total * vert_total);
horiz_freq_hz = pixel_clock_hz / horiz_total;
printf(" DTD:");
printf(" %5dx%-5d", t->horiz_active, t->vert_active);
if (t->interlaced) {
printf("i");
}
printf(" %10.6f Hz", refresh);
printf(" %3d:%-3d", horiz_ratio, vert_ratio);
printf(" %8.3f kHz %13.6f MHz", horiz_freq_hz / 1000,
t->pixel_clock_mhz);
printf(" (aspect ");
if (t->aspect_ratio == DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_UNDEFINED)
printf("undefined");
else
printf("%d:%d", horiz_ratio, vert_ratio);
printf(", %s", displayid_type_i_timing_stereo_3d_name(t->stereo_3d));
if (t->preferred)
printf(", preferred");
printf(")\n");
horiz_back_porch = t->horiz_blank - t->horiz_sync_width - t->horiz_offset;
printf(" Hfront %4d Hsync %3d Hback %4d Hpol %s",
t->horiz_offset, t->horiz_sync_width, horiz_back_porch,
displayid_type_i_timing_sync_polarity_name(t->horiz_sync_polarity));
printf("\n");
vert_back_porch = t->vert_blank - t->vert_sync_width - t->vert_offset;
printf(" Vfront %4d Vsync %3d Vback %4d Vpol %s",
t->vert_offset, t->vert_sync_width, vert_back_porch,
displayid_type_i_timing_sync_polarity_name(t->vert_sync_polarity));
printf("\n");
}
static void
print_displayid_type_i_timing_block(const struct di_displayid_data_block *data_block)
{
size_t i;
const struct di_displayid_type_i_timing *const *timings;
timings = di_displayid_data_block_get_type_i_timings(data_block);
for (i = 0; timings[i] != NULL; i++)
print_displayid_type_i_timing(timings[i]);
}
static const char *
displayid_product_type_name(enum di_displayid_product_type type)
{
switch (type) {
case DI_DISPLAYID_PRODUCT_TYPE_EXTENSION:
return "Extension Section";
case DI_DISPLAYID_PRODUCT_TYPE_TEST:
return "Test Structure; test equipment only";
case DI_DISPLAYID_PRODUCT_TYPE_DISPLAY_PANEL:
return "Display panel or other transducer, LCD or PDP module, etc.";
case DI_DISPLAYID_PRODUCT_TYPE_STANDALONE_DISPLAY:
return "Standalone display device";
case DI_DISPLAYID_PRODUCT_TYPE_TV_RECEIVER:
return "Television receiver";
case DI_DISPLAYID_PRODUCT_TYPE_REPEATER:
return "Repeater/translator";
case DI_DISPLAYID_PRODUCT_TYPE_DIRECT_DRIVE:
return "DIRECT DRIVE monitor";
}
abort();
}
static const char *
displayid_data_block_tag_name(enum di_displayid_data_block_tag tag)
{
switch (tag) {
case DI_DISPLAYID_DATA_BLOCK_PRODUCT_ID:
return "Product Identification Data Block (0x00)";
case DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS:
return "Display Parameters Data Block (0x01)";
case DI_DISPLAYID_DATA_BLOCK_COLOR_CHARACT:
return "Color Characteristics Data Block";
case DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING:
return "Video Timing Modes Type 1 - Detailed Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING:
return "Video Timing Modes Type 2 - Detailed Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING:
return "Video Timing Modes Type 3 - Short Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_TYPE_IV_TIMING:
return "Video Timing Modes Type 4 - DMT Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_VESA_TIMING:
return "Supported Timing Modes Type 1 - VESA DMT Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_CEA_TIMING:
return "Supported Timing Modes Type 2 - CTA-861 Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_TIMING_RANGE_LIMITS:
return "Video Timing Range Data Block";
case DI_DISPLAYID_DATA_BLOCK_PRODUCT_SERIAL:
return "Product Serial Number Data Block";
case DI_DISPLAYID_DATA_BLOCK_ASCII_STRING:
return "GP ASCII String Data Block";
case DI_DISPLAYID_DATA_BLOCK_DISPLAY_DEVICE_DATA:
return "Display Device Data Data Block";
case DI_DISPLAYID_DATA_BLOCK_INTERFACE_POWER_SEQ:
return "Interface Power Sequencing Data Block";
case DI_DISPLAYID_DATA_BLOCK_TRANSFER_CHARACT:
return "Transfer Characteristics Data Block";
case DI_DISPLAYID_DATA_BLOCK_DISPLAY_INTERFACE:
return "Display Interface Data Block";
case DI_DISPLAYID_DATA_BLOCK_STEREO_DISPLAY_INTERFACE:
return "Stereo Display Interface Data Block (0x10)";
case DI_DISPLAYID_DATA_BLOCK_TYPE_V_TIMING:
return "Video Timing Modes Type 5 - Short Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO:
return "Tiled Display Topology Data Block (0x12)";
case DI_DISPLAYID_DATA_BLOCK_TYPE_VI_TIMING:
return "Video Timing Modes Type 6 - Detailed Timings Data Block";
}
abort();
}
void
print_displayid(const struct di_displayid *displayid)
{
const struct di_displayid_data_block *const *data_blocks;
const struct di_displayid_data_block *data_block;
enum di_displayid_data_block_tag tag;
size_t i;
const struct di_displayid_display_params *display_params;
printf(" Version: %d.%d\n", di_displayid_get_version(displayid),
di_displayid_get_revision(displayid));
if (is_displayid_base_block)
printf(" Display Product Type: %s\n",
displayid_product_type_name(di_displayid_get_product_type(displayid)));
is_displayid_base_block = false;
data_blocks = di_displayid_get_data_blocks(displayid);
for (i = 0; data_blocks[i] != NULL; i++) {
data_block = data_blocks[i];
tag = di_displayid_data_block_get_tag(data_block);
printf(" %s:\n", displayid_data_block_tag_name(tag));
switch (tag) {
case DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS:
display_params = di_displayid_data_block_get_display_params(data_block);
print_displayid_display_params(display_params);
break;
case DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING:
print_displayid_type_i_timing_block(data_block);
break;
default:
break; /* Ignore */
}
}
}

View file

@ -3,38 +3,14 @@
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <getopt.h>
#include <libdisplay-info/cta.h>
#include <libdisplay-info/displayid.h>
#include <libdisplay-info/dmt.h> #include <libdisplay-info/dmt.h>
#include <libdisplay-info/edid.h> #include <libdisplay-info/edid.h>
#include <libdisplay-info/gtf.h> #include <libdisplay-info/gtf.h>
#include <libdisplay-info/info.h>
static struct { #include "di-edid-decode.h"
bool color_point_descriptor;
bool color_management_data;
bool cta_transfer_characteristics;
} contains_uncommon_feature;
static size_t num_detailed_timing_defs = 0; static size_t num_detailed_timing_defs = 0;
static bool is_displayid_base_block = true;
static const struct option long_options[] = {
{ "help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 }
};
static void usage(void)
{
fprintf(stderr, "Usage: di-edid-decode <options> [in]\n"
" [in]: EDID file to parse. Read from standard input (stdin),\n"
" if none given.\n"
"Example : di-edid-decode /sys/class/drm/card0-DP-2/edid \n"
"\nOptions:\n"
"-h, --help display the help message\n");
}
static const char * static const char *
standard_timing_aspect_ratio_name(enum di_edid_standard_timing_aspect_ratio aspect_ratio) standard_timing_aspect_ratio_name(enum di_edid_standard_timing_aspect_ratio aspect_ratio)
@ -261,7 +237,7 @@ detailed_timing_def_sync_polarity_name(enum di_edid_detailed_timing_def_sync_pol
abort(); abort();
} }
static void void
print_detailed_timing_def(const struct di_edid_detailed_timing_def *def) print_detailed_timing_def(const struct di_edid_detailed_timing_def *def)
{ {
int hbl, vbl, horiz_total, vert_total; int hbl, vbl, horiz_total, vert_total;
@ -556,7 +532,7 @@ print_display_desc(const struct di_edid *edid,
print_color_point(color_points[i]); print_color_point(color_points[i]);
} }
contains_uncommon_feature.color_point_descriptor = true; uncommon_features.color_point_descriptor = true;
break; break;
case DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III: case DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III:
established_timings_iii = di_edid_display_descriptor_get_established_timings_iii(desc); established_timings_iii = di_edid_display_descriptor_get_established_timings_iii(desc);
@ -578,37 +554,12 @@ print_display_desc(const struct di_edid *edid,
printf(" Blue a3 : %.2f\n", color_management_data->blue_a3); printf(" Blue a3 : %.2f\n", color_management_data->blue_a3);
printf(" Blue a2 : %.2f\n", color_management_data->blue_a2); printf(" Blue a2 : %.2f\n", color_management_data->blue_a2);
contains_uncommon_feature.color_management_data = true; uncommon_features.color_management_data = true;
break; break;
default: default:
printf("\n"); printf("\n");
break; /* TODO: print other tags */ break; /* TODO: print other tags */
} }
}
static const char *
ext_tag_name(enum di_edid_ext_tag tag)
{
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_DPVL:
return "Digital Packet Video Link Extension";
case DI_EDID_EXT_BLOCK_MAP:
return "Block Map Extension Block";
case DI_EDID_EXT_VENDOR:
return "Manufacturer-Specific Extension Block";
case DI_EDID_EXT_DISPLAYID:
return "DisplayID Extension Block";
}
abort();
} }
static const char * static const char *
@ -663,578 +614,9 @@ display_color_type_name(enum di_edid_display_color_type type)
abort(); abort();
} }
static void void
printf_cta_svds(const struct di_cta_svd *const *svds) print_edid(const struct di_edid *edid)
{ {
size_t i;
const struct di_cta_svd *svd;
for (i = 0; svds[i] != NULL; i++) {
svd = svds[i];
printf(" VIC %3" PRIu8, svd->vic);
if (svd->native)
printf(" (native)");
printf("\n");
// TODO: print detailed mode info
}
}
static uint8_t
encode_max_luminance(float max)
{
if (max == 0)
return 0;
return (uint8_t) (log2f(max / 50) * 32);
}
static uint8_t
encode_min_luminance(float min, float max)
{
if (min == 0)
return 0;
return (uint8_t) (255 * sqrtf(min / max * 100));
}
static void
print_cta_hdr_static_metadata(const struct di_cta_hdr_static_metadata_block *metadata)
{
printf(" Electro optical transfer functions:\n");
if (metadata->eotfs->traditional_sdr)
printf(" Traditional gamma - SDR luminance range\n");
if (metadata->eotfs->traditional_hdr)
printf(" Traditional gamma - HDR luminance range\n");
if (metadata->eotfs->pq)
printf(" SMPTE ST2084\n");
if (metadata->eotfs->hlg)
printf(" Hybrid Log-Gamma\n");
printf(" Supported static metadata descriptors:\n");
if (metadata->descriptors->type1)
printf(" Static metadata type 1\n");
/* TODO: figure out a way to print raw values? */
if (metadata->desired_content_max_luminance != 0)
printf(" Desired content max luminance: %" PRIu8 " (%.3f cd/m^2)\n",
encode_max_luminance(metadata->desired_content_max_luminance),
metadata->desired_content_max_luminance);
if (metadata->desired_content_max_frame_avg_luminance != 0)
printf(" Desired content max frame-average luminance: %" PRIu8 " (%.3f cd/m^2)\n",
encode_max_luminance(metadata->desired_content_max_frame_avg_luminance),
metadata->desired_content_max_frame_avg_luminance);
if (metadata->desired_content_min_luminance != 0)
printf(" Desired content min luminance: %" PRIu8 " (%.3f cd/m^2)\n",
encode_min_luminance(metadata->desired_content_min_luminance,
metadata->desired_content_max_luminance),
metadata->desired_content_min_luminance);
}
static void
print_cta_vesa_transfer_characteristics(const struct di_cta_vesa_transfer_characteristics *tf)
{
size_t i;
switch (tf->usage) {
case DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_WHITE:
printf(" White");
break;
case DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_RED:
printf(" Red");
break;
case DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_GREEN:
printf(" Green");
break;
case DI_CTA_VESA_TRANSFER_CHARACTERISTIC_USAGE_BLUE:
printf(" Blue");
break;
}
printf(" transfer characteristics:");
for (i = 0; i < tf->points_len; i++)
printf(" %u", (uint16_t) (tf->points[i] * 1023.0f));
printf("\n");
contains_uncommon_feature.cta_transfer_characteristics = true;
}
static const char *
cta_data_block_tag_name(enum di_cta_data_block_tag tag)
{
switch (tag) {
case DI_CTA_DATA_BLOCK_AUDIO:
return "Audio Data Block";
case DI_CTA_DATA_BLOCK_VIDEO:
return "Video Data Block";
case DI_CTA_DATA_BLOCK_SPEAKER_ALLOC:
return "Speaker Allocation Data Block";
case DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC:
return "VESA Display Transfer Characteristic Data Block";
case DI_CTA_DATA_BLOCK_VIDEO_CAP:
return "Video Capability Data Block";
case DI_CTA_DATA_BLOCK_VESA_DISPLAY_DEVICE:
return "VESA Display Device Data Block";
case DI_CTA_DATA_BLOCK_COLORIMETRY:
return "Colorimetry Data Block";
case DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA:
return "HDR Static Metadata Data Block";
case DI_CTA_DATA_BLOCK_HDR_DYNAMIC_METADATA:
return "HDR Dynamic Metadata Data Block";
case DI_CTA_DATA_BLOCK_VIDEO_FORMAT_PREF:
return "Video Format Preference Data Block";
case DI_CTA_DATA_BLOCK_YCBCR420:
return "YCbCr 4:2:0 Video Data Block";
case DI_CTA_DATA_BLOCK_YCBCR420_CAP_MAP:
return "YCbCr 4:2:0 Capability Map Data Block";
case DI_CTA_DATA_BLOCK_HDMI_AUDIO:
return "HDMI Audio Data Block";
case DI_CTA_DATA_BLOCK_ROOM_CONFIG:
return "Room Configuration Data Block";
case DI_CTA_DATA_BLOCK_SPEAKER_LOCATION:
return "Speaker Location Data Block";
case DI_CTA_DATA_BLOCK_INFOFRAME:
return "InfoFrame Data Block";
case DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VII:
return "DisplayID Type VII Video Timing Data Block";
case DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_VIII:
return "DisplayID Type VIII Video Timing Data Block";
case DI_CTA_DATA_BLOCK_DISPLAYID_VIDEO_TIMING_X:
return "DisplayID Type X Video Timing Data Block";
case DI_CTA_DATA_BLOCK_HDMI_EDID_EXT_OVERRIDE :
return "HDMI Forum EDID Extension Override Data Block";
case DI_CTA_DATA_BLOCK_HDMI_SINK_CAP:
return "HDMI Forum Sink Capability Data Block";
}
return "Unknown CTA-861 Data Block";
}
static const char *
video_cap_over_underscan_name(enum di_cta_video_cap_over_underscan over_underscan,
const char *unknown)
{
switch (over_underscan) {
case DI_CTA_VIDEO_CAP_UNKNOWN_OVER_UNDERSCAN:
return unknown;
case DI_CTA_VIDEO_CAP_ALWAYS_OVERSCAN:
return "Always Overscanned";
case DI_CTA_VIDEO_CAP_ALWAYS_UNDERSCAN:
return "Always Underscanned";
case DI_CTA_VIDEO_CAP_BOTH_OVER_UNDERSCAN:
return "Supports both over- and underscan";
}
abort();
}
static void
print_cta(const struct di_edid_cta *cta)
{
const struct di_edid_cta_flags *cta_flags;
const struct di_cta_data_block *const *data_blocks;
const struct di_cta_data_block *data_block;
enum di_cta_data_block_tag data_block_tag;
const struct di_cta_svd *const *svds;
const struct di_cta_video_cap_block *video_cap;
const struct di_cta_colorimetry_block *colorimetry;
const struct di_cta_hdr_static_metadata_block *hdr_static_metadata;
const struct di_cta_vesa_transfer_characteristics *transfer_characteristics;
size_t i;
const struct di_edid_detailed_timing_def *const *detailed_timing_defs;
printf(" Revision: %d\n", di_edid_cta_get_revision(cta));
cta_flags = di_edid_cta_get_flags(cta);
if (cta_flags->it_underscan) {
printf(" Underscans IT Video Formats by default\n");
}
if (cta_flags->basic_audio) {
printf(" Basic audio support\n");
}
if (cta_flags->ycc444) {
printf(" Supports YCbCr 4:4:4\n");
}
if (cta_flags->ycc422) {
printf(" Supports YCbCr 4:2:2\n");
}
printf(" Native detailed modes: %d\n", cta_flags->native_dtds);
data_blocks = di_edid_cta_get_data_blocks(cta);
for (i = 0; data_blocks[i] != NULL; i++) {
data_block = data_blocks[i];
data_block_tag = di_cta_data_block_get_tag(data_block);
printf(" %s:\n", cta_data_block_tag_name(data_block_tag));
switch (data_block_tag) {
case DI_CTA_DATA_BLOCK_VIDEO:
svds = di_cta_data_block_get_svds(data_block);
printf_cta_svds(svds);
break;
case DI_CTA_DATA_BLOCK_VIDEO_CAP:
video_cap = di_cta_data_block_get_video_cap(data_block);
printf(" YCbCr quantization: %s\n",
video_cap->selectable_ycc_quantization_range ?
"Selectable (via AVI YQ)" : "No Data");
printf(" RGB quantization: %s\n",
video_cap->selectable_ycc_quantization_range ?
"Selectable (via AVI Q)" : "No Data");
printf(" PT scan behavior: %s\n",
video_cap_over_underscan_name(video_cap->pt_over_underscan,
"No Data"));
printf(" IT scan behavior: %s\n",
video_cap_over_underscan_name(video_cap->it_over_underscan,
"IT video formats not supported"));
printf(" CE scan behavior: %s\n",
video_cap_over_underscan_name(video_cap->ce_over_underscan,
"CE video formats not supported"));
break;
case DI_CTA_DATA_BLOCK_COLORIMETRY:
colorimetry = di_cta_data_block_get_colorimetry(data_block);
if (colorimetry->xvycc_601)
printf(" xvYCC601\n");
if (colorimetry->xvycc_709)
printf(" xvYCC709\n");
if (colorimetry->sycc_601)
printf(" sYCC601\n");
if (colorimetry->opycc_601)
printf(" opYCC601\n");
if (colorimetry->oprgb)
printf(" opRGB\n");
if (colorimetry->bt2020_cycc)
printf(" BT2020cYCC\n");
if (colorimetry->bt2020_ycc)
printf(" BT2020YCC\n");
if (colorimetry->bt2020_rgb)
printf(" BT2020RGB\n");
if (colorimetry->ictcp)
printf(" ICtCp\n");
if (colorimetry->st2113_rgb)
printf(" ST2113RGB\n");
break;
case DI_CTA_DATA_BLOCK_HDR_STATIC_METADATA:
hdr_static_metadata = di_cta_data_block_get_hdr_static_metadata(data_block);
print_cta_hdr_static_metadata(hdr_static_metadata);
break;
case DI_CTA_DATA_BLOCK_VESA_DISPLAY_TRANSFER_CHARACTERISTIC:
transfer_characteristics = di_cta_data_block_get_vesa_transfer_characteristics(data_block);
print_cta_vesa_transfer_characteristics(transfer_characteristics);
break;
default:
break; /* Ignore */
}
}
detailed_timing_defs = di_edid_cta_get_detailed_timing_defs(cta);
if (detailed_timing_defs[0]) {
printf(" Detailed Timing Descriptors:\n");
}
for (i = 0; detailed_timing_defs[i] != NULL; i++) {
print_detailed_timing_def(detailed_timing_defs[i]);
}
}
static void
print_displayid_display_params(const struct di_displayid_display_params *params)
{
printf(" Image size: %.1f mm x %.1f mm\n",
params->horiz_image_mm, params->vert_image_mm);
printf(" Display native pixel format: %dx%d\n",
params->horiz_pixels, params->vert_pixels);
printf(" Feature support flags:\n");
if (params->features->audio)
printf(" Audio support on video interface\n");
if (params->features->separate_audio_inputs)
printf(" Separate audio inputs provided\n");
if (params->features->audio_input_override)
printf(" Audio input override\n");
if (params->features->power_management)
printf(" Power management (DPM)\n");
if (params->features->fixed_timing)
printf(" Fixed timing\n");
if (params->features->fixed_pixel_format)
printf(" Fixed pixel format\n");
if (params->features->ai)
printf(" Support ACP, ISRC1, or ISRC2packets\n");
if (params->features->deinterlacing)
printf(" De-interlacing\n");
if (params->gamma != 0)
printf(" Gamma: %.2f\n", params->gamma);
printf(" Aspect ratio: %.2f\n", params->aspect_ratio);
printf(" Dynamic bpc native: %d\n", params->bits_per_color_native);
printf(" Dynamic bpc overall: %d\n", params->bits_per_color_overall);
}
static void
get_displayid_type_i_timing_aspect_ratio(enum di_displayid_type_i_timing_aspect_ratio ratio,
int *horiz, int *vert)
{
switch (ratio) {
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_1_1:
*horiz = *vert = 1;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_5_4:
*horiz = 5;
*vert = 4;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_4_3:
*horiz = 4;
*vert = 3;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_15_9:
*horiz = 15;
*vert = 9;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_16_9:
*horiz = 16;
*vert = 9;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_16_10:
*horiz = 16;
*vert = 10;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_64_27:
*horiz = 64;
*vert = 27;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_256_135:
*horiz = 256;
*vert = 135;
return;
case DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_UNDEFINED:
*horiz = *vert = 0;
return;
}
abort(); /* Unreachable */
}
static const char *
displayid_type_i_timing_stereo_3d_name(enum di_displayid_type_i_timing_stereo_3d stereo_3d)
{
switch (stereo_3d) {
case DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_NEVER:
return "no 3D stereo";
case DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_ALWAYS:
return "3D stereo";
case DI_DISPLAYID_TYPE_I_TIMING_STEREO_3D_USER:
return "3D stereo depends on user action";
}
abort(); /* Unreachable */
}
static const char *
displayid_type_i_timing_sync_polarity_name(enum di_displayid_type_i_timing_sync_polarity pol)
{
switch (pol) {
case DI_DISPLAYID_TYPE_I_TIMING_SYNC_NEGATIVE:
return "N";
case DI_DISPLAYID_TYPE_I_TIMING_SYNC_POSITIVE:
return "P";
}
abort(); /* Unreachable */
}
static void
print_displayid_type_i_timing(const struct di_displayid_type_i_timing *t)
{
int horiz_total, vert_total;
int horiz_back_porch, vert_back_porch;
int horiz_ratio, vert_ratio;
double pixel_clock_hz, refresh, horiz_freq_hz;
get_displayid_type_i_timing_aspect_ratio(t->aspect_ratio,
&horiz_ratio, &vert_ratio);
horiz_total = t->horiz_active + t->horiz_blank;
vert_total = t->vert_active + t->vert_blank;
pixel_clock_hz = t->pixel_clock_mhz * 1000 * 1000;
refresh = pixel_clock_hz / (horiz_total * vert_total);
horiz_freq_hz = pixel_clock_hz / horiz_total;
printf(" DTD:");
printf(" %5dx%-5d", t->horiz_active, t->vert_active);
if (t->interlaced) {
printf("i");
}
printf(" %10.6f Hz", refresh);
printf(" %3d:%-3d", horiz_ratio, vert_ratio);
printf(" %8.3f kHz %13.6f MHz", horiz_freq_hz / 1000,
t->pixel_clock_mhz);
printf(" (aspect ");
if (t->aspect_ratio == DI_DISPLAYID_TYPE_I_TIMING_ASPECT_RATIO_UNDEFINED)
printf("undefined");
else
printf("%d:%d", horiz_ratio, vert_ratio);
printf(", %s", displayid_type_i_timing_stereo_3d_name(t->stereo_3d));
if (t->preferred)
printf(", preferred");
printf(")\n");
horiz_back_porch = t->horiz_blank - t->horiz_sync_width - t->horiz_offset;
printf(" Hfront %4d Hsync %3d Hback %4d Hpol %s",
t->horiz_offset, t->horiz_sync_width, horiz_back_porch,
displayid_type_i_timing_sync_polarity_name(t->horiz_sync_polarity));
printf("\n");
vert_back_porch = t->vert_blank - t->vert_sync_width - t->vert_offset;
printf(" Vfront %4d Vsync %3d Vback %4d Vpol %s",
t->vert_offset, t->vert_sync_width, vert_back_porch,
displayid_type_i_timing_sync_polarity_name(t->vert_sync_polarity));
printf("\n");
}
static void
print_displayid_type_i_timing_block(const struct di_displayid_data_block *data_block)
{
size_t i;
const struct di_displayid_type_i_timing *const *timings;
timings = di_displayid_data_block_get_type_i_timings(data_block);
for (i = 0; timings[i] != NULL; i++)
print_displayid_type_i_timing(timings[i]);
}
static const char *
displayid_product_type_name(enum di_displayid_product_type type)
{
switch (type) {
case DI_DISPLAYID_PRODUCT_TYPE_EXTENSION:
return "Extension Section";
case DI_DISPLAYID_PRODUCT_TYPE_TEST:
return "Test Structure; test equipment only";
case DI_DISPLAYID_PRODUCT_TYPE_DISPLAY_PANEL:
return "Display panel or other transducer, LCD or PDP module, etc.";
case DI_DISPLAYID_PRODUCT_TYPE_STANDALONE_DISPLAY:
return "Standalone display device";
case DI_DISPLAYID_PRODUCT_TYPE_TV_RECEIVER:
return "Television receiver";
case DI_DISPLAYID_PRODUCT_TYPE_REPEATER:
return "Repeater/translator";
case DI_DISPLAYID_PRODUCT_TYPE_DIRECT_DRIVE:
return "DIRECT DRIVE monitor";
}
abort();
}
static const char *
displayid_data_block_tag_name(enum di_displayid_data_block_tag tag)
{
switch (tag) {
case DI_DISPLAYID_DATA_BLOCK_PRODUCT_ID:
return "Product Identification Data Block (0x00)";
case DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS:
return "Display Parameters Data Block (0x01)";
case DI_DISPLAYID_DATA_BLOCK_COLOR_CHARACT:
return "Color Characteristics Data Block";
case DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING:
return "Video Timing Modes Type 1 - Detailed Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING:
return "Video Timing Modes Type 2 - Detailed Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING:
return "Video Timing Modes Type 3 - Short Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_TYPE_IV_TIMING:
return "Video Timing Modes Type 4 - DMT Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_VESA_TIMING:
return "Supported Timing Modes Type 1 - VESA DMT Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_CEA_TIMING:
return "Supported Timing Modes Type 2 - CTA-861 Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_TIMING_RANGE_LIMITS:
return "Video Timing Range Data Block";
case DI_DISPLAYID_DATA_BLOCK_PRODUCT_SERIAL:
return "Product Serial Number Data Block";
case DI_DISPLAYID_DATA_BLOCK_ASCII_STRING:
return "GP ASCII String Data Block";
case DI_DISPLAYID_DATA_BLOCK_DISPLAY_DEVICE_DATA:
return "Display Device Data Data Block";
case DI_DISPLAYID_DATA_BLOCK_INTERFACE_POWER_SEQ:
return "Interface Power Sequencing Data Block";
case DI_DISPLAYID_DATA_BLOCK_TRANSFER_CHARACT:
return "Transfer Characteristics Data Block";
case DI_DISPLAYID_DATA_BLOCK_DISPLAY_INTERFACE:
return "Display Interface Data Block";
case DI_DISPLAYID_DATA_BLOCK_STEREO_DISPLAY_INTERFACE:
return "Stereo Display Interface Data Block (0x10)";
case DI_DISPLAYID_DATA_BLOCK_TYPE_V_TIMING:
return "Video Timing Modes Type 5 - Short Timings Data Block";
case DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO:
return "Tiled Display Topology Data Block (0x12)";
case DI_DISPLAYID_DATA_BLOCK_TYPE_VI_TIMING:
return "Video Timing Modes Type 6 - Detailed Timings Data Block";
}
abort();
}
static void
print_displayid(const struct di_displayid *displayid)
{
const struct di_displayid_data_block *const *data_blocks;
const struct di_displayid_data_block *data_block;
enum di_displayid_data_block_tag tag;
size_t i;
const struct di_displayid_display_params *display_params;
printf(" Version: %d.%d\n", di_displayid_get_version(displayid),
di_displayid_get_revision(displayid));
if (is_displayid_base_block)
printf(" Display Product Type: %s\n",
displayid_product_type_name(di_displayid_get_product_type(displayid)));
is_displayid_base_block = false;
data_blocks = di_displayid_get_data_blocks(displayid);
for (i = 0; data_blocks[i] != NULL; i++) {
data_block = data_blocks[i];
tag = di_displayid_data_block_get_tag(data_block);
printf(" %s:\n", displayid_data_block_tag_name(tag));
switch (tag) {
case DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS:
display_params = di_displayid_data_block_get_display_params(data_block);
print_displayid_display_params(display_params);
break;
case DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING:
print_displayid_type_i_timing_block(data_block);
break;
default:
break; /* Ignore */
}
}
}
static void
print_ext(const struct di_edid_ext *ext, size_t ext_index)
{
const char *tag_name;
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);
switch (di_edid_ext_get_tag(ext)) {
case DI_EDID_EXT_CEA:
print_cta(di_edid_ext_get_cta(ext));
break;
case DI_EDID_EXT_DISPLAYID:
print_displayid(di_edid_ext_get_displayid(ext));
break;
default:
break; /* Ignore */
}
}
static size_t
edid_checksum_index(size_t block_index)
{
return 128 * (block_index + 1) - 1;
}
int
main(int argc, char *argv[])
{
FILE *in;
static uint8_t raw[32 * 1024];
size_t size = 0;
const struct di_edid *edid;
struct di_info *info;
const struct di_edid_vendor_product *vendor_product; const struct di_edid_vendor_product *vendor_product;
const struct di_edid_video_input_analog *video_input_analog; const struct di_edid_video_input_analog *video_input_analog;
const struct di_edid_video_input_digital *video_input_digital; const struct di_edid_video_input_digital *video_input_digital;
@ -1249,56 +631,7 @@ main(int argc, char *argv[])
const struct di_edid_standard_timing *const *standard_timings; const struct di_edid_standard_timing *const *standard_timings;
const struct di_edid_detailed_timing_def *const *detailed_timing_defs; const struct di_edid_detailed_timing_def *const *detailed_timing_defs;
const struct di_edid_display_descriptor *const *display_descs; const struct di_edid_display_descriptor *const *display_descs;
const struct di_edid_ext *const *exts;
const char *failure_msg;
size_t i; size_t i;
int opt;
in = stdin;
while (1) {
int option_index = 0;
opt = getopt_long(argc, argv, "h", long_options, &option_index);
if (opt == -1)
break;
switch (opt) {
case 'h':
usage();
return -1;
default:
usage();
return -1;
}
}
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)) {
perror("fread failed");
return 1;
} else if (size >= sizeof(raw)) {
fprintf(stderr, "input too large\n");
return 1;
}
}
fclose(in);
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("Block 0, Base EDID:\n");
printf(" EDID Structure Version & Revision: %d.%d\n", printf(" EDID Structure Version & Revision: %d.%d\n",
@ -1515,46 +848,4 @@ main(int argc, char *argv[])
for (i = 0; display_descs[i] != NULL; i++) { for (i = 0; display_descs[i] != NULL; i++) {
print_display_desc(edid, display_descs[i]); print_display_desc(edid, display_descs[i]);
} }
exts = di_edid_get_extensions(edid);
for (i = 0; exts[i] != NULL; i++);
if (i > 0) {
printf(" Extension blocks: %zu\n", i);
}
printf("Checksum: 0x%02hhx\n", raw[edid_checksum_index(0)]);
for (i = 0; exts[i] != NULL; i++) {
print_ext(exts[i], i);
printf("Checksum: 0x%02hhx\n", raw[edid_checksum_index(i + 1)]);
}
printf("\n----------------\n\n");
failure_msg = di_info_get_failure_msg(info);
if (failure_msg) {
printf("Failures:\n\n%s", failure_msg);
printf("EDID conformity: FAIL\n");
} else {
printf("EDID conformity: PASS\n");
}
if (contains_uncommon_feature.color_point_descriptor) {
fprintf(stderr, "The EDID blob contains an uncommon Color "
"Point Descriptor. Please share the EDID blob "
"with upstream!\n");
}
if (contains_uncommon_feature.color_management_data) {
fprintf(stderr, "The EDID blob contains an uncommon Color "
"Management Data Descriptor. Please share the "
"EDID blob with upstream!\n");
}
if (contains_uncommon_feature.cta_transfer_characteristics) {
fprintf(stderr, "The EDID blob contains an uncommon CTA VESA "
"Display Transfer Characteristic data block. "
"Please share the EDID blob with upstream!\n");
}
di_info_destroy(info);
return failure_msg ? 254 : 0;
} }

184
di-edid-decode/main.c Normal file
View file

@ -0,0 +1,184 @@
#include <assert.h>
#include <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <libdisplay-info/displayid.h>
#include <libdisplay-info/edid.h>
#include <libdisplay-info/info.h>
#include "di-edid-decode.h"
struct uncommon_features uncommon_features = {0};
static const struct option long_options[] = {
{ "help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 }
};
static void usage(void)
{
fprintf(stderr, "Usage: di-edid-decode <options> [in]\n"
" [in]: EDID file to parse. Read from standard input (stdin),\n"
" if none given.\n"
"Example : di-edid-decode /sys/class/drm/card0-DP-2/edid \n"
"\nOptions:\n"
"-h, --help display the help message\n");
}
static const char *
ext_tag_name(enum di_edid_ext_tag tag)
{
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_DPVL:
return "Digital Packet Video Link Extension";
case DI_EDID_EXT_BLOCK_MAP:
return "Block Map Extension Block";
case DI_EDID_EXT_VENDOR:
return "Manufacturer-Specific Extension Block";
case DI_EDID_EXT_DISPLAYID:
return "DisplayID Extension Block";
}
abort();
}
static void
print_ext(const struct di_edid_ext *ext, size_t ext_index)
{
const char *tag_name;
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);
switch (di_edid_ext_get_tag(ext)) {
case DI_EDID_EXT_CEA:
print_cta(di_edid_ext_get_cta(ext));
break;
case DI_EDID_EXT_DISPLAYID:
print_displayid(di_edid_ext_get_displayid(ext));
break;
default:
break; /* Ignore */
}
}
static size_t
edid_checksum_index(size_t block_index)
{
return 128 * (block_index + 1) - 1;
}
int
main(int argc, char *argv[])
{
FILE *in;
static uint8_t raw[32 * 1024];
size_t size = 0;
const struct di_edid *edid;
struct di_info *info;
const struct di_edid_ext *const *exts;
const char *failure_msg;
size_t i;
int opt;
in = stdin;
while (1) {
int option_index = 0;
opt = getopt_long(argc, argv, "h", long_options, &option_index);
if (opt == -1)
break;
switch (opt) {
case 'h':
usage();
return -1;
default:
usage();
return -1;
}
}
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)) {
perror("fread failed");
return 1;
} else if (size >= sizeof(raw)) {
fprintf(stderr, "input too large\n");
return 1;
}
}
fclose(in);
info = di_info_parse_edid(raw, size);
if (!info) {
perror("di_edid_parse failed");
return 1;
}
edid = di_info_get_edid(info);
print_edid(edid);
exts = di_edid_get_extensions(edid);
for (i = 0; exts[i] != NULL; i++);
if (i > 0) {
printf(" Extension blocks: %zu\n", i);
}
printf("Checksum: 0x%02hhx\n", raw[edid_checksum_index(0)]);
for (i = 0; exts[i] != NULL; i++) {
print_ext(exts[i], i);
printf("Checksum: 0x%02hhx\n", raw[edid_checksum_index(i + 1)]);
}
printf("\n----------------\n\n");
failure_msg = di_info_get_failure_msg(info);
if (failure_msg) {
printf("Failures:\n\n%s", failure_msg);
printf("EDID conformity: FAIL\n");
} else {
printf("EDID conformity: PASS\n");
}
if (uncommon_features.color_point_descriptor) {
fprintf(stderr, "The EDID blob contains an uncommon Color "
"Point Descriptor. Please share the EDID blob "
"with upstream!\n");
}
if (uncommon_features.color_management_data) {
fprintf(stderr, "The EDID blob contains an uncommon Color "
"Management Data Descriptor. Please share the "
"EDID blob with upstream!\n");
}
if (uncommon_features.cta_transfer_characteristics) {
fprintf(stderr, "The EDID blob contains an uncommon CTA VESA "
"Display Transfer Characteristic data block. "
"Please share the EDID blob with upstream!\n");
}
di_info_destroy(info);
return failure_msg ? 254 : 0;
}

31
include/di-edid-decode.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef DI_EDID_DECODE_H
#define DI_EDID_DECODE_H
#include <stdbool.h>
struct uncommon_features {
bool color_point_descriptor;
bool color_management_data;
bool cta_transfer_characteristics;
};
extern struct uncommon_features uncommon_features;
struct di_edid;
struct di_edid_detailed_timing_def;
struct di_edid_cta;
struct di_displayid;
void
print_edid(const struct di_edid *edid);
void
print_detailed_timing_def(const struct di_edid_detailed_timing_def *def);
void
print_cta(const struct di_edid_cta *cta);
void
print_displayid(const struct di_displayid *displayid);
#endif

View file

@ -68,7 +68,12 @@ di_dep = declare_dependency(
di_edid_decode = executable( di_edid_decode = executable(
'di-edid-decode', 'di-edid-decode',
'di-edid-decode.c', [
'di-edid-decode/cta.c',
'di-edid-decode/displayid.c',
'di-edid-decode/edid.c',
'di-edid-decode/main.c',
],
dependencies: [di_dep, math], dependencies: [di_dep, math],
install: true, install: true,
) )