Serenity Operating System
1/*
2 * Copyright (c) 2022, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibEDID/DMT.h>
8#include <LibEDID/EDID.h>
9#include <LibTest/TestCase.h>
10
11static const u8 edid1_bin[] = {
12 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x49, 0x14, 0x34, 0x12,
13 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x01, 0x04, 0xa5, 0x1a, 0x13, 0x78,
14 0x06, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x21,
15 0x08, 0x00, 0xe1, 0xc0, 0xd1, 0xc0, 0xd1, 0x00, 0xa9, 0x40, 0xb3, 0x00,
16 0x95, 0x00, 0x81, 0x80, 0x81, 0x40, 0x25, 0x20, 0x00, 0x66, 0x41, 0x00,
17 0x1a, 0x30, 0x00, 0x1e, 0x33, 0x40, 0x04, 0xc3, 0x10, 0x00, 0x00, 0x18,
18 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, 0x7d, 0x1e, 0xa0, 0x78, 0x01, 0x0a,
19 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x51,
20 0x45, 0x4d, 0x55, 0x20, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x0a,
21 0x00, 0x00, 0x00, 0xf7, 0x00, 0x0a, 0x00, 0x40, 0x82, 0x00, 0x28, 0x20,
22 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x02, 0x03, 0x0a, 0x00,
23 0x45, 0x7d, 0x65, 0x60, 0x59, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
24 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
26 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
27 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
28 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
31 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33 0x00, 0x00, 0x00, 0xf2
34};
35
36TEST_CASE(edid1)
37{
38 auto edid_load_result = EDID::Parser::from_bytes({ edid1_bin, sizeof(edid1_bin) });
39 EXPECT(!edid_load_result.is_error());
40 auto edid = edid_load_result.release_value();
41 EXPECT(edid.legacy_manufacturer_id() == "RHT");
42 EXPECT(!edid.aspect_ratio().has_value());
43 auto screen_size = edid.screen_size();
44 EXPECT(screen_size.has_value());
45 EXPECT(screen_size.value().horizontal_cm() == 26);
46 EXPECT(screen_size.value().vertical_cm() == 19);
47 auto gamma = edid.gamma();
48 EXPECT(gamma.has_value());
49 EXPECT(gamma.value() >= 2.19f && gamma.value() <= 2.21f);
50 EXPECT(edid.display_product_name() == "QEMU Monitor");
51
52 {
53 static constexpr struct {
54 unsigned width;
55 unsigned height;
56 unsigned refresh_rate;
57 EDID::Parser::EstablishedTiming::Source source;
58 u8 dmt_id { 0 };
59 } expected_established_timings[] = {
60 { 640, 480, 60, EDID::Parser::EstablishedTiming::Source::IBM, 0x4 },
61 { 800, 600, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x9 },
62 { 1024, 768, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x10 },
63 { 1280, 768, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x17 },
64 { 1360, 768, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x27 },
65 { 1400, 1050, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x2a },
66 { 1792, 1344, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x3e },
67 { 1856, 1392, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x41 },
68 { 1920, 1440, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x49 }
69 };
70 static constexpr size_t expected_established_timings_count = sizeof(expected_established_timings) / sizeof(expected_established_timings[0]);
71 size_t established_timings_found = 0;
72 auto result = edid.for_each_established_timing([&](auto& established_timings) {
73 EXPECT(established_timings_found < expected_established_timings_count);
74 auto& expected_timings = expected_established_timings[established_timings_found];
75 EXPECT(established_timings.width() == expected_timings.width);
76 EXPECT(established_timings.height() == expected_timings.height);
77 EXPECT(established_timings.refresh_rate() == expected_timings.refresh_rate);
78 EXPECT(established_timings.source() == expected_timings.source);
79 EXPECT(established_timings.dmt_id() == expected_timings.dmt_id);
80 established_timings_found++;
81 return IterationDecision::Continue;
82 });
83 EXPECT(!result.is_error());
84 EXPECT(result.value() == IterationDecision::Continue);
85 EXPECT(established_timings_found == expected_established_timings_count);
86 }
87
88 {
89 static constexpr struct {
90 unsigned width;
91 unsigned height;
92 unsigned refresh_rate;
93 u8 dmt_id { 0 };
94 } expected_standard_established_timings[] = {
95 { 2048, 1152, 60, 0x54 },
96 { 1920, 1080, 60, 0x52 },
97 { 1920, 1200, 60, 0x45 },
98 { 1600, 1200, 60, 0x33 },
99 { 1680, 1050, 60, 0x3a },
100 { 1440, 900, 60, 0x2f },
101 { 1280, 1024, 60, 0x23 },
102 { 1280, 960, 60, 0x20 }
103 };
104 static constexpr size_t expected_standard_timings_count = sizeof(expected_standard_established_timings) / sizeof(expected_standard_established_timings[0]);
105 size_t standard_timings_found = 0;
106 auto result = edid.for_each_standard_timing([&](auto& standard_timings) {
107 EXPECT(standard_timings_found < expected_standard_timings_count);
108 auto& expected_timings = expected_standard_established_timings[standard_timings_found];
109 EXPECT(standard_timings.dmt_id() == expected_timings.dmt_id);
110 EXPECT(standard_timings.width() == expected_timings.width);
111 EXPECT(standard_timings.height() == expected_timings.height);
112 EXPECT(standard_timings.refresh_rate() == expected_timings.refresh_rate);
113 standard_timings_found++;
114 return IterationDecision::Continue;
115 });
116 EXPECT(!result.is_error());
117 EXPECT(result.value() == IterationDecision::Continue);
118 EXPECT(standard_timings_found == expected_standard_timings_count);
119 }
120
121 {
122 static constexpr struct {
123 unsigned block_id;
124 unsigned width;
125 unsigned height;
126 unsigned refresh_rate;
127 } expected_detailed_timings[] = {
128 { 0, 1024, 768, 75 }
129 };
130 static constexpr size_t expected_detailed_timings_count = sizeof(expected_detailed_timings) / sizeof(expected_detailed_timings[0]);
131 size_t detailed_timings_found = 0;
132 auto result = edid.for_each_detailed_timing([&](auto& detailed_timing, unsigned block_id) {
133 EXPECT(detailed_timings_found < expected_detailed_timings_count);
134 auto& expected_timings = expected_detailed_timings[detailed_timings_found];
135 EXPECT(block_id == expected_timings.block_id);
136 EXPECT(detailed_timing.horizontal_addressable_pixels() == expected_timings.width);
137 EXPECT(detailed_timing.vertical_addressable_lines() == expected_timings.height);
138 EXPECT(detailed_timing.refresh_rate().lround() == expected_timings.refresh_rate);
139 detailed_timings_found++;
140 return IterationDecision::Continue;
141 });
142 EXPECT(!result.is_error());
143 EXPECT(result.value() == IterationDecision::Continue);
144 EXPECT(detailed_timings_found == expected_detailed_timings_count);
145 }
146
147 {
148 static constexpr u8 expected_vic_ids[] = { 125, 101, 96, 89, 31 };
149 static constexpr size_t expected_vic_ids_count = sizeof(expected_vic_ids) / sizeof(expected_vic_ids[0]);
150 size_t vic_ids_found = 0;
151 auto result = edid.for_each_short_video_descriptor([&](unsigned block_id, bool is_native, EDID::VIC::Details const& vic) {
152 EXPECT(vic_ids_found < expected_vic_ids_count);
153 EXPECT(block_id == 1);
154 EXPECT(!is_native); // none are marked as native
155 EXPECT(vic.vic_id == expected_vic_ids[vic_ids_found]);
156 vic_ids_found++;
157 return IterationDecision::Continue;
158 });
159 EXPECT(!result.is_error());
160 EXPECT(result.value() == IterationDecision::Continue);
161 EXPECT(vic_ids_found == expected_vic_ids_count);
162 }
163
164 {
165 // This edid has one CEA861 extension block only
166 size_t extension_blocks_found = 0;
167 auto result = edid.for_each_extension_block([&](unsigned block_id, u8 tag, u8 revision, ReadonlyBytes) {
168 EXPECT(block_id == 1);
169 EXPECT(tag == 0x2);
170 EXPECT(revision == 3);
171 extension_blocks_found++;
172 return IterationDecision::Continue;
173 });
174 EXPECT(!result.is_error());
175 EXPECT(result.value() == IterationDecision::Continue);
176 EXPECT(extension_blocks_found == 1);
177 }
178}
179
180static const u8 edid2_bin[] = {
181 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x04, 0x72, 0x1d, 0x08,
182 0xd2, 0x02, 0x96, 0x49, 0x20, 0x1e, 0x01, 0x04, 0xb5, 0x3c, 0x22, 0x78,
183 0x3b, 0xff, 0x15, 0xa6, 0x53, 0x4a, 0x98, 0x26, 0x0f, 0x50, 0x54, 0xbf,
184 0xef, 0x80, 0xd1, 0xc0, 0xb3, 0x00, 0x95, 0x00, 0x81, 0x80, 0x81, 0x40,
185 0x81, 0xc0, 0x01, 0x01, 0x01, 0x01, 0x86, 0x6f, 0x00, 0x3c, 0xa0, 0xa0,
186 0x0f, 0x50, 0x08, 0x20, 0x35, 0x00, 0x55, 0x50, 0x21, 0x00, 0x00, 0x1e,
187 0x56, 0x5e, 0x00, 0xa0, 0xa0, 0xa0, 0x29, 0x50, 0x30, 0x20, 0x35, 0x00,
188 0x55, 0x50, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x30,
189 0x4b, 0x78, 0x78, 0x1e, 0x01, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
190 0x00, 0x00, 0x00, 0xfc, 0x00, 0x43, 0x42, 0x32, 0x37, 0x32, 0x55, 0x0a,
191 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xc5, 0x02, 0x03, 0x33, 0x71,
192 0x4c, 0x12, 0x13, 0x04, 0x1f, 0x90, 0x14, 0x05, 0x01, 0x11, 0x02, 0x03,
193 0x4a, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0xe2, 0x00, 0xc0,
194 0x67, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x38, 0x3c, 0xe3, 0x05, 0xe3, 0x01,
195 0xe3, 0x0f, 0x00, 0x00, 0xe6, 0x06, 0x07, 0x01, 0x60, 0x60, 0x45, 0x01,
196 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, 0x55,
197 0x50, 0x21, 0x00, 0x00, 0x1e, 0x01, 0x1d, 0x00, 0xbc, 0x52, 0xd0, 0x1e,
198 0x20, 0xb8, 0x28, 0x55, 0x40, 0x55, 0x50, 0x21, 0x00, 0x00, 0x1e, 0x56,
199 0x5e, 0x00, 0xa0, 0xa0, 0xa0, 0x29, 0x50, 0x30, 0x20, 0x35, 0x00, 0x55,
200 0x50, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
201 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
202 0x00, 0x00, 0x00, 0xe1
203};
204
205TEST_CASE(edid2)
206{
207 auto edid_load_result = EDID::Parser::from_bytes({ edid2_bin, sizeof(edid2_bin) });
208 EXPECT(!edid_load_result.is_error());
209 auto edid = edid_load_result.release_value();
210 EXPECT(edid.legacy_manufacturer_id() == "ACR");
211 EXPECT(edid.serial_number() == 1234567890);
212 auto digital_interface = edid.digital_display();
213 EXPECT(digital_interface.has_value());
214 EXPECT(digital_interface.value().color_bit_depth() == EDID::Parser::DigitalDisplay::ColorBitDepth::BPP_10);
215 EXPECT(digital_interface.value().supported_interface() == EDID::Parser::DigitalDisplay::SupportedInterface::DisplayPort);
216 EXPECT(!digital_interface.value().features().supports_standby());
217 EXPECT(!digital_interface.value().features().supports_suspend());
218 EXPECT(digital_interface.value().features().supports_off());
219 EXPECT(digital_interface.value().features().preferred_timing_mode_includes_pixel_format_and_refresh_rate());
220 EXPECT(!digital_interface.value().features().srgb_is_default_color_space());
221 EXPECT(digital_interface.value().features().frequency() == EDID::Parser::DigitalDisplayFeatures::Frequency::Continuous);
222 EXPECT(digital_interface.value().features().supported_color_encodings() == EDID::Parser::DigitalDisplayFeatures::SupportedColorEncodings::RGB444_YCrCb444_YCrCb422);
223 EXPECT(!edid.aspect_ratio().has_value());
224 auto screen_size = edid.screen_size();
225 EXPECT(screen_size.has_value());
226 EXPECT(screen_size.value().horizontal_cm() == 60);
227 EXPECT(screen_size.value().vertical_cm() == 34);
228 auto gamma = edid.gamma();
229 EXPECT(gamma.has_value());
230 EXPECT(gamma.value() >= 2.19f && gamma.value() <= 2.21f);
231 EXPECT(edid.display_product_name() == "CB272U");
232
233 {
234 static constexpr struct {
235 unsigned width;
236 unsigned height;
237 unsigned refresh_rate;
238 EDID::Parser::EstablishedTiming::Source source;
239 u8 dmt_id { 0 };
240 } expected_established_timings[] = {
241 { 720, 400, 70, EDID::Parser::EstablishedTiming::Source::IBM },
242 { 640, 480, 60, EDID::Parser::EstablishedTiming::Source::IBM, 0x4 },
243 { 640, 480, 67, EDID::Parser::EstablishedTiming::Source::Apple },
244 { 640, 480, 73, EDID::Parser::EstablishedTiming::Source::VESA, 0x5 },
245 { 640, 480, 75, EDID::Parser::EstablishedTiming::Source::VESA, 0x6 },
246 { 800, 600, 56, EDID::Parser::EstablishedTiming::Source::VESA, 0x8 },
247 { 800, 600, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x9 },
248 { 800, 600, 72, EDID::Parser::EstablishedTiming::Source::VESA, 0xa },
249 { 800, 600, 75, EDID::Parser::EstablishedTiming::Source::VESA, 0xb },
250 { 832, 624, 75, EDID::Parser::EstablishedTiming::Source::Apple },
251 { 1024, 768, 60, EDID::Parser::EstablishedTiming::Source::VESA, 0x10 },
252 { 1024, 768, 70, EDID::Parser::EstablishedTiming::Source::VESA, 0x11 },
253 { 1024, 768, 75, EDID::Parser::EstablishedTiming::Source::VESA, 0x12 },
254 { 1280, 1024, 75, EDID::Parser::EstablishedTiming::Source::VESA, 0x24 },
255 { 1152, 870, 75, EDID::Parser::EstablishedTiming::Source::Apple }
256 };
257 static constexpr size_t expected_established_timings_count = sizeof(expected_established_timings) / sizeof(expected_established_timings[0]);
258 size_t established_timings_found = 0;
259 auto result = edid.for_each_established_timing([&](auto& established_timings) {
260 EXPECT(established_timings_found < expected_established_timings_count);
261 auto& expected_timings = expected_established_timings[established_timings_found];
262 EXPECT(established_timings.width() == expected_timings.width);
263 EXPECT(established_timings.height() == expected_timings.height);
264 EXPECT(established_timings.refresh_rate() == expected_timings.refresh_rate);
265 EXPECT(established_timings.source() == expected_timings.source);
266 EXPECT(established_timings.dmt_id() == expected_timings.dmt_id);
267 established_timings_found++;
268 return IterationDecision::Continue;
269 });
270 EXPECT(!result.is_error());
271 EXPECT(result.value() == IterationDecision::Continue);
272 EXPECT(established_timings_found == expected_established_timings_count);
273 }
274
275 {
276 static constexpr struct {
277 unsigned width;
278 unsigned height;
279 unsigned refresh_rate;
280 u8 dmt_id { 0 };
281 } expected_standard_established_timings[] = {
282 { 1920, 1080, 60, 0x52 },
283 { 1680, 1050, 60, 0x3a },
284 { 1440, 900, 60, 0x2f },
285 { 1280, 1024, 60, 0x23 },
286 { 1280, 960, 60, 0x20 },
287 { 1280, 720, 60, 0x55 },
288 };
289 static constexpr size_t expected_standard_timings_count = sizeof(expected_standard_established_timings) / sizeof(expected_standard_established_timings[0]);
290 size_t standard_timings_found = 0;
291 auto result = edid.for_each_standard_timing([&](auto& standard_timings) {
292 EXPECT(standard_timings_found < expected_standard_timings_count);
293 auto& expected_timings = expected_standard_established_timings[standard_timings_found];
294 EXPECT(standard_timings.dmt_id() == expected_timings.dmt_id);
295 EXPECT(standard_timings.width() == expected_timings.width);
296 EXPECT(standard_timings.height() == expected_timings.height);
297 EXPECT(standard_timings.refresh_rate() == expected_timings.refresh_rate);
298 standard_timings_found++;
299 return IterationDecision::Continue;
300 });
301 EXPECT(!result.is_error());
302 EXPECT(result.value() == IterationDecision::Continue);
303 EXPECT(standard_timings_found == expected_standard_timings_count);
304 }
305
306 {
307 static constexpr struct {
308 unsigned block_id;
309 unsigned width;
310 unsigned height;
311 unsigned refresh_rate;
312 } expected_detailed_timings[] = {
313 { 0, 2560, 1440, 75 },
314 { 0, 2560, 1440, 60 },
315 { 1, 1280, 720, 60 },
316 { 1, 1280, 720, 50 },
317 { 1, 2560, 1440, 60 }
318 };
319 static constexpr size_t expected_detailed_timings_count = sizeof(expected_detailed_timings) / sizeof(expected_detailed_timings[0]);
320 size_t detailed_timings_found = 0;
321 auto result = edid.for_each_detailed_timing([&](auto& detailed_timing, unsigned block_id) {
322 EXPECT(detailed_timings_found < expected_detailed_timings_count);
323 auto& expected_timings = expected_detailed_timings[detailed_timings_found];
324 EXPECT(block_id == expected_timings.block_id);
325 EXPECT(detailed_timing.horizontal_addressable_pixels() == expected_timings.width);
326 EXPECT(detailed_timing.vertical_addressable_lines() == expected_timings.height);
327 EXPECT(detailed_timing.refresh_rate().lround() == expected_timings.refresh_rate);
328 detailed_timings_found++;
329 return IterationDecision::Continue;
330 });
331 EXPECT(!result.is_error());
332 EXPECT(result.value() == IterationDecision::Continue);
333 EXPECT(detailed_timings_found == expected_detailed_timings_count);
334 }
335
336 {
337 static constexpr u8 expected_vic_ids[] = { 18, 19, 4, 31, 16, 20, 5, 1, 17, 2, 3, 74 };
338 static constexpr size_t expected_vic_ids_count = sizeof(expected_vic_ids) / sizeof(expected_vic_ids[0]);
339 size_t vic_ids_found = 0;
340 auto result = edid.for_each_short_video_descriptor([&](unsigned block_id, bool is_native, EDID::VIC::Details const& vic) {
341 EXPECT(vic_ids_found < expected_vic_ids_count);
342 EXPECT(block_id == 1);
343 EXPECT(is_native == (vic_ids_found == 4)); // the 5th value is marked native
344 EXPECT(vic.vic_id == expected_vic_ids[vic_ids_found]);
345 vic_ids_found++;
346 return IterationDecision::Continue;
347 });
348 EXPECT(!result.is_error());
349 EXPECT(result.value() == IterationDecision::Continue);
350 EXPECT(vic_ids_found == expected_vic_ids_count);
351 }
352
353 {
354 // This edid has one CEA861 extension block only
355 size_t extension_blocks_found = 0;
356 auto result = edid.for_each_extension_block([&](unsigned block_id, u8 tag, u8 revision, ReadonlyBytes) {
357 EXPECT(block_id == 1);
358 EXPECT(tag == 0x2);
359 EXPECT(revision == 3);
360 extension_blocks_found++;
361 return IterationDecision::Continue;
362 });
363 EXPECT(!result.is_error());
364 EXPECT(result.value() == IterationDecision::Continue);
365 EXPECT(extension_blocks_found == 1);
366 }
367}
368
369// This EDID has extension maps
370static const u8 edid_extension_maps[] = {
371 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4d, 0x29, 0x48, 0x44,
372 0x01, 0x00, 0x00, 0x00, 0x0a, 0x0d, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78,
373 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x20,
374 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
375 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c,
376 0x16, 0x20, 0x58, 0x2c, 0x25, 0x00, 0x20, 0xc2, 0x31, 0x00, 0x00, 0x9e,
377 0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00,
378 0x13, 0x8e, 0x21, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x48,
379 0x44, 0x4d, 0x49, 0x20, 0x54, 0x56, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
380 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 0x3d, 0x0f, 0x2e, 0x08, 0x02, 0x00,
381 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x03, 0xf1, 0xf0, 0x02, 0x02, 0x00,
382 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
383 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
384 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
385 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
386 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
387 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
388 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
389 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
390 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
391 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
392 0x00, 0x00, 0x00, 0x0c, 0x02, 0x03, 0x1e, 0xf1, 0x4a, 0x85, 0x04, 0x10,
393 0x02, 0x01, 0x06, 0x14, 0x12, 0x16, 0x13, 0x23, 0x09, 0x07, 0x07, 0x83,
394 0x01, 0x00, 0x00, 0x66, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x80, 0x01, 0x1d,
395 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, 0xc4, 0x8e,
396 0x21, 0x00, 0x00, 0x1e, 0xd6, 0x09, 0x80, 0xa0, 0x20, 0xe0, 0x2d, 0x10,
397 0x10, 0x60, 0x22, 0x00, 0x12, 0x8e, 0x21, 0x08, 0x08, 0x18, 0x8c, 0x0a,
398 0xd0, 0x90, 0x20, 0x40, 0x31, 0x20, 0x0c, 0x40, 0x55, 0x00, 0xc4, 0x8e,
399 0x21, 0x00, 0x00, 0x18, 0x01, 0x1d, 0x80, 0xd0, 0x72, 0x1c, 0x16, 0x20,
400 0x10, 0x2c, 0x25, 0x80, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x9e, 0x8c, 0x0a,
401 0xa0, 0x14, 0x51, 0xf0, 0x16, 0x00, 0x26, 0x7c, 0x43, 0x00, 0x13, 0x8e,
402 0x21, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5,
403 0x02, 0x03, 0x04, 0xf1, 0xf3, 0x39, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40,
404 0x58, 0x2c, 0x45, 0x00, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x1e, 0x8c, 0x0a,
405 0xa0, 0x20, 0x51, 0x20, 0x18, 0x10, 0x18, 0x7e, 0x23, 0x00, 0xc4, 0x8e,
406 0x21, 0x00, 0x00, 0x98, 0x01, 0x1d, 0x00, 0xbc, 0x52, 0xd0, 0x1e, 0x20,
407 0xb8, 0x28, 0x55, 0x40, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00,
408 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
409 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
410 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
411 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
412 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
413 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf
414};
415
416TEST_CASE(edid_extension_maps)
417{
418 auto edid_load_result = EDID::Parser::from_bytes({ edid_extension_maps, sizeof(edid_extension_maps) });
419 EXPECT(!edid_load_result.is_error());
420 auto edid = edid_load_result.release_value();
421 EXPECT(edid.legacy_manufacturer_id() == "SII");
422
423 {
424 static constexpr struct {
425 unsigned block_id;
426 unsigned width;
427 unsigned height;
428 unsigned refresh_rate;
429 } expected_detailed_timings[] = {
430 { 0, 1920, 1080, 60 },
431 { 0, 720, 480, 60 },
432 { 2, 1280, 720, 60 },
433 { 2, 640, 480, 60 },
434 { 2, 720, 576, 50 },
435 { 2, 1920, 1080, 50 },
436 { 2, 1440, 480, 60 },
437 { 3, 1920, 1080, 60 },
438 { 3, 1440, 576, 50 },
439 { 3, 1280, 720, 50 }
440 };
441 static constexpr size_t expected_detailed_timings_count = sizeof(expected_detailed_timings) / sizeof(expected_detailed_timings[0]);
442 size_t detailed_timings_found = 0;
443 auto result = edid.for_each_detailed_timing([&](auto& detailed_timing, unsigned block_id) {
444 EXPECT(detailed_timings_found < expected_detailed_timings_count);
445 auto& expected_timings = expected_detailed_timings[detailed_timings_found];
446 EXPECT(block_id == expected_timings.block_id);
447 EXPECT(detailed_timing.horizontal_addressable_pixels() == expected_timings.width);
448 EXPECT(detailed_timing.vertical_addressable_lines() == expected_timings.height);
449 EXPECT(detailed_timing.refresh_rate().lround() == expected_timings.refresh_rate);
450 detailed_timings_found++;
451 return IterationDecision::Continue;
452 });
453 EXPECT(!result.is_error());
454 EXPECT(result.value() == IterationDecision::Continue);
455 EXPECT(detailed_timings_found == expected_detailed_timings_count);
456 }
457}
458
459static const u8 edid_1_0[] = {
460 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x34, 0x38, 0xc2, 0x0b,
461 0x7b, 0x00, 0x00, 0x00, 0x0f, 0x0a, 0x01, 0x00, 0x28, 0x20, 0x18, 0x32,
462 0xe8, 0x7e, 0x4e, 0x9e, 0x57, 0x45, 0x98, 0x24, 0x10, 0x47, 0x4f, 0xa4,
463 0x42, 0x01, 0x31, 0x59, 0x45, 0x59, 0x61, 0x59, 0x71, 0x4f, 0x81, 0x80,
464 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xf9, 0x15, 0x20, 0xf8, 0x30, 0x58,
465 0x1f, 0x20, 0x20, 0x40, 0x13, 0x00, 0x40, 0xf0, 0x10, 0x00, 0x00, 0x1e,
466 0xa4, 0x1a, 0x20, 0x10, 0x31, 0x58, 0x24, 0x20, 0x2f, 0x55, 0x33, 0x00,
467 0x40, 0xf0, 0x10, 0x00, 0x00, 0x1e, 0x30, 0x2a, 0x00, 0x98, 0x51, 0x00,
468 0x2a, 0x40, 0x30, 0x70, 0x13, 0x00, 0x40, 0xf0, 0x10, 0x00, 0x00, 0x1e,
469 0xea, 0x24, 0x00, 0x60, 0x41, 0x00, 0x28, 0x30, 0x30, 0x60, 0x13, 0x00,
470 0x40, 0xf0, 0x10, 0x00, 0x00, 0x1e, 0x00, 0x72
471};
472
473TEST_CASE(edid_1_0)
474{
475 auto edid_load_result = EDID::Parser::from_bytes({ edid_1_0, sizeof(edid_1_0) });
476 EXPECT(!edid_load_result.is_error());
477 auto edid = edid_load_result.release_value();
478 EXPECT(edid.legacy_manufacturer_id() == "MAX");
479 EXPECT(edid.serial_number() == 123);
480
481 {
482 static constexpr struct {
483 unsigned block_id;
484 unsigned width;
485 unsigned height;
486 unsigned refresh_rate;
487 } expected_detailed_timings[] = {
488 { 0, 800, 600, 85 },
489 { 0, 800, 600, 100 },
490 { 0, 1280, 1024, 60 },
491 { 0, 1024, 768, 85 }
492 };
493 static constexpr size_t expected_detailed_timings_count = sizeof(expected_detailed_timings) / sizeof(expected_detailed_timings[0]);
494 size_t detailed_timings_found = 0;
495 auto result = edid.for_each_detailed_timing([&](auto& detailed_timing, unsigned block_id) {
496 EXPECT(detailed_timings_found < expected_detailed_timings_count);
497 auto& expected_timings = expected_detailed_timings[detailed_timings_found];
498 EXPECT(block_id == expected_timings.block_id);
499 EXPECT(detailed_timing.horizontal_addressable_pixels() == expected_timings.width);
500 EXPECT(detailed_timing.vertical_addressable_lines() == expected_timings.height);
501 EXPECT(detailed_timing.refresh_rate().lround() == expected_timings.refresh_rate);
502 detailed_timings_found++;
503 return IterationDecision::Continue;
504 });
505 EXPECT(!result.is_error());
506 EXPECT(result.value() == IterationDecision::Continue);
507 EXPECT(detailed_timings_found == expected_detailed_timings_count);
508 }
509}
510
511TEST_CASE(dmt_find_std_id)
512{
513 auto* dmt = EDID::DMT::find_timing_by_std_id(0xd1, 0xf);
514 EXPECT(dmt);
515 EXPECT(dmt->dmt_id == 0x46);
516 EXPECT(dmt->horizontal_pixels == 1920 && dmt->vertical_lines == 1200);
517}
518
519TEST_CASE(dmt_frequency)
520{
521 auto* dmt = EDID::DMT::find_timing_by_dmt_id(0x4);
522 EXPECT(dmt);
523
524 // FIXME: Use the FixedPoint(double) ctor like `expected_vertical_frequency(59.940)` instead of
525 // dividing by 1000 in the next line once FixedPoint::operator/ rounds.
526 // 1. DMT.cpp is built as part of the kernel (despite being in Userland/)
527 // 2. The Kernel can't use floating point
528 // 3. So it has to use FixedPoint(59940) / 1000
529 // 4. The FixedPoint(double) ctor rounds, but FixedPoint::operator/ currently doesn't,
530 // so FixedPoint(59.940) has a different lowest bit than
531 // FixedPoint(59940) / 1000. So the test can't use the FixedPoint(double) ctor at the moment.
532 static FixedPoint<16, u32> const expected_vertical_frequency(59940);
533 EXPECT(dmt->vertical_frequency_hz() == expected_vertical_frequency / 1000);
534 static FixedPoint<16, u32> const expected_horizontal_frequency(31469);
535 EXPECT(dmt->horizontal_frequency_khz() == expected_horizontal_frequency / 1000);
536}
537
538TEST_CASE(vic)
539{
540 EXPECT(!EDID::VIC::find_details_by_vic_id(0)); // invalid
541 EXPECT(!EDID::VIC::find_details_by_vic_id(160)); // forbidden range
542 EXPECT(!EDID::VIC::find_details_by_vic_id(250)); // reserved
543 auto* vic_def_32 = EDID::VIC::find_details_by_vic_id(32);
544 EXPECT(vic_def_32);
545 EXPECT(vic_def_32->vic_id == 32);
546 auto* vic_def_200 = EDID::VIC::find_details_by_vic_id(200);
547 EXPECT(vic_def_200);
548 EXPECT(vic_def_200->vic_id == 200);
549
550 for (unsigned vic_id = 0; vic_id <= 0xff; vic_id++) {
551 auto* vic_def = EDID::VIC::find_details_by_vic_id((u8)vic_id);
552 if (vic_def) {
553 EXPECT((vic_id >= 1 && vic_id <= 127) || (vic_id >= 193 && vic_id <= 219));
554 EXPECT(vic_def->vic_id == vic_id);
555 } else {
556 EXPECT(vic_id == 0 || (vic_id >= 128 && vic_id <= 192) || (vic_id >= 220));
557 }
558 }
559}