1 /* vim: set et fde fdm=syntax ft=c.doxygen ts=4 sts=4 sw=4 : */
2 /*
3 * Copyright © 2010-2011 Saleem Abdulrasool <compnerd@compnerd.org>.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
20 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
22 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #ifndef eds_edid_h
32 #define eds_edid_h
33
34 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
35
36 #include <assert.h>
37 #include <stdint.h>
38 #include <stdbool.h>
39
40 #define EDID_I2C_DDC_DATA_ADDRESS (0x50)
41
42 #define EDID_BLOCK_SIZE (0x80)
43 #define EDID_MAX_EXTENSIONS (0xfe)
44
45
46 static const uint8_t EDID_HEADER[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
47 static const uint8_t EDID_STANDARD_TIMING_DESCRIPTOR_INVALID[] = { 0x01, 0x01 };
48
49
50 enum edid_extension_type {
51 EDID_EXTENSION_TIMING = 0x01, // Timing Extension
52 EDID_EXTENSION_CEA = 0x02, // Additional Timing Block Data (CEA EDID Timing Extension)
53 EDID_EXTENSION_VTB = 0x10, // Video Timing Block Extension (VTB-EXT)
54 EDID_EXTENSION_EDID_2_0 = 0x20, // EDID 2.0 Extension
55 EDID_EXTENSION_DI = 0x40, // Display Information Extension (DI-EXT)
56 EDID_EXTENSION_LS = 0x50, // Localised String Extension (LS-EXT)
57 EDID_EXTENSION_MI = 0x60, // Microdisplay Interface Extension (MI-EXT)
58 EDID_EXTENSION_DTCDB_1 = 0xa7, // Display Transfer Characteristics Data Block (DTCDB)
59 EDID_EXTENSION_DTCDB_2 = 0xaf,
60 EDID_EXTENSION_DTCDB_3 = 0xbf,
61 EDID_EXTENSION_BLOCK_MAP = 0xf0, // Block Map
62 EDID_EXTENSION_DDDB = 0xff, // Display Device Data Block (DDDB)
63 };
64
65 enum edid_display_type {
66 EDID_DISPLAY_TYPE_MONOCHROME,
67 EDID_DISPLAY_TYPE_RGB,
68 EDID_DISPLAY_TYPE_NON_RGB,
69 EDID_DISPLAY_TYPE_UNDEFINED,
70 };
71
72 enum edid_aspect_ratio {
73 EDID_ASPECT_RATIO_16_10,
74 EDID_ASPECT_RATIO_4_3,
75 EDID_ASPECT_RATIO_5_4,
76 EDID_ASPECT_RATIO_16_9,
77 };
78
79 enum edid_signal_sync {
80 EDID_SIGNAL_SYNC_ANALOG_COMPOSITE,
81 EDID_SIGNAL_SYNC_BIPOLAR_ANALOG_COMPOSITE,
82 EDID_SIGNAL_SYNC_DIGITAL_COMPOSITE,
83 EDID_SIGNAL_SYNC_DIGITAL_SEPARATE,
84 };
85
86 enum edid_stereo_mode {
87 EDID_STEREO_MODE_NONE,
88 EDID_STEREO_MODE_RESERVED,
89 EDID_STEREO_MODE_FIELD_SEQUENTIAL_RIGHT,
90 EDID_STEREO_MODE_2_WAY_INTERLEAVED_RIGHT,
91 EDID_STEREO_MODE_FIELD_SEQUENTIAL_LEFT,
92 EDID_STEREO_MODE_2_WAY_INTERLEAVED_LEFT,
93 EDID_STEREO_MODE_4_WAY_INTERLEAVED,
94 EDID_STEREO_MODE_SIDE_BY_SIDE_INTERLEAVED,
95 };
96
97 enum edid_monitor_descriptor_type {
98 EDID_MONTIOR_DESCRIPTOR_MANUFACTURER_DEFINED = 0x0f,
99 EDID_MONITOR_DESCRIPTOR_STANDARD_TIMING_IDENTIFIERS = 0xfa,
100 EDID_MONITOR_DESCRIPTOR_COLOR_POINT = 0xfb,
101 EDID_MONITOR_DESCRIPTOR_MONITOR_NAME = 0xfc,
102 EDID_MONITOR_DESCRIPTOR_MONITOR_RANGE_LIMITS = 0xfd,
103 EDID_MONITOR_DESCRIPTOR_ASCII_STRING = 0xfe,
104 EDID_MONITOR_DESCRIPTOR_MONITOR_SERIAL_NUMBER = 0xff,
105 };
106
107 enum edid_secondary_timing_support {
108 EDID_SECONDARY_TIMING_NOT_SUPPORTED,
109 EDID_SECONDARY_TIMING_GFT = 0x02,
110 };
111
112
113 struct __attribute__ (( packed )) edid_detailed_timing_descriptor {
114 uint16_t pixel_clock; /* = value * 10000 */
115
116 uint8_t horizontal_active_lo;
117 uint8_t horizontal_blanking_lo;
118
119 unsigned horizontal_blanking_hi : 4;
120 unsigned horizontal_active_hi : 4;
121
122 uint8_t vertical_active_lo;
123 uint8_t vertical_blanking_lo;
124
125 unsigned vertical_blanking_hi : 4;
126 unsigned vertical_active_hi : 4;
127
128 uint8_t horizontal_sync_offset_lo;
129 uint8_t horizontal_sync_pulse_width_lo;
130
131 unsigned vertical_sync_pulse_width_lo : 4;
132 unsigned vertical_sync_offset_lo : 4;
133
134 unsigned vertical_sync_pulse_width_hi : 2;
135 unsigned vertical_sync_offset_hi : 2;
136 unsigned horizontal_sync_pulse_width_hi : 2;
137 unsigned horizontal_sync_offset_hi : 2;
138
139 uint8_t horizontal_image_size_lo;
140 uint8_t vertical_image_size_lo;
141
142 unsigned vertical_image_size_hi : 4;
143 unsigned horizontal_image_size_hi : 4;
144
145 uint8_t horizontal_border;
146 uint8_t vertical_border;
147
148 unsigned stereo_mode_lo : 1;
149 unsigned signal_pulse_polarity : 1; /* pulse on sync, composite/horizontal polarity */
150 unsigned signal_serration_polarity : 1; /* serrate on sync, vertical polarity */
151 unsigned signal_sync : 2;
152 unsigned stereo_mode_hi : 2;
153 unsigned interlaced : 1;
154 };
155
156 static inline uint32_t
edid_detailed_timing_pixel_clock(const struct edid_detailed_timing_descriptor * const dtb)157 edid_detailed_timing_pixel_clock(const struct edid_detailed_timing_descriptor * const dtb)
158 {
159 return dtb->pixel_clock * 10000;
160 }
161
162 static inline uint16_t
edid_detailed_timing_horizontal_blanking(const struct edid_detailed_timing_descriptor * const dtb)163 edid_detailed_timing_horizontal_blanking(const struct edid_detailed_timing_descriptor * const dtb)
164 {
165 return (dtb->horizontal_blanking_hi << 8) | dtb->horizontal_blanking_lo;
166 }
167
168 static inline uint16_t
edid_detailed_timing_horizontal_active(const struct edid_detailed_timing_descriptor * const dtb)169 edid_detailed_timing_horizontal_active(const struct edid_detailed_timing_descriptor * const dtb)
170 {
171 return (dtb->horizontal_active_hi << 8) | dtb->horizontal_active_lo;
172 }
173
174 static inline uint16_t
edid_detailed_timing_vertical_blanking(const struct edid_detailed_timing_descriptor * const dtb)175 edid_detailed_timing_vertical_blanking(const struct edid_detailed_timing_descriptor * const dtb)
176 {
177 return (dtb->vertical_blanking_hi << 8) | dtb->vertical_blanking_lo;
178 }
179
180 static inline uint16_t
edid_detailed_timing_vertical_active(const struct edid_detailed_timing_descriptor * const dtb)181 edid_detailed_timing_vertical_active(const struct edid_detailed_timing_descriptor * const dtb)
182 {
183 return (dtb->vertical_active_hi << 8) | dtb->vertical_active_lo;
184 }
185
186 static inline uint8_t
edid_detailed_timing_vertical_sync_offset(const struct edid_detailed_timing_descriptor * const dtb)187 edid_detailed_timing_vertical_sync_offset(const struct edid_detailed_timing_descriptor * const dtb)
188 {
189 return (dtb->vertical_sync_offset_hi << 4) | dtb->vertical_sync_offset_lo;
190 }
191
192 static inline uint8_t
edid_detailed_timing_vertical_sync_pulse_width(const struct edid_detailed_timing_descriptor * const dtb)193 edid_detailed_timing_vertical_sync_pulse_width(const struct edid_detailed_timing_descriptor * const dtb)
194 {
195 return (dtb->vertical_sync_pulse_width_hi << 4) | dtb->vertical_sync_pulse_width_lo;
196 }
197
198 static inline uint8_t
edid_detailed_timing_horizontal_sync_offset(const struct edid_detailed_timing_descriptor * const dtb)199 edid_detailed_timing_horizontal_sync_offset(const struct edid_detailed_timing_descriptor * const dtb)
200 {
201 return (dtb->horizontal_sync_offset_hi << 4) | dtb->horizontal_sync_offset_lo;
202 }
203
204 static inline uint8_t
edid_detailed_timing_horizontal_sync_pulse_width(const struct edid_detailed_timing_descriptor * const dtb)205 edid_detailed_timing_horizontal_sync_pulse_width(const struct edid_detailed_timing_descriptor * const dtb)
206 {
207 return (dtb->horizontal_sync_pulse_width_hi << 4) | dtb->horizontal_sync_pulse_width_lo;
208 }
209
210 static inline uint16_t
edid_detailed_timing_horizontal_image_size(const struct edid_detailed_timing_descriptor * const dtb)211 edid_detailed_timing_horizontal_image_size(const struct edid_detailed_timing_descriptor * const dtb)
212 {
213 return (dtb->horizontal_image_size_hi << 8) | dtb->horizontal_image_size_lo;
214 }
215
216 static inline uint16_t
edid_detailed_timing_vertical_image_size(const struct edid_detailed_timing_descriptor * const dtb)217 edid_detailed_timing_vertical_image_size(const struct edid_detailed_timing_descriptor * const dtb)
218 {
219 return (dtb->vertical_image_size_hi << 8) | dtb->vertical_image_size_lo;
220 }
221
222 static inline uint8_t
edid_detailed_timing_stereo_mode(const struct edid_detailed_timing_descriptor * const dtb)223 edid_detailed_timing_stereo_mode(const struct edid_detailed_timing_descriptor * const dtb)
224 {
225 return (dtb->stereo_mode_hi << 2 | dtb->stereo_mode_lo);
226 }
227
228
229 struct __attribute__ (( packed )) edid_monitor_descriptor {
230 uint16_t flag0;
231 uint8_t flag1;
232 uint8_t tag;
233 uint8_t flag2;
234 uint8_t data[13];
235 };
236
237 typedef char edid_monitor_descriptor_string[sizeof(((struct edid_monitor_descriptor *)0)->data) + 1];
238
239
240 struct __attribute__ (( packed )) edid_monitor_range_limits {
241 uint8_t minimum_vertical_rate; /* Hz */
242 uint8_t maximum_vertical_rate; /* Hz */
243 uint8_t minimum_horizontal_rate; /* kHz */
244 uint8_t maximum_horizontal_rate; /* kHz */
245 uint8_t maximum_supported_pixel_clock; /* = (value * 10) Mhz (round to 10 MHz) */
246
247 /* secondary timing formula */
248 uint8_t secondary_timing_support;
249 uint8_t reserved;
250 uint8_t secondary_curve_start_frequency; /* horizontal frequency / 2 kHz */
251 uint8_t c; /* = (value >> 1) */
252 uint16_t m;
253 uint8_t k;
254 uint8_t j; /* = (value >> 1) */
255 };
256
257
258 struct __attribute__ (( packed )) edid_standard_timing_descriptor {
259 uint8_t horizontal_active_pixels; /* = (value + 31) * 8 */
260
261 unsigned refresh_rate : 6; /* = value + 60 */
262 unsigned image_aspect_ratio : 2;
263 };
264
265 uint32_t
edid_standard_timing_horizontal_active(const struct edid_standard_timing_descriptor * const desc)266 edid_standard_timing_horizontal_active(const struct edid_standard_timing_descriptor * const desc)
267 {
268 return ((desc->horizontal_active_pixels + 31) << 3);
269 }
270
271 uint32_t
edid_standard_timing_vertical_active(const struct edid_standard_timing_descriptor * const desc)272 edid_standard_timing_vertical_active(const struct edid_standard_timing_descriptor * const desc)
273 {
274 const uint32_t hres = edid_standard_timing_horizontal_active(desc);
275
276 switch (desc->image_aspect_ratio) {
277 case EDID_ASPECT_RATIO_16_10:
278 return ((hres * 10) >> 4);
279 case EDID_ASPECT_RATIO_4_3:
280 return ((hres * 3) >> 2);
281 case EDID_ASPECT_RATIO_5_4:
282 return ((hres << 2) / 5);
283 case EDID_ASPECT_RATIO_16_9:
284 return ((hres * 9) >> 4);
285 }
286
287 return hres;
288 }
289
290 uint32_t
edid_standard_timing_refresh_rate(const struct edid_standard_timing_descriptor * const desc)291 edid_standard_timing_refresh_rate(const struct edid_standard_timing_descriptor * const desc)
292 {
293 return (desc->refresh_rate + 60);
294 }
295
296
297 struct __attribute__ (( packed )) edid {
298 /* header information */
299 uint8_t header[8];
300
301 /* vendor/product identification */
302 uint16_t manufacturer;
303
304 uint8_t product[2];
305 uint8_t serial_number[4];
306 uint8_t manufacture_week;
307 uint8_t manufacture_year; /* = value + 1990 */
308
309 /* EDID version */
310 uint8_t version;
311 uint8_t revision;
312
313 /* basic display parameters and features */
314 union {
315 struct __attribute__ (( packed )) {
316 unsigned dfp_1x : 1; /* VESA DFP 1.x */
317 unsigned : 6;
318 unsigned digital : 1;
319 } digital;
320 struct __attribute__ (( packed )) {
321 unsigned vsync_serration : 1;
322 unsigned green_video_sync : 1;
323 unsigned composite_sync : 1;
324 unsigned separate_sync : 1;
325 unsigned blank_to_black_setup : 1;
326 unsigned signal_level_standard : 2;
327 unsigned digital : 1;
328 } analog;
329 } video_input_definition;
330
331 uint8_t maximum_horizontal_image_size; /* cm */
332 uint8_t maximum_vertical_image_size; /* cm */
333
334 uint8_t display_transfer_characteristics; /* gamma = (value + 100) / 100 */
335
336 struct __attribute__ (( packed )) {
337 unsigned default_gtf : 1; /* generalised timing formula */
338 unsigned preferred_timing_mode : 1;
339 unsigned standard_default_color_space : 1;
340 unsigned display_type : 2;
341 unsigned active_off : 1;
342 unsigned suspend : 1;
343 unsigned standby : 1;
344 } feature_support;
345
346 /* color characteristics block */
347 unsigned green_y_low : 2;
348 unsigned green_x_low : 2;
349 unsigned red_y_low : 2;
350 unsigned red_x_low : 2;
351
352 unsigned white_y_low : 2;
353 unsigned white_x_low : 2;
354 unsigned blue_y_low : 2;
355 unsigned blue_x_low : 2;
356
357 uint8_t red_x;
358 uint8_t red_y;
359 uint8_t green_x;
360 uint8_t green_y;
361 uint8_t blue_x;
362 uint8_t blue_y;
363 uint8_t white_x;
364 uint8_t white_y;
365
366 /* established timings */
367 struct __attribute__ (( packed )) {
368 unsigned timing_800x600_60 : 1;
369 unsigned timing_800x600_56 : 1;
370 unsigned timing_640x480_75 : 1;
371 unsigned timing_640x480_72 : 1;
372 unsigned timing_640x480_67 : 1;
373 unsigned timing_640x480_60 : 1;
374 unsigned timing_720x400_88 : 1;
375 unsigned timing_720x400_70 : 1;
376
377 unsigned timing_1280x1024_75 : 1;
378 unsigned timing_1024x768_75 : 1;
379 unsigned timing_1024x768_70 : 1;
380 unsigned timing_1024x768_60 : 1;
381 unsigned timing_1024x768_87 : 1;
382 unsigned timing_832x624_75 : 1;
383 unsigned timing_800x600_75 : 1;
384 unsigned timing_800x600_72 : 1;
385 } established_timings;
386
387 struct __attribute__ (( packed )) {
388 unsigned reserved : 7;
389 unsigned timing_1152x870_75 : 1;
390 } manufacturer_timings;
391
392 /* standard timing id */
393 struct edid_standard_timing_descriptor standard_timing_id[8];
394
395 /* detailed timing */
396 union {
397 struct edid_monitor_descriptor monitor;
398 struct edid_detailed_timing_descriptor timing;
399 } detailed_timings[4];
400
401 uint8_t extensions;
402 uint8_t checksum;
403 };
404
405 static inline void
edid_manufacturer(const struct edid * const edid,char manufacturer[4])406 edid_manufacturer(const struct edid * const edid, char manufacturer[4])
407 {
408 manufacturer[0] = '@' + ((edid->manufacturer & 0x007c) >> 2);
409 manufacturer[1] = '@' + (((edid->manufacturer & 0x0003) >> 00) << 3)
410 | (((edid->manufacturer & 0xe000) >> 13) << 0);
411 manufacturer[2] = '@' + ((edid->manufacturer & 0x1f00) >> 8);
412 manufacturer[3] = '\0';
413 }
414
415 static inline double
edid_gamma(const struct edid * const edid)416 edid_gamma(const struct edid * const edid)
417 {
418 return (edid->display_transfer_characteristics + 100) / 100.0;
419 }
420
421 static inline bool
edid_detailed_timing_is_monitor_descriptor(const struct edid * const edid,const uint8_t timing)422 edid_detailed_timing_is_monitor_descriptor(const struct edid * const edid,
423 const uint8_t timing)
424 {
425 const struct edid_monitor_descriptor * const mon =
426 &edid->detailed_timings[timing].monitor;
427
428 assert(timing < ARRAY_SIZE(edid->detailed_timings));
429
430 return mon->flag0 == 0x0000 && mon->flag1 == 0x00 && mon->flag2 == 0x00;
431 }
432
433
434 struct __attribute__ (( packed )) edid_color_characteristics_data {
435 struct {
436 uint16_t x;
437 uint16_t y;
438 } red, green, blue, white;
439 };
440
441 static inline struct edid_color_characteristics_data
edid_color_characteristics(const struct edid * const edid)442 edid_color_characteristics(const struct edid * const edid)
443 {
444 const struct edid_color_characteristics_data characteristics = {
445 .red = {
446 .x = (edid->red_x << 2) | edid->red_x_low,
447 .y = (edid->red_y << 2) | edid->red_y_low,
448 },
449 .green = {
450 .x = (edid->green_x << 2) | edid->green_x_low,
451 .y = (edid->green_y << 2) | edid->green_y_low,
452 },
453 .blue = {
454 .x = (edid->blue_x << 2) | edid->blue_x_low,
455 .y = (edid->blue_y << 2) | edid->blue_y_low,
456 },
457 .white = {
458 .x = (edid->white_x << 2) | edid->white_x_low,
459 .y = (edid->white_y << 2) | edid->white_y_low,
460 },
461 };
462
463 return characteristics;
464 }
465
466
467 struct __attribute__ (( packed )) edid_block_map {
468 uint8_t tag;
469 uint8_t extension_tag[126];
470 uint8_t checksum;
471 };
472
473
474 struct __attribute__ (( packed )) edid_extension {
475 uint8_t tag;
476 uint8_t revision;
477 uint8_t extension_data[125];
478 uint8_t checksum;
479 };
480
481
482 static inline bool
edid_verify_checksum(const uint8_t * const block)483 edid_verify_checksum(const uint8_t * const block)
484 {
485 uint8_t checksum = 0;
486 int i;
487
488 for (i = 0; i < EDID_BLOCK_SIZE; i++)
489 checksum += block[i];
490
491 return (checksum == 0);
492 }
493
494 static inline double
edid_decode_fixed_point(uint16_t value)495 edid_decode_fixed_point(uint16_t value)
496 {
497 double result = 0.0;
498
499 assert((~value & 0xfc00) == 0xfc00); /* edid fraction is 10 bits */
500
501 for (uint8_t i = 0; value && (i < 10); i++, value >>= 1)
502 result = result + ((value & 0x1) * (1.0 / (1 << (10 - i))));
503
504 return result;
505 }
506
507 #endif
508
509