20 #define MODULE_PREFIX "edid"
23 #include "edid-parser.hpp"
30 #define UNUSED(_x_) _x_ = _x_
33 namespace edid_parser {
35 enum EDID_EXTENSION_TAG_BLOCK_MAP
38 ADDITIONAL_TIMING = 0x02,
39 EDID20_EXTENSION = 0x20,
40 COLOR_INFORMATION_TYPE = 0x30,
41 DVI_FEATURE_DATA = 0x40,
42 TOUCH_SCREEN_DATA = 0x50,
43 EXTENSION_TAG_BLOCK_MAP = 0xF0,
44 MANUFACTURER_EXTENSION = 0xFF
47 static void parse_std_timing(
unsigned char* bytes, edid_data_t* data_ptr) {
50 if (bytes[idx] == 1 && bytes[idx + 1] == 1)
return;
51 int h = ((bytes[idx]) + 31) * 8;
53 switch ((bytes[idx + 1] & 0xC0) >> 6) {
54 case 0: v = (h * 10) / 16;
break;
55 case 1: v = (h * 3) / 4;
break;
56 case 2: v = (h * 4) / 5;
break;
57 case 3: v = (h * 9) / 16;
break;
60 int r = (bytes[idx + 1] & 0x3F) + 60;
61 INT_DEBUG(
"STD %dx%d@%d\n", h, v, r);
68 void parse_monitor_descriptor(
unsigned char* bytes, edid_data_t* data_ptr) {
70 if ((bytes[0] == 0) && (bytes[1] == 0) && (bytes[2] == 0)) {
72 if (bytes[3] == 0xFC) {
73 memset(data_ptr->monitor_name,
'\0',
sizeof(data_ptr->monitor_name));
74 for (
int i = 5; (i < (5 + 14)) && (bytes[i] !=
'\n') && isprint(bytes[i]); i++) {
75 data_ptr->monitor_name[i - 5] = bytes[i];
77 INT_DEBUG(
"Monitor name:'%s'\n", data_ptr->monitor_name);
82 static void parse_dtd(
unsigned char* bytes, edid_data_t* data_ptr,
char native) {
85 if (bytes[idx] == 0 && bytes[idx + 1] == 0) {
86 parse_monitor_descriptor(bytes, data_ptr);
90 if (bytes[idx] == 0 || bytes[idx + 1] == 0)
return;
97 int h = ((bytes[idx] & 0xF0) << 4) | bytes[idx - 2];
104 int v = ((bytes[idx] & 0xF0) << 4) | bytes[idx - 2];
125 char p = (bytes[idx] & 0x80) ? 0 : 1;
128 INT_DEBUG(
"DTD, %dx%d@%c, native: %d (%s found already)\n", h, v, (p ?
'p' :
'i'), native, (data_ptr->res.native == EDID_NOT_NATIVE ?
"not" :
""));
131 data_ptr->res.progressive = p ? EDID_PROGRESSIVE : EDID_INTERLACED;
132 data_ptr->res.width = h;
133 data_ptr->res.height = v * ((data_ptr->res.progressive == EDID_INTERLACED) ? 2 : 1);
134 data_ptr->res.native = EDID_NATIVE;
149 {640, 480, 1, 60, 4, 3},
150 {720, 480, 1, 60, 4, 3},
151 {720, 480, 1, 60, 16, 9},
152 {1280, 720, 1, 60, 16, 9},
153 {1920, 1080, 0, 60, 16, 9},
154 {1440, 480, 0, 60, 4, 3},
155 {1440, 480, 0, 60, 16, 9},
156 {1440, 240, 0, 60, 4, 3},
157 {1440, 240, 1, 60, 16, 9},
158 {2880, 480, 0, 60, 4, 3},
159 {2880, 480, 0, 60, 16, 9},
160 {2880, 240, 1, 60, 4, 3},
161 {2880, 240, 1, 60, 16, 9},
162 {1440, 480, 1, 60, 4, 3},
163 {1440, 480, 1, 60, 16, 9},
164 {1920, 1080,1, 60, 16, 9},
165 {720, 576, 1, 50, 4, 3},
166 {720, 576, 1, 50, 16, 9},
167 {1280, 720, 1, 50, 16, 9},
168 {1920, 1080, 0, 50, 16, 9},
169 {1440, 576, 0, 50, 4, 3},
170 {1440, 576, 0, 50, 16, 9},
171 {1440 ,288, 1, 50, 4, 3},
172 {1440, 288, 1, 50, 16, 9},
173 {2880, 576, 0, 50, 4, 3},
174 {2880, 576, 0, 50, 16, 9},
175 {2880, 288, 1, 50, 4, 3},
176 {2880, 288, 1, 50, 16, 9},
177 {1440, 576, 1, 50, 4, 3},
178 {1440, 576, 1, 50, 16, 9},
179 {1920, 1080, 1, 50, 16, 9},
180 {1920, 1080, 1, 24, 16, 9},
181 {1920, 1080, 1, 25, 16, 9},
182 {1920, 1080, 1, 30, 16, 9},
183 {2880, 480, 1, 60, 4, 3},
184 {2880, 480, 1, 60, 16, 9},
185 {2880, 576, 1, 50, 4, 3},
186 {2880, 576, 1, 50, 16, 9},
187 {1920, 1080, 0, 50, 16, 9},
188 {1920, 1080, 0, 100, 16, 9},
189 {1280, 720, 1, 100, 16, 9},
190 {720, 576, 1, 100, 4, 3},
191 {720, 576, 1, 100, 16, 9},
192 {1440, 576, 0, 100, 4, 3},
193 {1440, 576, 0, 100, 16, 9},
194 {1920, 1080, 0, 120, 16, 9},
195 {1280, 720, 1, 120, 16, 9},
196 {720, 480, 1,120, 4, 3},
197 {720, 480, 1, 120, 16, 9},
198 {1440, 480, 0, 120,4, 3},
199 {1440, 480, 0, 120,16, 9},
200 {720, 576, 1, 200, 4, 3},
201 {720, 576, 1, 200, 16, 9},
202 {1440, 576, 0, 200, 4, 3},
203 {720, 576, 0, 200, 16, 9},
204 {720, 480, 1, 240, 4, 3},
205 {720, 480, 1, 240, 16, 9},
206 {720, 480, 0, 240, 4, 3},
207 {720, 480, 0, 240, 16, 9},
208 {1280, 720, 1, 24, 16, 9},
209 {1280, 720, 1, 25, 16, 9},
210 {1280, 720, 1, 30, 16, 9},
211 {1920, 1080, 1, 120, 16, 9},
212 {1920, 1080, 1, 100, 16, 9},
213 {1280, 720, 1, 24, 64, 27},
214 {1280, 720, 1, 25, 64, 27},
215 {1280, 720, 1, 30, 64, 27},
216 {1280, 720, 1, 50, 64, 27},
217 {1280, 720, 1, 60, 64, 27},
218 {1280, 720, 1, 100, 64, 27},
219 {1280, 720, 1, 120, 64, 27},
220 {1920, 1080, 1, 24, 64, 27},
221 {1920, 1080, 1, 25, 64, 27},
222 {1920, 1080, 1, 30, 64, 27},
223 {1920, 1080, 1, 50, 64, 27},
224 {1920, 1080, 1, 60, 64, 27},
225 {1920, 1080, 1, 100, 64, 27},
226 {1920, 1080, 1, 120, 64, 27},
227 {1680, 720, 1, 24, 64, 27},
228 {1680, 720, 1, 25, 64, 27},
229 {1680, 720, 1, 30, 64, 27},
230 {1680, 720, 1, 50, 64, 27},
231 {1680, 720, 1, 60, 64, 27},
232 {1680, 720, 1, 100,64, 27},
233 {1680, 720, 1, 120, 64, 27},
234 {2560, 1080, 1, 24, 64, 27},
235 {2560, 1080, 1, 25, 64, 27},
236 {2560, 1080, 1, 30, 64, 27},
237 {2560, 1080, 1, 50, 64, 27},
238 {2560, 1080, 1, 60, 64, 27},
239 {2560, 1080, 1, 100, 64, 27},
240 {2560, 1080, 1, 120, 64, 27},
241 {3840, 2160, 1, 24, 16, 9},
242 {3840, 2160, 1, 25, 16, 9},
243 {3840, 2160, 1, 30, 16, 9},
244 {3840, 2160, 1, 50, 16, 9},
245 {3840, 2160, 1, 60, 16, 9},
246 {4096, 2160, 1, 24, 256, 135},
247 {4096, 2160, 1, 25, 256, 135},
248 {4096, 2160, 1, 30, 256, 135},
249 {4096, 2160, 1, 50,256, 135},
250 {4096, 2160, 1, 60, 256, 135},
251 {3840, 2160, 1, 24, 64, 27},
252 {3840, 2160, 1, 25, 64, 27},
253 {3840, 2160, 1, 30, 64, 27},
254 {3840, 2160, 1, 50, 64, 27},
255 {3840, 2160, 1, 60, 64, 27},
258 static void parse_ext_video(
const unsigned char* bytes, edid_res_t* edid_res,
const unsigned int len,
const unsigned int native_cnt) {
260 if (edid_res->native == EDID_NOT_NATIVE) {
261 int found_native = 0;
265 for (idx = 0; idx < len; idx++) {
267 INT_DEBUG(
"EXT RES byte 0x%x (num: %d) %s\n", res, (((res >= 129) && (res <= 192)) ? (res & 0x7F) : res), (((res >= 129) && (res <= 192)) ?
"native" :
""));
269 if ((res >= 129) && (res <= 192)) {
271 res = (res & 0x7F) - 1;
278 else if ((native_cnt == 0) && (idx == 0)) {
283 if (res <
sizeof(vic) /
sizeof(edid_vic_t)) {
284 edid_res->width = vic[res].w;
285 edid_res->height = vic[res].h;
286 edid_res->refresh = vic[res].r;
287 edid_res->progressive = vic[res].p ? EDID_PROGRESSIVE : EDID_INTERLACED;
288 edid_res->native = EDID_NATIVE;
289 INT_DEBUG(
"EXT RES native found: %dx%d%c@%d\n", vic[res].w, vic[res].h, vic[res].p ?
'p' :
'i', vic[res].r);
297 static void parse_colorimetry_block(
unsigned char* bytes, edid_data_t* data_ptr) {
298 data_ptr->colorimetry_info = ((uint32_t)bytes[2]) | ((uint32_t)bytes[3] << 8);
299 INT_DEBUG(
"colorimetry info:0x%x\n", data_ptr->colorimetry_info);
302 static void parse_extended_db(uint8_t* bytes, edid_data_t* data_ptr) {
303 const uint8_t extended_code = bytes[1];
304 INT_DEBUG(
"parse_extended_db extended_code=%d\n", extended_code);
307 if (extended_code == 5) {
308 parse_colorimetry_block(bytes, data_ptr);
311 else if (extended_code == 6) {
313 uint8_t eotfByte = bytes[2];
314 data_ptr->hdr_capabilities = 0;
316 if (eotfByte & 0x01) {
317 data_ptr->hdr_capabilities |= HDR_standard_SDR;
320 if (eotfByte & 0x02) {
321 data_ptr->hdr_capabilities |= HDR_standard_Traditional_HDR;
325 if (eotfByte & 0x04) {
326 data_ptr->hdr_capabilities |= HDR_standard_HDR10;
329 if (eotfByte & 0x08) {
330 data_ptr->hdr_capabilities |= HDR_standard_HLG;
333 INT_DEBUG(
"Extended DB - extended code %d not supported\n", extended_code);
337 static void parse_vendodr_specific_block(
unsigned char* bytes, edid_data_t* data_ptr) {
338 data_ptr->physical_address_a = bytes[4] >> 4;
339 data_ptr->physical_address_b = bytes[4] & 0x0F;
340 data_ptr->physical_address_c = bytes[3] >> 4;
341 data_ptr->physical_address_d = bytes[5] & 0x0F;
342 INT_DEBUG(
"Vendor specific bolck, physical adress a:0x%x b:0x%x c:0x%x d:0x%x\n",
343 data_ptr->physical_address_a,
344 data_ptr->physical_address_b,
345 data_ptr->physical_address_c,
346 data_ptr->physical_address_d);
349 static void parse_ext_timing(
unsigned char* bytes, edid_data_t* data_ptr) {
352 INT_DEBUG(
"TimingExtension version: %d\n", bytes[-1]);
353 int dtd_start = bytes[idx] - 2;
356 int native_cnt = bytes[idx] & 0x07;
357 INT_DEBUG(
"Native cnt: %d\n", native_cnt);
359 if (dtd_start != 2) {
361 edid_res_t ext_video_res;
363 ext_video_res.native = EDID_NOT_NATIVE;
365 int tag = (bytes[idx] & 0xE0) >> 5;
366 int len = bytes[idx] & 0x1F;
367 INT_DEBUG(
"parse_ext_timing: extension tag=%d len=%d\n", tag, len);
375 case 2: parse_ext_video(&bytes[idx + 1], &ext_video_res, len, native_cnt);
break;
377 case 3: parse_vendodr_specific_block(&bytes[idx], data_ptr);
break;
385 case 7: parse_extended_db(&bytes[idx], data_ptr);
break;
387 default: INT_DEBUG(
"Unsupported extension tag: 0x%X\n", tag);
394 if (ext_video_res.native != EDID_NOT_NATIVE) {
395 data_ptr->res.width = ext_video_res.width;
396 data_ptr->res.height = ext_video_res.height;
397 data_ptr->res.refresh = ext_video_res.refresh;
398 data_ptr->res.progressive = ext_video_res.progressive;
399 data_ptr->res.native = ext_video_res.native;
405 if (data_ptr->res.native == EDID_NOT_NATIVE) {
407 while (idx < dtd_start + (4 * 18)) {
408 parse_dtd(&bytes[idx], data_ptr, (data_ptr->res.native == EDID_NOT_NATIVE));
414 static void parse_est_timing(
unsigned char b1,
unsigned char b2,
unsigned char b3, edid_data_t* data_ptr) {
418 char progressive = 1;
419 if ((b1 & 0x01) == 0x01) {
424 if ((b1 & 0x02) == 0x02) {
429 if ((b1 & 0x04) == 0x04) {
434 if ((b1 & 0x08) == 0x08) {
439 if ((b1 & 0x10) == 0x10) {
444 if ((b1 & 0x20) == 0x20) {
449 if ((b1 & 0x40) == 0x40) {
454 if ((b1 & 0x80) == 0x80) {
459 if ((b2 & 0x01) == 0x01) {
464 if ((b2 & 0x02) == 0x02) {
469 if ((b2 & 0x04) == 0x04) {
474 if ((b2 & 0x08) == 0x08) {
479 if ((b2 & 0x10) == 0x10) {
485 if ((b2 & 0x20) == 0x20) {
490 if ((b2 & 0x40) == 0x40) {
495 if ((b2 & 0x80) == 0x80) {
500 if ((b3 & 0x80) == 0x80) {
506 INT_DEBUG(
"EST %dx%d%c@%d\n", width, height, progressive ?
'p' :
'i', refresh);
515 int block_checksum_ok(
unsigned char* bytes) {
522 for (
size_t idx=0; idx < 127; ++idx) {
525 return bytes[127] == (256 - (sum % 256)) % 256;
528 edid_status_e parse_extension_block(uint32_t base_idx,
unsigned char* bytes, edid_data_t* data_ptr)
530 int ext_tag = bytes[base_idx];
535 case LCD_TIMING:
break;
537 case ADDITIONAL_TIMING: parse_ext_timing(&bytes[base_idx + 2], data_ptr);
break;
539 case EDID20_EXTENSION:
break;
541 case COLOR_INFORMATION_TYPE:
break;
543 case DVI_FEATURE_DATA:
break;
545 case TOUCH_SCREEN_DATA:
break;
547 case EXTENSION_TAG_BLOCK_MAP:
break;
549 case MANUFACTURER_EXTENSION:
break;
550 default: INT_DEBUG(
"Unsupported tag: 0x%X\n", ext_tag);
553 return EDID_STATUS_OK;
556 edid_status_e parse_extension_blocks(uint32_t extensions,
unsigned char* bytes,
size_t count, edid_data_t* data_ptr)
558 if (count < 128 * (1 + extensions)) {
559 INT_ERROR(
"parse_extension_blocks: too short for extension count - count:%zu extensions:%u\n",count,extensions);
560 return EDID_STATUS_INVALID_HEADER;
568 if (count == 128 * (2 + extensions)) {
571 INT_WARN(
"extensions:%d, but count: %zu - increase extension cnt\n", extensions, count);
576 const int first_ext_tag = bytes[128];
577 uint32_t extension_block_idx = 0;
579 if (extensions > 1 ) {
580 if (first_ext_tag != EXTENSION_TAG_BLOCK_MAP) {
581 INT_ERROR(
"Incorrect input, more than one extension: %d - but block 1 is not extension block map\n", extensions);
582 return EDID_STATUS_NOT_SUPPORTED;
585 extension_block_idx = 1;
589 edid_status_e ret = EDID_STATUS_OK;
591 for ( ; extension_block_idx < extensions; ++extension_block_idx) {
592 const uint32_t base_idx = (1 + extension_block_idx) * 128;
593 ret = parse_extension_block(base_idx, bytes, data_ptr);
594 if (ret != EDID_STATUS_OK)
break;
600 #define SET_LETTER(x) ( isprint((x) + '@') ? (x) + '@' : '\0')
602 static void parse_manufacturer_name(uint8_t b1, uint8_t b2, edid_data_t* data_ptr)
604 data_ptr->manufacturer_name[0] = SET_LETTER((b1 & 0x7C) >> 2);
605 data_ptr->manufacturer_name[1] = SET_LETTER(((b1 & 0x03) << 3) | ((b2 & 0xE0) >> 5));
606 data_ptr->manufacturer_name[2] = SET_LETTER(b2 & 0x1F);
607 data_ptr->manufacturer_name[3] =
'\0';
608 INT_DEBUG(
"Manufacturer name:'%s'\n", data_ptr->manufacturer_name);
611 static void parse_product_code(uint8_t b1, uint8_t b2, edid_data_t* data_ptr)
613 data_ptr->product_code = (((int32_t)b1 << 8) | (int32_t)b2);
614 INT_DEBUG(
"Product code:%d\n", data_ptr->product_code);
617 static void parse_serial_number(uint8_t* serial_ptr, edid_data_t* data_ptr)
619 data_ptr->serial_number = ((int32_t)serial_ptr[0] << 24) | ((int32_t)serial_ptr[1] << 16) | ((int32_t)serial_ptr[2] << 8) | ((int32_t)serial_ptr[3]);
620 INT_DEBUG(
"Serial number:%d\n", data_ptr->serial_number);
623 static void parse_manufacture_week(uint8_t b1, edid_data_t* data_ptr)
625 data_ptr->manufacture_week = b1;
626 INT_DEBUG(
"Manufacture week:%d\n", data_ptr->manufacture_week);
629 static void parse_manufacture_year(uint8_t b1, edid_data_t* data_ptr)
631 data_ptr->manufacture_year = b1;
632 INT_DEBUG(
"Manufacture year:%d\n", data_ptr->manufacture_year);
635 static void parse_edid_version(uint8_t b1, uint8_t b2, edid_data_t* data_ptr)
637 data_ptr->edid_version[0] = b1;
638 data_ptr->edid_version[1] = b2;
639 INT_DEBUG(
"EDID version:%d.%d\n", data_ptr->edid_version[0], data_ptr->edid_version[1]);
642 edid_status_e EDID_Verify(
unsigned char* bytes,
size_t count) {
643 if (!bytes || count < 128) {
644 return EDID_STATUS_INVALID_PARAMETER;
646 static const unsigned char header[8] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
647 if (memcmp(bytes, header,
sizeof(header)) != 0) {
648 INT_ERROR(
"Incorrect input, header does not match: %02x %02x %02x %02x %02x %02x %02x %02x\n", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]);
649 return EDID_STATUS_INVALID_HEADER;
651 return EDID_STATUS_OK;
654 edid_status_e EDID_Parse(
unsigned char* bytes,
size_t count, edid_data_t* data_ptr) {
656 INT_ERROR(
"Incorrect input, data_ptr null\n");
657 return EDID_STATUS_INVALID_PARAMETER;
659 edid_status_e verify_status = EDID_Verify(bytes, count);
660 if (verify_status != EDID_STATUS_OK) {
661 return verify_status;
664 memset(data_ptr, 0,
sizeof(edid_data_t));
665 data_ptr->res.native = EDID_NOT_NATIVE;
671 parse_manufacturer_name(bytes[idx], bytes[idx + 1], data_ptr);
672 parse_product_code(bytes[idx + 2], bytes[idx + 3], data_ptr);
673 parse_serial_number(&bytes[idx + 4], data_ptr);
674 parse_manufacture_week(bytes[idx + 8], data_ptr);
675 parse_manufacture_year(bytes[idx + 9], data_ptr);
678 parse_edid_version(bytes[idx], bytes[idx + 1], data_ptr);
685 parse_est_timing(bytes[idx], bytes[idx + 1], bytes[idx + 2], data_ptr);
688 for (
int i = idx; i < idx + 16; i += 2) {
689 parse_std_timing(&bytes[i], data_ptr);
693 for (
int i = idx; i < idx + 72; i += 18) {
694 parse_dtd(&bytes[i], data_ptr, i == idx);
698 int extension = bytes[idx];
701 edid_status_e ret = EDID_STATUS_OK;
706 ret = parse_extension_blocks(extension,bytes,count,data_ptr);