From 97a8f16d1f0214e5842caecd0050021d0153e922 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 4 Oct 2022 18:15:16 +0200 Subject: [PATCH] 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 --- di-edid-decode/cta.c | 277 +++++++++ di-edid-decode/displayid.c | 276 +++++++++ di-edid-decode.c => di-edid-decode/edid.c | 721 +--------------------- di-edid-decode/main.c | 184 ++++++ include/di-edid-decode.h | 31 + meson.build | 7 +- 6 files changed, 780 insertions(+), 716 deletions(-) create mode 100644 di-edid-decode/cta.c create mode 100644 di-edid-decode/displayid.c rename di-edid-decode.c => di-edid-decode/edid.c (56%) create mode 100644 di-edid-decode/main.c create mode 100644 include/di-edid-decode.h diff --git a/di-edid-decode/cta.c b/di-edid-decode/cta.c new file mode 100644 index 0000000..aa03882 --- /dev/null +++ b/di-edid-decode/cta.c @@ -0,0 +1,277 @@ +#include +#include +#include +#include + +#include + +#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]); + } +} diff --git a/di-edid-decode/displayid.c b/di-edid-decode/displayid.c new file mode 100644 index 0000000..9f185a2 --- /dev/null +++ b/di-edid-decode/displayid.c @@ -0,0 +1,276 @@ +#include +#include + +#include + +#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 */ + } + } +} diff --git a/di-edid-decode.c b/di-edid-decode/edid.c similarity index 56% rename from di-edid-decode.c rename to di-edid-decode/edid.c index 4fb0808..cc032d2 100644 --- a/di-edid-decode.c +++ b/di-edid-decode/edid.c @@ -3,38 +3,14 @@ #include #include #include -#include -#include -#include #include #include #include -#include -static struct { - bool color_point_descriptor; - bool color_management_data; - bool cta_transfer_characteristics; -} contains_uncommon_feature; +#include "di-edid-decode.h" 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 [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 * 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(); } -static void +void print_detailed_timing_def(const struct di_edid_detailed_timing_def *def) { 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]); } - contains_uncommon_feature.color_point_descriptor = true; + uncommon_features.color_point_descriptor = true; break; case DI_EDID_DISPLAY_DESCRIPTOR_ESTABLISHED_TIMINGS_III: 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 a2 : %.2f\n", color_management_data->blue_a2); - contains_uncommon_feature.color_management_data = true; + uncommon_features.color_management_data = true; break; default: printf("\n"); 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 * @@ -663,578 +614,9 @@ display_color_type_name(enum di_edid_display_color_type type) abort(); } -static void -printf_cta_svds(const struct di_cta_svd *const *svds) +void +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_video_input_analog *video_input_analog; 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_detailed_timing_def *const *detailed_timing_defs; const struct di_edid_display_descriptor *const *display_descs; - 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); printf("Block 0, Base EDID:\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++) { 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; } diff --git a/di-edid-decode/main.c b/di-edid-decode/main.c new file mode 100644 index 0000000..bb3d9e7 --- /dev/null +++ b/di-edid-decode/main.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 [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; +} diff --git a/include/di-edid-decode.h b/include/di-edid-decode.h new file mode 100644 index 0000000..c8fece7 --- /dev/null +++ b/include/di-edid-decode.h @@ -0,0 +1,31 @@ +#ifndef DI_EDID_DECODE_H +#define DI_EDID_DECODE_H + +#include + +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 diff --git a/meson.build b/meson.build index 2bcbbdc..3e0f7ac 100644 --- a/meson.build +++ b/meson.build @@ -68,7 +68,12 @@ di_dep = declare_dependency( di_edid_decode = executable( '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], install: true, )