1 // SPDX-License-Identifier: MIT
3 * Copyright 2006-2012 Red Hat, Inc.
4 * Copyright 2018-2020 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
6 * Author: Adam Jackson <ajax@nwnk.net>
7 * Maintainer: Hans Verkuil <hverkuil-cisco@xs4all.nl>
15 #include "edid-decode.h"
17 static char *manufacturer_name(const unsigned char *x
)
21 name
[0] = ((x
[0] & 0x7c) >> 2) + '@';
22 name
[1] = ((x
[0] & 0x03) << 3) + ((x
[1] & 0xe0) >> 5) + '@';
23 name
[2] = (x
[1] & 0x1f) + '@';
26 if (!isupper(name
[0]) || !isupper(name
[1]) || !isupper(name
[2]))
27 fail("Manufacturer name field contains garbage.\n");
38 { 0x01, 0x0000, 0x000000, { 640, 350, 64, 35, 31500, 0, false,
39 32, 64, 96, true, 32, 3, 60, false } },
41 { 0x02, 0x3119, 0x000000, { 640, 400, 16, 10, 31500, 0, false,
42 32, 64, 96, false, 1, 3, 41, true } },
44 { 0x03, 0x0000, 0x000000, { 720, 400, 9, 5, 35500, 0, false,
45 36, 72, 108, false, 1, 3, 42, true } },
47 { 0x04, 0x3140, 0x000000, { 640, 480, 4, 3, 25175, 0, false,
48 8, 96, 40, false, 2, 2, 25, false, 8, 8 } },
49 { 0x05, 0x314c, 0x000000, { 640, 480, 4, 3, 31500, 0, false,
50 16, 40, 120, false, 1, 3, 20, false, 8, 8 } },
51 { 0x06, 0x314f, 0x000000, { 640, 480, 4, 3, 31500, 0, false,
52 16, 64, 120, false, 1, 3, 16, false } },
53 { 0x07, 0x3159, 0x000000, { 640, 480, 4, 3, 36000, 0, false,
54 56, 56, 80, false, 1, 3, 25, false } },
56 { 0x08, 0x0000, 0x000000, { 800, 600, 4, 3, 36000, 0, false,
57 24, 72, 128, true, 1, 2, 22, true } },
58 { 0x09, 0x4540, 0x000000, { 800, 600, 4, 3, 40000, 0, false,
59 40, 128, 88, true, 1, 4, 23, true } },
60 { 0x0a, 0x454c, 0x000000, { 800, 600, 4, 3, 50000, 0, false,
61 56, 120, 64, true, 37, 6, 23, true } },
62 { 0x0b, 0x454f, 0x000000, { 800, 600, 4, 3, 49500, 0, false,
63 16, 80, 160, true, 1, 3, 21, true } },
64 { 0x0c, 0x4559, 0x000000, { 800, 600, 4, 3, 56250, 0, false,
65 32, 64, 152, true, 1, 3, 27, true } },
66 { 0x0d, 0x0000, 0x000000, { 800, 600, 4, 3, 73250, 1, false,
67 48, 32, 80, true, 3, 4, 29, false } },
69 { 0x0e, 0x0000, 0x000000, { 848, 480, 16, 9, 33750, 0, false,
70 16, 112, 112, true, 6, 8, 23, true } },
72 { 0x0f, 0x0000, 0x000000, { 1024, 768, 4, 3, 44900, 0, true,
73 8, 176, 56, true, 0, 4, 20, true } },
74 { 0x10, 0x6140, 0x000000, { 1024, 768, 4, 3, 65000, 0, false,
75 24, 136, 160, false, 3, 6, 29, false } },
76 { 0x11, 0x614c, 0x000000, { 1024, 768, 4, 3, 75000, 0, false,
77 24, 136, 144, false, 3, 6, 29, false } },
78 { 0x12, 0x614f, 0x000000, { 1024, 768, 4, 3, 78750, 0, false,
79 16, 96, 176, true, 1, 3, 28, true } },
80 { 0x13, 0x6159, 0x000000, { 1024, 768, 4, 3, 94500, 0, false,
81 48, 96, 208, true, 1, 3, 36, true } },
82 { 0x14, 0x0000, 0x000000, { 1024, 768, 4, 3, 115500, 1, false,
83 48, 32, 80, true, 3, 4, 38, false } },
85 { 0x15, 0x714f, 0x000000, { 1152, 864, 4, 3, 108000, 0, false,
86 64, 128, 256, true, 1, 3, 32, true } },
88 { 0x55, 0x81c0, 0x000000, { 1280, 720, 16, 9, 74250, 0, false,
89 110, 40, 220, true, 5, 5, 20, true } },
91 { 0x16, 0x0000, 0x7f1c21, { 1280, 768, 5, 3, 68250, 1, false,
92 48, 32, 80, true, 3, 7, 12, false } },
93 { 0x17, 0x0000, 0x7f1c28, { 1280, 768, 5, 3, 79500, 0, false,
94 64, 128, 192, false, 3, 7, 20, true } },
95 { 0x18, 0x0000, 0x7f1c44, { 1280, 768, 5, 3, 102250, 0, false,
96 80, 128, 208, false, 3, 7, 27, true } },
97 { 0x19, 0x0000, 0x7f1c62, { 1280, 768, 5, 3, 117500, 0, false,
98 80, 136, 216, false, 3, 7, 31, true } },
99 { 0x1a, 0x0000, 0x000000, { 1280, 768, 5, 3, 140250, 0, false,
100 48, 32, 80, true, 3, 7, 35, false } },
102 { 0x1b, 0x0000, 0x8f1821, { 1280, 800, 16, 10, 71000, 1, false,
103 48, 32, 80, true, 3, 6, 14, false } },
104 { 0x1c, 0x8100, 0x8f1828, { 1280, 800, 16, 10, 83500, 0, false,
105 72, 128, 200, false, 3, 6, 22, true } },
106 { 0x1d, 0x810f, 0x8f1844, { 1280, 800, 16, 10, 106500, 0, false,
107 80, 128, 208, false, 3, 6, 29, true } },
108 { 0x1e, 0x8119, 0x8f1862, { 1280, 800, 16, 10, 122500, 0, false,
109 80, 136, 216, false, 3, 6, 34, true } },
110 { 0x1f, 0x0000, 0x000000, { 1280, 800, 16, 10, 146250, 1, false,
111 48, 32, 80, true, 3, 6, 38, false } },
113 { 0x20, 0x8140, 0x000000, { 1280, 960, 4, 3, 108000, 0, false,
114 96, 112, 312, true, 1, 3, 36, true } },
115 { 0x21, 0x8159, 0x000000, { 1280, 960, 4, 3, 148500, 0, false,
116 64, 160, 224, true, 1, 3, 47, true } },
117 { 0x22, 0x0000, 0x000000, { 1280, 960, 4, 3, 175500, 1, false,
118 48, 32, 80, true, 3, 4, 50, false } },
120 { 0x23, 0x8180, 0x000000, { 1280, 1024, 5, 4, 108000, 0, false,
121 48, 112, 248, true, 1, 3, 38, true } },
122 { 0x24, 0x818f, 0x000000, { 1280, 1024, 5, 4, 135000, 0, false,
123 16, 144, 248, true, 1, 3, 38, true } },
124 { 0x25, 0x8199, 0x000000, { 1280, 1024, 5, 4, 157500, 0, false,
125 64, 160, 224, true, 1, 3, 44, true } },
126 { 0x26, 0x0000, 0x000000, { 1280, 1024, 5, 4, 187250, 1, false,
127 48, 32, 80, true, 3, 7, 50, false } },
129 { 0x27, 0x0000, 0x000000, { 1360, 768, 85, 48, 85500, 0, false,
130 64, 112, 256, true, 3, 6, 18, true } },
131 { 0x28, 0x0000, 0x000000, { 1360, 768, 85, 48, 148250, 1, false,
132 48, 32, 80, true, 3, 5, 37, false } },
134 { 0x51, 0x0000, 0x000000, { 1366, 768, 85, 48, 85500, 0, false,
135 70, 143, 213, true, 3, 3, 24, true } },
136 { 0x56, 0x0000, 0x000000, { 1366, 768, 85, 48, 72000, 1, false,
137 14, 56, 64, true, 1, 3, 28, true } },
139 { 0x29, 0x0000, 0x0c2021, { 1400, 1050, 4, 3, 101000, 1, false,
140 48, 32, 80, true, 3, 4, 23, false } },
141 { 0x2a, 0x9040, 0x0c2028, { 1400, 1050, 4, 3, 121750, 0, false,
142 88, 144, 232, false, 3, 4, 32, true } },
143 { 0x2b, 0x904f, 0x0c2044, { 1400, 1050, 4, 3, 156000, 0, false,
144 104, 144, 248, false, 3, 4, 42, true } },
145 { 0x2c, 0x9059, 0x0c2062, { 1400, 1050, 4, 3, 179500, 0, false,
146 104, 152, 256, false, 3, 4, 48, true } },
147 { 0x2d, 0x0000, 0x000000, { 1400, 1050, 4, 3, 208000, 1, false,
148 48, 32, 80, true, 3, 4, 55, false } },
150 { 0x2e, 0x0000, 0xc11821, { 1440, 900, 16, 10, 88750, 1, false,
151 48, 32, 80, true, 3, 6, 17, false } },
152 { 0x2f, 0x9500, 0xc11828, { 1440, 900, 16, 10, 106500, 0, false,
153 80, 152, 232, false, 3, 6, 25, true } },
154 { 0x30, 0x950f, 0xc11844, { 1440, 900, 16, 10, 136750, 0, false,
155 96, 152, 248, false, 3, 6, 33, true } },
156 { 0x31, 0x9519, 0xc11868, { 1440, 900, 16, 10, 157000, 0, false,
157 104, 152, 256, false, 3, 6, 39, true } },
158 { 0x32, 0x0000, 0x000000, { 1440, 900, 16, 10, 182750, 1, false,
159 48, 32, 80, true, 3, 6, 44, false } },
161 { 0x53, 0xa9c0, 0x000000, { 1600, 900, 16, 9, 108000, 1, false,
162 24, 80, 96, true, 1, 3, 96, true } },
164 { 0x33, 0xa940, 0x000000, { 1600, 1200, 4, 3, 162000, 0, false,
165 64, 192, 304, true, 1, 3, 46, true } },
166 { 0x34, 0xa945, 0x000000, { 1600, 1200, 4, 3, 175500, 0, false,
167 64, 192, 304, true, 1, 3, 46, true } },
168 { 0x35, 0xa94a, 0x000000, { 1600, 1200, 4, 3, 189000, 0, false,
169 64, 192, 304, true, 1, 3, 46, true } },
170 { 0x36, 0xa94f, 0x000000, { 1600, 1200, 4, 3, 202500, 0, false,
171 64, 192, 304, true, 1, 3, 46, true } },
172 { 0x37, 0xa959, 0x000000, { 1600, 1200, 4, 3, 229500, 0, false,
173 64, 192, 304, true, 1, 3, 46, true } },
174 { 0x38, 0x0000, 0x000000, { 1600, 1200, 4, 3, 268250, 1, false,
175 48, 32, 80, true, 3, 4, 64, false } },
177 { 0x39, 0x0000, 0x0c2821, { 1680, 1050, 16, 10, 119000, 1, false,
178 48, 32, 80, true, 3, 6, 21, false } },
179 { 0x3a, 0xb300, 0x0c2828, { 1680, 1050, 16, 10, 146250, 0, false,
180 104, 176, 280, false, 3, 6, 30, true } },
181 { 0x3b, 0xb30f, 0x0c2844, { 1680, 1050, 16, 10, 187000, 0, false,
182 120, 176, 296, false, 3, 6, 40, true } },
183 { 0x3c, 0xb319, 0x0c2868, { 1680, 1050, 16, 10, 214750, 0, false,
184 128, 176, 304, false, 3, 6, 46, true } },
185 { 0x3d, 0x0000, 0x000000, { 1680, 1050, 16, 10, 245500, 1, false,
186 48, 32, 80, true, 3, 6, 53, false } },
188 { 0x3e, 0xc140, 0x000000, { 1792, 1344, 4, 3, 204750, 0, false,
189 128, 200, 328, false, 1, 3, 46, true } },
190 { 0x3f, 0xc14f, 0x000000, { 1792, 1344, 4, 3, 261000, 0, false,
191 96, 216, 352, false, 1, 3, 69, true } },
192 { 0x40, 0x0000, 0x000000, { 1792, 1344, 4, 3, 333250, 1, false,
193 48, 32, 80, true, 3, 4, 72, false } },
195 { 0x41, 0xc940, 0x000000, { 1856, 1392, 4, 3, 218250, 0, false,
196 96, 224, 352, false, 1, 3, 43, true } },
197 { 0x42, 0xc94f, 0x000000, { 1856, 1392, 4, 3, 288000, 0, false,
198 128, 224, 352, false, 1, 3, 104, true } },
199 { 0x43, 0x0000, 0x000000, { 1856, 1392, 4, 3, 356500, 1, false,
200 48, 32, 80, true, 3, 4, 74, false } },
202 { 0x52, 0xd1c0, 0x000000, { 1920, 1080, 16, 9, 148500, 0, false,
203 88, 44, 148, true, 4, 5, 36, true } },
205 { 0x44, 0x0000, 0x572821, { 1920, 1200, 16, 10, 154000, 1, false,
206 48, 32, 80, true, 3, 6, 26, false } },
207 { 0x45, 0xd100, 0x572828, { 1920, 1200, 16, 10, 193250, 0, false,
208 136, 200, 336, false, 3, 6, 36, true } },
209 { 0x46, 0xd10f, 0x572844, { 1920, 1200, 16, 10, 245250, 0, false,
210 136, 208, 344, false, 3, 6, 46, true } },
211 { 0x47, 0xd119, 0x572862, { 1920, 1200, 16, 10, 281250, 0, false,
212 144, 208, 352, false, 3, 6, 53, true } },
213 { 0x48, 0x0000, 0x000000, { 1920, 1200, 16, 10, 317000, 1, false,
214 48, 32, 80, true, 3, 6, 62, false } },
216 { 0x49, 0xd140, 0x000000, { 1920, 1440, 4, 3, 234000, 0, false,
217 128, 208, 344, false, 1, 3, 56, true } },
218 { 0x4a, 0xd14f, 0x000000, { 1920, 1440, 4, 3, 297000, 0, false,
219 144, 224, 352, false, 1, 3, 56, true } },
220 { 0x4b, 0x0000, 0x000000, { 1920, 1440, 4, 3, 380500, 1, false,
221 48, 32, 80, true, 2, 3, 78, false } },
223 { 0x54, 0xe1c0, 0x000000, { 2048, 1152, 16, 9, 162000, 1, false,
224 26, 80, 96, true, 1, 3, 44, true } },
226 { 0x4c, 0x0000, 0x1f3821, { 2560, 1600, 16, 10, 268500, 1, false,
227 48, 32, 80, true, 3, 6, 37, false } },
228 { 0x4d, 0x0000, 0x1f3828, { 2560, 1600, 16, 10, 348500, 0, false,
229 192, 280, 472, false, 3, 6, 49, true } },
230 { 0x4e, 0x0000, 0x1f3844, { 2560, 1600, 16, 10, 443250, 0, false,
231 208, 280, 488, false, 3, 6, 63, true } },
232 { 0x4f, 0x0000, 0x1f3862, { 2560, 1600, 16, 10, 505250, 0, false,
233 208, 280, 488, false, 3, 6, 73, true } },
234 { 0x50, 0x0000, 0x000000, { 2560, 1600, 16, 10, 552750, 1, false,
235 48, 32, 80, true, 3, 6, 85, false } },
237 { 0x57, 0x0000, 0x000000, { 4096, 2160, 256, 135, 556744, 1, false,
238 8, 32, 40, true, 48, 8, 6, false } },
239 { 0x58, 0x0000, 0x000000, { 4096, 2160, 256, 135, 556188, 1, false,
240 8, 32, 40, true, 48, 8, 6, false } },
243 // The timings for the IBM/Apple modes are copied from the linux
244 // kernel timings in drivers/gpu/drm/drm_edid.c, except for the
245 // 1152x870 Apple format, which is copied from
246 // drivers/video/fbdev/macmodes.c since the drm_edid.c version
247 // describes a 1152x864 format.
248 static const struct {
252 } established_timings12
[] = {
254 { 0x00, { 720, 400, 9, 5, 28320, 0, false,
255 18, 108, 54, false, 21, 2, 26, true }, "IBM" },
256 { 0x00, { 720, 400, 9, 5, 35500, 0, false,
257 18, 108, 54, false, 12, 2, 35, true }, "IBM" },
259 { 0x00, { 640, 480, 4, 3, 30240, 0, false,
260 64, 64, 96, false, 3, 3, 39, false }, "Apple" },
268 { 0x00, { 832, 624, 4, 3, 57284, 0, false,
269 32, 64, 224, false, 1, 3, 39, false }, "Apple" },
276 { 0x00, { 1152, 870, 192, 145, 100000, 0, false,
277 48, 128, 128, true, 3, 3, 39, true }, "Apple" },
280 // The bits in the Established Timings III map to DMT timings,
281 // this array has the DMT IDs.
282 static const unsigned char established_timings3_dmt_ids
[] = {
293 0x16, // 1280x768@60 RB
299 0x23, // 1280x1024@60
300 0x25, // 1280x1024@85
303 0x2e, // 1440x900@60 RB
307 0x29, // 1400x1050@60 RB
308 0x2a, // 1400x1050@60
309 0x2b, // 1400x1050@75
311 0x2c, // 1400x1050@85
312 0x39, // 1680x1050@60 RB
313 0x3a, // 1680x1050@60
314 0x3b, // 1680x1050@75
315 0x3c, // 1680x1050@85
316 0x33, // 1600x1200@60
317 0x34, // 1600x1200@65
318 0x35, // 1600x1200@70
320 0x36, // 1600x1200@75
321 0x37, // 1600x1200@85
322 0x3e, // 1792x1344@60
323 0x3f, // 1792x1344@75
324 0x41, // 1856x1392@60
325 0x42, // 1856x1392@75
326 0x44, // 1920x1200@60 RB
327 0x45, // 1920x1200@60
329 0x46, // 1920x1200@75
330 0x47, // 1920x1200@85
331 0x49, // 1920x1440@60
332 0x4a, // 1920x1440@75
335 const struct timings
*find_dmt_id(unsigned char dmt_id
)
339 for (i
= 0; i
< ARRAY_SIZE(dmt_timings
); i
++)
340 if (dmt_timings
[i
].dmt_id
== dmt_id
)
341 return &dmt_timings
[i
].t
;
345 static const struct timings
*find_std_id(unsigned short std_id
, unsigned char &dmt_id
)
349 for (i
= 0; i
< ARRAY_SIZE(dmt_timings
); i
++)
350 if (dmt_timings
[i
].std_id
== std_id
) {
351 dmt_id
= dmt_timings
[i
].dmt_id
;
352 return &dmt_timings
[i
].t
;
357 void edid_state::list_established_timings()
359 printf("Established Timings I & II, 'Byte' is the EDID address:\n\n");
360 for (unsigned i
= 0; i
< ARRAY_SIZE(established_timings12
); i
++) {
361 unsigned char dmt_id
= established_timings12
[i
].dmt_id
;
362 const struct timings
*t
;
366 sprintf(type
, "DMT 0x%02x", dmt_id
);
367 t
= find_dmt_id(dmt_id
);
369 t
= &established_timings12
[i
].t
;
370 sprintf(type
, "%-8s", established_timings12
[i
].type
);
372 printf("Byte 0x%02x, Bit %u: ", 0x23 + i
/ 8, 7 - i
% 8);
373 print_timings("", t
, type
, "", false, false);
375 printf("\nEstablished timings III, 'Byte' is the offset from the start of the descriptor:\n\n");
376 for (unsigned i
= 0; i
< ARRAY_SIZE(established_timings3_dmt_ids
); i
++) {
377 unsigned char dmt_id
= established_timings3_dmt_ids
[i
];
380 sprintf(type
, "DMT 0x%02x", dmt_id
);
381 printf("Byte 0x%02x, Bit %u: ", 6 + i
/ 8, 7 - i
% 8);
382 print_timings("", find_dmt_id(dmt_id
), type
, "", false, false);
386 const struct timings
*close_match_to_dmt(const timings
&t
, unsigned &dmt
)
388 for (unsigned i
= 0; i
< ARRAY_SIZE(dmt_timings
); i
++) {
389 if (timings_close_match(t
, dmt_timings
[i
].t
)) {
390 dmt
= dmt_timings
[i
].dmt_id
;
391 return &dmt_timings
[i
].t
;
398 void edid_state::list_dmts()
402 for (unsigned i
= 0; i
< ARRAY_SIZE(dmt_timings
); i
++) {
403 sprintf(type
, "DMT 0x%02x", dmt_timings
[i
].dmt_id
);
405 if (dmt_timings
[i
].std_id
)
406 flags
+= std::string("STD: ") +
407 utohex(dmt_timings
[i
].std_id
>> 8) + " " +
408 utohex(dmt_timings
[i
].std_id
& 0xff);
409 if (dmt_timings
[i
].cvt_id
)
410 add_str(flags
, std::string("CVT: ") +
411 utohex(dmt_timings
[i
].cvt_id
>> 16) + " " +
412 utohex((dmt_timings
[i
].cvt_id
>> 8) & 0xff) + " " +
413 utohex(dmt_timings
[i
].cvt_id
& 0xff));
414 print_timings("", &dmt_timings
[i
].t
, type
, flags
.c_str(), false, false);
418 void edid_state::detailed_cvt_descriptor(const char *prefix
, const unsigned char *x
, bool first
)
420 static const unsigned char empty
[3] = { 0, 0, 0 };
421 struct timings cvt_t
= {};
422 unsigned char preferred
;
424 if (!first
&& !memcmp(x
, empty
, 3))
429 fail("CVT byte 0 is 0, which is a reserved value.\n");
430 cvt_t
.vact
|= (x
[1] & 0xf0) << 4;
434 switch (x
[1] & 0x0c) {
436 default: /* avoids 'width/ratio may be used uninitialized' warnings */
453 cvt_t
.hact
= 8 * (((cvt_t
.vact
* cvt_t
.hratio
) / cvt_t
.vratio
) / 8);
456 fail("Reserved bits of CVT byte 1 are non-zero.\n");
458 fail("Reserved bit of CVT byte 2 is non-zero.\n");
460 fail("CVT byte 2 does not support any vertical rates.\n");
461 preferred
= (x
[2] & 0x60) >> 5;
462 if (preferred
== 1 && (x
[2] & 0x01))
464 if (!(x
[2] & (1 << (4 - preferred
))))
465 fail("The preferred CVT Vertical Rate is not supported.\n");
467 static const char *s_pref
= "preferred vertical rate";
470 edid_cvt_mode(50, cvt_t
);
471 print_timings(prefix
, &cvt_t
, "CVT", preferred
== 0 ? s_pref
: "");
474 edid_cvt_mode(60, cvt_t
);
475 print_timings(prefix
, &cvt_t
, "CVT", preferred
== 1 ? s_pref
: "");
478 edid_cvt_mode(75, cvt_t
);
479 print_timings(prefix
, &cvt_t
, "CVT", preferred
== 2 ? s_pref
: "");
482 edid_cvt_mode(85, cvt_t
);
483 print_timings(prefix
, &cvt_t
, "CVT", preferred
== 3 ? s_pref
: "");
486 cvt_t
.rb
= RB_CVT_V1
;
487 edid_cvt_mode(60, cvt_t
);
488 print_timings(prefix
, &cvt_t
, "CVT", preferred
== 4 ? s_pref
: "");
492 // Base Block uses Code Page 437, unprintable characters are represented by ▯
493 static const char *cp437
[256] = {
494 "▯", "☺", "☻", "♥", "♦", "♣", "♠", "•", "◘", "○", "◙", "♂", "♀", "♪", "♫", "☼",
495 "►", "◄", "↕", "‼", "¶", "§", "▬", "↨", "↑", "↓", "→", "←", "∟", "↔", "▲", "▼",
496 " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
497 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?",
498 "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
499 "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_",
500 "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
501 "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "⌂",
502 "Ç", "ü", "é", "â", "ä", "à", "å", "ç", "ê", "ë", "è", "ï", "î", "ì", "Ä", "Å",
503 "É", "æ", "Æ", "ô", "ö", "ò", "û", "ù", "ÿ", "Ö", "Ü", "¢", "£", "¥", "₧", "ƒ",
504 "á", "í", "ó", "ú", "ñ", "Ñ", "ª", "º", "¿", "⌐", "¬", "½", "¼", "¡", "«", "»",
505 "░", "▒", "▓", "│", "┤", "╡", "╢", "╖", "╕", "╣", "║", "╗", "╝", "╜", "╛", "┐",
506 "└", "┴", "┬", "├", "─", "┼", "╞", "╟", "╚", "╔", "╩", "╦", "╠", "═", "╬", "╧",
507 "╨", "╤", "╥", "╙", "╘", "╒", "╓", "╫", "╪", "┘", "┌", "█", "▄", "▌", "▐", "▀",
508 "α", "ß", "Γ", "π", "Σ", "σ", "µ", "τ", "Φ", "Θ", "Ω", "δ", "∞", "φ", "ε", "∩",
509 "≡", "±", "≥", "≤", "⌠", "⌡", "÷", "≈", "°", "∙", "·", "√", "ⁿ", "²", "■", "▯"
512 // DisplayID uses ISO 8859-1, unprintable chararcters are represented by ▯
513 static const char *ascii
[256] = {
514 "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯",
515 "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯",
516 " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
517 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?",
518 "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
519 "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_",
520 "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
521 "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "▯",
522 "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯",
523 "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯", "▯",
524 "▯", "¡", "¢", "£", "¤", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "▯", "®", "¯",
525 "°", "±", "²", "³", "´", "µ", "¶", "·", "¸", "¹", "º", "»", "¼", "½", "¾", "¿",
526 "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï",
527 "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß",
528 "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï",
529 "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷", "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ"
532 bool to_utf8
= false;
534 /* extract a string from a detailed subblock, checking for termination */
535 char *extract_string(const unsigned char *x
, unsigned len
, bool is_cp437
)
538 const char **conv
= is_cp437
? cp437
: ascii
;
539 bool seen_newline
= false;
540 bool added_space
= false;
543 memset(s
, 0, sizeof(s
));
545 for (i
= 0; i
< len
; i
++) {
546 // The encoding is cp437, so any character is allowed,
547 // but in practice it is unwise to use a non-ASCII character.
548 bool non_ascii
= (x
[i
] >= 1 && x
[i
] < 0x20 && x
[i
] != 0x0a) || x
[i
] >= 0x7f;
552 fail("Non-space after newline.\n");
555 } else if (x
[i
] == 0x0a) {
558 fail("Empty string.\n");
559 else if (added_space
)
560 fail("One or more trailing spaces before newline.\n");
563 // While incorrect, a \0 is often used to end the string
564 fail("NUL byte at position %u.\n", i
);
566 } else if (x
[i
] == 0xff) {
567 // 0xff is sometimes (incorrectly) used to pad the remainder
569 fail("0xff byte at position %u.\n", i
);
571 } else if (!non_ascii
) {
572 added_space
= x
[i
] == ' ';
574 strcat(s
, conv
[x
[i
]]);
579 warn("Non-ASCII character 0x%02x (%s) at position %u, can cause problems.\n",
580 x
[i
], conv
[x
[i
]], i
);
581 strcat(s
, conv
[x
[i
]]);
583 warn("Non-ASCII character 0x%02x at position %u, can cause problems.\n",
590 /* Does the string end with a space? */
591 if (!seen_newline
&& added_space
)
592 fail("No newline, but one or more trailing spaces.\n");
597 void edid_state::print_standard_timing(const char *prefix
, unsigned char b1
, unsigned char b2
,
598 bool gtf_only
, bool show_both
)
600 const struct timings
*t
;
601 struct timings formula
= {};
602 unsigned hratio
, vratio
;
603 unsigned hact
, vact
, refresh
;
604 unsigned char dmt_id
= 0;
607 if (b1
!= 0x01 || b2
!= 0x01)
608 fail("Use 0x0101 as the invalid Standard Timings code, not 0x%02x%02x.\n", b1
, b2
);
612 t
= find_std_id((b1
<< 8) | b2
, dmt_id
);
615 sprintf(type
, "DMT 0x%02x", dmt_id
);
616 print_timings(prefix
, t
, type
);
619 hact
= (b1
+ 31) * 8;
620 switch ((b2
>> 6) & 0x3) {
622 if (gtf_only
|| show_both
|| base
.edid_minor
>= 3) {
643 vact
= (double)hact
* vratio
/ hratio
;
644 refresh
= (b2
& 0x3f) + 60;
648 formula
.hratio
= hratio
;
649 formula
.vratio
= vratio
;
651 if (!gtf_only
&& (show_both
|| base
.edid_minor
>= 4)) {
652 if (show_both
|| base
.supports_cvt
) {
653 edid_cvt_mode(refresh
, formula
);
654 print_timings(prefix
, &formula
, "CVT ",
655 show_both
? "" : "EDID 1.4 source");
658 * An EDID 1.3 source will assume GTF, so both GTF and CVT
659 * have to be supported.
661 edid_gtf_mode(refresh
, formula
);
662 if (base
.supports_cvt
)
663 print_timings(prefix
, &formula
, "GTF ", "EDID 1.3 source");
665 print_timings(prefix
, &formula
, "GTF ");
666 } else if (gtf_only
|| base
.edid_minor
>= 2) {
667 edid_gtf_mode(refresh
, formula
);
668 print_timings(prefix
, &formula
, "GTF ");
670 printf("%sUnknown : %5ux%-5u %3u.000 Hz %3u:%u\n",
671 prefix
, hact
, vact
, refresh
, hratio
, vratio
);
672 min_vert_freq_hz
= min(min_vert_freq_hz
, refresh
);
673 max_vert_freq_hz
= max(max_vert_freq_hz
, refresh
);
676 // See Ref. D-8 in the EDID-1.4 spec
678 warn("Standard Timing %ux%u has a dubious odd vertical resolution.\n", hact
, vact
);
681 void edid_state::detailed_display_range_limits(const unsigned char *x
)
683 int h_max_offset
= 0, h_min_offset
= 0;
684 int v_max_offset
= 0, v_min_offset
= 0;
686 bool has_sec_gtf
= false;
687 std::string range_class
;
689 data_block
= "Display Range Limits";
690 printf(" %s:\n", data_block
.c_str());
691 base
.has_display_range_descriptor
= 1;
693 if (base
.edid_minor
>= 4) {
709 * despite the values, this is not a bitfield.
712 case 0x00: /* default gtf */
714 if (base
.edid_minor
>= 4 && !base
.supports_continuous_freq
)
715 fail("GTF is supported, but the display does not support continuous frequencies.\n");
716 if (base
.edid_minor
>= 4)
717 warn("GTF support is deprecated in EDID 1.4.\n");
719 case 0x01: /* range limits only */
720 range_class
= "Range Limits Only";
721 if (base
.edid_minor
< 4)
722 fail("'%s' is not allowed for EDID < 1.4.\n", range_class
.c_str());
724 case 0x02: /* secondary gtf curve */
725 range_class
= "Secondary GTF";
726 if (base
.edid_minor
>= 4 && !base
.supports_continuous_freq
)
727 fail("Secondary GTF is supported, but the display does not support continuous frequencies.\n");
728 if (base
.edid_minor
>= 4)
729 warn("GTF support is deprecated in EDID 1.4.\n");
735 if (base
.edid_minor
< 4)
736 fail("'%s' is not allowed for EDID < 1.4.\n", range_class
.c_str());
737 else if (!base
.supports_continuous_freq
)
738 fail("CVT is supported, but the display does not support continuous frequencies.\n");
740 default: /* invalid */
741 fail("Unknown range class (0x%02x).\n", x
[10]);
742 range_class
= std::string("Unknown (") + utohex(x
[10]) + ")";
746 if (x
[5] + v_min_offset
> x
[6] + v_max_offset
)
747 fail("Min vertical rate > max vertical rate.\n");
748 base
.min_display_vert_freq_hz
= x
[5] + v_min_offset
;
749 base
.max_display_vert_freq_hz
= x
[6] + v_max_offset
;
750 if (x
[7] + h_min_offset
> x
[8] + h_max_offset
)
751 fail("Min horizontal freq > max horizontal freq.\n");
752 base
.min_display_hor_freq_hz
= (x
[7] + h_min_offset
) * 1000;
753 base
.max_display_hor_freq_hz
= (x
[8] + h_max_offset
) * 1000;
754 printf(" Monitor ranges (%s): %d-%d Hz V, %d-%d kHz H",
756 x
[5] + v_min_offset
, x
[6] + v_max_offset
,
757 x
[7] + h_min_offset
, x
[8] + h_max_offset
);
759 // For EDID 1.3 the horizontal frequency maxes out at 255 kHz.
760 // So to avoid false range-check warnings due to this limitation,
761 // just double the max_display_hor_freq_hz in this case.
762 if (base
.edid_minor
< 4 && x
[8] == 0xff)
763 base
.max_display_hor_freq_hz
*= 2;
765 // For EDID 1.3 the vertical frequency maxes out at 255 Hz.
766 // So to avoid false range-check warnings due to this limitation,
767 // just double the max_display_vert_freq_hz in this case.
768 if (base
.edid_minor
< 4 && x
[6] == 0xff)
769 base
.max_display_vert_freq_hz
*= 2;
772 base
.max_display_pixclk_khz
= x
[9] * 10000;
773 printf(", max dotclock %d MHz\n", x
[9] * 10);
776 if (base
.edid_minor
>= 4)
777 fail("EDID 1.4 block does not set max dotclock.\n");
782 fail("Byte 11 is 0x%02x instead of 0x00.\n", x
[11]);
783 if (memchk(x
+ 12, 6)) {
784 fail("Zeroed Secondary Curve Block.\n");
786 printf(" GTF Secondary Curve Block:\n");
787 printf(" Start frequency: %u kHz\n", x
[12] * 2);
788 printf(" C: %.1f%%\n", x
[13] / 2.0);
789 printf(" M: %u%%/kHz\n", (x
[15] << 8) | x
[14]);
790 printf(" K: %u\n", x
[16]);
791 printf(" J: %.1f%%\n", x
[17] / 2.0);
794 int max_h_pixels
= 0;
796 printf(" CVT version %d.%d\n", (x
[11] & 0xf0) >> 4, x
[11] & 0x0f);
799 unsigned raw_offset
= (x
[12] & 0xfc) >> 2;
801 printf(" Real max dotclock: %.2f MHz\n",
802 (x
[9] * 10) - (raw_offset
* 0.25));
803 if (raw_offset
>= 40)
804 warn("CVT block corrects dotclock by more than 9.75 MHz.\n");
807 max_h_pixels
= x
[12] & 0x03;
809 max_h_pixels
|= x
[13];
812 printf(" Max active pixels per line: %d\n", max_h_pixels
);
814 printf(" Supported aspect ratios:%s%s%s%s%s\n",
815 x
[14] & 0x80 ? " 4:3" : "",
816 x
[14] & 0x40 ? " 16:9" : "",
817 x
[14] & 0x20 ? " 16:10" : "",
818 x
[14] & 0x10 ? " 5:4" : "",
819 x
[14] & 0x08 ? " 15:9" : "");
821 fail("Reserved bits of byte 14 are non-zero.\n");
823 printf(" Preferred aspect ratio: ");
824 switch ((x
[15] & 0xe0) >> 5) {
841 printf("Unknown (0x%02x)", (x
[15] & 0xe0) >> 5);
842 fail("Invalid preferred aspect ratio 0x%02x.\n",
843 (x
[15] & 0xe0) >> 5);
849 printf(" Supports CVT standard blanking\n");
851 printf(" Supports CVT reduced blanking\n");
854 fail("Reserved bits of byte 15 are non-zero.\n");
857 printf(" Supported display scaling:\n");
859 printf(" Horizontal shrink\n");
861 printf(" Horizontal stretch\n");
863 printf(" Vertical shrink\n");
865 printf(" Vertical stretch\n");
869 fail("Reserved bits of byte 16 are non-zero.\n");
872 printf(" Preferred vertical refresh: %d Hz\n", x
[17]);
874 warn("CVT block does not set preferred refresh rate.\n");
877 fail("Byte 11 is 0x%02x instead of 0x0a.\n", x
[11]);
878 for (unsigned i
= 12; i
<= 17; i
++) {
880 fail("Bytes 12-17 must be 0x20.\n");
887 void edid_state::detailed_epi(const unsigned char *x
)
889 data_block
= "EPI Descriptor";
890 printf(" %s:\n", data_block
.c_str());
892 unsigned v
= x
[5] & 0x07;
894 printf(" Bits per pixel: %u\n", 18 + v
* 6);
896 fail("Invalid bits per pixel.\n");
897 v
= (x
[5] & 0x18) >> 3;
898 printf(" Pixels per clock: %u\n", 1 << v
);
900 fail("Invalid pixels per clock.\n");
901 v
= (x
[5] & 0x60) >> 5;
902 printf(" Data color mapping: %sconventional\n", v
? "non-" : "");
904 fail("Unknown data color mapping (0x%02x).\n", v
);
906 fail("Non-zero reserved field in byte 5.\n");
909 printf(" Interface type: ");
911 case 0x00: printf("LVDS TFT\n"); break;
912 case 0x01: printf("monoSTN 4/8 Bit\n"); break;
913 case 0x02: printf("colorSTN 8/16 Bit\n"); break;
914 case 0x03: printf("18 Bit TFT\n"); break;
915 case 0x04: printf("24 Bit TFT\n"); break;
916 case 0x05: printf("TMDS\n"); break;
918 printf("Unknown (0x%02x)\n", v
);
919 fail("Invalid interface type 0x%02x.\n", v
);
922 printf(" DE polarity: DE %s active\n",
923 (x
[6] & 0x10) ? "low" : "high");
924 printf(" FPSCLK polarity: FPSCLK %sinverted\n",
925 (x
[6] & 0x20) ? "" : "not ");
927 fail("Non-zero reserved field in byte 6.\n");
929 printf(" Vertical display mode: %s\n",
930 (x
[7] & 0x01) ? "Up/Down reverse mode" : "normal");
931 printf(" Horizontal display mode: %s\n",
932 (x
[7] & 0x02) ? "Left/Right reverse mode" : "normal");
934 fail("Non-zero reserved field in byte 7.\n");
937 printf(" Total power on sequencing delay: ");
939 printf("%u ms\n", v
* 10);
941 printf("VGA controller default\n");
942 v
= (x
[8] & 0xf0) >> 4;
943 printf(" Total power off sequencing delay: ");
945 printf("%u ms\n", v
* 10);
947 printf("VGA controller default\n");
950 printf(" Contrast power on sequencing delay: ");
952 printf("%u ms\n", v
* 10);
954 printf("VGA controller default\n");
955 v
= (x
[9] & 0xf0) >> 4;
956 printf(" Contrast power off sequencing delay: ");
958 printf("%u ms\n", v
* 10);
960 printf("VGA controller default\n");
963 const char *s
= (x
[10] & 0x80) ? "" : " (ignored)";
965 printf(" Backlight brightness control: %u steps%s\n", v
, s
);
966 printf(" Backlight enable at boot: %s%s\n",
967 (x
[10] & 0x40) ? "off" : "on", s
);
968 printf(" Backlight control enable: %s\n",
969 (x
[10] & 0x80) ? "enabled" : "disabled");
972 s
= (x
[11] & 0x80) ? "" : " (ignored)";
974 printf(" Contrast voltable control: %u steps%s\n", v
, s
);
976 fail("Non-zero reserved field in byte 11.\n");
977 printf(" Contrast control enable: %s\n",
978 (x
[11] & 0x80) ? "enabled" : "disabled");
980 if (x
[12] || x
[13] || x
[14] || x
[15] || x
[16])
981 fail("Non-zero values in reserved bytes 12-16.\n");
983 printf(" EPI Version: %u.%u\n", (x
[17] & 0xf0) >> 4, x
[17] & 0x0f);
986 void edid_state::detailed_timings(const char *prefix
, const unsigned char *x
,
989 struct timings t
= {};
991 std::string s_sync
, s_flags
;
993 // Only count DTDs in base block 0 or CTA-861 extension blocks
996 data_block
= "Detailed Timing Descriptor #" + std::to_string(base
.dtd_cnt
);
997 t
.pixclk_khz
= (x
[0] + (x
[1] << 8)) * 10;
998 if (t
.pixclk_khz
< 10000) {
999 printf("%sDetailed mode: ", prefix
);
1000 hex_block("", x
, 18, true, 18);
1002 fail("First two bytes are 0, invalid data.\n");
1004 fail("Pixelclock < 10 MHz, assuming invalid data 0x%02x 0x%02x.\n",
1010 * If the borders are non-zero, then it is unclear how to interpret
1011 * the DTD blanking parameters.
1013 * According to EDID 1.3 (3.12) the Hor/Vert Blanking includes the
1014 * borders, and so does the Hor/Vert Sync Offset.
1016 * According to EDID 1.4 (3.12) the Hor/Vert Blanking excludes the
1017 * borders, and they are also excluded from the Hor/Vert Front Porch.
1019 * But looking at what is really done in EDIDs is that the Hor/Vert
1020 * Blanking follows EDID 1.3, but the Hor/Vert Front Porch does not
1021 * include the border.
1023 * So hbl/vbl includes the borders, so those need to be subtracted,
1024 * but hfp/vfp is used as-is.
1026 * In practice you really shouldn't use non-zero borders in DTDs
1027 * since clearly nobody knows how to interpret the timing.
1029 t
.hact
= (x
[2] + ((x
[4] & 0xf0) << 4));
1031 hbl
= (x
[3] + ((x
[4] & 0x0f) << 8)) - t
.hborder
* 2;
1032 t
.hfp
= (x
[8] + ((x
[11] & 0xc0) << 2));
1033 t
.hsync
= (x
[9] + ((x
[11] & 0x30) << 4));
1034 t
.hbp
= hbl
- t
.hsync
- t
.hfp
;
1035 t
.vact
= (x
[5] + ((x
[7] & 0xf0) << 4));
1037 vbl
= (x
[6] + ((x
[7] & 0x0f) << 8)) - t
.vborder
* 2;
1038 t
.vfp
= ((x
[10] >> 4) + ((x
[11] & 0x0c) << 2));
1039 t
.vsync
= ((x
[10] & 0x0f) + ((x
[11] & 0x03) << 4));
1040 t
.vbp
= vbl
- t
.vsync
- t
.vfp
;
1042 unsigned char flags
= x
[17];
1044 if (base
.has_spwg
&& base
.detailed_block_cnt
== 2)
1047 switch ((flags
& 0x18) >> 3) {
1049 s_flags
= "analog composite";
1050 #ifdef __EMSCRIPTEN__
1051 [[clang::fallthrough
]];
1055 if (s_flags
.empty())
1056 s_flags
= "bipolar analog composite";
1057 switch ((flags
& 0x06) >> 1) {
1059 add_str(s_flags
, "sync-on-green");
1064 add_str(s_flags
, "serrate, sync-on-green");
1067 add_str(s_flags
, "serrate");
1072 if (flags
& (1 << 1))
1073 t
.pos_pol_hsync
= true;
1074 t
.no_pol_vsync
= true;
1075 s_flags
= "digital composite";
1076 if (flags
& (1 << 2))
1077 add_str(s_flags
, "serrate");
1080 if (flags
& (1 << 1))
1081 t
.pos_pol_hsync
= true;
1082 if (flags
& (1 << 2))
1083 t
.pos_pol_vsync
= true;
1084 s_sync
= t
.pos_pol_hsync
? "+hsync " : "-hsync ";
1085 s_sync
+= t
.pos_pol_vsync
? "+vsync " : "-vsync ";
1086 if (base
.has_spwg
&& (flags
& 0x01))
1087 s_flags
= "DE timing only";
1091 t
.interlaced
= true;
1094 * Check if this DTD matches VIC code 39 with special
1095 * interlaced timings.
1097 if (t
.hact
== 1920 && t
.vact
== 1080 && t
.pixclk_khz
== 72000 &&
1098 t
.hfp
== 32 && t
.hsync
== 168 && t
.hbp
== 184 && !t
.hborder
&&
1099 t
.vfp
== 23 && t
.vsync
== 5 && t
.vbp
== 57 && !t
.vborder
&&
1100 !base
.has_spwg
&& cta
.preparsed_has_vic
[0][39] && (flags
& 0x1e) == 0x1a)
1101 t
.even_vtotal
= true;
1103 switch (flags
& 0x61) {
1105 add_str(s_flags
, "field sequential L/R");
1108 add_str(s_flags
, "field sequential R/L");
1111 add_str(s_flags
, "interleaved right even");
1114 add_str(s_flags
, "interleaved left even");
1117 add_str(s_flags
, "four way interleaved");
1120 add_str(s_flags
, "side by side interleaved");
1126 t
.hsize_mm
= x
[12] + ((x
[14] & 0xf0) << 4);
1127 t
.vsize_mm
= x
[13] + ((x
[14] & 0x0f) << 8);
1131 std::string s_type
= base_or_cta
? dtd_type() : "DTD";
1132 bool ok
= print_timings(prefix
, &t
, s_type
.c_str(), s_flags
.c_str(), true);
1133 timings_ext
te(t
, s_type
, s_flags
);
1135 if (block_nr
== 0 && base
.dtd_cnt
== 1) {
1137 base
.preferred_timing
= te
;
1139 cta
.preferred_timings
.push_back(te
);
1140 cta
.native_timings
.push_back(te
);
1144 cta
.vec_dtds
.push_back(te
);
1146 if (t
.hborder
|| t
.vborder
)
1147 warn("The use of non-zero borders in a DTD is not recommended.\n");
1148 if ((base
.max_display_width_mm
&& !t
.hsize_mm
) ||
1149 (base
.max_display_height_mm
&& !t
.vsize_mm
)) {
1150 fail("Mismatch of image size vs display size: image size is not set, but display size is.\n");
1152 if (base
.has_spwg
&& base
.detailed_block_cnt
== 2)
1153 printf("%sSPWG Module Revision: %hhu\n", prefix
, x
[17]);
1155 std::string s
= prefix
;
1158 hex_block(s
.c_str(), x
, 18, true, 18);
1162 bool edid_state::preparse_detailed_block(unsigned char *x
)
1170 case 0x00: /* default gtf */
1171 base
.supports_gtf
= true;
1173 case 0x02: /* secondary gtf curve */
1174 base
.supports_gtf
= true;
1175 base
.supports_sec_gtf
= !memchk(x
+ 12, 6);
1176 base
.sec_gtf_start_freq
= x
[12] * 2;
1177 base
.C
= x
[13] / 2.0;
1178 base
.M
= (x
[15] << 8) | x
[14];
1180 base
.J
= x
[17] / 2.0;
1182 case 0x04: /* cvt */
1183 if (base
.edid_minor
>= 4) {
1184 /* GTF is implied if CVT is signaled */
1185 base
.supports_gtf
= true;
1186 base
.supports_cvt
= true;
1192 data_block
= "Display Product Serial Number";
1193 serial_strings
.push_back(extract_string(x
+ 5, 13, true));
1195 if (replace_unique_ids
) {
1196 // Replace with 123456
1197 static const unsigned char sernum
[13] = {
1198 '1', '2', '3', '4', '5', '6',
1199 '\n', ' ', ' ', ' ', ' ', ' ', ' '
1201 memcpy(x
+ 5, sernum
, sizeof(sernum
));
1209 void edid_state::detailed_block(const unsigned char *x
)
1211 static const unsigned char zero_descr
[18] = { 0 };
1215 base
.detailed_block_cnt
++;
1217 detailed_timings(" ", x
);
1218 if (base
.seen_non_detailed_descriptor
)
1219 fail("Invalid detailed timing descriptor ordering.\n");
1223 data_block
= "Display Descriptor #" + std::to_string(base
.detailed_block_cnt
);
1224 /* Monitor descriptor block, not detailed timing descriptor. */
1227 fail("Monitor descriptor block has byte 2 nonzero (0x%02x).\n", x
[2]);
1229 if ((base
.edid_minor
< 4 || x
[3] != 0xfd) && x
[4] != 0x00) {
1231 fail("Monitor descriptor block has byte 4 nonzero (0x%02x).\n", x
[4]);
1234 base
.seen_non_detailed_descriptor
= true;
1235 if (base
.edid_minor
== 0)
1236 fail("Has descriptor blocks other than detailed timings.\n");
1238 if (!memcmp(x
, zero_descr
, sizeof(zero_descr
))) {
1239 data_block
= "Empty Descriptor";
1240 printf(" %s\n", data_block
.c_str());
1241 fail("Use Dummy Descriptor instead of all zeroes.\n");
1250 data_block
= "Dummy Descriptor";
1251 printf(" %s:\n", data_block
.c_str());
1252 for (i
= 5; i
< 18; i
++) {
1254 fail("Dummy block filled with garbage.\n");
1260 data_block
= "Established timings III";
1261 printf(" %s:\n", data_block
.c_str());
1262 for (i
= 0; i
< ARRAY_SIZE(established_timings3_dmt_ids
); i
++)
1263 if (x
[6 + i
/ 8] & (1 << (7 - i
% 8))) {
1264 unsigned char dmt_id
= established_timings3_dmt_ids
[i
];
1267 sprintf(type
, "DMT 0x%02x", dmt_id
);
1268 print_timings(" ", find_dmt_id(dmt_id
), type
);
1270 if (base
.edid_minor
< 4)
1271 fail("Not allowed for EDID < 1.4.\n");
1274 data_block
= "CVT 3 Byte Timing Codes";
1275 printf(" %s:\n", data_block
.c_str());
1277 fail("Invalid version number %u.\n", x
[5]);
1280 for (i
= 0; i
< 4; i
++)
1281 detailed_cvt_descriptor(" ", x
+ 6 + (i
* 3), !i
);
1282 if (base
.edid_minor
< 4)
1283 fail("Not allowed for EDID < 1.4.\n");
1286 data_block
= "Display Color Management Data";
1287 printf(" %s:\n", data_block
.c_str());
1288 printf(" Version : %d\n", x
[5]);
1289 printf(" Red a3 : %.2f\n", (short)(x
[6] | (x
[7] << 8)) / 100.0);
1290 printf(" Red a2 : %.2f\n", (short)(x
[8] | (x
[9] << 8)) / 100.0);
1291 printf(" Green a3: %.2f\n", (short)(x
[10] | (x
[11] << 8)) / 100.0);
1292 printf(" Green a2: %.2f\n", (short)(x
[12] | (x
[13] << 8)) / 100.0);
1293 printf(" Blue a3 : %.2f\n", (short)(x
[14] | (x
[15] << 8)) / 100.0);
1294 printf(" Blue a2 : %.2f\n", (short)(x
[16] | (x
[17] << 8)) / 100.0);
1297 data_block
= "Standard Timing Identifications";
1298 printf(" %s:\n", data_block
.c_str());
1299 for (cnt
= i
= 0; i
< 6; i
++) {
1300 if (x
[5 + i
* 2] != 0x01 || x
[5 + i
* 2 + 1] != 0x01)
1302 print_standard_timing(" ", x
[5 + i
* 2], x
[5 + i
* 2 + 1]);
1305 warn("%s block without any timings.\n", data_block
.c_str());
1311 data_block
= "Color Point Data";
1312 printf(" %s:\n", data_block
.c_str());
1313 w_x
= (x
[7] << 2) | ((x
[6] >> 2) & 3);
1314 w_y
= (x
[8] << 2) | (x
[6] & 3);
1316 printf(" Index: %u White: 0.%04u, 0.%04u", x
[5],
1317 (w_x
* 10000) / 1024, (w_y
* 10000) / 1024);
1319 printf(" Gamma: is defined in an extension block");
1321 printf(" Gamma: %.2f", ((gamma
+ 100.0) / 100.0));
1325 w_x
= (x
[12] << 2) | ((x
[11] >> 2) & 3);
1326 w_y
= (x
[13] << 2) | (x
[11] & 3);
1328 printf(" Index: %u White: 0.%04u, 0.%04u", x
[10],
1329 (w_x
* 10000) / 1024, (w_y
* 10000) / 1024);
1331 printf(" Gamma: is defined in an extension block");
1333 printf(" Gamma: %.2f", ((gamma
+ 100.0) / 100.0));
1338 data_block
= "Display Product Name";
1339 base
.has_name_descriptor
= 1;
1340 printf(" %s: '%s'\n", data_block
.c_str(), extract_string(x
+ 5, 13, true));
1343 detailed_display_range_limits(x
);
1346 if (!base
.has_spwg
|| base
.detailed_block_cnt
< 3) {
1347 data_block
= "Alphanumeric Data String";
1348 printf(" %s: '%s'\n", data_block
.c_str(),
1349 extract_string(x
+ 5, 13, true));
1352 if (base
.detailed_block_cnt
== 3) {
1353 char buf
[6] = { 0 };
1355 data_block
= "SPWG Descriptor #3";
1356 printf(" %s:\n", data_block
.c_str());
1357 memcpy(buf
, x
+ 5, 5);
1358 if (strlen(buf
) != 5)
1359 fail("Invalid PC Maker P/N length.\n");
1360 printf(" SPWG PC Maker P/N: '%s'\n", buf
);
1361 printf(" SPWG LCD Supplier EEDID Revision: %hhu\n", x
[10]);
1362 printf(" SPWG Manufacturer P/N: '%s'\n", extract_string(x
+ 11, 7, true));
1364 data_block
= "SPWG Descriptor #4";
1365 printf(" %s:\n", data_block
.c_str());
1366 printf(" SMBUS Values: 0x%02hhx 0x%02hhx 0x%02hhx 0x%02hhx"
1367 " 0x%02hhx 0x%02hhx 0x%02hhx 0x%02hhx\n",
1368 x
[5], x
[6], x
[7], x
[8], x
[9], x
[10], x
[11], x
[12]);
1369 printf(" LVDS Channels: %hhu\n", x
[13]);
1370 printf(" Panel Self Test %sPresent\n", x
[14] ? "" : "Not ");
1371 if (x
[15] != 0x0a || x
[16] != 0x20 || x
[17] != 0x20)
1372 fail("Invalid trailing data.\n");
1376 static const char * const dummy_sn
[] = {
1388 "H1AK500000", // Often used with Samsung displays
1392 data_block
= "Display Product Serial Number";
1393 const char *sn
= serial_strings
[serial_string_cnt
++].c_str();
1394 if (hide_serial_numbers
)
1395 printf(" %s: ...\n", data_block
.c_str());
1396 else if (replace_unique_ids
)
1397 printf(" %s: '123456'\n", data_block
.c_str());
1399 printf(" %s: '%s'\n", data_block
.c_str(), sn
);
1401 // Any serial numbers consisting only of spaces, 0, and/or 1
1402 // characters are always considered dummy values.
1403 for (unsigned i
= 0; i
< strlen(sn
); i
++) {
1404 if (!strchr(" 01", sn
[i
])) {
1409 // In addition, check against a list of known dummy S/Ns
1410 for (unsigned i
= 0; !dummy
&& dummy_sn
[i
]; i
++) {
1411 if (!strcmp(sn
, dummy_sn
[i
])) {
1417 warn("The serial number is one of the known dummy values, is that intended?\n");
1421 printf(" %s Display Descriptor (0x%02hhx):",
1422 x
[3] <= 0x0f ? "Manufacturer-Specified" : "Unknown", x
[3]);
1423 hex_block(" ", x
+ 2, 16);
1425 fail("Unknown Type 0x%02hhx.\n", x
[3]);
1431 * The sRGB chromaticities are (x, y):
1433 * green: 0.300, 0.600
1434 * blue: 0.150, 0.060
1435 * white: 0.3127, 0.3290
1437 static const unsigned char srgb_chromaticity
[10] = {
1438 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54
1441 void edid_state::preparse_base_block(unsigned char *x
)
1443 bool update_checksum
= false;
1445 base
.serial_number
= x
[0x0c] + (x
[0x0d] << 8) +
1446 (x
[0x0e] << 16) + (x
[0x0f] << 24);
1448 if (base
.serial_number
&& replace_unique_ids
) {
1449 // Replace by 123456
1454 update_checksum
= true;
1457 base
.week
= x
[0x10];
1458 base
.year
= x
[0x11];
1459 if (replace_unique_ids
&& base
.week
!= 0xff) {
1462 update_checksum
= true;
1466 * Need to find the Display Range Limit info before reading
1467 * the standard timings.
1469 update_checksum
|= preparse_detailed_block(x
+ 0x36);
1470 update_checksum
|= preparse_detailed_block(x
+ 0x48);
1471 update_checksum
|= preparse_detailed_block(x
+ 0x5a);
1472 update_checksum
|= preparse_detailed_block(x
+ 0x6c);
1474 if (update_checksum
)
1475 replace_checksum(x
, EDID_PAGE_SIZE
);
1478 void edid_state::parse_base_block(const unsigned char *x
)
1483 unsigned col_x
, col_y
;
1484 bool has_preferred_timing
= false;
1487 data_block
= "EDID Structure Version & Revision";
1488 printf(" %s: %hhu.%hhu\n", data_block
.c_str(), x
[0x12], x
[0x13]);
1490 base
.edid_minor
= x
[0x13];
1491 if (base
.edid_minor
> 4)
1492 warn("Unknown EDID minor version %u, assuming 1.4 conformance.\n", base
.edid_minor
);
1493 if (base
.edid_minor
< 3)
1494 warn("EDID 1.%u is deprecated, do not use.\n", base
.edid_minor
);
1496 fail("Unknown EDID major version.\n");
1499 data_block
= "Vendor & Product Identification";
1500 manufacturer
= manufacturer_name(x
+ 0x08);
1501 printf(" %s:\n", data_block
.c_str());
1502 printf(" Manufacturer: %s\n Model: %u\n",
1504 (unsigned short)(x
[0x0a] + (x
[0x0b] << 8)));
1505 if (!strcmp(manufacturer
, "CID")) {
1506 if (has_cta
&& !cta
.has_pidb
)
1507 fail("Manufacturer name is set to CID, but there is no CTA-861 Product Information Data Block.\n");
1508 if (has_dispid
&& !has_cta
&& !dispid
.has_product_identification
)
1509 fail("Manufacturer name is set to CID, but there is no DisplayID Product Identification Data Block.\n");
1511 if (base
.serial_number
) {
1512 unsigned sn
= base
.serial_number
;
1514 if (hide_serial_numbers
)
1515 printf(" Serial Number: ...\n");
1517 printf(" Serial Number: %u (0x%08x)\n", sn
, sn
);
1519 // This is a list of known dummy values that are often used in EDIDs:
1529 warn("The serial number is one of the known dummy values, it should probably be set to 0.\n");
1535 ptm
= localtime(&the_time
);
1537 unsigned char week
= base
.week
;
1538 int year
= 1990 + base
.year
;
1541 if (base
.edid_minor
<= 3 && week
== 0xff)
1542 fail("EDID 1.3 does not support week 0xff.\n");
1543 // The max week is 53 in EDID 1.3 and 54 in EDID 1.4.
1544 // No idea why there is a difference.
1545 if (base
.edid_minor
<= 3 && week
== 54)
1546 fail("EDID 1.3 does not support week 54.\n");
1547 if (week
!= 0xff && week
> 54)
1548 fail("Invalid week of manufacture (> 54).\n");
1550 if (year
- 1 > ptm
->tm_year
+ 1900)
1551 fail("The year is more than one year in the future.\n");
1554 printf(" Model year: %d\n", year
);
1555 else if (replace_unique_ids
)
1556 printf(" Made in: 2000\n");
1558 printf(" Made in: week %hhu of %d\n", week
, year
);
1560 printf(" Made in: %d\n", year
);
1562 /* display section */
1564 data_block
= "Basic Display Parameters & Features";
1565 printf(" %s:\n", data_block
.c_str());
1566 if (x
[0x14] & 0x80) {
1568 printf(" Digital display\n");
1569 if (base
.edid_minor
>= 4) {
1570 if ((x
[0x14] & 0x70) == 0x00)
1571 printf(" Color depth is undefined\n");
1572 else if ((x
[0x14] & 0x70) == 0x70)
1573 fail("Color Bit Depth set to reserved value.\n");
1575 printf(" Bits per primary color channel: %u\n",
1576 ((x
[0x14] & 0x70) >> 3) + 4);
1579 switch (x
[0x14] & 0x0f) {
1580 case 0x00: printf("Digital interface is not defined\n"); break;
1581 case 0x01: printf("DVI interface\n"); break;
1582 case 0x02: printf("HDMI-a interface\n"); break;
1583 case 0x03: printf("HDMI-b interface\n"); break;
1584 case 0x04: printf("MDDI interface\n"); break;
1585 case 0x05: printf("DisplayPort interface\n"); break;
1587 printf("Unknown interface: 0x%02x\n", x
[0x14] & 0x0f);
1588 fail("Digital Video Interface Standard set to reserved value 0x%02x.\n", x
[0x14] & 0x0f);
1591 } else if (base
.edid_minor
>= 2) {
1592 if (x
[0x14] & 0x01) {
1593 printf(" DFP 1.x compatible TMDS\n");
1596 fail("Digital Video Interface Standard set to reserved value 0x%02x.\n", x
[0x14] & 0x7e);
1597 } else if (x
[0x14] & 0x7f) {
1598 fail("Digital Video Interface Standard set to reserved value 0x%02x.\n", x
[0x14] & 0x7f);
1601 static const char * const voltages
[] = {
1602 "0.700 : 0.300 : 1.000 V p-p",
1603 "0.714 : 0.286 : 1.000 V p-p",
1604 "1.000 : 0.400 : 1.400 V p-p",
1605 "0.700 : 0.000 : 0.700 V p-p"
1607 unsigned voltage
= (x
[0x14] & 0x60) >> 5;
1608 unsigned sync
= (x
[0x14] & 0x0f);
1611 printf(" Analog display\n");
1612 printf(" Signal Level Standard: %s\n", voltages
[voltage
]);
1615 printf(" Blank-to-black setup/pedestal\n");
1617 printf(" Blank level equals black level\n");
1620 printf(" Sync:%s%s%s%s\n",
1621 sync
& 0x08 ? " Separate" : "",
1622 sync
& 0x04 ? " Composite" : "",
1623 sync
& 0x02 ? " SyncOnGreen" : "",
1624 sync
& 0x01 ? " Serration" : "");
1627 if (x
[0x15] && x
[0x16]) {
1628 printf(" Maximum image size: %u cm x %u cm\n", x
[0x15], x
[0x16]);
1629 base
.max_display_width_mm
= x
[0x15] * 10;
1630 base
.max_display_height_mm
= x
[0x16] * 10;
1631 image_width
= base
.max_display_width_mm
* 10;
1632 image_height
= base
.max_display_height_mm
* 10;
1633 if (x
[0x15] < 10 || x
[0x16] < 10)
1634 warn("Dubious maximum image size (%ux%u is smaller than 10x10 cm).\n",
1637 else if (base
.edid_minor
>= 4 && (x
[0x15] || x
[0x16])) {
1639 printf(" Aspect ratio: %.2f (landscape)\n", (x
[0x15] + 99) / 100.0);
1641 printf(" Aspect ratio: %.2f (portrait)\n", 100.0 / (x
[0x16] + 99));
1643 /* Either or both can be zero for 1.3 and before */
1644 printf(" Image size is variable\n");
1647 if (x
[0x17] == 0xff)
1648 printf(" Gamma is defined in an extension block\n");
1650 printf(" Gamma: %.2f\n", (x
[0x17] + 100.0) / 100.0);
1652 if (x
[0x18] & 0xe0) {
1653 printf(" DPMS levels:");
1654 if (x
[0x18] & 0x80) printf(" Standby");
1655 if (x
[0x18] & 0x40) printf(" Suspend");
1656 if (x
[0x18] & 0x20) printf(" Off");
1660 if (analog
|| base
.edid_minor
< 4) {
1662 switch (x
[0x18] & 0x18) {
1663 case 0x00: printf("Monochrome or grayscale display\n"); break;
1664 case 0x08: printf("RGB color display\n"); break;
1665 case 0x10: printf("Non-RGB color display\n"); break;
1666 case 0x18: printf("Undefined display color type\n"); break;
1669 printf(" Supported color formats: RGB 4:4:4");
1671 printf(", YCrCb 4:4:4");
1673 printf(", YCrCb 4:2:2");
1677 if (x
[0x18] & 0x04) {
1678 printf(" Default (sRGB) color space is primary color space\n");
1679 if (memcmp(x
+ 0x19, srgb_chromaticity
, sizeof(srgb_chromaticity
)))
1680 fail("sRGB is signaled, but the chromaticities do not match.\n");
1682 warn("sRGB is signaled, but the gamma != 2.2.\n");
1683 base
.uses_srgb
= true;
1684 } else if (!memcmp(x
+ 0x19, srgb_chromaticity
, sizeof(srgb_chromaticity
))) {
1685 fail("The chromaticities match sRGB, but sRGB is not signaled.\n");
1686 base
.uses_srgb
= true;
1689 if (base
.edid_minor
>= 4) {
1690 /* 1.4 always has a preferred timing and this bit means something else. */
1691 has_preferred_timing
= true;
1692 base
.preferred_is_also_native
= x
[0x18] & 0x02;
1693 printf(" First detailed timing %s the native pixel format and preferred refresh rate\n",
1694 base
.preferred_is_also_native
? "includes" : "does not include");
1696 if (x
[0x18] & 0x02) {
1697 printf(" First detailed timing is the preferred timing\n");
1698 has_preferred_timing
= true;
1699 // 1.3 recommends that the preferred timing corresponds to the
1700 // native timing, but it is not a requirement.
1701 // That said, we continue with the assumption that it actually
1702 // is the native timing.
1703 base
.preferred_is_also_native
= true;
1704 } else if (base
.edid_minor
== 3) {
1705 fail("EDID 1.3 requires that the first detailed timing is the preferred timing.\n");
1709 if (x
[0x18] & 0x01) {
1710 if (base
.edid_minor
>= 4) {
1711 base
.supports_continuous_freq
= true;
1712 printf(" Display supports continuous frequencies\n");
1714 printf(" Supports GTF timings within operating range\n");
1715 base
.supports_gtf
= true;
1719 data_block
= "Color Characteristics";
1720 printf(" %s:\n", data_block
.c_str());
1721 col_x
= (x
[0x1b] << 2) | (x
[0x19] >> 6);
1722 col_y
= (x
[0x1c] << 2) | ((x
[0x19] >> 4) & 3);
1723 printf(" Red : 0.%04u, 0.%04u\n",
1724 (col_x
* 10000) / 1024, (col_y
* 10000) / 1024);
1725 col_x
= (x
[0x1d] << 2) | ((x
[0x19] >> 2) & 3);
1726 col_y
= (x
[0x1e] << 2) | (x
[0x19] & 3);
1727 printf(" Green: 0.%04u, 0.%04u\n",
1728 (col_x
* 10000) / 1024, (col_y
* 10000) / 1024);
1729 col_x
= (x
[0x1f] << 2) | (x
[0x1a] >> 6);
1730 col_y
= (x
[0x20] << 2) | ((x
[0x1a] >> 4) & 3);
1731 printf(" Blue : 0.%04u, 0.%04u\n",
1732 (col_x
* 10000) / 1024, (col_y
* 10000) / 1024);
1733 col_x
= (x
[0x21] << 2) | ((x
[0x1a] >> 2) & 3);
1734 col_y
= (x
[0x22] << 2) | (x
[0x1a] & 3);
1735 printf(" White: 0.%04u, 0.%04u\n",
1736 (col_x
* 10000) / 1024, (col_y
* 10000) / 1024);
1738 data_block
= "Established Timings I & II";
1739 if (x
[0x23] || x
[0x24] || x
[0x25]) {
1740 printf(" %s:\n", data_block
.c_str());
1741 for (unsigned i
= 0; i
< ARRAY_SIZE(established_timings12
); i
++) {
1742 if (x
[0x23 + i
/ 8] & (1 << (7 - i
% 8))) {
1743 unsigned char dmt_id
= established_timings12
[i
].dmt_id
;
1744 const struct timings
*t
;
1748 sprintf(type
, "DMT 0x%02x", dmt_id
);
1749 t
= find_dmt_id(dmt_id
);
1751 t
= &established_timings12
[i
].t
;
1752 sprintf(type
, "%-8s", established_timings12
[i
].type
);
1754 print_timings(" ", t
, type
);
1758 printf(" %s: none\n", data_block
.c_str());
1760 base
.has_640x480p60_est_timing
= x
[0x23] & 0x20;
1762 data_block
= "Standard Timings";
1764 for (unsigned i
= 0; i
< 8; i
++) {
1765 if (x
[0x26 + i
* 2] != 0x01 || x
[0x26 + i
* 2 + 1] != 0x01) {
1771 printf(" %s:\n", data_block
.c_str());
1772 for (unsigned i
= 0; i
< 8; i
++)
1773 print_standard_timing(" ", x
[0x26 + i
* 2], x
[0x26 + i
* 2 + 1]);
1775 printf(" %s: none\n", data_block
.c_str());
1778 /* 18 byte descriptors */
1779 if (has_preferred_timing
&& !x
[0x36] && !x
[0x37])
1780 fail("Missing preferred timing.\n");
1782 /* Look for SPWG Noteboook Panel EDID data blocks */
1783 if ((x
[0x36] || x
[0x37]) &&
1784 (x
[0x48] || x
[0x49]) &&
1785 !x
[0x5a] && !x
[0x5b] && x
[0x5d] == 0xfe &&
1786 !x
[0x6c] && !x
[0x6d] && x
[0x6f] == 0xfe &&
1787 (x
[0x79] == 1 || x
[0x79] == 2) && x
[0x7a] <= 1)
1788 base
.has_spwg
= true;
1790 for (unsigned i
= 0; i
< (base
.has_spwg
? 2 : 4); i
++)
1791 if (x
[0x36 + i
* 18] || x
[0x37 + i
* 18])
1792 cta
.preparsed_total_dtds
++;
1794 data_block
= "Detailed Timing Descriptors";
1795 printf(" %s:\n", data_block
.c_str());
1796 detailed_block(x
+ 0x36);
1797 detailed_block(x
+ 0x48);
1798 detailed_block(x
+ 0x5a);
1799 detailed_block(x
+ 0x6c);
1800 base
.has_spwg
= false;
1801 if (!base
.preferred_is_also_native
) {
1802 cta
.native_timings
.clear();
1803 base
.preferred_timing
= timings_ext();
1808 printf(" Extension blocks: %u\n", x
[0x7e]);
1810 block
= block_name(0x00);
1812 do_checksum("", x
, EDID_PAGE_SIZE
, EDID_PAGE_SIZE
- 1);
1813 if (base
.edid_minor
>= 3) {
1814 if (!base
.has_name_descriptor
)
1815 msg(base
.edid_minor
>= 4, "Missing Display Product Name.\n");
1816 if ((base
.edid_minor
== 3 || base
.supports_continuous_freq
) &&
1817 !base
.has_display_range_descriptor
)
1818 fail("Missing Display Range Limits Descriptor.\n");
1822 void edid_state::check_base_block(const unsigned char *x
)
1824 data_block
= "Base EDID";
1827 * Allow for regular rounding of vertical and horizontal frequencies.
1828 * The spec says that the pixelclock shall be rounded up, so there is
1829 * no need to take rounding into account.
1831 if (base
.has_display_range_descriptor
&&
1832 (min_vert_freq_hz
+ 0.5 < base
.min_display_vert_freq_hz
||
1833 (max_vert_freq_hz
>= base
.max_display_vert_freq_hz
+ 0.5 && base
.max_display_vert_freq_hz
) ||
1834 min_hor_freq_hz
+ 500 < base
.min_display_hor_freq_hz
||
1835 (max_hor_freq_hz
>= base
.max_display_hor_freq_hz
+ 500 && base
.max_display_hor_freq_hz
) ||
1836 (max_pixclk_khz
> base
.max_display_pixclk_khz
&& base
.max_display_pixclk_khz
))) {
1838 * Check if it is really out of range, or if it could be a rounding error.
1839 * The EDID spec is not very clear about rounding.
1842 min_vert_freq_hz
+ 1.0 <= base
.min_display_vert_freq_hz
||
1843 (max_vert_freq_hz
>= base
.max_display_vert_freq_hz
+ 1.0 && base
.max_display_vert_freq_hz
) ||
1844 min_hor_freq_hz
+ 1000 <= base
.min_display_hor_freq_hz
||
1845 (max_hor_freq_hz
>= base
.max_display_hor_freq_hz
+ 1000 && base
.max_display_hor_freq_hz
) ||
1846 (max_pixclk_khz
>= base
.max_display_pixclk_khz
+ 10000 && base
.max_display_pixclk_khz
);
1848 std::string
err("Some timings are out of range of the Monitor Ranges:\n");
1851 if (min_vert_freq_hz
+ 0.5 < base
.min_display_vert_freq_hz
||
1852 (max_vert_freq_hz
>= base
.max_display_vert_freq_hz
+ 0.5 && base
.max_display_vert_freq_hz
)) {
1853 sprintf(buf
, " Vertical Freq: %.3f - %.3f Hz (Monitor: %u.000 - %u.000 Hz)\n",
1854 min_vert_freq_hz
, max_vert_freq_hz
,
1855 base
.min_display_vert_freq_hz
, base
.max_display_vert_freq_hz
);
1859 if (min_hor_freq_hz
+ 500 < base
.min_display_hor_freq_hz
||
1860 (max_hor_freq_hz
>= base
.max_display_hor_freq_hz
+ 500 && base
.max_display_hor_freq_hz
)) {
1861 sprintf(buf
, " Horizontal Freq: %.3f - %.3f kHz (Monitor: %.3f - %.3f kHz)\n",
1862 min_hor_freq_hz
/ 1000.0, max_hor_freq_hz
/ 1000.0,
1863 base
.min_display_hor_freq_hz
/ 1000.0, base
.max_display_hor_freq_hz
/ 1000.0);
1867 if (max_pixclk_khz
>= base
.max_display_pixclk_khz
&& base
.max_display_pixclk_khz
) {
1868 sprintf(buf
, " Maximum Clock: %.3f MHz (Monitor: %.3f MHz)\n",
1869 max_pixclk_khz
/ 1000.0, base
.max_display_pixclk_khz
/ 1000.0);
1874 err
+= " Could be due to a Monitor Range off-by-one rounding issue\n";
1876 warn("%s", err
.c_str());
1879 if ((image_width
&& dtd_max_hsize_mm
>= 10 + image_width
/ 10) ||
1880 (image_height
&& dtd_max_vsize_mm
>= 10 + image_height
/ 10))
1881 fail("The DTD max image size is %ux%umm, which is larger than the display size %.1fx%.1fmm.\n",
1882 dtd_max_hsize_mm
, dtd_max_vsize_mm
,
1883 image_width
/ 10.0, image_height
/ 10.0);
1884 if ((!image_width
&& dtd_max_hsize_mm
) || (!image_height
&& dtd_max_vsize_mm
))
1885 fail("The DTD max image size is %ux%umm, but the display size is not specified anywhere.\n",
1886 dtd_max_hsize_mm
, dtd_max_vsize_mm
);
1888 // Secondary GTF curves start at a specific frequency. Any legacy timings
1889 // that have a positive hsync and negative vsync must be less than that
1890 // frequency to avoid confusion.
1891 if (base
.supports_sec_gtf
&& base
.max_pos_neg_hor_freq_khz
>= base
.sec_gtf_start_freq
)
1892 fail("Second GTF start frequency %u is less than the highest P/N frequency %u.\n",
1893 base
.sec_gtf_start_freq
, base
.max_pos_neg_hor_freq_khz
);
1894 if (x
[0x7e] + 1U != num_blocks
&& !cta
.hf_eeodb_blocks
)
1895 fail("EDID specified %u extension block(s), but found %u extension block(s).\n",
1896 x
[0x7e], num_blocks
- 1);
1897 else if (x
[0x7e] != 1 && cta
.hf_eeodb_blocks
)
1898 fail("HDMI Forum EDID Extension Override Data Block is present, but Block 0 defined %u instead of 1 Extension Blocks.\n",
1900 else if (x
[0x7e] == 1 && cta
.hf_eeodb_blocks
&& cta
.hf_eeodb_blocks
+ 1 != num_blocks
)
1901 fail("HDMI Forum EDID Extension Override Data Block specified %u extension block(s), but found %u extension block(s).\n",
1902 cta
.hf_eeodb_blocks
, num_blocks
- 1);
1904 if (base
.edid_minor
== 3 && num_blocks
> 2 && !block_map
.saw_block_1
&& !cta
.hf_eeodb_blocks
)
1905 fail("EDID 1.3 requires a Block Map Extension in Block 1 if there are more than 2 blocks in the EDID.\n");
1906 if (base
.edid_minor
== 3 && num_blocks
> 128 && !block_map
.saw_block_128
&& !cta
.hf_eeodb_blocks
)
1907 fail("EDID 1.3 requires a Block Map Extension in Block 128 if there are more than 128 blocks in the EDID.\n");
1908 if (block_map
.saw_block_128
&& num_blocks
> 255)
1909 fail("If there is a Block Map Extension in Block 128 then the maximum number of blocks is 255.\n");
1911 if (serial_strings
.size() > 1)
1912 warn("Multiple Display Product Serial Numbers are specified, is that intended?\n");