| #include <cctype> |
| #include <cstring> |
| |
| #include <linux/v4l2-subdev.h> |
| |
| #include "compiler.h" |
| #include "v4l2-ctl.h" |
| |
| /* |
| * The 24-bit IEEE Registration Identifier for the HDMI-LLC Vendor |
| * Specific Data Block. |
| */ |
| #define HDMI_VSDB_EXT_TAG 0x000c03 |
| /* |
| * The 24-bit IEEE Registration Identifier for the HDMI-Forum Vendor |
| * Specific Data Block. |
| */ |
| #define HF_VSDB_EXT_TAG 0xc45dd8 |
| |
| #define VID_CAP_EXT_TAG 0 |
| #define COLORIMETRY_EXT_TAG 5 |
| #define HDR_MD_EXT_TAG 6 |
| |
| #define VSDB_TAG 3 |
| #define SPEAKER_TAG 4 |
| #define EXTENDED_TAG 7 |
| |
| enum format { |
| HEX, |
| RAW, |
| CARRAY |
| }; |
| |
| static struct v4l2_edid sedid; |
| static char *file_in; |
| |
| static struct v4l2_edid gedid; |
| static struct v4l2_edid info_edid; |
| static char *file_out; |
| static enum format gformat; |
| static enum format sformat; |
| static unsigned clear_pad; |
| static long phys_addr = -1; |
| |
| static __u8 toggle_cta861_hdr_flags; |
| #define CTA861_HDR_UNDERSCAN (1 << 6) |
| #define CTA861_HDR_AUDIO (1 << 6) |
| #define CTA861_HDR_YCBCR444 (1 << 5) |
| #define CTA861_HDR_YCBCR422 (1 << 4) |
| |
| static __u8 toggle_speaker1_flags; |
| #define SPEAKER1_FL_FR (1 << 0) |
| #define SPEAKER1_LFE (1 << 1) |
| #define SPEAKER1_FC (1 << 2) |
| #define SPEAKER1_BL_BR (1 << 3) |
| #define SPEAKER1_BC (1 << 4) |
| #define SPEAKER1_FLC_FRC (1 << 5) |
| #define SPEAKER1_RLC_RRC (1 << 6) |
| #define SPEAKER1_FLW_FRW (1 << 7) |
| |
| static __u8 toggle_speaker2_flags; |
| #define SPEAKER2_TPFL_TPFR (1 << 0) |
| #define SPEAKER2_TPC (1 << 1) |
| #define SPEAKER2_TPFC (1 << 2) |
| #define SPEAKER2_LS_RS (1 << 3) |
| #define SPEAKER2_LFE2 (1 << 4) |
| #define SPEAKER2_TPBC (1 << 5) |
| #define SPEAKER2_SIL_SIR (1 << 6) |
| #define SPEAKER2_TPSIL_TPSIR (1 << 7) |
| |
| static __u8 toggle_speaker3_flags; |
| #define SPEAKER3_TPBL_TPBR (1 << 0) |
| #define SPEAKER3_BTFC (1 << 1) |
| #define SPEAKER3_BTFL_BTFR (1 << 2) |
| #define SPEAKER3_TPLS_TPRS (1 << 3) |
| |
| static __u8 toggle_hdmi_vsdb_dc_flags; |
| #define HDMI_VSDB_Y444_BIT (1 << 3) |
| #define HDMI_VSDB_30_BIT (1 << 4) |
| #define HDMI_VSDB_36_BIT (1 << 5) |
| #define HDMI_VSDB_48_BIT (1 << 6) |
| static __u8 toggle_hdmi_vsdb_cnc_flags; |
| #define HDMI_VSDB_GRAPHICS (1 << 0) |
| #define HDMI_VSDB_PHOTO (1 << 1) |
| #define HDMI_VSDB_CINEMA (1 << 2) |
| #define HDMI_VSDB_GAME (1 << 3) |
| #define HDMI_VSDB_I_LATENCY (1 << 6) |
| #define HDMI_VSDB_LATENCY (1 << 7) |
| |
| static __u8 toggle_hf_vsdb_flags; |
| #define HF_VSDB_SCSD_PRESENT (1 << 7) |
| |
| static int mod_s_pt = -1; |
| static int mod_s_it = -1; |
| static int mod_s_ce = -1; |
| static __u8 toggle_vid_cap_flags; |
| #define VID_CAP_QS (1 << 6) |
| #define VID_CAP_QY (1 << 7) |
| |
| static __u8 toggle_colorimetry_flags1; |
| #define COLORIMETRY_XVYCC601 (1 << 0) |
| #define COLORIMETRY_XVYCC709 (1 << 1) |
| #define COLORIMETRY_SYCC (1 << 2) |
| #define COLORIMETRY_OPYCC (1 << 3) |
| #define COLORIMETRY_OPRGB (1 << 4) |
| #define COLORIMETRY_BT2020CYCC (1 << 5) |
| #define COLORIMETRY_BT2020YCC (1 << 6) |
| #define COLORIMETRY_BT2020RGB (1 << 7) |
| |
| static __u8 toggle_colorimetry_flags2; |
| #define COLORIMETRY_DCIP3 (1 << 0) |
| |
| static __u8 toggle_hdr_md_flags; |
| #define HDR_MD_SDR (1 << 0) |
| #define HDR_MD_HDR (1 << 1) |
| #define HDR_MD_SMPTE_2084 (1 << 2) |
| #define HDR_MD_HLG (1 << 3) |
| |
| void edid_usage() |
| { |
| printf("\nEDID options:\n" |
| " --set-edid pad=<pad>[,type=<type>|file=<file>][,format=<fmt>][modifiers]\n" |
| " <pad> is the input index for which to set the EDID.\n" |
| " <type> can be one of:\n" |
| " list: list all EDID types\n" |
| " vga: Base Block supporting VGA interface (1920x1200p60)\n" |
| " dvid: Base Block supporting DVI-D interface (1920x1200p60)\n" |
| " hdmi: CTA-861 with HDMI support up to 1080p60\n" |
| " hdmi-4k-170mhz: CTA-861 with HDMI support up to 1080p60 or 4kp30 4:2:0\n" |
| " hdmi-4k-300mhz: CTA-861 with HDMI support up to 4kp30\n" |
| " hdmi-4k-600mhz: CTA-861 with HDMI support up to 4kp60\n" |
| " hdmi-4k-600mhz-with-displayid: Block Map Extension Block, CTA-861 with\n" |
| " HDMI support up to 4kp60, DisplayID Extension Block\n" |
| " displayport: DisplayID supporting a DisplayPort interface (1920x1200)\n" |
| " displayport-with-cta861: DisplayID supporting a DisplayPort interface,\n" |
| " CTA-861 Extension Block (1080p60)\n" |
| "\n" |
| " If <file> is '-', then the data is read from stdin, otherwise it is\n" |
| " read from the given file. The file format must be in hex as in get-edid.\n" |
| " The 'type' or 'file' arguments are mutually exclusive. One of the two\n" |
| " must be specified.\n" |
| " <fmt> is one of:\n" |
| " hex: hex numbers in ascii text (default)\n" |
| " raw: raw binary EDID content\n" |
| "\n" |
| " [modifiers] is a comma-separate list of EDID modifiers:\n" |
| "\n" |
| " CTA-861 Header modifiers:\n" |
| " underscan: toggle the underscan bit.\n" |
| " audio: toggle the audio bit.\n" |
| " ycbcr444: toggle the YCbCr 4:4:4 bit.\n" |
| " ycbcr422: toggle the YCbCr 4:2:2 bit.\n" |
| "\n" |
| " Speaker Allocation Data Block modifiers:\n" |
| " fl-fr: Front Left/Right.\n" |
| " lfe: Low Frequency Effects.\n" |
| " fc: Front Center.\n" |
| " bl-br: Back Left/Right.\n" |
| " bc: Back Center.\n" |
| " rlc-frc: Front Left/Right of Center.\n" |
| " rlc-rrc: Rear Left/Right of Center.\n" |
| " flw-frw: Front Left/Right Wide.\n" |
| " tpfl-tpfr: Top Front Left/Right.\n" |
| " tpc: Top Center.\n" |
| " tpfc: Top Front Center.\n" |
| " ls-rs: Left/Right Surround.\n" |
| " lfe2: Low Frequency Effects 2.\n" |
| " tpbc: Top Back Center.\n" |
| " sil-sir: Side Left/Right\n" |
| " tpsil-tpsir: Top Side Left/Right.\n" |
| " tpbl-tpbr: Top Back Left/Right.\n" |
| " btfc: Bottom Front Center.\n" |
| " btfl-btbr: Bottom Front Left/Right.\n" |
| " tpls-tprs: Top Left/Right Surround.\n" |
| "\n" |
| " HDMI Vendor-Specific Data Block modifiers:\n" |
| " pa=<pa>: change the physical address.\n" |
| " y444: toggle the YCbCr 4:4:4 Deep Color bit.\n" |
| " 30-bit: toggle the 30 bits/pixel bit.\n" |
| " 36-bit: toggle the 36 bits/pixel bit.\n" |
| " 48-bit: toggle the 48 bits/pixel bit.\n" |
| " graphics: toggle the Graphics Content Type bit.\n" |
| " photo: toggle the Photo Content Type bit.\n" |
| " cinema: toggle the Cinema Content Type bit.\n" |
| " game: toggle the Game Content Type bit.\n" |
| "\n" |
| " HDMI Forum Vendor-Specific Data Block modifiers:\n" |
| " scdc: toggle the SCDC Present bit.\n" |
| "\n" |
| " CTA-861 Video Capability Descriptor modifiers:\n" |
| " qy: toggle the QY YCC Quantization Range bit.\n" |
| " qs: toggle the QS RGB Quantization Range bit.\n" |
| " s-pt=<0-3>: set the PT Preferred Format Over/underscan bits.\n" |
| " s-it=<0-3>: set the IT Over/underscan bits.\n" |
| " s-ce=<0-3>: set the CE Over/underscan bits.\n" |
| "\n" |
| " CTA-861 Colorimetry Data Block modifiers:\n" |
| " xvycc-601: toggle the xvYCC 601 bit.\n" |
| " xvycc-709: toggle the xvYCC 709 bit.\n" |
| " sycc: toggle the sYCC 601 bit.\n" |
| " opycc: toggle the opYCC 601 bit.\n" |
| " oprgb: toggle the opRGB bit.\n" |
| " bt2020-rgb: toggle the BT2020 RGB bit.\n" |
| " bt2020-ycc: toggle the BT2020 YCC bit.\n" |
| " bt2020-cycc: toggle the BT2020 cYCC bit.\n" |
| " dci-p3: toggle the DCI-P3 bit.\n" |
| "\n" |
| " CTA-861 HDR Static Metadata Data Block modifiers:\n" |
| " sdr: toggle the Traditional gamma SDR bit.\n" |
| " hdr: toggle the Traditional gamma HDR bit.\n" |
| " smpte2084: toggle the SMPTE ST 2084 bit.\n" |
| " hlg: toggle the Hybrid Log-Gamma bit.\n" |
| " --clear-edid <pad> clear the EDID for the input index <pad>.\n" |
| " --info-edid <pad> print the current EDID's modifiers\n" |
| " <pad> is the input or output index for which to get the EDID.\n" |
| " --show-edid [type=<type>|file=<file>][,format=<fmt>][modifiers]\n" |
| " Same as --set-edid, but only dumps the resulting EDID in hex\n" |
| " instead of actually setting the EDID.\n" |
| " --get-edid pad=<pad>,startblock=<startblock>,blocks=<blocks>,format=<fmt>,file=<file>\n" |
| " <pad> is the input or output index for which to get the EDID.\n" |
| " <startblock> is the first block number you want to read. Default 0.\n" |
| " <blocks> is the number of blocks you want to read. Default is\n" |
| " all blocks.\n" |
| " <fmt> is one of:\n" |
| " hex: hex numbers in ascii text (default)\n" |
| " raw: can be piped directly into the edid-decode tool\n" |
| " carray: c-program struct\n" |
| " If <file> is '-' or not the 'file' argument is not supplied, then the data\n" |
| " is written to stdout.\n" |
| " --fix-edid-checksums\n" |
| " If specified then any checksum errors will be fixed silently.\n" |
| ); |
| } |
| |
| static void edid_add_block(struct v4l2_edid *e) |
| { |
| e->blocks++; |
| if (e->blocks > 256) { |
| fprintf(stderr, "edid file error: too long\n"); |
| free(e->edid); |
| e->edid = nullptr; |
| std::exit(EXIT_FAILURE); |
| } |
| e->edid = static_cast<unsigned char *>(realloc(e->edid, e->blocks * 128)); |
| } |
| |
| static void read_edid_file(FILE *f, struct v4l2_edid *e) |
| { |
| static const char *ignore_chars = ",:;"; |
| char value[3] = { 0 }; |
| char buf[256]; |
| unsigned i = 0; |
| int c; |
| |
| fseek(f, SEEK_SET, 0); |
| e->edid = nullptr; |
| e->blocks = 0; |
| |
| /* |
| * Skip the first line if it matches the edid-decode output. |
| * After that first line the hex dump starts, and that's what we |
| * want to use here. |
| * |
| * This makes it possible to use the edid-decode output as EDID |
| * file. |
| */ |
| if (fgets(buf, sizeof(buf), f) && |
| !strstr(buf, "EDID (hex):") && |
| !strstr(buf, "edid-decode (hex):")) |
| fseek(f, SEEK_SET, 0); |
| |
| while ((c = fgetc(f)) != EOF) { |
| if (sformat == RAW) { |
| if (i % 256 == 0) |
| edid_add_block(e); |
| e->edid[i / 2] = c; |
| i += 2; |
| continue; |
| } |
| /* Handle '0x' prefix */ |
| if ((i & 1) && value[0] == '0' && (c == 'x' || c == 'X')) |
| i--; |
| if (isspace(c) || strchr(ignore_chars, c)) |
| continue; |
| if (!isxdigit(c)) |
| break; |
| if (i & 0x01) { |
| value[1] = c; |
| if (i % 256 == 1) |
| edid_add_block(e); |
| e->edid[i / 2] = strtoul(value, nullptr, 16); |
| } else { |
| value[0] = c; |
| } |
| i++; |
| } |
| } |
| |
| static unsigned char crc_calc(const unsigned char *b) |
| { |
| unsigned char sum = 0; |
| int i; |
| |
| for (i = 0; i < 127; i++) |
| sum += b[i]; |
| return 256 - sum; |
| } |
| |
| static bool crc_ok(const unsigned char *b) |
| { |
| return crc_calc(b) == b[127]; |
| } |
| |
| static void fix_edid(struct v4l2_edid *e) |
| { |
| for (unsigned b = 0; b < e->blocks; b++) { |
| unsigned char *buf = e->edid + 128 * b; |
| |
| if (!crc_ok(buf)) |
| buf[127] = crc_calc(buf); |
| } |
| } |
| |
| static bool verify_edid(struct v4l2_edid *e) |
| { |
| bool valid = true; |
| |
| for (unsigned b = 0; b < e->blocks; b++) { |
| const unsigned char *buf = e->edid + 128 * b; |
| |
| if (!crc_ok(buf)) { |
| fprintf(stderr, "Block %u has a checksum error (should be 0x%02x)\n", |
| b, crc_calc(buf)); |
| valid = false; |
| } |
| } |
| return valid; |
| } |
| |
| static void hexdumpedid(FILE *f, struct v4l2_edid *e) |
| { |
| for (unsigned b = 0; b < e->blocks; b++) { |
| unsigned char *buf = e->edid + 128 * b; |
| |
| if (b) |
| fprintf(f, "\n"); |
| for (unsigned i = 0; i < 128; i += 0x10) { |
| fprintf(f, "%02x", buf[i]); |
| for (unsigned j = 1; j < 0x10; j++) { |
| fprintf(f, " %02x", buf[i + j]); |
| } |
| fprintf(f, "\n"); |
| } |
| if (!crc_ok(buf)) |
| fprintf(f, "Block %u has a checksum error (should be 0x%02x)\n", |
| b, crc_calc(buf)); |
| } |
| } |
| |
| static void rawdumpedid(FILE *f, struct v4l2_edid *e) |
| { |
| for (unsigned b = 0; b < e->blocks; b++) { |
| unsigned char *buf = e->edid + 128 * b; |
| |
| for (unsigned i = 0; i < 128; i++) |
| fprintf(f, "%c", buf[i]); |
| if (!crc_ok(buf)) |
| fprintf(stderr, "Block %u has a checksum error (should be %02x)\n", |
| b, crc_calc(buf)); |
| } |
| } |
| |
| static void carraydumpedid(FILE *f, struct v4l2_edid *e) |
| { |
| fprintf(f, "unsigned char edid[] = {\n"); |
| for (unsigned b = 0; b < e->blocks; b++) { |
| unsigned char *buf = e->edid + 128 * b; |
| |
| if (b) |
| fprintf(f, "\n"); |
| for (unsigned i = 0; i < 128; i += 8) { |
| fprintf(f, "\t0x%02x,", buf[i]); |
| for (unsigned j = 1; j < 8; j++) { |
| fprintf(f, " 0x%02x,", buf[i + j]); |
| } |
| fprintf(f, "\n"); |
| } |
| if (!crc_ok(buf)) |
| fprintf(f, "\t/* Block %u has a checksum error (should be 0x%02x) */\n", |
| b, crc_calc(buf)); |
| } |
| fprintf(f, "};\n"); |
| } |
| |
| static void printedid(FILE *f, struct v4l2_edid *e, enum format gf) |
| { |
| switch (gf) { |
| default: |
| case HEX: |
| hexdumpedid(f, e); |
| break; |
| case RAW: |
| rawdumpedid(f, e); |
| break; |
| case CARRAY: |
| carraydumpedid(f, e); |
| break; |
| } |
| } |
| |
| static unsigned find_cta_offset(const unsigned char *edid, unsigned size) |
| { |
| for (unsigned i = 1; i < size / 128; i++) |
| if (edid[i * 128] == 0x02) |
| return i * 128; |
| return 0; |
| } |
| |
| static int get_edid_tag_location(const unsigned char *edid, unsigned size, |
| unsigned char want_tag, __u32 ext_tag) |
| { |
| unsigned offset = find_cta_offset(edid, size); |
| unsigned char d; |
| |
| if (!offset) |
| return -1; |
| edid += offset; |
| |
| /* search tag */ |
| d = edid[0x02] & 0x7f; |
| if (d <= 4) |
| return -1; |
| |
| int i = 0x04; |
| int end = 0x00 + d; |
| |
| do { |
| unsigned char tag = edid[i] >> 5; |
| unsigned char len = edid[i] & 0x1f; |
| |
| if (tag != want_tag || i + len > end) { |
| i += len + 1; |
| continue; |
| } |
| |
| /* |
| * Tag 3 (Vendor-Specific Data Block) has |
| * a 24 bit IEEE identifier. |
| */ |
| if (tag == VSDB_TAG && len >= 3 && |
| edid[i + 1] == (ext_tag & 0xff) && |
| edid[i + 2] == ((ext_tag >> 8) & 0xff) && |
| edid[i + 3] == ((ext_tag >> 16) & 0xff)) |
| return offset + i; |
| /* |
| * Tag 7 has an extended tag, others (0-2, 4-6) |
| * have no identifiers. |
| */ |
| if ((tag < EXTENDED_TAG && tag != VSDB_TAG) || |
| (tag == EXTENDED_TAG && len >= 1 && edid[i + 1] == ext_tag)) |
| return offset + i; |
| i += len + 1; |
| } while (i < end); |
| return -1; |
| } |
| |
| static int get_edid_cta861_hdr_location(const unsigned char *edid, unsigned size) |
| { |
| unsigned offset = find_cta_offset(edid, size); |
| |
| return offset ? offset + 3 : -1; |
| } |
| |
| static int get_edid_spa_location(const unsigned char *edid, unsigned size) |
| { |
| int loc = get_edid_tag_location(edid, size, VSDB_TAG, HDMI_VSDB_EXT_TAG); |
| |
| if (loc < 0) |
| return loc; |
| |
| return (edid[loc] & 0x1f) >= 5 ? loc + 4 : -1; |
| } |
| |
| static int get_edid_hdmi_vsdb_location(const unsigned char *edid, unsigned size) |
| { |
| int loc = get_edid_tag_location(edid, size, VSDB_TAG, HDMI_VSDB_EXT_TAG); |
| |
| if (loc < 0) |
| return loc; |
| |
| return (edid[loc] & 0x1f) >= 5 ? loc : -1; |
| } |
| |
| static int get_edid_hf_vsdb_location(const unsigned char *edid, unsigned size) |
| { |
| int loc = get_edid_tag_location(edid, size, VSDB_TAG, HF_VSDB_EXT_TAG); |
| |
| if (loc < 0) |
| return loc; |
| |
| return (edid[loc] & 0x1f) >= 6 ? loc + 5 : -1; |
| } |
| |
| static int get_edid_speaker_location(const unsigned char *edid, unsigned size) |
| { |
| int loc = get_edid_tag_location(edid, size, SPEAKER_TAG, 0); |
| |
| if (loc < 0) |
| return loc; |
| |
| return (edid[loc] & 0x1f) >= 3 ? loc + 1 : -1; |
| } |
| |
| static int get_edid_vid_cap_location(const unsigned char *edid, unsigned size) |
| { |
| int loc = get_edid_tag_location(edid, size, EXTENDED_TAG, VID_CAP_EXT_TAG); |
| |
| if (loc < 0) |
| return loc; |
| |
| return (edid[loc] & 0x1f) >= 2 ? loc + 2 : -1; |
| } |
| |
| static int get_edid_colorimetry_location(const unsigned char *edid, unsigned size) |
| { |
| int loc = get_edid_tag_location(edid, size, EXTENDED_TAG, COLORIMETRY_EXT_TAG); |
| |
| if (loc < 0) |
| return loc; |
| |
| return (edid[loc] & 0x1f) >= 3 ? loc + 2 : -1; |
| } |
| |
| static int get_edid_hdr_md_location(const unsigned char *edid, unsigned size) |
| { |
| int loc = get_edid_tag_location(edid, size, EXTENDED_TAG, HDR_MD_EXT_TAG); |
| |
| if (loc < 0) |
| return loc; |
| |
| return (edid[loc] & 0x1f) >= 3 ? loc + 2 : -1; |
| } |
| |
| static void set_edid_phys_addr(unsigned char *edid, unsigned size, unsigned short phys_addr) |
| { |
| int loc = get_edid_spa_location(edid, size); |
| unsigned char sum = 0; |
| int i; |
| |
| if (loc < 0) |
| return; |
| edid[loc] = phys_addr >> 8; |
| edid[loc + 1] = phys_addr & 0xff; |
| loc &= ~0x7f; |
| |
| for (i = loc; i < loc + 127; i++) |
| sum += edid[i]; |
| edid[i] = 256 - sum; |
| } |
| |
| static unsigned short get_edid_phys_addr(const unsigned char *edid, unsigned size) |
| { |
| int loc = get_edid_spa_location(edid, size); |
| |
| if (loc < 0) |
| return 0xffff; |
| return (edid[loc] << 8) | edid[loc + 1]; |
| } |
| |
| static void print_edid_mods(const struct v4l2_edid *e) |
| { |
| unsigned short pa = get_edid_phys_addr(e->edid, e->blocks * 128); |
| int loc; |
| |
| loc = get_edid_cta861_hdr_location(e->edid, e->blocks * 128); |
| if (loc >= 0) { |
| __u8 v = e->edid[loc]; |
| |
| printf("\nCTA-861 Header\n"); |
| printf(" IT Formats Underscanned: %s\n", (v & CTA861_HDR_UNDERSCAN) ? "yes" : "no"); |
| printf(" Audio: %s\n", (v & CTA861_HDR_AUDIO) ? "yes" : "no"); |
| printf(" YCbCr 4:4:4: %s\n", (v & CTA861_HDR_YCBCR444) ? "yes" : "no"); |
| printf(" YCbCr 4:2:2: %s\n", (v & CTA861_HDR_YCBCR422) ? "yes" : "no"); |
| } |
| loc = get_edid_speaker_location(e->edid, e->blocks * 128); |
| if (loc >= 0) { |
| __u8 v = e->edid[loc]; |
| |
| printf("\nSpeaker Allocation Data Block\n"); |
| printf(" FL/FR: %s\n", (v & SPEAKER1_FL_FR) ? "yes" : "no"); |
| printf(" LFE: %s\n", (v & SPEAKER1_LFE) ? "yes" : "no"); |
| printf(" FC: %s\n", (v & SPEAKER1_FC) ? "yes" : "no"); |
| printf(" BL/BR: %s\n", (v & SPEAKER1_BL_BR) ? "yes" : "no"); |
| printf(" BC: %s\n", (v & SPEAKER1_BC) ? "yes" : "no"); |
| printf(" FLC/FRC: %s\n", (v & SPEAKER1_FLC_FRC) ? "yes" : "no"); |
| printf(" RLC/RRC: %s\n", (v & SPEAKER1_RLC_RRC) ? "yes" : "no"); |
| printf(" FLW/FRW: %s\n", (v & SPEAKER1_FLW_FRW) ? "yes" : "no"); |
| |
| v = e->edid[loc + 1]; |
| printf(" TpFL/TpFR: %s\n", (v & SPEAKER2_TPFL_TPFR) ? "yes" : "no"); |
| printf(" TpC: %s\n", (v & SPEAKER2_TPC) ? "yes" : "no"); |
| printf(" TpFC: %s\n", (v & SPEAKER2_TPFC) ? "yes" : "no"); |
| printf(" LS/RS: %s\n", (v & SPEAKER2_LS_RS) ? "yes" : "no"); |
| printf(" LFE2: %s\n", (v & SPEAKER2_LFE2) ? "yes" : "no"); |
| printf(" TpBC: %s\n", (v & SPEAKER2_TPBC) ? "yes" : "no"); |
| printf(" SiL/SiR: %s\n", (v & SPEAKER2_SIL_SIR) ? "yes" : "no"); |
| printf(" TpSiL/TpSiR: %s\n", (v & SPEAKER2_TPSIL_TPSIR) ? "yes" : "no"); |
| |
| v = e->edid[loc + 2]; |
| printf(" TpBL/TpBR: %s\n", (v & SPEAKER3_TPBL_TPBR) ? "yes" : "no"); |
| printf(" BtFC: %s\n", (v & SPEAKER3_BTFC) ? "yes" : "no"); |
| printf(" BtLS/BtRS: %s\n", (v & SPEAKER3_BTFL_BTFR) ? "yes" : "no"); |
| printf(" TpLS/TpRS: %s\n", (v & SPEAKER3_TPLS_TPRS) ? "yes" : "no"); |
| } |
| loc = get_edid_hdmi_vsdb_location(e->edid, e->blocks * 128); |
| if (loc >= 0) { |
| __u8 len = e->edid[loc] & 0x1f; |
| __u8 v = len >= 7 ? e->edid[loc + 7] : 0; |
| |
| printf("\nHDMI Vendor-Specific Data Block\n"); |
| if (v) |
| printf(" Max TMDS Clock: %u MHz\n", v * 5); |
| printf(" Physical Address: %x.%x.%x.%x\n", |
| pa >> 12, (pa >> 8) & 0xf, (pa >> 4) & 0xf, pa & 0xf); |
| if (len >= 6) { |
| v = e->edid[loc + 6]; |
| printf(" YCbCr 4:4:4 Deep Color: %s\n", (v & HDMI_VSDB_Y444_BIT) ? "yes" : "no"); |
| printf(" 30-bit: %s\n", (v & HDMI_VSDB_30_BIT) ? "yes" : "no"); |
| printf(" 36-bit: %s\n", (v & HDMI_VSDB_36_BIT) ? "yes" : "no"); |
| printf(" 48-bit: %s\n", (v & HDMI_VSDB_48_BIT) ? "yes" : "no"); |
| } |
| if (len >= 8) { |
| v = e->edid[loc + 8]; |
| printf(" Graphics: %s\n", (v & HDMI_VSDB_GRAPHICS) ? "yes" : "no"); |
| printf(" Photo: %s\n", (v & HDMI_VSDB_PHOTO) ? "yes" : "no"); |
| printf(" Cinema: %s\n", (v & HDMI_VSDB_CINEMA) ? "yes" : "no"); |
| printf(" Game: %s\n", (v & HDMI_VSDB_GAME) ? "yes" : "no"); |
| if ((v & HDMI_VSDB_LATENCY) && len >= 10) { |
| __u8 lat = e->edid[loc + 9]; |
| if (lat == 255) |
| printf(" Video Latency: unsupported\n"); |
| else if (lat > 0) |
| printf(" Video Latency: %u.%ums\n", |
| (lat - 1) / 2, ((lat - 1) & 1) ? 0 : 5); |
| lat = e->edid[loc + 10]; |
| if (lat == 255) |
| printf(" Audio Latency: unsupported\n"); |
| else if (lat > 0) |
| printf(" Audio Latency: %u.%ums\n", |
| (lat - 1) / 2, ((lat - 1) & 1) ? 0 : 5); |
| } |
| if ((v & HDMI_VSDB_I_LATENCY) && len >= 12) { |
| __u8 lat = e->edid[loc + 11]; |
| if (lat == 255) |
| printf(" IL Video Latency: unsupported\n"); |
| else if (lat > 0) |
| printf(" IL Video Latency: %u.%ums\n", |
| (lat - 1) / 2, ((lat - 1) & 1) ? 0 : 5); |
| lat = e->edid[loc + 12]; |
| if (lat == 255) |
| printf(" IL Audio Latency: unsupported\n"); |
| else if (lat > 0) |
| printf(" IL Audio Latency: %u.%ums\n", |
| (lat - 1) / 2, ((lat - 1) & 1) ? 0 : 5); |
| } |
| } |
| } |
| loc = get_edid_hf_vsdb_location(e->edid, e->blocks * 128); |
| if (loc >= 0) { |
| __u8 v = e->edid[loc]; |
| |
| printf("\nHDMI Forum Vendor-Specific Data Block\n"); |
| if (v) |
| printf(" Max TMDS Character Rate: %u MHz\n", v * 5); |
| v = e->edid[loc + 1]; |
| printf(" SCDC Present: %s\n", (v & HF_VSDB_SCSD_PRESENT) ? "yes" : "no"); |
| } |
| loc = get_edid_vid_cap_location(e->edid, e->blocks * 128); |
| if (loc >= 0) { |
| static constexpr const char *pt_scan[] = { |
| "No Data", |
| "Always Overscanned", |
| "Always Underscanned", |
| "Supports both over- and underscan" |
| }; |
| static constexpr const char *it_scan[] = { |
| "IT Formats not supported", |
| "Always Overscanned", |
| "Always Underscanned", |
| "Supports both over- and underscan" |
| }; |
| static constexpr const char *ce_scan[] = { |
| "CE Formats not supported", |
| "Always Overscanned", |
| "Always Underscanned", |
| "Supports both over- and underscan" |
| }; |
| __u8 v = e->edid[loc]; |
| |
| printf("\nCTA-861 Video Capability Descriptor\n"); |
| printf(" RGB Quantization Range: %s\n", (v & VID_CAP_QS) ? "yes" : "no"); |
| printf(" YCC Quantization Range: %s\n", (v & VID_CAP_QY) ? "yes" : "no"); |
| printf(" PT: %s\n", pt_scan[(v >> 4) & 3]); |
| printf(" IT: %s\n", it_scan[(v >> 2) & 3]); |
| printf(" CE: %s\n", ce_scan[(v >> 0) & 3]); |
| } |
| loc = get_edid_colorimetry_location(e->edid, e->blocks * 128); |
| if (loc >= 0) { |
| __u8 v1 = e->edid[loc]; |
| __u8 v2 = e->edid[loc + 1]; |
| |
| printf("\nCTA-861 Colorimetry Data Block\n"); |
| printf(" xvYCC 601: %s\n", (v1 & COLORIMETRY_XVYCC601) ? "yes" : "no"); |
| printf(" xvYCC 709: %s\n", (v1 & COLORIMETRY_XVYCC709) ? "yes" : "no"); |
| printf(" sYCC: %s\n", (v1 & COLORIMETRY_SYCC) ? "yes" : "no"); |
| printf(" opRGB: %s\n", (v1 & COLORIMETRY_OPRGB) ? "yes" : "no"); |
| printf(" opYCC: %s\n", (v1 & COLORIMETRY_OPYCC) ? "yes" : "no"); |
| printf(" BT.2020 RGB: %s\n", (v1 & COLORIMETRY_BT2020RGB) ? "yes" : "no"); |
| printf(" BT.2020 YCC: %s\n", (v1 & COLORIMETRY_BT2020YCC) ? "yes" : "no"); |
| printf(" BT.2020 cYCC: %s\n", (v1 & COLORIMETRY_BT2020CYCC) ? "yes" : "no"); |
| printf(" DCI-P3: %s\n", (v2 & COLORIMETRY_DCIP3) ? "yes" : "no"); |
| } |
| loc = get_edid_hdr_md_location(e->edid, e->blocks * 128); |
| if (loc >= 0) { |
| __u8 v = e->edid[loc]; |
| |
| printf("\nCTA-861 HDR Static Metadata Data Block\n"); |
| printf(" SDR (Traditional Gamma): %s\n", (v & HDR_MD_SDR) ? "yes" : "no"); |
| printf(" HDR (Traditional Gamma): %s\n", (v & HDR_MD_HDR) ? "yes" : "no"); |
| printf(" SMPTE 2084: %s\n", (v & HDR_MD_SMPTE_2084) ? "yes" : "no"); |
| printf(" Hybrid Log-Gamma: %s\n", (v & HDR_MD_HLG) ? "yes" : "no"); |
| } |
| } |
| |
| static unsigned short parse_phys_addr(const char *value) |
| { |
| unsigned p1, p2, p3, p4; |
| |
| if (!std::strchr(value, '.')) |
| return strtoul(value, nullptr, 0); |
| if (sscanf(value, "%x.%x.%x.%x", &p1, &p2, &p3, &p4) != 4) { |
| fprintf(stderr, "Expected a physical address of the form x.x.x.x\n"); |
| return 0xffff; |
| } |
| if (p1 > 0xf || p2 > 0xf || p3 > 0xf || p4 > 0xf) { |
| fprintf(stderr, "Physical address components should never be larger than 0xf\n"); |
| return 0xffff; |
| } |
| return (p1 << 12) | (p2 << 8) | (p3 << 4) | p4; |
| } |
| |
| /****************** EDIDs *****************************/ |
| static uint8_t vga_edid[128] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, |
| 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, |
| 0x22, 0x1a, 0x01, 0x04, 0x08, 0x30, 0x1e, 0x78, |
| 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, |
| 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59, |
| 0x45, 0x59, 0x61, 0x59, 0x81, 0x40, 0x81, 0x80, |
| 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x28, 0x3c, |
| 0x80, 0xa0, 0x70, 0xb0, 0x23, 0x40, 0x30, 0x20, |
| 0x36, 0x00, 0xe0, 0x2c, 0x11, 0x00, 0x00, 0x1a, |
| 0x00, 0x00, 0x00, 0xfd, 0x00, 0x31, 0x55, 0x18, |
| 0x5e, 0x11, 0x04, 0x12, 0x00, 0xf0, 0xf8, 0x58, |
| 0xf0, 0x3c, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x76, |
| 0x67, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, |
| 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x10, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, |
| }; |
| |
| static uint8_t dvid_edid[128] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, |
| 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, |
| 0x22, 0x1a, 0x01, 0x04, 0xa1, 0x30, 0x1e, 0x78, |
| 0x07, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, |
| 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59, |
| 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, |
| 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x28, 0x3c, |
| 0x80, 0xa0, 0x70, 0xb0, 0x23, 0x40, 0x30, 0x20, |
| 0x36, 0x00, 0xe0, 0x2c, 0x11, 0x00, 0x00, 0x1a, |
| 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, |
| 0x5e, 0x11, 0x04, 0x12, 0x00, 0xf0, 0xf8, 0x58, |
| 0xf0, 0x3c, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x64, |
| 0x76, 0x69, 0x2d, 0x64, 0x0a, 0x20, 0x20, 0x20, |
| 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x10, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, |
| }; |
| |
| static uint8_t hdmi_edid[256] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, |
| 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, |
| 0x22, 0x1a, 0x01, 0x03, 0x80, 0x30, 0x1b, 0x78, |
| 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, |
| 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59, |
| 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, |
| 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x02, 0x3a, |
| 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, |
| 0x45, 0x00, 0xe0, 0x0e, 0x11, 0x00, 0x00, 0x1e, |
| 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, |
| 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, |
| 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68, |
| 0x64, 0x6d, 0x69, 0x0a, 0x20, 0x20, 0x20, 0x20, |
| 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x10, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, |
| |
| 0x02, 0x03, 0x30, 0xf1, 0x4c, 0x10, 0x1f, 0x04, |
| 0x13, 0x22, 0x21, 0x20, 0x05, 0x14, 0x02, 0x11, |
| 0x01, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, |
| 0x00, 0x68, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x00, |
| 0x22, 0x01, 0xe2, 0x00, 0xca, 0xe3, 0x05, 0x00, |
| 0x00, 0xe3, 0x06, 0x01, 0x00, 0xe2, 0x0d, 0x10, |
| 0x1a, 0x36, 0x80, 0xa0, 0x70, 0x38, 0x1f, 0x40, |
| 0x30, 0x20, 0x35, 0x00, 0xe0, 0x0e, 0x11, 0x00, |
| 0x00, 0x1a, 0x1a, 0x1d, 0x00, 0x80, 0x51, 0xd0, |
| 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0xe0, 0x0e, |
| 0x11, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, |
| }; |
| |
| static uint8_t hdmi_edid_4k_170[256] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, |
| 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, |
| 0x22, 0x1a, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, |
| 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, |
| 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59, |
| 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, |
| 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x02, 0x3a, |
| 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, |
| 0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e, |
| 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, |
| 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, |
| 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68, |
| 0x64, 0x6d, 0x69, 0x2d, 0x34, 0x6b, 0x2d, 0x31, |
| 0x37, 0x30, 0x0a, 0x20, 0x00, 0x00, 0x00, 0x10, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb9, |
| |
| 0x02, 0x03, 0x36, 0xf1, 0x4c, 0x10, 0x1f, 0x04, |
| 0x13, 0x22, 0x21, 0x20, 0x05, 0x14, 0x02, 0x11, |
| 0x01, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, |
| 0x00, 0x68, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x00, |
| 0x22, 0x01, 0xe2, 0x00, 0xca, 0xe4, 0x0e, 0x5f, |
| 0x5e, 0x5d, 0xe3, 0x05, 0x00, 0x00, 0xe3, 0x06, |
| 0x01, 0x00, 0xe3, 0x0d, 0x10, 0x5f, 0x1a, 0x36, |
| 0x80, 0xa0, 0x70, 0x38, 0x1f, 0x40, 0x30, 0x20, |
| 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1a, |
| 0x1a, 0x1d, 0x00, 0x80, 0x51, 0xd0, 0x1c, 0x20, |
| 0x40, 0x80, 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00, |
| 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, |
| }; |
| |
| static uint8_t hdmi_edid_4k_300[256] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, |
| 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, |
| 0x22, 0x1a, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, |
| 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, |
| 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59, |
| 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, |
| 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x04, 0x74, |
| 0x00, 0x30, 0xf2, 0x70, 0x5a, 0x80, 0xb0, 0x58, |
| 0x8a, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e, |
| 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, |
| 0x87, 0x1e, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, |
| 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68, |
| 0x64, 0x6d, 0x69, 0x2d, 0x34, 0x6b, 0x2d, 0x33, |
| 0x30, 0x30, 0x0a, 0x20, 0x00, 0x00, 0x00, 0x10, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc5, |
| |
| 0x02, 0x03, 0x40, 0xf1, 0x4f, 0x5f, 0x5e, 0x5d, |
| 0x10, 0x1f, 0x04, 0x13, 0x22, 0x21, 0x20, 0x05, |
| 0x14, 0x02, 0x11, 0x01, 0x23, 0x09, 0x07, 0x07, |
| 0x83, 0x01, 0x00, 0x00, 0x6d, 0x03, 0x0c, 0x00, |
| 0x10, 0x00, 0x00, 0x3c, 0x21, 0x00, 0x60, 0x01, |
| 0x02, 0x03, 0x67, 0xd8, 0x5d, 0xc4, 0x01, 0x00, |
| 0x00, 0x00, 0xe2, 0x00, 0xca, 0xe3, 0x05, 0x00, |
| 0x00, 0xe3, 0x06, 0x01, 0x00, 0xe2, 0x0d, 0x5f, |
| 0xa3, 0x66, 0x00, 0xa0, 0xf0, 0x70, 0x1f, 0x80, |
| 0x30, 0x20, 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00, |
| 0x00, 0x1e, 0x1a, 0x36, 0x80, 0xa0, 0x70, 0x38, |
| 0x1f, 0x40, 0x30, 0x20, 0x35, 0x00, 0xc0, 0x1c, |
| 0x32, 0x00, 0x00, 0x1a, 0x1a, 0x1d, 0x00, 0x80, |
| 0x51, 0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, |
| 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1c, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, |
| }; |
| |
| static uint8_t hdmi_edid_4k_600[256] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, |
| 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, |
| 0x22, 0x1a, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, |
| 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, |
| 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59, |
| 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, |
| 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x08, 0xe8, |
| 0x00, 0x30, 0xf2, 0x70, 0x5a, 0x80, 0xb0, 0x58, |
| 0x8a, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e, |
| 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, |
| 0x87, 0x3c, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, |
| 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68, |
| 0x64, 0x6d, 0x69, 0x2d, 0x34, 0x6b, 0x2d, 0x36, |
| 0x30, 0x30, 0x0a, 0x20, 0x00, 0x00, 0x00, 0x10, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2c, |
| |
| 0x02, 0x03, 0x42, 0xf1, 0x51, 0x61, 0x60, 0x5f, |
| 0x5e, 0x5d, 0x10, 0x1f, 0x04, 0x13, 0x22, 0x21, |
| 0x20, 0x05, 0x14, 0x02, 0x11, 0x01, 0x23, 0x09, |
| 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x6d, 0x03, |
| 0x0c, 0x00, 0x10, 0x00, 0x00, 0x3c, 0x21, 0x00, |
| 0x60, 0x01, 0x02, 0x03, 0x67, 0xd8, 0x5d, 0xc4, |
| 0x01, 0x78, 0x80, 0x08, 0xe2, 0x00, 0xca, 0xe3, |
| 0x05, 0x00, 0x00, 0xe3, 0x06, 0x01, 0x00, 0xe2, |
| 0x0d, 0x61, 0x4d, 0xd0, 0x00, 0xa0, 0xf0, 0x70, |
| 0x3e, 0x80, 0x30, 0x20, 0x35, 0x00, 0xc0, 0x1c, |
| 0x32, 0x00, 0x00, 0x1e, 0x1a, 0x36, 0x80, 0xa0, |
| 0x70, 0x38, 0x1f, 0x40, 0x30, 0x20, 0x35, 0x00, |
| 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1a, 0x1a, 0x1d, |
| 0x00, 0x80, 0x51, 0xd0, 0x1c, 0x20, 0x40, 0x80, |
| 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1c, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, |
| }; |
| |
| static uint8_t hdmi_edid_4k_600_with_displayid[512] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, |
| 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, |
| 0x22, 0x1a, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, |
| 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, |
| 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59, |
| 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, |
| 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x08, 0xe8, |
| 0x00, 0x30, 0xf2, 0x70, 0x5a, 0x80, 0xb0, 0x58, |
| 0x8a, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e, |
| 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, |
| 0x87, 0x3c, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, |
| 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68, |
| 0x64, 0x6d, 0x69, 0x20, 0x34, 0x2d, 0x62, 0x6c, |
| 0x6f, 0x63, 0x6b, 0x73, 0x00, 0x00, 0x00, 0x10, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe4, |
| |
| 0xf0, 0x02, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, |
| |
| 0x02, 0x03, 0x3f, 0xf1, 0x51, 0x61, 0x60, 0x5f, |
| 0x5e, 0x5d, 0x10, 0x1f, 0x04, 0x13, 0x22, 0x21, |
| 0x20, 0x05, 0x14, 0x02, 0x11, 0x01, 0x23, 0x09, |
| 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x6d, 0x03, |
| 0x0c, 0x00, 0x10, 0x00, 0x00, 0x3c, 0x21, 0x00, |
| 0x60, 0x01, 0x02, 0x03, 0x67, 0xd8, 0x5d, 0xc4, |
| 0x01, 0x78, 0x00, 0x00, 0xe2, 0x00, 0xca, 0xe3, |
| 0x05, 0x00, 0x00, 0xe3, 0x06, 0x01, 0x00, 0x4d, |
| 0xd0, 0x00, 0xa0, 0xf0, 0x70, 0x3e, 0x80, 0x30, |
| 0x20, 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, |
| 0x1e, 0x1a, 0x36, 0x80, 0xa0, 0x70, 0x38, 0x1f, |
| 0x40, 0x30, 0x20, 0x35, 0x00, 0xc0, 0x1c, 0x32, |
| 0x00, 0x00, 0x1a, 0x1a, 0x1d, 0x00, 0x80, 0x51, |
| 0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0xc0, |
| 0x1c, 0x32, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, |
| |
| 0x70, 0x12, 0x79, 0x03, 0x00, 0x00, 0x00, 0x1c, |
| 0x4c, 0x4e, 0x58, 0x34, 0x12, 0x56, 0x34, 0x12, |
| 0x00, 0x22, 0x10, 0x10, 0x48, 0x44, 0x4d, 0x49, |
| 0x20, 0x2b, 0x20, 0x44, 0x69, 0x73, 0x70, 0x6c, |
| 0x61, 0x79, 0x49, 0x44, 0x01, 0x00, 0x0c, 0x80, |
| 0x25, 0x18, 0x15, 0x00, 0x0f, 0x70, 0x08, 0x80, |
| 0x78, 0x4e, 0x77, 0x0f, 0x00, 0x0a, 0x71, 0x20, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x03, 0x01, 0x14, 0x07, 0xe8, 0x00, 0x84, 0xff, |
| 0x0e, 0x2f, 0x02, 0xaf, 0x80, 0x57, 0x00, 0x6f, |
| 0x08, 0x59, 0x00, 0x07, 0x80, 0x09, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0x90, |
| }; |
| |
| static uint8_t displayport_edid[256] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, |
| 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, |
| 0x22, 0x1a, 0x01, 0x04, 0xa1, 0x30, 0x1e, 0x78, |
| 0x07, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, |
| 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59, |
| 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, |
| 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x28, 0x3c, |
| 0x80, 0xa0, 0x70, 0xb0, 0x23, 0x40, 0x30, 0x20, |
| 0x36, 0x00, 0xe0, 0x2c, 0x11, 0x00, 0x00, 0x1a, |
| 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, |
| 0x5e, 0x11, 0x04, 0x12, 0x00, 0xf0, 0xf8, 0x58, |
| 0xf0, 0x3c, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x64, |
| 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x70, 0x6f, |
| 0x72, 0x74, 0x0a, 0x20, 0x00, 0x00, 0x00, 0x10, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc2, |
| |
| 0x70, 0x12, 0x79, 0x03, 0x00, 0x00, 0x00, 0x17, |
| 0x4c, 0x4e, 0x58, 0x34, 0x12, 0x56, 0x34, 0x12, |
| 0x00, 0x22, 0x10, 0x0b, 0x44, 0x69, 0x73, 0x70, |
| 0x6c, 0x61, 0x79, 0x50, 0x6f, 0x72, 0x74, 0x01, |
| 0x00, 0x0c, 0xc0, 0x12, 0xb8, 0x0b, 0x80, 0x07, |
| 0xb0, 0x04, 0x10, 0x78, 0x3c, 0x77, 0x0f, 0x00, |
| 0x0a, 0xa4, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x45, 0x00, 0x00, 0x03, 0x01, 0x14, 0x27, 0x3c, |
| 0x00, 0x84, 0x7f, 0x07, 0x9f, 0x00, 0x2f, 0x80, |
| 0x1f, 0x00, 0xaf, 0x04, 0x22, 0x00, 0x02, 0x00, |
| 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd5, 0x90, |
| }; |
| |
| static uint8_t displayport_edid_with_cta861[384] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, |
| 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, |
| 0x22, 0x1a, 0x01, 0x04, 0xa5, 0x30, 0x1b, 0x78, |
| 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, |
| 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59, |
| 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, |
| 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x02, 0x3a, |
| 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, |
| 0x45, 0x00, 0xe0, 0x0e, 0x11, 0x00, 0x00, 0x1e, |
| 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, |
| 0x5e, 0x11, 0x04, 0x12, 0x04, 0xf0, 0xf8, 0x38, |
| 0xf0, 0x3c, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x44, |
| 0x50, 0x2b, 0x43, 0x54, 0x41, 0x38, 0x36, 0x31, |
| 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x10, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0d, |
| |
| 0x02, 0x03, 0x24, 0xf1, 0x4c, 0x10, 0x1f, 0x04, |
| 0x13, 0x22, 0x21, 0x20, 0x05, 0x14, 0x02, 0x11, |
| 0x01, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, |
| 0x00, 0xe2, 0x00, 0xca, 0xe3, 0x05, 0x00, 0x00, |
| 0xe3, 0x06, 0x01, 0x00, 0x1a, 0x36, 0x80, 0xa0, |
| 0x70, 0x38, 0x1f, 0x40, 0x30, 0x20, 0x35, 0x00, |
| 0xe0, 0x0e, 0x11, 0x00, 0x00, 0x1a, 0x1a, 0x1d, |
| 0x00, 0x80, 0x51, 0xd0, 0x1c, 0x20, 0x40, 0x80, |
| 0x35, 0x00, 0xe0, 0x0e, 0x11, 0x00, 0x00, 0x1c, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, |
| |
| 0x70, 0x12, 0x79, 0x03, 0x00, 0x00, 0x00, 0x24, |
| 0x4c, 0x4e, 0x58, 0x34, 0x12, 0x56, 0x34, 0x12, |
| 0x00, 0x22, 0x10, 0x18, 0x44, 0x69, 0x73, 0x70, |
| 0x6c, 0x61, 0x79, 0x50, 0x6f, 0x72, 0x74, 0x20, |
| 0x77, 0x69, 0x74, 0x68, 0x20, 0x43, 0x54, 0x41, |
| 0x2d, 0x38, 0x36, 0x31, 0x0f, 0x00, 0x0a, 0xa4, |
| 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x01, 0x00, 0x0c, 0xc0, 0x12, 0x8c, 0x0a, |
| 0x80, 0x07, 0x38, 0x04, 0x90, 0x78, 0x4e, 0x77, |
| 0x03, 0x01, 0x14, 0x01, 0x3a, 0x00, 0x84, 0x7f, |
| 0x07, 0x17, 0x01, 0x57, 0x80, 0x2b, 0x00, 0x37, |
| 0x04, 0x2c, 0x00, 0x03, 0x80, 0x04, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x90, |
| }; |
| |
| /******************************************************/ |
| |
| void edid_cmd(int ch, char *optarg) |
| { |
| char *value, *subs; |
| |
| switch (ch) { |
| case OptSetEdid: |
| case OptShowEdid: |
| memset(&sedid, 0, sizeof(sedid)); |
| file_in = nullptr; |
| if (!optarg) |
| break; |
| subs = optarg; |
| while (*subs != '\0') { |
| static constexpr const char *subopts[] = { |
| "pad", |
| "type", |
| "edid", |
| "file", |
| "format", |
| "pa", |
| "s-pt", |
| "s-it", |
| "s-ce", |
| "y444", |
| "30-bit", |
| "36-bit", |
| "48-bit", |
| "graphics", |
| "photo", |
| "cinema", |
| "game", |
| "scdc", |
| "underscan", |
| "audio", |
| "ycbcr444", |
| "ycbcr422", |
| "qy", |
| "qs", |
| "xvycc-601", |
| "xvycc-709", |
| "sycc", |
| "opycc", |
| "oprgb", |
| "bt2020-rgb", |
| "bt2020-ycc", |
| "bt2020-cycc", |
| "dci-p3", |
| "sdr", |
| "hdr", |
| "smpte2084", |
| "hlg", |
| "fl-fr", |
| "lfe", |
| "fc", |
| "bl-br", |
| "bc", |
| "flc-frc", |
| "rlc-rrc", |
| "flw-frw", |
| "tpfl-tpfr", |
| "tpc", |
| "tpfc", |
| "ls-rs", |
| "lfe2", |
| "tpbc", |
| "sil-sir", |
| "tpsil-tpsir", |
| "tpbl-tpbr", |
| "btfc", |
| "btfl-btbr", |
| "tpls-tprs", |
| nullptr |
| }; |
| |
| int opt = v4l_getsubopt(&subs, (char* const*)subopts, &value); |
| |
| if (opt == -1) { |
| fprintf(stderr, "Invalid suboptions specified\n"); |
| edid_usage(); |
| std::exit(EXIT_FAILURE); |
| } |
| if (value == nullptr && opt <= 8) { |
| fprintf(stderr, "No value given to suboption <%s>\n", |
| subopts[opt]); |
| edid_usage(); |
| std::exit(EXIT_FAILURE); |
| } |
| switch (opt) { |
| case 1: |
| case 2: /* keep edid for compat reasons, it's the same as type */ |
| if (!strcmp(value, "dvid")) { |
| sedid.edid = dvid_edid; |
| sedid.blocks = sizeof(dvid_edid) / 128; |
| } else if (!strcmp(value, "vga")) { |
| sedid.edid = vga_edid; |
| sedid.blocks = sizeof(vga_edid) / 128; |
| } else if (!strcmp(value, "hdmi-4k-170mhz")) { |
| sedid.edid = hdmi_edid_4k_170; |
| sedid.blocks = sizeof(hdmi_edid_4k_170) / 128; |
| } else if (!strcmp(value, "hdmi-4k-300mhz")) { |
| sedid.edid = hdmi_edid_4k_300; |
| sedid.blocks = sizeof(hdmi_edid_4k_300) / 128; |
| } else if (!strcmp(value, "hdmi-4k-600mhz-with-displayid")) { |
| sedid.edid = hdmi_edid_4k_600_with_displayid; |
| sedid.blocks = sizeof(hdmi_edid_4k_600_with_displayid) / 128; |
| } else if (!strcmp(value, "hdmi-4k-600mhz")) { |
| sedid.edid = hdmi_edid_4k_600; |
| sedid.blocks = sizeof(hdmi_edid_4k_600) / 128; |
| } else if (!strcmp(value, "hdmi")) { |
| sedid.edid = hdmi_edid; |
| sedid.blocks = sizeof(hdmi_edid) / 128; |
| } else if (!strcmp(value, "displayport-with-cta861")) { |
| sedid.edid = displayport_edid_with_cta861; |
| sedid.blocks = sizeof(displayport_edid_with_cta861) / 128; |
| } else if (!strcmp(value, "displayport")) { |
| sedid.edid = displayport_edid; |
| sedid.blocks = sizeof(displayport_edid) / 128; |
| } else if (!strcmp(value, "list")) { |
| printf("EDID types:\n"); |
| printf("vga: Base Block supporting VGA interface (1920x1200p60)\n"); |
| printf("dvid: Base Block supporting DVI-D interface (1920x1200p60)\n"); |
| printf("hdmi: CTA-861 with HDMI support up to 1080p60\n"); |
| printf("hdmi-4k-170mhz: CTA-861 with HDMI support up to 1080p60 or 4kp30 4:2:0\n"); |
| printf("hdmi-4k-300mhz: CTA-861 with HDMI support up to 4kp30\n"); |
| printf("hdmi-4k-600mhz: CTA-861 with HDMI support up to 4kp60\n"); |
| printf("hdmi-4k-600mhz-with-displayid: Block Map Extension Block, CTA-861 with\n"); |
| printf("\tHDMI support up to 4kp60, DisplayID Extension Block\n"); |
| printf("displayport: DisplayID supporting a DisplayPort interface (1920x1200)\n"); |
| printf("displayport-with-cta861: DisplayID supporting a DisplayPort interface,\n"); |
| printf("\tCTA-861 Extension Block (1080p60)\n"); |
| std::exit(EXIT_FAILURE); |
| } else { |
| edid_usage(); |
| std::exit(EXIT_FAILURE); |
| } |
| if (file_in) { |
| fprintf(stderr, "The edid and file options can't be used together.\n"); |
| std::exit(EXIT_FAILURE); |
| } |
| break; |
| case 3: |
| if (value) { |
| file_in = value; |
| if (sedid.edid) { |
| fprintf(stderr, "The edid and file options can't be used together.\n"); |
| std::exit(EXIT_FAILURE); |
| } |
| } |
| break; |
| case 4: |
| if (!strcmp(value, "hex")) { |
| sformat = HEX; |
| } else if (!strcmp(value, "raw")) { |
| sformat = RAW; |
| } else { |
| edid_usage(); |
| std::exit(EXIT_FAILURE); |
| } |
| break; |
| case 5: |
| if (value) |
| phys_addr = parse_phys_addr(value); |
| break; |
| case 6: |
| mod_s_pt = strtoul(value, nullptr, 0) & 3; |
| break; |
| case 7: |
| mod_s_it = strtoul(value, nullptr, 0) & 3; |
| break; |
| case 8: |
| mod_s_ce = strtoul(value, nullptr, 0) & 3; |
| break; |
| case 9: toggle_hdmi_vsdb_dc_flags |= HDMI_VSDB_Y444_BIT; break; |
| case 10: toggle_hdmi_vsdb_dc_flags |= HDMI_VSDB_30_BIT; break; |
| case 11: toggle_hdmi_vsdb_dc_flags |= HDMI_VSDB_36_BIT; break; |
| case 12: toggle_hdmi_vsdb_dc_flags |= HDMI_VSDB_48_BIT; break; |
| case 13: toggle_hdmi_vsdb_cnc_flags |= HDMI_VSDB_GRAPHICS; break; |
| case 14: toggle_hdmi_vsdb_cnc_flags |= HDMI_VSDB_PHOTO; break; |
| case 15: toggle_hdmi_vsdb_cnc_flags |= HDMI_VSDB_CINEMA; break; |
| case 16: toggle_hdmi_vsdb_cnc_flags |= HDMI_VSDB_GAME; break; |
| case 17: toggle_hf_vsdb_flags |= HF_VSDB_SCSD_PRESENT; break; |
| case 18: toggle_cta861_hdr_flags |= CTA861_HDR_UNDERSCAN; break; |
| case 19: toggle_cta861_hdr_flags |= CTA861_HDR_AUDIO; break; |
| case 20: toggle_cta861_hdr_flags |= CTA861_HDR_YCBCR444; break; |
| case 21: toggle_cta861_hdr_flags |= CTA861_HDR_YCBCR422; break; |
| case 22: toggle_vid_cap_flags |= VID_CAP_QY; break; |
| case 23: toggle_vid_cap_flags |= VID_CAP_QS; break; |
| case 24: toggle_colorimetry_flags1 |= COLORIMETRY_XVYCC601; break; |
| case 25: toggle_colorimetry_flags1 |= COLORIMETRY_XVYCC709; break; |
| case 26: toggle_colorimetry_flags1 |= COLORIMETRY_SYCC; break; |
| case 27: toggle_colorimetry_flags1 |= COLORIMETRY_OPYCC; break; |
| case 28: toggle_colorimetry_flags1 |= COLORIMETRY_OPRGB; break; |
| case 29: toggle_colorimetry_flags1 |= COLORIMETRY_BT2020RGB; break; |
| case 30: toggle_colorimetry_flags1 |= COLORIMETRY_BT2020YCC; break; |
| case 31: toggle_colorimetry_flags1 |= COLORIMETRY_BT2020CYCC; break; |
| case 32: toggle_colorimetry_flags2 |= COLORIMETRY_DCIP3; break; |
| case 33: toggle_hdr_md_flags |= HDR_MD_SDR; break; |
| case 34: toggle_hdr_md_flags |= HDR_MD_HDR; break; |
| case 35: toggle_hdr_md_flags |= HDR_MD_SMPTE_2084; break; |
| case 36: toggle_hdr_md_flags |= HDR_MD_HLG; break; |
| case 37: toggle_speaker1_flags |= SPEAKER1_FL_FR; break; |
| case 38: toggle_speaker1_flags |= SPEAKER1_LFE; break; |
| case 39: toggle_speaker1_flags |= SPEAKER1_FC; break; |
| case 40: toggle_speaker1_flags |= SPEAKER1_BL_BR; break; |
| case 41: toggle_speaker1_flags |= SPEAKER1_BC; break; |
| case 42: toggle_speaker1_flags |= SPEAKER1_FLC_FRC; break; |
| case 43: toggle_speaker1_flags |= SPEAKER1_RLC_RRC; break; |
| case 44: toggle_speaker1_flags |= SPEAKER1_FLW_FRW; break; |
| case 45: toggle_speaker2_flags |= SPEAKER2_TPFL_TPFR; break; |
| case 46: toggle_speaker2_flags |= SPEAKER2_TPC; break; |
| case 47: toggle_speaker2_flags |= SPEAKER2_TPFC; break; |
| case 48: toggle_speaker2_flags |= SPEAKER2_LS_RS; break; |
| case 49: toggle_speaker2_flags |= SPEAKER2_LFE2; break; |
| case 50: toggle_speaker2_flags |= SPEAKER2_TPBC; break; |
| case 51: toggle_speaker2_flags |= SPEAKER2_SIL_SIR; break; |
| case 52: toggle_speaker2_flags |= SPEAKER2_TPSIL_TPSIR; break; |
| case 53: toggle_speaker3_flags |= SPEAKER3_TPBL_TPBR; break; |
| case 54: toggle_speaker3_flags |= SPEAKER3_BTFC; break; |
| case 55: toggle_speaker3_flags |= SPEAKER3_BTFL_BTFR; break; |
| case 56: toggle_speaker3_flags |= SPEAKER3_TPLS_TPRS; break; |
| case 0: |
| if (ch == OptSetEdid) { |
| sedid.pad = strtoul(value, nullptr, 0); |
| break; |
| } |
| fallthrough; |
| default: |
| edid_usage(); |
| std::exit(EXIT_FAILURE); |
| } |
| } |
| break; |
| |
| case OptClearEdid: |
| if (optarg) |
| clear_pad = strtoul(optarg, nullptr, 0); |
| break; |
| |
| case OptGetEdid: |
| memset(&gedid, 0, sizeof(gedid)); |
| gedid.blocks = 256; /* default all blocks */ |
| gformat = HEX; /* default hex output */ |
| file_out = nullptr; |
| if (!optarg) |
| break; |
| subs = optarg; |
| while (*subs != '\0') { |
| static constexpr const char *subopts[] = { |
| "pad", |
| "startblock", |
| "blocks", |
| "format", |
| "file", |
| nullptr |
| }; |
| |
| switch (parse_subopt(&subs, subopts, &value)) { |
| case 0: |
| gedid.pad = strtoul(value, nullptr, 0); |
| break; |
| case 1: |
| gedid.start_block = strtoul(value, nullptr, 0); |
| if (gedid.start_block > 255) { |
| fprintf(stderr, "startblock %d too large, max 255\n", gedid.start_block); |
| std::exit(EXIT_FAILURE); |
| } |
| break; |
| case 2: |
| gedid.blocks = strtoul(value, nullptr, 0); |
| break; |
| case 3: |
| if (!strcmp(value, "hex")) { |
| gformat = HEX; |
| } else if (!strcmp(value, "raw")) { |
| gformat = RAW; |
| } else if (!strcmp(value, "carray")) { |
| gformat = CARRAY; |
| } else { |
| edid_usage(); |
| std::exit(EXIT_FAILURE); |
| } |
| break; |
| case 4: |
| if (value) |
| file_out = value; |
| break; |
| default: |
| edid_usage(); |
| std::exit(EXIT_FAILURE); |
| } |
| } |
| if (gedid.start_block + gedid.blocks > 256) |
| gedid.blocks = 256 - gedid.start_block; |
| break; |
| |
| case OptInfoEdid: |
| memset(&info_edid, 0, sizeof(info_edid)); |
| if (optarg) |
| info_edid.pad = strtoul(optarg, nullptr, 0); |
| break; |
| } |
| } |
| |
| void edid_set(cv4l_fd &_fd) |
| { |
| int fd = _fd.g_fd(); |
| int loc; |
| |
| if (options[OptClearEdid]) { |
| struct v4l2_edid edid; |
| |
| memset(&edid, 0, sizeof(edid)); |
| edid.pad = clear_pad; |
| doioctl(fd, VIDIOC_S_EDID, &edid); |
| } |
| |
| if (options[OptSetEdid] || options[OptShowEdid]) { |
| FILE *fin = nullptr; |
| bool must_fix_edid = options[OptFixEdidChecksums]; |
| |
| if (file_in) { |
| if (!strcmp(file_in, "-")) |
| fin = stdin; |
| else |
| fin = fopen(file_in, "r"); |
| if (!fin) { |
| fprintf(stderr, "Failed to open %s: %s\n", file_in, |
| strerror(errno)); |
| std::exit(EXIT_FAILURE); |
| } |
| } |
| if (fin) { |
| read_edid_file(fin, &sedid); |
| if (sedid.blocks == 0) { |
| fprintf(stderr, "%s contained an empty EDID, ignoring.\n", |
| file_in ? file_in : "stdin"); |
| std::exit(EXIT_FAILURE); |
| } |
| } |
| if (toggle_cta861_hdr_flags || phys_addr >= 0) { |
| loc = get_edid_cta861_hdr_location(sedid.edid, sedid.blocks * 128); |
| if (loc >= 0) { |
| sedid.edid[loc] ^= toggle_cta861_hdr_flags; |
| if (phys_addr >= 0) |
| set_edid_phys_addr(sedid.edid, sedid.blocks * 128, phys_addr); |
| must_fix_edid = true; |
| } |
| } |
| if (toggle_speaker1_flags || toggle_speaker2_flags || toggle_speaker3_flags) { |
| loc = get_edid_speaker_location(sedid.edid, sedid.blocks * 128); |
| if (loc >= 0) { |
| sedid.edid[loc] ^= toggle_speaker1_flags; |
| sedid.edid[loc + 1] ^= toggle_speaker2_flags; |
| sedid.edid[loc + 2] ^= toggle_speaker3_flags; |
| must_fix_edid = true; |
| } |
| } |
| if (toggle_hdmi_vsdb_dc_flags || toggle_hdmi_vsdb_cnc_flags) { |
| loc = get_edid_hdmi_vsdb_location(sedid.edid, sedid.blocks * 128); |
| |
| if (loc >= 0) { |
| __u8 len = sedid.edid[loc] & 0x1f; |
| |
| if (len >= 6) { |
| sedid.edid[loc + 6] ^= toggle_hdmi_vsdb_dc_flags; |
| must_fix_edid = true; |
| } |
| if (len >= 8) { |
| sedid.edid[loc + 8] ^= toggle_hdmi_vsdb_cnc_flags; |
| must_fix_edid = true; |
| } |
| } |
| } |
| if (toggle_hf_vsdb_flags) { |
| loc = get_edid_hf_vsdb_location(sedid.edid, sedid.blocks * 128); |
| if (loc >= 0) { |
| sedid.edid[loc + 1] ^= toggle_hf_vsdb_flags; |
| must_fix_edid = true; |
| } |
| } |
| if (toggle_vid_cap_flags || mod_s_pt >= 0 || |
| mod_s_ce >= 0 || mod_s_it >= 0) { |
| loc = get_edid_vid_cap_location(sedid.edid, sedid.blocks * 128); |
| if (loc >= 0) { |
| sedid.edid[loc] ^= toggle_vid_cap_flags; |
| if (mod_s_ce >= 0) { |
| sedid.edid[loc] &= 0xfc; |
| sedid.edid[loc] |= mod_s_ce << 0; |
| } |
| if (mod_s_it >= 0) { |
| sedid.edid[loc] &= 0xf3; |
| sedid.edid[loc] |= mod_s_it << 2; |
| } |
| if (mod_s_pt >= 0) { |
| sedid.edid[loc] &= 0xcf; |
| sedid.edid[loc] |= mod_s_pt << 4; |
| } |
| must_fix_edid = true; |
| } |
| } |
| if (toggle_colorimetry_flags1 || toggle_colorimetry_flags2) { |
| loc = get_edid_colorimetry_location(sedid.edid, sedid.blocks * 128); |
| if (loc >= 0) { |
| sedid.edid[loc] ^= toggle_colorimetry_flags1; |
| sedid.edid[loc + 1] ^= toggle_colorimetry_flags2; |
| must_fix_edid = true; |
| } |
| } |
| if (toggle_hdr_md_flags) { |
| loc = get_edid_hdr_md_location(sedid.edid, sedid.blocks * 128); |
| if (loc >= 0) { |
| sedid.edid[loc] ^= toggle_hdr_md_flags; |
| must_fix_edid = true; |
| } |
| } |
| if (must_fix_edid) |
| fix_edid(&sedid); |
| if (verbose && options[OptSetEdid]) |
| print_edid_mods(&sedid); |
| if (options[OptShowEdid]) { |
| for (unsigned b = 0; b < sedid.blocks; b++) { |
| if (b) |
| printf("\n"); |
| for (unsigned i = 0; i < 128; i += 16) { |
| for (unsigned j = i; j < i + 16; j++) { |
| if (j > i) |
| printf(" "); |
| printf("%02x", sedid.edid[b * 128 + j]); |
| } |
| printf("\n"); |
| } |
| } |
| } else if (verify_edid(&sedid)) { |
| doioctl(fd, VIDIOC_S_EDID, &sedid); |
| } else { |
| fprintf(stderr, "EDID not set due to checksum errors\n"); |
| } |
| if (fin) { |
| if (sedid.edid) { |
| free(sedid.edid); |
| sedid.edid = nullptr; |
| } |
| if (fin != stdin) |
| fclose(fin); |
| } |
| } |
| } |
| |
| void edid_get(cv4l_fd &_fd) |
| { |
| int fd = _fd.g_fd(); |
| |
| if (options[OptGetEdid]) { |
| FILE *fout = stdout; |
| |
| if (file_out) { |
| if (!strcmp(file_out, "-")) |
| fout = stdout; |
| else |
| fout = fopen(file_out, "w+"); |
| if (!fout) { |
| fprintf(stderr, "Failed to open %s: %s\n", file_out, |
| strerror(errno)); |
| std::exit(EXIT_FAILURE); |
| } |
| } |
| gedid.edid = static_cast<unsigned char *>(malloc(gedid.blocks * 128)); |
| if (doioctl(fd, VIDIOC_G_EDID, &gedid) == 0) { |
| if (options[OptFixEdidChecksums]) |
| fix_edid(&gedid); |
| printedid(fout, &gedid, gformat); |
| } |
| if (file_out && fout != stdout) |
| fclose(fout); |
| free(gedid.edid); |
| } |
| if (options[OptInfoEdid]) { |
| info_edid.blocks = 2; |
| info_edid.edid = static_cast<unsigned char *>(malloc(info_edid.blocks * 128)); |
| if (doioctl(fd, VIDIOC_G_EDID, &info_edid) == 0) |
| print_edid_mods(&info_edid); |
| free(info_edid.edid); |
| } |
| } |