diff options
author | Hans Verkuil <hans.verkuil@cisco.com> | 2017-08-31 13:41:07 +0200 |
---|---|---|
committer | Adam Jackson <ajax@redhat.com> | 2017-09-07 12:13:31 -0400 |
commit | 0f3958e1bd00283e793a5762ebdbc4ff9775a545 (patch) | |
tree | 7b0c11a0df0b615043cafb452aa1469e5dc9e300 | |
parent | 0cdf932cef9f9d2a4b38895939bc85d0fcddbf28 (diff) |
edid-decode: check monitor min/max range against supported timings
Calculate the minimum and maximum required frequencies given the list
of timings. Check this against the listed monitor frequencies.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
-rw-r--r-- | edid-decode.c | 487 |
1 files changed, 298 insertions, 189 deletions
diff --git a/edid-decode.c b/edid-decode.c index a7875a6..a3f2340 100644 --- a/edid-decode.c +++ b/edid-decode.c @@ -33,6 +33,8 @@ #include <ctype.h> #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) enum { EDID_PAGE_SIZE = 128u @@ -76,6 +78,17 @@ static int nonconformant_hf_vsdb_position = 0; static int nonconformant_srgb_chromaticity = 0; static int nonconformant_cea861_640x480 = 0; +static int min_hor_freq_hz = 0xfffffff; +static int max_hor_freq_hz = 0; +static int min_vert_freq_hz = 0xfffffff; +static int max_vert_freq_hz = 0; +static int max_pixclk_khz = 0; +static int mon_min_hor_freq_hz = 0; +static int mon_max_hor_freq_hz = 0; +static int mon_min_vert_freq_hz = 0; +static int mon_max_vert_freq_hz = 0; +static int mon_max_pixclk_khz = 0; + static int conformant = 1; struct value { @@ -169,6 +182,7 @@ detailed_cvt_descriptor(unsigned char *x, int first) int width, height; int valid = 1; int fifty = 0, sixty = 0, seventyfive = 0, eightyfive = 0, reduced = 0; + int min_refresh = 0xfffffff, max_refresh = 0; if (!first && !memcmp(x, empty, 3)) return valid; @@ -210,6 +224,9 @@ detailed_cvt_descriptor(unsigned char *x, int first) eightyfive = (x[2] & 0x02); reduced = (x[2] & 0x01); + min_refresh = (fifty ? 50 : (sixty ? 60 : (seventyfive ? 75 : (eightyfive ? 85 : min_refresh)))); + max_refresh = (eightyfive ? 85 : (seventyfive ? 75 : (sixty ? 60 : (fifty ? 50 : max_refresh)))); + if (!valid) { printf(" (broken)\n"); } else { @@ -222,52 +239,17 @@ detailed_cvt_descriptor(unsigned char *x, int first) ratio, names[(x[2] & 0x60) >> 5], (((x[2] & 0x60) == 0x20) && reduced) ? "RB" : ""); + min_vert_freq_hz = min(min_vert_freq_hz, min_refresh); + max_vert_freq_hz = max(max_vert_freq_hz, max_refresh); + /* TODO + min_hor_freq_hz = min(min_hor_freq_hz, established_timings3[i].hor_freq_hz); + max_hor_freq_hz = max(max_hor_freq_hz, established_timings3[i].hor_freq_hz); + max_pixclk_khz = max(max_pixclk_khz, established_timings3[i].pixclk_khz);*/ } return valid; } -static void print_standard_timing(uint8_t b1, uint8_t b2) -{ - const char *ratio; - unsigned int x, y, refresh; - - if (b1 == 0x01 && b2 == 0x01) - return; - - if (b1 == 0) { - printf("non-conformant standard timing (0 horiz)\n"); - return; - } - x = (b1 + 31) * 8; - switch ((b2 >> 6) & 0x3) { - case 0x00: - if (claims_one_point_three) { - y = x * 10 / 16; - ratio = "16:10"; - } else { - y = x; - ratio = "1:1"; - } - break; - case 0x01: - y = x * 3 / 4; - ratio = "4:3"; - break; - case 0x02: - y = x * 4 / 5; - ratio = "5:4"; - break; - case 0x03: - y = x * 9 / 16; - ratio = "16:9"; - break; - } - refresh = 60 + (b2 & 0x3f); - - printf(" %dx%d@%dHz %s\n", x, y, refresh, ratio); -} - /* extract a string from a detailed subblock, checking for termination */ static char * extract_string(unsigned char *x, int *valid_termination, int len) @@ -302,6 +284,32 @@ extract_string(unsigned char *x, int *valid_termination, int len) static const struct { int x, y, refresh, ratio_w, ratio_h; + int hor_freq_hz, pixclk_khz, interlaced; +} established_timings[] = { + /* 0x23 bit 7 - 0 */ + {720, 400, 70, 9, 5, 31469, 28320}, + {720, 400, 88, 9, 5, 39500, 35500}, + {640, 480, 60, 4, 3, 31469, 25175}, + {640, 480, 67, 4, 3, 35000, 30240}, + {640, 480, 72, 4, 3, 37900, 31500}, + {640, 480, 75, 4, 3, 37500, 31500}, + {800, 600, 56, 4, 3, 35200, 36000}, + {800, 600, 60, 4, 3, 37900, 40000}, + /* 0x24 bit 7 - 0 */ + {800, 600, 72, 4, 3, 48100, 50000}, + {800, 600, 75, 4, 3, 46900, 49500}, + {832, 624, 75, 4, 3, 49726, 57284}, + {1280, 768, 87, 5, 3, 35522, 44900, 1}, + {1024, 768, 60, 4, 3, 48400, 65000}, + {1024, 768, 70, 4, 3, 56500, 75000}, + {1024, 768, 75, 4, 3, 60000, 78750}, + {1280, 1024, 75, 5, 4, 80000, 135000}, + /* 0x25 bit 7*/ + {1152, 870, 75, 192, 145, 67500, 108000}, +}; + +static const struct { + int x, y, refresh, ratio_w, ratio_h; int hor_freq_hz, pixclk_khz, rb; } established_timings3[] = { /* 0x06 bit 7 - 0 */ @@ -356,12 +364,92 @@ static const struct { {1920, 1440, 75, 4, 3, 112500, 297000}, }; +static void print_standard_timing(uint8_t b1, uint8_t b2) +{ + int ratio_w, ratio_h; + unsigned int x, y, refresh; + int pixclk_khz = 0, hor_freq_hz = 0; + int i; + + if (b1 == 0x01 && b2 == 0x01) + return; + + if (b1 == 0) { + printf("non-conformant standard timing (0 horiz)\n"); + return; + } + x = (b1 + 31) * 8; + switch ((b2 >> 6) & 0x3) { + case 0x00: + if (claims_one_point_three) { + y = x * 10 / 16; + ratio_w = 16; + ratio_h = 10; + } else { + y = x; + ratio_w = 1; + ratio_h = 1; + } + break; + case 0x01: + y = x * 3 / 4; + ratio_w = 4; + ratio_h = 3; + break; + case 0x02: + y = x * 4 / 5; + ratio_w = 5; + ratio_h = 4; + break; + case 0x03: + y = x * 9 / 16; + ratio_w = 16; + ratio_h = 9; + break; + } + refresh = 60 + (b2 & 0x3f); + + printf(" %dx%d@%dHz %d:%d\n", x, y, refresh, ratio_w, ratio_h); + min_vert_freq_hz = min(min_vert_freq_hz, refresh); + max_vert_freq_hz = max(max_vert_freq_hz, refresh); + for (i = 0; i < ARRAY_SIZE(established_timings); i++) { + if (established_timings[i].x == x && + established_timings[i].y == y && + established_timings[i].refresh == refresh && + established_timings[i].ratio_w == ratio_w && + established_timings[i].ratio_h == ratio_h) { + pixclk_khz = established_timings[i].pixclk_khz; + hor_freq_hz = established_timings[i].hor_freq_hz; + break; + } + } + if (pixclk_khz == 0) { + for (i = 0; i < ARRAY_SIZE(established_timings3); i++) { + if (established_timings3[i].x == x && + established_timings3[i].y == y && + established_timings3[i].refresh == refresh && + established_timings3[i].ratio_w == ratio_w && + established_timings3[i].ratio_h == ratio_h) { + pixclk_khz = established_timings3[i].pixclk_khz; + hor_freq_hz = established_timings3[i].hor_freq_hz; + break; + } + } + } + if (pixclk_khz) { + min_hor_freq_hz = min(min_hor_freq_hz, hor_freq_hz); + max_hor_freq_hz = max(max_hor_freq_hz, hor_freq_hz); + max_pixclk_khz = max(max_pixclk_khz, pixclk_khz); + } +} + /* 1 means valid data */ static int detailed_block(unsigned char *x, int in_extension) { static unsigned char name[53]; int ha, hbl, hso, hspw, hborder, va, vbl, vso, vspw, vborder; + int refresh, pixclk_khz; int i; char phsync, pvsync, *syncmethod, *stereo; @@ -412,6 +500,11 @@ detailed_block(unsigned char *x, int in_extension) established_timings3[i].y, established_timings3[i].refresh, established_timings3[i].rb ? "RB " : "", established_timings3[i].ratio_w, established_timings3[i].ratio_h); + min_vert_freq_hz = min(min_vert_freq_hz, established_timings3[i].refresh); + max_vert_freq_hz = max(max_vert_freq_hz, established_timings3[i].refresh); + min_hor_freq_hz = min(min_hor_freq_hz, established_timings3[i].hor_freq_hz); + max_hor_freq_hz = max(max_hor_freq_hz, established_timings3[i].hor_freq_hz); + max_pixclk_khz = max(max_pixclk_khz, established_timings3[i].pixclk_khz); } } return 1; @@ -543,15 +636,20 @@ detailed_block(unsigned char *x, int in_extension) if (x[5] + v_min_offset > x[6] + v_max_offset) has_valid_range_descriptor = 0; + mon_min_vert_freq_hz = x[5] + v_min_offset; + mon_max_vert_freq_hz = x[6] + v_max_offset; if (x[7] + h_min_offset > x[8] + h_max_offset) has_valid_range_descriptor = 0; + mon_min_hor_freq_hz = (x[7] + h_min_offset) * 1000; + mon_max_hor_freq_hz = (x[8] + h_max_offset) * 1000; printf("Monitor ranges (%s): %d-%dHz V, %d-%dkHz H", range_class, x[5] + v_min_offset, x[6] + v_max_offset, x[7] + h_min_offset, x[8] + h_max_offset); - if (x[9]) + if (x[9]) { + mon_max_pixclk_khz = x[9] * 10000; printf(", max dotclock %dMHz\n", x[9] * 10); - else { + } else { if (claims_one_point_four) has_valid_max_dotclock = 0; printf("\n"); @@ -706,11 +804,13 @@ detailed_block(unsigned char *x, int in_extension) break; } + pixclk_khz = (x[0] + (x[1] << 8)) * 10; + refresh = (pixclk_khz * 1000) / ((ha + hbl) * (va + vbl)); printf("Detailed mode: Clock %.3f MHz, %d mm x %d mm\n" " %4d %4d %4d %4d hborder %d\n" " %4d %4d %4d %4d vborder %d\n" " %chsync %cvsync%s%s %s\n", - (x[0] + (x[1] << 8)) / 100.0, + pixclk_khz / 1000.0, (x[12] + ((x[14] & 0xF0) << 4)), (x[13] + ((x[14] & 0x0F) << 8)), ha, ha + hso, ha + hso + hspw, ha + hbl, hborder, @@ -718,6 +818,11 @@ detailed_block(unsigned char *x, int in_extension) phsync, pvsync, syncmethod, x[17] & 0x80 ? " interlaced" : "", stereo ); + min_vert_freq_hz = min(min_vert_freq_hz, refresh); + max_vert_freq_hz = max(max_vert_freq_hz, refresh); + min_hor_freq_hz = min(min_hor_freq_hz, (pixclk_khz * 1000) / (ha + hbl)); + max_hor_freq_hz = max(max_hor_freq_hz, (pixclk_khz * 1000) / (ha + hbl)); + max_pixclk_khz = max(max_pixclk_khz, pixclk_khz); /* XXX flag decode */ return 1; @@ -807,126 +912,126 @@ cea_audio_block(unsigned char *x) static struct { const char *name; - int hor_freq_hz, pixclk_khz; + int refresh, hor_freq_hz, pixclk_khz; } edid_cea_modes[] = { /* VIC 1 */ - {"640x480@60Hz 4:3", 31469, 25175}, - {"720x480@60Hz 4:3", 31469, 27000}, - {"720x480@60Hz 16:9", 31469, 27000}, - {"1280x720@60Hz 16:9", 45000, 74250}, - {"1920x1080i@60Hz 16:9", 33750, 74250}, - {"1440x480i@60Hz 4:3", 15734, 27000}, - {"1440x480i@60Hz 16:9", 15734, 27000}, - {"1440x240@60Hz 4:3", 15734, 27000}, - {"1440x240@60Hz 16:9", 15734, 27000}, - {"2880x480i@60Hz 4:3", 15734, 54000}, + {"640x480@60Hz 4:3", 60, 31469, 25175}, + {"720x480@60Hz 4:3", 60, 31469, 27000}, + {"720x480@60Hz 16:9", 60, 31469, 27000}, + {"1280x720@60Hz 16:9", 60, 45000, 74250}, + {"1920x1080i@60Hz 16:9", 60, 33750, 74250}, + {"1440x480i@60Hz 4:3", 60, 15734, 27000}, + {"1440x480i@60Hz 16:9", 60, 15734, 27000}, + {"1440x240@60Hz 4:3", 60, 15734, 27000}, + {"1440x240@60Hz 16:9", 60, 15734, 27000}, + {"2880x480i@60Hz 4:3", 60, 15734, 54000}, /* VIC 11 */ - {"2880x480i@60Hz 16:9", 15734, 54000}, - {"2880x240@60Hz 4:3", 15734, 54000}, - {"2880x240@60Hz 16:9", 15734, 54000}, - {"1440x480@60Hz 4:3", 31469, 54000}, - {"1440x480@60Hz 16:9", 31469, 54000}, - {"1920x1080@60Hz 16:9", 67500, 148500}, - {"720x576@50Hz 4:3", 31250, 27000}, - {"720x576@50Hz 16:9", 31250, 27000}, - {"1280x720@50Hz 16:9", 37500, 74250}, - {"1920x1080i@50Hz 16:9", 28125, 74250}, + {"2880x480i@60Hz 16:9", 60, 15734, 54000}, + {"2880x240@60Hz 4:3", 60, 15734, 54000}, + {"2880x240@60Hz 16:9", 60, 15734, 54000}, + {"1440x480@60Hz 4:3", 60, 31469, 54000}, + {"1440x480@60Hz 16:9", 60, 31469, 54000}, + {"1920x1080@60Hz 16:9", 60, 67500, 148500}, + {"720x576@50Hz 4:3", 50, 31250, 27000}, + {"720x576@50Hz 16:9", 50, 31250, 27000}, + {"1280x720@50Hz 16:9", 50, 37500, 74250}, + {"1920x1080i@50Hz 16:9", 50, 28125, 74250}, /* VIC 21 */ - {"1440x576i@50Hz 4:3", 15625, 27000}, - {"1440x576i@50Hz 16:9", 15625, 27000}, - {"1440x288@50Hz 4:3", 15625, 27000}, - {"1440x288@50Hz 16:9", 15625, 27000}, - {"2880x576i@50Hz 4:3", 15625, 54000}, - {"2880x576i@50Hz 16:9", 15625, 54000}, - {"2880x288@50Hz 4:3", 15625, 54000}, - {"2880x288@50Hz 16:9", 15625, 54000}, - {"1440x576@50Hz 4:3", 31250, 54000}, - {"1440x576@50Hz 16:9", 31250, 54000}, + {"1440x576i@50Hz 4:3", 50, 15625, 27000}, + {"1440x576i@50Hz 16:9", 50, 15625, 27000}, + {"1440x288@50Hz 4:3", 50, 15625, 27000}, + {"1440x288@50Hz 16:9", 50, 15625, 27000}, + {"2880x576i@50Hz 4:3", 50, 15625, 54000}, + {"2880x576i@50Hz 16:9", 50, 15625, 54000}, + {"2880x288@50Hz 4:3", 50, 15625, 54000}, + {"2880x288@50Hz 16:9", 50, 15625, 54000}, + {"1440x576@50Hz 4:3", 50, 31250, 54000}, + {"1440x576@50Hz 16:9", 50, 31250, 54000}, /* VIC 31 */ - {"1920x1080@50Hz 16:9", 56250, 148500}, - {"1920x1080@24Hz 16:9", 27000, 74250}, - {"1920x1080@25Hz 16:9", 28125, 74250}, - {"1920x1080@30Hz 16:9", 33750, 74250}, - {"2880x480@60Hz 4:3", 31469, 108000}, - {"2880x480@60Hz 16:9", 31469, 108000}, - {"2880x576@50Hz 4:3", 31250, 108000}, - {"2880x576@50Hz 16:9", 31250, 108000}, - {"1920x1080i@50Hz 16:9", 31250, 72000}, - {"1920x1080i@100Hz 16:9", 56250, 148500}, + {"1920x1080@50Hz 16:9", 50, 56250, 148500}, + {"1920x1080@24Hz 16:9", 24, 27000, 74250}, + {"1920x1080@25Hz 16:9", 25, 28125, 74250}, + {"1920x1080@30Hz 16:9", 30, 33750, 74250}, + {"2880x480@60Hz 4:3", 60, 31469, 108000}, + {"2880x480@60Hz 16:9", 60, 31469, 108000}, + {"2880x576@50Hz 4:3", 50, 31250, 108000}, + {"2880x576@50Hz 16:9", 50, 31250, 108000}, + {"1920x1080i@50Hz 16:9", 50, 31250, 72000}, + {"1920x1080i@100Hz 16:9", 100, 56250, 148500}, /* VIC 41 */ - {"1280x720@100Hz 16:9", 75000, 148500}, - {"720x576@100Hz 4:3", 62500, 54000}, - {"720x576@100Hz 16:9", 62500, 54000}, - {"1440x576@100Hz 4:3", 31250, 54000}, - {"1440x576@100Hz 16:9", 31250, 54000}, - {"1920x1080i@120Hz 16:9", 67500, 148500}, - {"1280x720@120Hz 16:9", 90000, 148500}, - {"720x480@120Hz 4:3", 62937, 54000}, - {"720x480@120Hz 16:9", 62937, 54000}, - {"1440x480i@120Hz 4:3", 31469, 54000}, + {"1280x720@100Hz 16:9", 100, 75000, 148500}, + {"720x576@100Hz 4:3", 100, 62500, 54000}, + {"720x576@100Hz 16:9", 100, 62500, 54000}, + {"1440x576@100Hz 4:3", 100, 31250, 54000}, + {"1440x576@100Hz 16:9", 100, 31250, 54000}, + {"1920x1080i@120Hz 16:9", 120, 67500, 148500}, + {"1280x720@120Hz 16:9", 120, 90000, 148500}, + {"720x480@120Hz 4:3", 120, 62937, 54000}, + {"720x480@120Hz 16:9", 120, 62937, 54000}, + {"1440x480i@120Hz 4:3", 120, 31469, 54000}, /* VIC 51 */ - {"1440x480i@120Hz 16:9", 31469, 54000}, - {"720x576@200Hz 4:3", 125000, 108000}, - {"720x576@200Hz 16:9", 125000, 108000}, - {"1440x576i@200Hz 4:3", 62500, 108000}, - {"1440x576i@200Hz 16:9", 62500, 108000}, - {"720x480@240Hz 4:3", 125874, 108000}, - {"720x480@240Hz 16:9", 125874, 108000}, - {"1440x480i@240Hz 4:3", 62937, 108000}, - {"1440x480i@240Hz 16:9", 62937, 108000}, - {"1280x720@24Hz 16:9", 18000, 59400}, + {"1440x480i@120Hz 16:9", 120, 31469, 54000}, + {"720x576@200Hz 4:3", 200, 125000, 108000}, + {"720x576@200Hz 16:9", 200, 125000, 108000}, + {"1440x576i@200Hz 4:3", 200, 62500, 108000}, + {"1440x576i@200Hz 16:9", 200, 62500, 108000}, + {"720x480@240Hz 4:3", 240, 125874, 108000}, + {"720x480@240Hz 16:9", 240, 125874, 108000}, + {"1440x480i@240Hz 4:3", 240, 62937, 108000}, + {"1440x480i@240Hz 16:9", 240, 62937, 108000}, + {"1280x720@24Hz 16:9", 24, 18000, 59400}, /* VIC 61 */ - {"1280x720@25Hz 16:9", 18750, 74250}, - {"1280x720@30Hz 16:9", 22500, 74250}, - {"1920x1080@120Hz 16:9", 135000, 297000}, - {"1920x1080@100Hz 16:9", 112500, 297000}, - {"1280x720@24Hz 64:27", 18000, 59400}, - {"1280x720@25Hz 64:27", 18750, 74250}, - {"1280x720@30Hz 64:27", 22500, 74250}, - {"1280x720@50Hz 64:27", 37500, 74250}, - {"1280x720@60Hz 64:27", 45000, 74250}, - {"1280x720@100Hz 64:27", 75000, 148500}, + {"1280x720@25Hz 16:9", 25, 18750, 74250}, + {"1280x720@30Hz 16:9", 30, 22500, 74250}, + {"1920x1080@120Hz 16:9", 120, 135000, 297000}, + {"1920x1080@100Hz 16:9", 100, 112500, 297000}, + {"1280x720@24Hz 64:27", 24, 18000, 59400}, + {"1280x720@25Hz 64:27", 25, 18750, 74250}, + {"1280x720@30Hz 64:27", 30, 22500, 74250}, + {"1280x720@50Hz 64:27", 50, 37500, 74250}, + {"1280x720@60Hz 64:27", 60, 45000, 74250}, + {"1280x720@100Hz 64:27", 100, 75000, 148500}, /* VIC 71 */ - {"1280x720@120Hz 64:27", 91000, 148500}, - {"1920x1080@24Hz 64:27", 27000, 74250}, - {"1920x1080@25Hz 64:27", 28125, 74250}, - {"1920x1080@30Hz 64:27", 33750, 74250}, - {"1920x1080@50Hz 64:27", 56250, 148500}, - {"1920x1080@60Hz 64:27", 67500, 148500}, - {"1920x1080@100Hz 64:27", 112500, 297000}, - {"1920x1080@120Hz 64:27", 135000, 297000}, - {"1680x720@24Hz 64:27", 18000, 59400}, - {"1680x720@25Hz 64:27", 18750, 59400}, + {"1280x720@120Hz 64:27", 120, 91000, 148500}, + {"1920x1080@24Hz 64:27", 24, 27000, 74250}, + {"1920x1080@25Hz 64:27", 25, 28125, 74250}, + {"1920x1080@30Hz 64:27", 30, 33750, 74250}, + {"1920x1080@50Hz 64:27", 50, 56250, 148500}, + {"1920x1080@60Hz 64:27", 60, 67500, 148500}, + {"1920x1080@100Hz 64:27", 100, 112500, 297000}, + {"1920x1080@120Hz 64:27", 120, 135000, 297000}, + {"1680x720@24Hz 64:27", 24, 18000, 59400}, + {"1680x720@25Hz 64:27", 25, 18750, 59400}, /* VIC 81 */ - {"1680x720@30Hz 64:27", 22500, 59400}, - {"1680x720@50Hz 64:27", 37500, 82500}, - {"1680x720@60Hz 64:27", 45000, 99000}, - {"1680x720@100Hz 64:27", 82500, 165000}, - {"1680x720@120Hz 64:27", 99000, 198000}, - {"2560x1080@24Hz 64:27", 26400, 99000}, - {"2560x1080@25Hz 64:27", 28125, 90000}, - {"2560x1080@30Hz 64:27", 33750, 118800}, - {"2560x1080@50Hz 64:27", 56250, 185625}, - {"2560x1080@60Hz 64:27", 66000, 198000}, + {"1680x720@30Hz 64:27", 30, 22500, 59400}, + {"1680x720@50Hz 64:27", 50, 37500, 82500}, + {"1680x720@60Hz 64:27", 60, 45000, 99000}, + {"1680x720@100Hz 64:27", 100, 82500, 165000}, + {"1680x720@120Hz 64:27", 120, 99000, 198000}, + {"2560x1080@24Hz 64:27", 24, 26400, 99000}, + {"2560x1080@25Hz 64:27", 25, 28125, 90000}, + {"2560x1080@30Hz 64:27", 30, 33750, 118800}, + {"2560x1080@50Hz 64:27", 50, 56250, 185625}, + {"2560x1080@60Hz 64:27", 60, 66000, 198000}, /* VIC 91 */ - {"2560x1080@100Hz 64:27", 125000, 371250}, - {"2560x1080@120Hz 64:27", 150000, 495000}, - {"3840x2160@24Hz 16:9", 54000, 297000}, - {"3840x2160@25Hz 16:9", 56250, 297000}, - {"3840x2160@30Hz 16:9", 67500, 297000}, - {"3840x2160@50Hz 16:9", 112500, 594000}, - {"3840x2160@60Hz 16:9", 135000, 594000}, - {"4096x2160@24Hz 256:135", 54000, 297000}, - {"4096x2160@25Hz 256:135", 56250, 297000}, - {"4096x2160@30Hz 256:135", 67500, 297000}, + {"2560x1080@100Hz 64:27", 100, 125000, 371250}, + {"2560x1080@120Hz 64:27", 120, 150000, 495000}, + {"3840x2160@24Hz 16:9", 24, 54000, 297000}, + {"3840x2160@25Hz 16:9", 25, 56250, 297000}, + {"3840x2160@30Hz 16:9", 30, 67500, 297000}, + {"3840x2160@50Hz 16:9", 50, 112500, 594000}, + {"3840x2160@60Hz 16:9", 60, 135000, 594000}, + {"4096x2160@24Hz 256:135", 24, 54000, 297000}, + {"4096x2160@25Hz 256:135", 25, 56250, 297000}, + {"4096x2160@30Hz 256:135", 30, 67500, 297000}, /* VIC 101 */ - {"4096x2160@50Hz 256:135", 112500, 594000}, - {"4096x2160@60Hz 256:135", 135000, 594000}, - {"3840x2160@24Hz 64:27", 54000, 297000}, - {"3840x2160@25Hz 64:27", 56250, 297000}, - {"3840x2160@30Hz 64:27", 67500, 297000}, - {"3840x2160@50Hz 64:27", 112500, 594000}, - {"3840x2160@60Hz 64:27", 135000, 594000}, + {"4096x2160@50Hz 256:135", 50, 112500, 594000}, + {"4096x2160@60Hz 256:135", 60, 135000, 594000}, + {"3840x2160@24Hz 64:27", 24, 54000, 297000}, + {"3840x2160@25Hz 64:27", 25, 56250, 297000}, + {"3840x2160@30Hz 64:27", 30, 67500, 297000}, + {"3840x2160@50Hz 64:27", 50, 112500, 594000}, + {"3840x2160@60Hz 64:27", 60, 135000, 594000}, }; static void @@ -951,10 +1056,16 @@ cea_svd(unsigned char *x, int n) native = svd & 0x80; } - if (vic > 0 && vic <= ARRAY_SIZE(edid_cea_modes)) + if (vic > 0 && vic <= ARRAY_SIZE(edid_cea_modes)) { mode = edid_cea_modes[vic - 1].name; - else + min_vert_freq_hz = min(min_vert_freq_hz, edid_cea_modes[vic - 1].refresh); + max_vert_freq_hz = max(max_vert_freq_hz, edid_cea_modes[vic - 1].refresh); + min_hor_freq_hz = min(min_hor_freq_hz, edid_cea_modes[vic - 1].hor_freq_hz); + max_hor_freq_hz = max(max_hor_freq_hz, edid_cea_modes[vic - 1].hor_freq_hz); + max_pixclk_khz = max(max_pixclk_khz, edid_cea_modes[vic - 1].pixclk_khz); + } else { mode = "Unknown mode"; + } printf(" VIC %3d %s %s\n", vic, mode, native ? "(native)" : ""); if (vic == 1) @@ -1010,12 +1121,12 @@ cea_vfpdb(unsigned char *x) static struct { const char *name; - int hor_freq_hz, pixclk_khz; + int refresh, hor_freq_hz, pixclk_khz; } edid_cea_hdmi_modes[] = { - {"3840x2160@30Hz 16:9", 67500, 297000}, - {"3840x2160@25Hz 16:9", 56250, 297000}, - {"3840x2160@24Hz 16:9", 54000, 297000}, - {"4096x2160@24Hz 256:135", 54000, 297000}, + {"3840x2160@30Hz 16:9", 30, 67500, 297000}, + {"3840x2160@25Hz 16:9", 25, 56250, 297000}, + {"3840x2160@24Hz 16:9", 24, 54000, 297000}, + {"4096x2160@24Hz 256:135", 24, 54000, 297000}, }; static void @@ -1101,10 +1212,16 @@ cea_hdmi_block(unsigned char *x) const char *mode; vic--; - if (vic < ARRAY_SIZE(edid_cea_hdmi_modes)) - mode = edid_cea_hdmi_modes[vic].name; - else - mode = "Unknown mode"; + if (vic < ARRAY_SIZE(edid_cea_hdmi_modes)) { + mode = edid_cea_hdmi_modes[vic].name; + min_vert_freq_hz = min(min_vert_freq_hz, edid_cea_hdmi_modes[vic].refresh); + max_vert_freq_hz = max(max_vert_freq_hz, edid_cea_hdmi_modes[vic].refresh); + min_hor_freq_hz = min(min_hor_freq_hz, edid_cea_hdmi_modes[vic].hor_freq_hz); + max_hor_freq_hz = max(max_hor_freq_hz, edid_cea_hdmi_modes[vic].hor_freq_hz); + max_pixclk_khz = max(max_pixclk_khz, edid_cea_hdmi_modes[vic].pixclk_khz); + } else { + mode = "Unknown mode"; + } printf(" HDMI VIC %d %s\n", vic, mode); } @@ -1933,32 +2050,6 @@ extract_edid(int fd) return out; } -static const struct { - int x, y, refresh, ratio_w, ratio_h; - int hor_freq_hz, pixclk_khz, interlaced; -} established_timings[] = { - /* 0x23 bit 7 - 0 */ - {720, 400, 70, 9, 5, 31469, 28320}, - {720, 400, 88, 9, 5, 39500, 35500}, - {640, 480, 60, 4, 3, 31469, 25175}, - {640, 480, 67, 4, 3, 35000, 30240}, - {640, 480, 72, 4, 3, 37900, 31500}, - {640, 480, 75, 4, 3, 37500, 31500}, - {800, 600, 56, 4, 3, 35200, 36000}, - {800, 600, 60, 4, 3, 37900, 40000}, - /* 0x24 bit 7 - 0 */ - {800, 600, 72, 4, 3, 48100, 50000}, - {800, 600, 75, 4, 3, 46900, 49500}, - {832, 624, 75, 4, 3, 49726, 57284}, - {1280, 768, 87, 5, 3, 35522, 44900, 1}, - {1024, 768, 60, 4, 3, 48400, 65000}, - {1024, 768, 70, 4, 3, 56500, 75000}, - {1024, 768, 75, 4, 3, 60000, 78750}, - {1280, 1024, 75, 5, 4, 80000, 135000}, - /* 0x25 bit 7*/ - {1152, 870, 75, 192, 145, 67500, 108000}, -}; - static void print_subsection(char *name, unsigned char *edid, int start, int end) { @@ -2252,6 +2343,11 @@ int main(int argc, char **argv) established_timings[i].interlaced ? "i" : "", established_timings[i].refresh, established_timings[i].ratio_w, established_timings[i].ratio_h); + min_vert_freq_hz = min(min_vert_freq_hz, established_timings[i].refresh); + max_vert_freq_hz = max(max_vert_freq_hz, established_timings[i].refresh); + min_hor_freq_hz = min(min_hor_freq_hz, established_timings[i].hor_freq_hz); + max_hor_freq_hz = max(max_hor_freq_hz, established_timings[i].hor_freq_hz); + max_pixclk_khz = max(max_pixclk_khz, established_timings[i].pixclk_khz); } } has_640x480p60_est_timing = edid[0x23] & 0x20; @@ -2335,6 +2431,19 @@ int main(int argc, char **argv) printf("\tHas descriptor blocks other than detailed timings\n"); } + if (has_valid_range_descriptor && + (min_vert_freq_hz < mon_min_vert_freq_hz || + max_vert_freq_hz > mon_max_vert_freq_hz || + min_hor_freq_hz < mon_min_hor_freq_hz || + max_hor_freq_hz > mon_max_hor_freq_hz || + max_pixclk_khz > mon_max_pixclk_khz)) { + conformant = 0; + printf("One or more of the timings is out of range of the Monitor Ranges:\n"); + printf(" Vertical Freq: %d - %d Hz\n", min_vert_freq_hz, max_vert_freq_hz); + printf(" Horizontal Freq: %d - %d Hz\n", min_hor_freq_hz, max_hor_freq_hz); + printf(" Maximum Clock: %.3f MHz\n", max_pixclk_khz / 1000.0); + } + if (nonconformant_extension || !has_valid_checksum || !has_valid_cvt || |