├── README ├── .gitignore ├── CMakeLists.txt └── src ├── eds ├── macros.h ├── hdmi.h ├── cea861.h └── edid.h └── examples └── parse-edid └── parse-edid.c /README: -------------------------------------------------------------------------------- 1 | 2 | 3 | eds is a library of EDID data structure definitions as well as a small set of 4 | utility functions. It is designed to be used in userspace and kernelspace 5 | application and thus relies on as few library methods as possible for the 6 | utility functions. 7 | 8 | Patches to fix bugs or TODO items are more than welcome. 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | 6 | .*.sw[op] 7 | *~ 8 | *.[oa] 9 | *.lo 10 | .deps 11 | .dirstamp 12 | .libs 13 | tags 14 | 15 | /aclocal.m4 16 | /autom4te.cache/ 17 | /config.log 18 | /config/ 19 | /config.status 20 | /configure 21 | /depcomp 22 | /install-sh 23 | /libtool 24 | /m4/ 25 | /Makefile.in 26 | /Makefile 27 | /missing 28 | /src/examples/parse-edid/parse-edid 29 | 30 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Saleem Abdulrasool 2 | 3 | cmake_minimum_required(VERSION 3.4) 4 | project(eds LANGUAGES C) 5 | 6 | set(CMAKE_C_STANDARD 99) 7 | 8 | option(WITH_EXAMPLES "build example program" YES) 9 | 10 | include(CheckFunctionExists) 11 | include(CheckLibraryExists) 12 | include(GNUInstallDirs) 13 | 14 | check_function_exists(sqrt HAVE_SQRT) 15 | if(NOT HAVE_SQRT) 16 | unset(HAVE_SQRT CACHE) 17 | check_library_exists(m sqrt "" HAVE_SQRT) 18 | if(NOT HAVE_SQRT) 19 | message(SEND_ERROR "unable to find `sqrt`") 20 | endif() 21 | set(LIBM_LIBS m) 22 | endif() 23 | 24 | if(WITH_EXAMPLES) 25 | add_executable(parse-edid 26 | src/examples/parse-edid/parse-edid.c) 27 | if(MSVC) 28 | target_compile_options(parse-edid PRIVATE 29 | /FI${CMAKE_SOURCE_DIR}/src/eds/macros.h) 30 | else() 31 | target_compile_options(parse-edid PRIVATE 32 | -include;${CMAKE_SOURCE_DIR}/src/eds/macros.h) 33 | endif() 34 | target_include_directories(parse-edid PRIVATE 35 | src 36 | src/eds) 37 | endif() 38 | 39 | install(FILES 40 | src/eds/cea861.h 41 | src/eds/edid.h 42 | src/eds/hdmi.h 43 | DESTINATION 44 | ${CMAKE_INSTALL_FULL_INCLUDE_DIR}/eds) 45 | 46 | -------------------------------------------------------------------------------- /src/eds/macros.h: -------------------------------------------------------------------------------- 1 | /* vim: set et fde fdm=syntax ft=c.doxygen ts=4 sts=4 sw=4 : */ 2 | /* 3 | * Copyright © 2010-2011 Saleem Abdulrasool . 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_macros_h 32 | #define eds_macros_h 33 | 34 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) 35 | 36 | #endif 37 | 38 | -------------------------------------------------------------------------------- /src/eds/hdmi.h: -------------------------------------------------------------------------------- 1 | /* vim: set et fde fdm=syntax ft=c.doxygen ts=4 sts=4 sw=4 : */ 2 | /* 3 | * Copyright © 2010-2011 Saleem Abdulrasool . 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_hdmi_h 32 | #define eds_hdmi_h 33 | 34 | #include "cea861.h" 35 | 36 | /*! \todo figure out a better way to determine the offsets */ 37 | #define HDMI_VSDB_EXTENSION_FLAGS_OFFSET (0x06) 38 | #define HDMI_VSDB_MAX_TMDS_OFFSET (0x07) 39 | #define HDMI_VSDB_LATENCY_FIELDS_OFFSET (0x08) 40 | 41 | static const uint8_t HDMI_OUI[] = { 0x00, 0x0C, 0x03 }; 42 | 43 | struct __attribute__ (( packed )) hdmi_vendor_specific_data_block { 44 | struct cea861_data_block_header header; 45 | 46 | uint8_t ieee_registration_id[3]; /* LSB */ 47 | 48 | unsigned port_configuration_b : 4; 49 | unsigned port_configuration_a : 4; 50 | unsigned port_configuration_d : 4; 51 | unsigned port_configuration_c : 4; 52 | 53 | /* extension fields */ 54 | unsigned dvi_dual_link : 1; 55 | unsigned : 2; 56 | unsigned yuv_444_supported : 1; 57 | unsigned colour_depth_30_bit : 1; 58 | unsigned colour_depth_36_bit : 1; 59 | unsigned colour_depth_48_bit : 1; 60 | unsigned audio_info_frame : 1; 61 | 62 | uint8_t max_tmds_clock; /* = value * 5 */ 63 | 64 | unsigned : 6; 65 | unsigned interlaced_latency_fields : 1; 66 | unsigned latency_fields : 1; 67 | 68 | uint8_t video_latency; /* = (value - 1) * 2 */ 69 | uint8_t audio_latency; /* = (value - 1) * 2 */ 70 | uint8_t interlaced_video_latency; 71 | uint8_t interlaced_audio_latency; 72 | 73 | uint8_t reserved[]; 74 | }; 75 | 76 | #endif 77 | 78 | -------------------------------------------------------------------------------- /src/eds/cea861.h: -------------------------------------------------------------------------------- 1 | /* vim: set et fde fdm=syntax ft=c.doxygen ts=4 sts=4 sw=4 : */ 2 | /* 3 | * Copyright © 2010-2011 Saleem Abdulrasool . 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_cea861_h 32 | #define eds_cea861_h 33 | 34 | #define CEA861_NO_DTDS_PRESENT (0x04) 35 | 36 | enum cea861_data_block_type { 37 | CEA861_DATA_BLOCK_TYPE_RESERVED0, 38 | CEA861_DATA_BLOCK_TYPE_AUDIO, 39 | CEA861_DATA_BLOCK_TYPE_VIDEO, 40 | CEA861_DATA_BLOCK_TYPE_VENDOR_SPECIFIC, 41 | CEA861_DATA_BLOCK_TYPE_SPEAKER_ALLOCATION, 42 | CEA861_DATA_BLOCK_TYPE_VESA_DTC, 43 | CEA861_DATA_BLOCK_TYPE_RESERVED6, 44 | CEA861_DATA_BLOCK_TYPE_EXTENDED, 45 | }; 46 | 47 | enum cea861_audio_format { 48 | CEA861_AUDIO_FORMAT_RESERVED, 49 | CEA861_AUDIO_FORMAT_LPCM, 50 | CEA861_AUDIO_FORMAT_AC_3, 51 | CEA861_AUDIO_FORMAT_MPEG_1, 52 | CEA861_AUDIO_FORMAT_MP3, 53 | CEA861_AUDIO_FORMAT_MPEG2, 54 | CEA861_AUDIO_FORMAT_AAC_LC, 55 | CEA861_AUDIO_FORMAT_DTS, 56 | CEA861_AUDIO_FORMAT_ATRAC, 57 | CEA861_AUDIO_FORMAT_DSD, 58 | CEA861_AUDIO_FORMAT_E_AC_3, 59 | CEA861_AUDIO_FORMAT_DTS_HD, 60 | CEA861_AUDIO_FORMAT_MLP, 61 | CEA861_AUDIO_FORMAT_DST, 62 | CEA861_AUDIO_FORMAT_WMA_PRO, 63 | CEA861_AUDIO_FORMAT_EXTENDED, 64 | }; 65 | 66 | struct __attribute__ (( packed )) cea861_timing_block { 67 | /* CEA Extension Header */ 68 | uint8_t tag; 69 | uint8_t revision; 70 | uint8_t dtd_offset; 71 | 72 | /* Global Declarations */ 73 | unsigned native_dtds : 4; 74 | unsigned yuv_422_supported : 1; 75 | unsigned yuv_444_supported : 1; 76 | unsigned basic_audio_supported : 1; 77 | unsigned underscan_supported : 1; 78 | 79 | uint8_t data[123]; 80 | 81 | uint8_t checksum; 82 | }; 83 | 84 | struct __attribute__ (( packed )) cea861_data_block_header { 85 | unsigned length : 5; 86 | unsigned tag : 3; 87 | }; 88 | 89 | struct __attribute__ (( packed )) cea861_short_video_descriptor { 90 | unsigned video_identification_code : 7; 91 | unsigned native : 1; 92 | }; 93 | 94 | struct __attribute__ (( packed )) cea861_video_data_block { 95 | struct cea861_data_block_header header; 96 | struct cea861_short_video_descriptor svd[]; 97 | }; 98 | 99 | struct __attribute__ (( packed )) cea861_short_audio_descriptor { 100 | unsigned channels : 3; /* = value + 1 */ 101 | unsigned audio_format : 4; 102 | unsigned : 1; 103 | 104 | unsigned sample_rate_32_kHz : 1; 105 | unsigned sample_rate_44_1_kHz : 1; 106 | unsigned sample_rate_48_kHz : 1; 107 | unsigned sample_rate_88_2_kHz : 1; 108 | unsigned sample_rate_96_kHz : 1; 109 | unsigned sample_rate_176_4_kHz : 1; 110 | unsigned sample_rate_192_kHz : 1; 111 | unsigned : 1; 112 | 113 | union { 114 | struct __attribute__ (( packed )) { 115 | unsigned bitrate_16_bit : 1; 116 | unsigned bitrate_20_bit : 1; 117 | unsigned bitrate_24_bit : 1; 118 | unsigned : 5; 119 | } lpcm; 120 | 121 | uint8_t maximum_bit_rate; /* formats 2-8; = value * 8 kHz */ 122 | 123 | uint8_t format_dependent; /* formats 9-13; */ 124 | 125 | struct __attribute__ (( packed )) { 126 | unsigned profile : 3; 127 | unsigned : 5; 128 | } wma_pro; 129 | 130 | struct __attribute__ (( packed )) { 131 | unsigned : 3; 132 | unsigned code : 5; 133 | } extension; 134 | } flags; 135 | }; 136 | 137 | struct __attribute__ (( packed )) cea861_audio_data_block { 138 | struct cea861_data_block_header header; 139 | struct cea861_short_audio_descriptor sad[]; 140 | }; 141 | 142 | struct __attribute__ (( packed )) cea861_speaker_allocation { 143 | unsigned front_left_right : 1; 144 | unsigned front_lfe : 1; /* low frequency effects */ 145 | unsigned front_center : 1; 146 | unsigned rear_left_right : 1; 147 | unsigned rear_center : 1; 148 | unsigned front_left_right_center : 1; 149 | unsigned rear_left_right_center : 1; 150 | unsigned front_left_right_wide : 1; 151 | 152 | unsigned front_left_right_high : 1; 153 | unsigned top_center : 1; 154 | unsigned front_center_high : 1; 155 | unsigned : 5; 156 | 157 | unsigned : 8; 158 | }; 159 | 160 | struct __attribute__ (( packed )) cea861_speaker_allocation_data_block { 161 | struct cea861_data_block_header header; 162 | struct cea861_speaker_allocation payload; 163 | }; 164 | 165 | struct __attribute__ (( packed )) cea861_vendor_specific_data_block { 166 | struct cea861_data_block_header header; 167 | uint8_t ieee_registration[3]; 168 | uint8_t data[30]; 169 | }; 170 | 171 | static const struct cea861_timing { 172 | const uint16_t hactive; 173 | const uint16_t vactive; 174 | const enum { 175 | INTERLACED, 176 | PROGRESSIVE, 177 | } mode; 178 | const uint16_t htotal; 179 | const uint16_t hblank; 180 | const uint16_t vtotal; 181 | const double vblank; 182 | const double hfreq; 183 | const double vfreq; 184 | const double pixclk; 185 | } cea861_timings[] = { 186 | [1] = { 640, 480, PROGRESSIVE, 800, 160, 525, 45.0, 31.469, 59.940, 25.175 }, 187 | [2] = { 720, 480, PROGRESSIVE, 858, 138, 525, 45.0, 31.469, 59.940, 27.000 }, 188 | [3] = { 720, 480, PROGRESSIVE, 858, 138, 525, 45.0, 31.469, 59.940, 27.000 }, 189 | [4] = { 1280, 720, PROGRESSIVE, 1650, 370, 750, 30.0, 45.000, 60.000, 74.250 }, 190 | [5] = { 1920, 1080, INTERLACED, 2200, 280, 1125, 22.5, 33.750, 60.000, 72.250 }, 191 | [6] = { 1440, 480, INTERLACED, 1716, 276, 525, 22.5, 15.734, 59.940, 27.000 }, 192 | [7] = { 1440, 480, INTERLACED, 1716, 276, 525, 22.5, 15.734, 59.940, 27.000 }, 193 | [8] = { 1440, 240, PROGRESSIVE, 1716, 276, 262, 22.0, 15.734, 60.054, 27.000 }, /* 9 */ 194 | [9] = { 1440, 240, PROGRESSIVE, 1716, 276, 262, 22.0, 15.734, 59.826, 27.000 }, /* 8 */ 195 | [10] = { 2880, 480, INTERLACED, 3432, 552, 525, 22.5, 15.734, 59.940, 54.000 }, 196 | [11] = { 2880, 480, INTERLACED, 3432, 552, 525, 22.5, 15.734, 59.940, 54.000 }, 197 | [12] = { 2880, 240, PROGRESSIVE, 3432, 552, 262, 22.0, 15.734, 60.054, 54.000 }, /* 13 */ 198 | [13] = { 2880, 240, PROGRESSIVE, 3432, 552, 262, 22.0, 15.734, 59.826, 54.000 }, /* 12 */ 199 | [14] = { 1440, 480, PROGRESSIVE, 1716, 276, 525, 45.0, 31.469, 59.940, 54.000 }, 200 | [15] = { 1440, 480, PROGRESSIVE, 1716, 276, 525, 45.0, 31.469, 59.940, 54.000 }, 201 | [16] = { 1920, 1080, PROGRESSIVE, 2200, 280, 1125, 45.0, 67.500, 60.000, 148.500 }, 202 | [17] = { 720, 576, PROGRESSIVE, 864, 144, 625, 49.0, 31.250, 50.000, 27.000 }, 203 | [18] = { 720, 576, PROGRESSIVE, 864, 144, 625, 49.0, 31.250, 50.000, 27.000 }, 204 | [19] = { 1280, 720, PROGRESSIVE, 1980, 700, 750, 30.0, 37.500, 50.000, 74.250 }, 205 | [20] = { 1920, 1080, INTERLACED, 2640, 720, 1125, 22.5, 28.125, 50.000, 74.250 }, 206 | [21] = { 1440, 576, INTERLACED, 1728, 288, 625, 24.5, 15.625, 50.000, 27.000 }, 207 | [22] = { 1440, 576, INTERLACED, 1728, 288, 625, 24.5, 15.625, 50.000, 27.000 }, 208 | [23] = { 1440, 288, PROGRESSIVE, 1728, 288, 312, 24.0, 15.625, 50.080, 27.000 }, /* 24 */ 209 | [24] = { 1440, 288, PROGRESSIVE, 1728, 288, 313, 25.0, 15.625, 49.920, 27.000 }, /* 23 */ 210 | // [24] = { 1440, 288, PROGRESSIVE, 1728, 288, 314, 26.0, 15.625, 49.761, 27.000 }, 211 | [25] = { 2880, 576, INTERLACED, 3456, 576, 625, 24.5, 15.625, 50.000, 54.000 }, 212 | [26] = { 2880, 576, INTERLACED, 3456, 576, 625, 24.5, 15.625, 50.000, 54.000 }, 213 | [27] = { 2880, 288, PROGRESSIVE, 3456, 576, 312, 24.0, 15.625, 50.080, 54.000 }, /* 28 */ 214 | [28] = { 2880, 288, PROGRESSIVE, 3456, 576, 313, 25.0, 15.625, 49.920, 54.000 }, /* 27 */ 215 | // [28] = { 2880, 288, PROGRESSIVE, 3456, 576, 314, 26.0, 15.625, 49.761, 54.000 }, 216 | [29] = { 1440, 576, PROGRESSIVE, 1728, 288, 625, 49.0, 31.250, 50.000, 54.000 }, 217 | [30] = { 1440, 576, PROGRESSIVE, 1728, 288, 625, 49.0, 31.250, 50.000, 54.000 }, 218 | [31] = { 1920, 1080, PROGRESSIVE, 2640, 720, 1125, 45.0, 56.250, 50.000, 148.500 }, 219 | [32] = { 1920, 1080, PROGRESSIVE, 2750, 830, 1125, 45.0, 27.000, 24.000, 74.250 }, 220 | [33] = { 1920, 1080, PROGRESSIVE, 2640, 720, 1125, 45.0, 28.125, 25.000, 74.250 }, 221 | [34] = { 1920, 1080, PROGRESSIVE, 2200, 280, 1125, 45.0, 33.750, 30.000, 74.250 }, 222 | [35] = { 2880, 480, PROGRESSIVE, 3432, 552, 525, 45.0, 31.469, 59.940, 108.500 }, 223 | [36] = { 2880, 480, PROGRESSIVE, 3432, 552, 525, 45.0, 31.469, 59.940, 108.500 }, 224 | [37] = { 2880, 576, PROGRESSIVE, 3456, 576, 625, 49.0, 31.250, 50.000, 108.000 }, 225 | [38] = { 2880, 576, PROGRESSIVE, 3456, 576, 625, 49.0, 31.250, 50.000, 108.000 }, 226 | [39] = { 1920, 1080, INTERLACED, 2304, 384, 1250, 85.0, 31.250, 50.000, 72.000 }, 227 | [40] = { 1920, 1080, INTERLACED, 2640, 720, 1125, 22.5, 56.250, 100.000, 148.500 }, 228 | [41] = { 1280, 720, PROGRESSIVE, 1980, 700, 750, 30.0, 75.000, 100.000, 148.500 }, 229 | [42] = { 720, 576, PROGRESSIVE, 864, 144, 625, 49.0, 62.500, 100.000, 54.000 }, 230 | [43] = { 720, 576, PROGRESSIVE, 864, 144, 625, 49.0, 62.500, 100.000, 54.000 }, 231 | [44] = { 1440, 576, INTERLACED, 1728, 288, 625, 24.5, 31.250, 100.000, 54.000 }, 232 | [45] = { 1440, 576, INTERLACED, 1728, 288, 625, 24.5, 31.250, 100.000, 54.000 }, 233 | [46] = { 1920, 1080, INTERLACED, 2200, 280, 1125, 22.5, 67.500, 120.000, 148.500 }, 234 | [47] = { 1280, 720, PROGRESSIVE, 1650, 370, 750, 30.0, 90.000, 120.000, 148.500 }, 235 | [48] = { 720, 480, PROGRESSIVE, 858, 138, 525, 45.0, 62.937, 119.880, 54.000 }, 236 | [49] = { 720, 480, PROGRESSIVE, 858, 138, 525, 45.0, 62.937, 119.880, 54.000 }, 237 | [50] = { 1440, 480, INTERLACED, 1716, 276, 525, 22.5, 31.469, 119.880, 54.000 }, 238 | [51] = { 1440, 480, INTERLACED, 1716, 276, 525, 22.5, 31.469, 119.880, 54.000 }, 239 | [52] = { 720, 576, PROGRESSIVE, 864, 144, 625, 49.0, 125.000, 200.000, 108.000 }, 240 | [53] = { 720, 576, PROGRESSIVE, 864, 144, 625, 49.0, 125.000, 200.000, 108.000 }, 241 | [54] = { 1440, 576, INTERLACED, 1728, 288, 625, 24.5, 62.500, 200.000, 108.000 }, 242 | [55] = { 1440, 576, INTERLACED, 1728, 288, 625, 24.5, 62.500, 200.000, 108.000 }, 243 | [56] = { 720, 480, PROGRESSIVE, 858, 138, 525, 45.0, 125.874, 239.760, 108.000 }, 244 | [57] = { 720, 480, PROGRESSIVE, 858, 138, 525, 45.0, 125.874, 239.760, 108.000 }, 245 | [58] = { 1440, 480, INTERLACED, 1716, 276, 525, 22.5, 62.937, 239.760, 108.000 }, 246 | [59] = { 1440, 480, INTERLACED, 1716, 276, 525, 22.5, 62.937, 239.760, 108.000 }, 247 | [60] = { 1280, 720, PROGRESSIVE, 3300, 2020, 750, 30.0, 18.000, 24.000, 59.400 }, 248 | [61] = { 1280, 720, PROGRESSIVE, 3960, 2680, 750, 30.0, 18.750, 25.000, 74.250 }, 249 | [62] = { 1280, 720, PROGRESSIVE, 3300, 2020, 750, 30.0, 22.500, 30.000, 74.250 }, 250 | [63] = { 1920, 1080, PROGRESSIVE, 2200, 280, 1125, 45.0, 135.000, 120.000, 297.000 }, 251 | [64] = { 1920, 1080, PROGRESSIVE, 2640, 720, 1125, 45.0, 112.500, 100.000, 297.000 }, 252 | }; 253 | 254 | #endif 255 | 256 | -------------------------------------------------------------------------------- /src/eds/edid.h: -------------------------------------------------------------------------------- 1 | /* vim: set et fde fdm=syntax ft=c.doxygen ts=4 sts=4 sw=4 : */ 2 | /* 3 | * Copyright © 2010-2011 Saleem Abdulrasool . 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 | #include 35 | #include 36 | #include 37 | 38 | #define EDID_I2C_DDC_DATA_ADDRESS (0x50) 39 | 40 | #define EDID_BLOCK_SIZE (0x80) 41 | #define EDID_MAX_EXTENSIONS (0xfe) 42 | 43 | 44 | static const uint8_t EDID_HEADER[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; 45 | static const uint8_t EDID_STANDARD_TIMING_DESCRIPTOR_INVALID[] = { 0x01, 0x01 }; 46 | 47 | 48 | enum edid_extension_type { 49 | EDID_EXTENSION_TIMING = 0x01, // Timing Extension 50 | EDID_EXTENSION_CEA = 0x02, // Additional Timing Block Data (CEA EDID Timing Extension) 51 | EDID_EXTENSION_VTB = 0x10, // Video Timing Block Extension (VTB-EXT) 52 | EDID_EXTENSION_EDID_2_0 = 0x20, // EDID 2.0 Extension 53 | EDID_EXTENSION_DI = 0x40, // Display Information Extension (DI-EXT) 54 | EDID_EXTENSION_LS = 0x50, // Localised String Extension (LS-EXT) 55 | EDID_EXTENSION_MI = 0x60, // Microdisplay Interface Extension (MI-EXT) 56 | EDID_EXTENSION_DTCDB_1 = 0xa7, // Display Transfer Characteristics Data Block (DTCDB) 57 | EDID_EXTENSION_DTCDB_2 = 0xaf, 58 | EDID_EXTENSION_DTCDB_3 = 0xbf, 59 | EDID_EXTENSION_BLOCK_MAP = 0xf0, // Block Map 60 | EDID_EXTENSION_DDDB = 0xff, // Display Device Data Block (DDDB) 61 | }; 62 | 63 | enum edid_display_type { 64 | EDID_DISPLAY_TYPE_MONOCHROME, 65 | EDID_DISPLAY_TYPE_RGB, 66 | EDID_DISPLAY_TYPE_NON_RGB, 67 | EDID_DISPLAY_TYPE_UNDEFINED, 68 | }; 69 | 70 | enum edid_aspect_ratio { 71 | EDID_ASPECT_RATIO_16_10, 72 | EDID_ASPECT_RATIO_4_3, 73 | EDID_ASPECT_RATIO_5_4, 74 | EDID_ASPECT_RATIO_16_9, 75 | }; 76 | 77 | enum edid_signal_sync { 78 | EDID_SIGNAL_SYNC_ANALOG_COMPOSITE, 79 | EDID_SIGNAL_SYNC_BIPOLAR_ANALOG_COMPOSITE, 80 | EDID_SIGNAL_SYNC_DIGITAL_COMPOSITE, 81 | EDID_SIGNAL_SYNC_DIGITAL_SEPARATE, 82 | }; 83 | 84 | enum edid_stereo_mode { 85 | EDID_STEREO_MODE_NONE, 86 | EDID_STEREO_MODE_RESERVED, 87 | EDID_STEREO_MODE_FIELD_SEQUENTIAL_RIGHT, 88 | EDID_STEREO_MODE_2_WAY_INTERLEAVED_RIGHT, 89 | EDID_STEREO_MODE_FIELD_SEQUENTIAL_LEFT, 90 | EDID_STEREO_MODE_2_WAY_INTERLEAVED_LEFT, 91 | EDID_STEREO_MODE_4_WAY_INTERLEAVED, 92 | EDID_STEREO_MODE_SIDE_BY_SIDE_INTERLEAVED, 93 | }; 94 | 95 | enum edid_monitor_descriptor_type { 96 | EDID_MONTIOR_DESCRIPTOR_MANUFACTURER_DEFINED = 0x0f, 97 | EDID_MONITOR_DESCRIPTOR_STANDARD_TIMING_IDENTIFIERS = 0xfa, 98 | EDID_MONITOR_DESCRIPTOR_COLOR_POINT = 0xfb, 99 | EDID_MONITOR_DESCRIPTOR_MONITOR_NAME = 0xfc, 100 | EDID_MONITOR_DESCRIPTOR_MONITOR_RANGE_LIMITS = 0xfd, 101 | EDID_MONITOR_DESCRIPTOR_ASCII_STRING = 0xfe, 102 | EDID_MONITOR_DESCRIPTOR_MONITOR_SERIAL_NUMBER = 0xff, 103 | }; 104 | 105 | enum edid_secondary_timing_support { 106 | EDID_SECONDARY_TIMING_NOT_SUPPORTED, 107 | EDID_SECONDARY_TIMING_GFT = 0x02, 108 | }; 109 | 110 | 111 | struct __attribute__ (( packed )) edid_detailed_timing_descriptor { 112 | uint16_t pixel_clock; /* = value * 10000 */ 113 | 114 | uint8_t horizontal_active_lo; 115 | uint8_t horizontal_blanking_lo; 116 | 117 | unsigned horizontal_blanking_hi : 4; 118 | unsigned horizontal_active_hi : 4; 119 | 120 | uint8_t vertical_active_lo; 121 | uint8_t vertical_blanking_lo; 122 | 123 | unsigned vertical_blanking_hi : 4; 124 | unsigned vertical_active_hi : 4; 125 | 126 | uint8_t horizontal_sync_offset_lo; 127 | uint8_t horizontal_sync_pulse_width_lo; 128 | 129 | unsigned vertical_sync_pulse_width_lo : 4; 130 | unsigned vertical_sync_offset_lo : 4; 131 | 132 | unsigned vertical_sync_pulse_width_hi : 2; 133 | unsigned vertical_sync_offset_hi : 2; 134 | unsigned horizontal_sync_pulse_width_hi : 2; 135 | unsigned horizontal_sync_offset_hi : 2; 136 | 137 | uint8_t horizontal_image_size_lo; 138 | uint8_t vertical_image_size_lo; 139 | 140 | unsigned vertical_image_size_hi : 4; 141 | unsigned horizontal_image_size_hi : 4; 142 | 143 | uint8_t horizontal_border; 144 | uint8_t vertical_border; 145 | 146 | unsigned stereo_mode_lo : 1; 147 | unsigned signal_pulse_polarity : 1; /* pulse on sync, composite/horizontal polarity */ 148 | unsigned signal_serration_polarity : 1; /* serrate on sync, vertical polarity */ 149 | unsigned signal_sync : 2; 150 | unsigned stereo_mode_hi : 2; 151 | unsigned interlaced : 1; 152 | }; 153 | 154 | static inline uint32_t 155 | edid_detailed_timing_pixel_clock(const struct edid_detailed_timing_descriptor * const dtb) 156 | { 157 | return dtb->pixel_clock * 10000; 158 | } 159 | 160 | static inline uint16_t 161 | edid_detailed_timing_horizontal_blanking(const struct edid_detailed_timing_descriptor * const dtb) 162 | { 163 | return (dtb->horizontal_blanking_hi << 8) | dtb->horizontal_blanking_lo; 164 | } 165 | 166 | static inline uint16_t 167 | edid_detailed_timing_horizontal_active(const struct edid_detailed_timing_descriptor * const dtb) 168 | { 169 | return (dtb->horizontal_active_hi << 8) | dtb->horizontal_active_lo; 170 | } 171 | 172 | static inline uint16_t 173 | edid_detailed_timing_vertical_blanking(const struct edid_detailed_timing_descriptor * const dtb) 174 | { 175 | return (dtb->vertical_blanking_hi << 8) | dtb->vertical_blanking_lo; 176 | } 177 | 178 | static inline uint16_t 179 | edid_detailed_timing_vertical_active(const struct edid_detailed_timing_descriptor * const dtb) 180 | { 181 | return (dtb->vertical_active_hi << 8) | dtb->vertical_active_lo; 182 | } 183 | 184 | static inline uint8_t 185 | edid_detailed_timing_vertical_sync_offset(const struct edid_detailed_timing_descriptor * const dtb) 186 | { 187 | return (dtb->vertical_sync_offset_hi << 4) | dtb->vertical_sync_offset_lo; 188 | } 189 | 190 | static inline uint8_t 191 | edid_detailed_timing_vertical_sync_pulse_width(const struct edid_detailed_timing_descriptor * const dtb) 192 | { 193 | return (dtb->vertical_sync_pulse_width_hi << 4) | dtb->vertical_sync_pulse_width_lo; 194 | } 195 | 196 | static inline uint8_t 197 | edid_detailed_timing_horizontal_sync_offset(const struct edid_detailed_timing_descriptor * const dtb) 198 | { 199 | return (dtb->horizontal_sync_offset_hi << 4) | dtb->horizontal_sync_offset_lo; 200 | } 201 | 202 | static inline uint8_t 203 | edid_detailed_timing_horizontal_sync_pulse_width(const struct edid_detailed_timing_descriptor * const dtb) 204 | { 205 | return (dtb->horizontal_sync_pulse_width_hi << 4) | dtb->horizontal_sync_pulse_width_lo; 206 | } 207 | 208 | static inline uint16_t 209 | edid_detailed_timing_horizontal_image_size(const struct edid_detailed_timing_descriptor * const dtb) 210 | { 211 | return (dtb->horizontal_image_size_hi << 8) | dtb->horizontal_image_size_lo; 212 | } 213 | 214 | static inline uint16_t 215 | edid_detailed_timing_vertical_image_size(const struct edid_detailed_timing_descriptor * const dtb) 216 | { 217 | return (dtb->vertical_image_size_hi << 8) | dtb->vertical_image_size_lo; 218 | } 219 | 220 | static inline uint8_t 221 | edid_detailed_timing_stereo_mode(const struct edid_detailed_timing_descriptor * const dtb) 222 | { 223 | return (dtb->stereo_mode_hi << 2 | dtb->stereo_mode_lo); 224 | } 225 | 226 | 227 | struct __attribute__ (( packed )) edid_monitor_descriptor { 228 | uint16_t flag0; 229 | uint8_t flag1; 230 | uint8_t tag; 231 | uint8_t flag2; 232 | uint8_t data[13]; 233 | }; 234 | 235 | typedef char edid_monitor_descriptor_string[sizeof(((struct edid_monitor_descriptor *)0)->data) + 1]; 236 | 237 | 238 | struct __attribute__ (( packed )) edid_monitor_range_limits { 239 | uint8_t minimum_vertical_rate; /* Hz */ 240 | uint8_t maximum_vertical_rate; /* Hz */ 241 | uint8_t minimum_horizontal_rate; /* kHz */ 242 | uint8_t maximum_horizontal_rate; /* kHz */ 243 | uint8_t maximum_supported_pixel_clock; /* = (value * 10) Mhz (round to 10 MHz) */ 244 | 245 | /* secondary timing formula */ 246 | uint8_t secondary_timing_support; 247 | uint8_t reserved; 248 | uint8_t secondary_curve_start_frequency; /* horizontal frequency / 2 kHz */ 249 | uint8_t c; /* = (value >> 1) */ 250 | uint16_t m; 251 | uint8_t k; 252 | uint8_t j; /* = (value >> 1) */ 253 | }; 254 | 255 | 256 | struct __attribute__ (( packed )) edid_standard_timing_descriptor { 257 | uint8_t horizontal_active_pixels; /* = (value + 31) * 8 */ 258 | 259 | unsigned refresh_rate : 6; /* = value + 60 */ 260 | unsigned image_aspect_ratio : 2; 261 | }; 262 | 263 | const inline uint32_t 264 | edid_standard_timing_horizontal_active(const struct edid_standard_timing_descriptor * const desc) 265 | { 266 | return ((desc->horizontal_active_pixels + 31) << 3); 267 | } 268 | 269 | const inline uint32_t 270 | edid_standard_timing_vertical_active(const struct edid_standard_timing_descriptor * const desc) 271 | { 272 | const uint32_t hres = edid_standard_timing_horizontal_active(desc); 273 | 274 | switch (desc->image_aspect_ratio) { 275 | case EDID_ASPECT_RATIO_16_10: 276 | return ((hres * 10) >> 4); 277 | case EDID_ASPECT_RATIO_4_3: 278 | return ((hres * 3) >> 2); 279 | case EDID_ASPECT_RATIO_5_4: 280 | return ((hres << 2) / 5); 281 | case EDID_ASPECT_RATIO_16_9: 282 | return ((hres * 9) >> 4); 283 | } 284 | 285 | return hres; 286 | } 287 | 288 | const inline uint32_t 289 | edid_standard_timing_refresh_rate(const struct edid_standard_timing_descriptor * const desc) 290 | { 291 | return (desc->refresh_rate + 60); 292 | } 293 | 294 | 295 | struct __attribute__ (( packed )) edid { 296 | /* header information */ 297 | uint8_t header[8]; 298 | 299 | /* vendor/product identification */ 300 | uint16_t manufacturer; 301 | 302 | uint8_t product[2]; 303 | uint8_t serial_number[4]; 304 | uint8_t manufacture_week; 305 | uint8_t manufacture_year; /* = value + 1990 */ 306 | 307 | /* EDID version */ 308 | uint8_t version; 309 | uint8_t revision; 310 | 311 | /* basic display parameters and features */ 312 | union { 313 | struct __attribute__ (( packed )) { 314 | unsigned dfp_1x : 1; /* VESA DFP 1.x */ 315 | unsigned : 6; 316 | unsigned digital : 1; 317 | } digital; 318 | struct __attribute__ (( packed )) { 319 | unsigned vsync_serration : 1; 320 | unsigned green_video_sync : 1; 321 | unsigned composite_sync : 1; 322 | unsigned separate_sync : 1; 323 | unsigned blank_to_black_setup : 1; 324 | unsigned signal_level_standard : 2; 325 | unsigned digital : 1; 326 | } analog; 327 | } video_input_definition; 328 | 329 | uint8_t maximum_horizontal_image_size; /* cm */ 330 | uint8_t maximum_vertical_image_size; /* cm */ 331 | 332 | uint8_t display_transfer_characteristics; /* gamma = (value + 100) / 100 */ 333 | 334 | struct __attribute__ (( packed )) { 335 | unsigned default_gtf : 1; /* generalised timing formula */ 336 | unsigned preferred_timing_mode : 1; 337 | unsigned standard_default_color_space : 1; 338 | unsigned display_type : 2; 339 | unsigned active_off : 1; 340 | unsigned suspend : 1; 341 | unsigned standby : 1; 342 | } feature_support; 343 | 344 | /* color characteristics block */ 345 | unsigned green_y_low : 2; 346 | unsigned green_x_low : 2; 347 | unsigned red_y_low : 2; 348 | unsigned red_x_low : 2; 349 | 350 | unsigned white_y_low : 2; 351 | unsigned white_x_low : 2; 352 | unsigned blue_y_low : 2; 353 | unsigned blue_x_low : 2; 354 | 355 | uint8_t red_x; 356 | uint8_t red_y; 357 | uint8_t green_x; 358 | uint8_t green_y; 359 | uint8_t blue_x; 360 | uint8_t blue_y; 361 | uint8_t white_x; 362 | uint8_t white_y; 363 | 364 | /* established timings */ 365 | struct __attribute__ (( packed )) { 366 | unsigned timing_800x600_60 : 1; 367 | unsigned timing_800x600_56 : 1; 368 | unsigned timing_640x480_75 : 1; 369 | unsigned timing_640x480_72 : 1; 370 | unsigned timing_640x480_67 : 1; 371 | unsigned timing_640x480_60 : 1; 372 | unsigned timing_720x400_88 : 1; 373 | unsigned timing_720x400_70 : 1; 374 | 375 | unsigned timing_1280x1024_75 : 1; 376 | unsigned timing_1024x768_75 : 1; 377 | unsigned timing_1024x768_70 : 1; 378 | unsigned timing_1024x768_60 : 1; 379 | unsigned timing_1024x768_87 : 1; 380 | unsigned timing_832x624_75 : 1; 381 | unsigned timing_800x600_75 : 1; 382 | unsigned timing_800x600_72 : 1; 383 | } established_timings; 384 | 385 | struct __attribute__ (( packed )) { 386 | unsigned reserved : 7; 387 | unsigned timing_1152x870_75 : 1; 388 | } manufacturer_timings; 389 | 390 | /* standard timing id */ 391 | struct edid_standard_timing_descriptor standard_timing_id[8]; 392 | 393 | /* detailed timing */ 394 | union { 395 | struct edid_monitor_descriptor monitor; 396 | struct edid_detailed_timing_descriptor timing; 397 | } detailed_timings[4]; 398 | 399 | uint8_t extensions; 400 | uint8_t checksum; 401 | }; 402 | 403 | static inline void 404 | edid_manufacturer(const struct edid * const edid, char manufacturer[4]) 405 | { 406 | manufacturer[0] = '@' + ((edid->manufacturer & 0x007c) >> 2); 407 | manufacturer[1] = '@' + (((edid->manufacturer & 0x0003) >> 00) << 3) 408 | | (((edid->manufacturer & 0xe000) >> 13) << 0); 409 | manufacturer[2] = '@' + ((edid->manufacturer & 0x1f00) >> 8); 410 | manufacturer[3] = '\0'; 411 | } 412 | 413 | static inline double 414 | edid_gamma(const struct edid * const edid) 415 | { 416 | return (edid->display_transfer_characteristics + 100) / 100.0; 417 | } 418 | 419 | static inline bool 420 | edid_detailed_timing_is_monitor_descriptor(const struct edid * const edid, 421 | const uint8_t timing) 422 | { 423 | const struct edid_monitor_descriptor * const mon = 424 | &edid->detailed_timings[timing].monitor; 425 | 426 | assert(timing < ARRAY_SIZE(edid->detailed_timings)); 427 | 428 | return mon->flag0 == 0x0000 && mon->flag1 == 0x00 && mon->flag2 == 0x00; 429 | } 430 | 431 | 432 | struct __attribute__ (( packed )) edid_color_characteristics_data { 433 | struct { 434 | uint16_t x; 435 | uint16_t y; 436 | } red, green, blue, white; 437 | }; 438 | 439 | static inline struct edid_color_characteristics_data 440 | edid_color_characteristics(const struct edid * const edid) 441 | { 442 | const struct edid_color_characteristics_data characteristics = { 443 | .red = { 444 | .x = (edid->red_x << 2) | edid->red_x_low, 445 | .y = (edid->red_y << 2) | edid->red_y_low, 446 | }, 447 | .green = { 448 | .x = (edid->green_x << 2) | edid->green_x_low, 449 | .y = (edid->green_y << 2) | edid->green_y_low, 450 | }, 451 | .blue = { 452 | .x = (edid->blue_x << 2) | edid->blue_x_low, 453 | .y = (edid->blue_y << 2) | edid->blue_y_low, 454 | }, 455 | .white = { 456 | .x = (edid->white_x << 2) | edid->white_x_low, 457 | .y = (edid->white_y << 2) | edid->white_y_low, 458 | }, 459 | }; 460 | 461 | return characteristics; 462 | } 463 | 464 | 465 | struct __attribute__ (( packed )) edid_block_map { 466 | uint8_t tag; 467 | uint8_t extension_tag[126]; 468 | uint8_t checksum; 469 | }; 470 | 471 | 472 | struct __attribute__ (( packed )) edid_extension { 473 | uint8_t tag; 474 | uint8_t revision; 475 | uint8_t extension_data[125]; 476 | uint8_t checksum; 477 | }; 478 | 479 | 480 | static inline bool 481 | edid_verify_checksum(const uint8_t * const block) 482 | { 483 | uint8_t checksum = 0; 484 | int i; 485 | 486 | for (i = 0; i < EDID_BLOCK_SIZE; i++) 487 | checksum += block[i]; 488 | 489 | return (checksum == 0); 490 | } 491 | 492 | static inline double 493 | edid_decode_fixed_point(uint16_t value) 494 | { 495 | double result = 0.0; 496 | 497 | assert((~value & 0xfc00) == 0xfc00); /* edid fraction is 10 bits */ 498 | 499 | for (uint8_t i = 0; value && (i < 10); i++, value >>= 1) 500 | result = result + ((value & 0x1) * (1.0 / (1 << (10 - i)))); 501 | 502 | return result; 503 | } 504 | 505 | #endif 506 | 507 | -------------------------------------------------------------------------------- /src/examples/parse-edid/parse-edid.c: -------------------------------------------------------------------------------- 1 | /* vim: set et fde fdm=syntax ft=c.doxygen ts=4 sts=4 sw=4 : */ 2 | /* 3 | * Copyright © 2011 Saleem Abdulrasool . 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 | #define _GNU_SOURCE 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | #include 42 | 43 | #define CM_2_MM(cm) ((cm) * 10) 44 | #define CM_2_IN(cm) ((cm) * 0.3937) 45 | 46 | #define HZ_2_MHZ(hz) ((hz) / 1000000) 47 | 48 | 49 | static inline void 50 | dump_section(const char * const name, 51 | const uint8_t * const buffer, 52 | const uint8_t offset, 53 | const uint8_t length) 54 | { 55 | /*! \todo remove magic number usage */ 56 | 57 | const uint8_t *value = buffer + offset; 58 | 59 | printf("%33.33s: ", name); 60 | 61 | for (uint8_t i = 0, l = 35; i < length; i++) { 62 | if ((l += 3) > 89) { 63 | printf("\b\n%35s", ""); 64 | l = 35; 65 | } 66 | printf("%02x ", *value++); 67 | } 68 | 69 | printf("\b\n"); 70 | } 71 | 72 | static void 73 | dump_edid1(const uint8_t * const buffer) 74 | { 75 | dump_section("header", buffer, 0x00, 0x08); 76 | dump_section("vendor/product identification", buffer, 0x08, 0x0a); 77 | dump_section("edid struct version/revision", buffer, 0x12, 0x02); 78 | dump_section("basic display parameters/features", buffer, 0x14, 0x05); 79 | dump_section("color characteristics", buffer, 0x19, 0x0a); 80 | dump_section("established timings", buffer, 0x23, 0x03); 81 | dump_section("standard timing identification", buffer, 0x26, 0x10); 82 | dump_section("detailed timing 0", buffer, 0x36, 0x12); 83 | dump_section("detailed timing 1", buffer, 0x48, 0x12); 84 | dump_section("detailed timing 2", buffer, 0x5a, 0x12); 85 | dump_section("detailed timing 3", buffer, 0x6c, 0x12); 86 | dump_section("extensions", buffer, 0x7e, 0x01); 87 | dump_section("checksum", buffer, 0x7f, 0x01); 88 | 89 | printf("\n"); 90 | } 91 | 92 | static void 93 | dump_cea861(const uint8_t * const buffer) 94 | { 95 | const struct edid_detailed_timing_descriptor *dtd = NULL; 96 | const struct cea861_timing_block * const ctb = 97 | (struct cea861_timing_block *) buffer; 98 | const uint8_t dof = offsetof(struct cea861_timing_block, data); 99 | 100 | dump_section("cea extension header", buffer, 0x00, 0x04); 101 | 102 | if (ctb->dtd_offset - dof) 103 | dump_section("data block collection", buffer, 0x04, ctb->dtd_offset - dof); 104 | 105 | dtd = (struct edid_detailed_timing_descriptor *) (buffer + ctb->dtd_offset); 106 | for (uint8_t i = 0; dtd->pixel_clock; i++, dtd++) { 107 | char *header = NULL; 108 | 109 | asprintf(&header, "detailed timing descriptor %03u", i); 110 | dump_section(header, (uint8_t *) dtd, 0x00, sizeof(*dtd)); 111 | free(header); 112 | } 113 | 114 | dump_section("padding", buffer, (uint8_t *) dtd - buffer, 115 | dof + sizeof(ctb->data) - ((uint8_t *) dtd - buffer)); 116 | dump_section("checksum", buffer, 0x7f, 0x01); 117 | 118 | printf("\n"); 119 | } 120 | 121 | 122 | static inline const char * const 123 | _aspect_ratio(const uint16_t hres, const uint16_t vres) 124 | { 125 | #define HAS_RATIO_OF(x, y) (hres == (vres * (x) / (y)) && !((vres * (x)) % (y))) 126 | 127 | if (HAS_RATIO_OF(16, 10)) 128 | return "16:10"; 129 | if (HAS_RATIO_OF(4, 3)) 130 | return "4:3"; 131 | if (HAS_RATIO_OF(5, 4)) 132 | return "5:4"; 133 | if (HAS_RATIO_OF(16, 9)) 134 | return "16:9"; 135 | 136 | #undef HAS_RATIO 137 | 138 | return "unknown"; 139 | } 140 | 141 | static inline char * 142 | _edid_timing_string(const struct edid_detailed_timing_descriptor * const dtb) 143 | { 144 | char *timing = NULL; 145 | const uint16_t hres = edid_detailed_timing_horizontal_active(dtb); 146 | const uint16_t vres = edid_detailed_timing_vertical_active(dtb); 147 | const uint32_t htotal = hres + edid_detailed_timing_horizontal_blanking(dtb); 148 | const uint32_t vtotal = vres + edid_detailed_timing_vertical_blanking(dtb); 149 | 150 | asprintf(&timing, 151 | "%ux%u%c at %.fHz (%s)", 152 | hres, 153 | vres, 154 | dtb->interlaced ? 'i' : 'p', 155 | (double) edid_detailed_timing_pixel_clock(dtb) / (vtotal * htotal), 156 | _aspect_ratio(hres, vres)); 157 | 158 | return timing; 159 | } 160 | 161 | static inline char * 162 | _edid_mode_string(const struct edid_detailed_timing_descriptor * const dtb) 163 | { 164 | char *modestr = NULL; 165 | const uint16_t xres = edid_detailed_timing_horizontal_active(dtb); 166 | const uint16_t yres = edid_detailed_timing_vertical_active(dtb); 167 | const uint32_t pixclk = edid_detailed_timing_pixel_clock(dtb); 168 | const uint16_t lower_margin = edid_detailed_timing_vertical_sync_offset(dtb); 169 | const uint16_t right_margin = edid_detailed_timing_horizontal_sync_offset(dtb); 170 | 171 | asprintf(&modestr, 172 | "\"%ux%u\" %.3f %u %u %u %u %u %u %u %u %chsync %cvsync", 173 | /* resolution */ 174 | xres, yres, 175 | 176 | /* dot clock frequence (MHz) */ 177 | HZ_2_MHZ((double) pixclk), 178 | 179 | /* horizontal timings */ 180 | xres, 181 | xres + right_margin, 182 | xres + right_margin + edid_detailed_timing_horizontal_sync_pulse_width(dtb), 183 | xres + edid_detailed_timing_horizontal_blanking(dtb), 184 | 185 | /* vertical timings */ 186 | yres, 187 | yres + lower_margin, 188 | yres + lower_margin + edid_detailed_timing_vertical_sync_pulse_width(dtb), 189 | yres + edid_detailed_timing_vertical_blanking(dtb), 190 | 191 | /* sync direction */ 192 | dtb->signal_pulse_polarity ? '+' : '-', 193 | dtb->signal_serration_polarity ? '+' : '-'); 194 | 195 | return modestr; 196 | } 197 | 198 | static void 199 | disp_edid1(const struct edid * const edid) 200 | { 201 | const struct edid_monitor_range_limits *monitor_range_limits = NULL; 202 | edid_monitor_descriptor_string monitor_serial_number = {0}; 203 | edid_monitor_descriptor_string monitor_model_name = {0}; 204 | bool has_ascii_string = false; 205 | char manufacturer[4] = {0}; 206 | 207 | struct edid_color_characteristics_data characteristics; 208 | const uint8_t vlen = edid->maximum_vertical_image_size; 209 | const uint8_t hlen = edid->maximum_horizontal_image_size; 210 | uint8_t i; 211 | 212 | static const char * const display_type[] = { 213 | [EDID_DISPLAY_TYPE_MONOCHROME] = "Monochrome or greyscale", 214 | [EDID_DISPLAY_TYPE_RGB] = "sRGB colour", 215 | [EDID_DISPLAY_TYPE_NON_RGB] = "Non-sRGB colour", 216 | [EDID_DISPLAY_TYPE_UNDEFINED] = "Undefined", 217 | }; 218 | 219 | edid_manufacturer(edid, manufacturer); 220 | characteristics = edid_color_characteristics(edid); 221 | 222 | 223 | for (i = 0; i < ARRAY_SIZE(edid->detailed_timings); i++) { 224 | const struct edid_monitor_descriptor * const mon = 225 | &edid->detailed_timings[i].monitor; 226 | 227 | if (!edid_detailed_timing_is_monitor_descriptor(edid, i)) 228 | continue; 229 | 230 | switch (mon->tag) { 231 | case EDID_MONTIOR_DESCRIPTOR_MANUFACTURER_DEFINED: 232 | /* This is arbitrary data, just silently ignore it. */ 233 | break; 234 | case EDID_MONITOR_DESCRIPTOR_ASCII_STRING: 235 | has_ascii_string = true; 236 | break; 237 | case EDID_MONITOR_DESCRIPTOR_MONITOR_NAME: 238 | strncpy(monitor_model_name, (char *) mon->data, 239 | sizeof(monitor_model_name) - 1); 240 | *strchrnul(monitor_model_name, '\n') = '\0'; 241 | break; 242 | case EDID_MONITOR_DESCRIPTOR_MONITOR_RANGE_LIMITS: 243 | monitor_range_limits = (struct edid_monitor_range_limits *) &mon->data; 244 | break; 245 | case EDID_MONITOR_DESCRIPTOR_MONITOR_SERIAL_NUMBER: 246 | strncpy(monitor_serial_number, (char *) mon->data, 247 | sizeof(monitor_serial_number) - 1); 248 | *strchrnul(monitor_serial_number, '\n') = '\0'; 249 | break; 250 | default: 251 | fprintf(stderr, "unknown monitor descriptor type 0x%02x\n", 252 | mon->tag); 253 | break; 254 | } 255 | } 256 | 257 | printf("Monitor\n"); 258 | 259 | printf(" Model name............... %s\n", 260 | *monitor_model_name ? monitor_model_name : "n/a"); 261 | 262 | printf(" Manufacturer............. %s\n", 263 | manufacturer); 264 | 265 | printf(" Product code............. %u\n", 266 | *(uint16_t *) edid->product); 267 | 268 | if (*(uint32_t *) edid->serial_number) 269 | printf(" Module serial number..... %u\n", 270 | *(uint32_t *) edid->serial_number); 271 | 272 | #if defined(DISPLAY_UNKNOWN) 273 | printf(" Plug and Play ID......... %s\n", NULL); 274 | #endif 275 | 276 | printf(" Serial number............ %s\n", 277 | *monitor_serial_number ? monitor_serial_number : "n/a"); 278 | 279 | printf(" Manufacture date......... %u", edid->manufacture_year + 1990); 280 | if (edid->manufacture_week <= 52) 281 | printf(", ISO week %u", edid->manufacture_week); 282 | printf("\n"); 283 | 284 | printf(" EDID revision............ %u.%u\n", 285 | edid->version, edid->revision); 286 | 287 | printf(" Input signal type........ %s\n", 288 | edid->video_input_definition.digital.digital ? "Digital" : "Analog"); 289 | 290 | if (edid->video_input_definition.digital.digital) { 291 | printf(" VESA DFP 1.x supported... %s\n", 292 | edid->video_input_definition.digital.dfp_1x ? "Yes" : "No"); 293 | } else { 294 | /* TODO print analog flags */ 295 | } 296 | 297 | #if defined(DISPLAY_UNKNOWN) 298 | printf(" Color bit depth.......... %s\n", NULL); 299 | #endif 300 | 301 | printf(" Display type............. %s\n", 302 | display_type[edid->feature_support.display_type]); 303 | 304 | printf(" Screen size.............. %u mm x %u mm (%.1f in)\n", 305 | CM_2_MM(hlen), CM_2_MM(vlen), 306 | CM_2_IN(sqrt(hlen * hlen + vlen * vlen))); 307 | 308 | printf(" Power management......... %s%s%s%s\n", 309 | edid->feature_support.active_off ? "Active off, " : "", 310 | edid->feature_support.suspend ? "Suspend, " : "", 311 | edid->feature_support.standby ? "Standby, " : "", 312 | 313 | (edid->feature_support.active_off || 314 | edid->feature_support.suspend || 315 | edid->feature_support.standby) ? "\b\b " : "n/a"); 316 | 317 | printf(" Extension blocks......... %u\n", 318 | edid->extensions); 319 | 320 | #if defined(DISPLAY_UNKNOWN) 321 | printf(" DDC/CI................... %s\n", NULL); 322 | #endif 323 | 324 | printf("\n"); 325 | 326 | if (has_ascii_string) { 327 | edid_monitor_descriptor_string string = {0}; 328 | 329 | printf("General purpose ASCII string\n"); 330 | 331 | for (i = 0; i < ARRAY_SIZE(edid->detailed_timings); i++) { 332 | const struct edid_monitor_descriptor * const mon = 333 | &edid->detailed_timings[i].monitor; 334 | 335 | if (!edid_detailed_timing_is_monitor_descriptor(edid, i)) 336 | continue; 337 | 338 | if (mon->tag == EDID_MONITOR_DESCRIPTOR_ASCII_STRING) { 339 | strncpy(string, (char *) mon->data, sizeof(string) - 1); 340 | *strchrnul(string, '\n') = '\0'; 341 | 342 | printf(" ASCII string............. %s\n", string); 343 | } 344 | } 345 | 346 | printf("\n"); 347 | } 348 | 349 | printf("Color characteristics\n"); 350 | 351 | printf(" Default color space...... %ssRGB\n", 352 | edid->feature_support.standard_default_color_space ? "" : "Non-"); 353 | 354 | printf(" Display gamma............ %.2f\n", 355 | edid_gamma(edid)); 356 | 357 | printf(" Red chromaticity......... Rx %0.3f - Ry %0.3f\n", 358 | edid_decode_fixed_point(characteristics.red.x), 359 | edid_decode_fixed_point(characteristics.red.y)); 360 | 361 | printf(" Green chromaticity....... Gx %0.3f - Gy %0.3f\n", 362 | edid_decode_fixed_point(characteristics.green.x), 363 | edid_decode_fixed_point(characteristics.green.y)); 364 | 365 | printf(" Blue chromaticity........ Bx %0.3f - By %0.3f\n", 366 | edid_decode_fixed_point(characteristics.blue.x), 367 | edid_decode_fixed_point(characteristics.blue.y)); 368 | 369 | printf(" White point (default).... Wx %0.3f - Wy %0.3f\n", 370 | edid_decode_fixed_point(characteristics.white.x), 371 | edid_decode_fixed_point(characteristics.white.y)); 372 | 373 | #if defined(DISPLAY_UNKNOWN) 374 | printf(" Additional descriptors... %s\n", NULL); 375 | #endif 376 | 377 | printf("\n"); 378 | 379 | printf("Timing characteristics\n"); 380 | 381 | if (monitor_range_limits) { 382 | printf(" Horizontal scan range.... %u - %u kHz\n", 383 | monitor_range_limits->minimum_horizontal_rate, 384 | monitor_range_limits->maximum_horizontal_rate); 385 | 386 | printf(" Vertical scan range...... %u - %u Hz\n", 387 | monitor_range_limits->minimum_vertical_rate, 388 | monitor_range_limits->maximum_vertical_rate); 389 | 390 | printf(" Video bandwidth.......... %u MHz\n", 391 | monitor_range_limits->maximum_supported_pixel_clock * 10); 392 | } 393 | 394 | #if defined(DISPLAY_UNKNOWN) 395 | printf(" CVT standard............. %s\n", NULL); 396 | #endif 397 | 398 | printf(" GTF standard............. %sSupported\n", 399 | edid->feature_support.default_gtf ? "" : "Not "); 400 | 401 | #if defined(DISPLAY_UNKNOWN) 402 | printf(" Additional descriptors... %s\n", NULL); 403 | #endif 404 | 405 | printf(" Preferred timing......... %s\n", 406 | edid->feature_support.preferred_timing_mode ? "Yes" : "No"); 407 | 408 | if (edid->feature_support.preferred_timing_mode) { 409 | char *string = NULL; 410 | 411 | string = _edid_timing_string(&edid->detailed_timings[0].timing); 412 | printf(" Native/preferred timing.. %s\n", string); 413 | free(string); 414 | 415 | string = _edid_mode_string(&edid->detailed_timings[0].timing); 416 | printf(" Modeline............... %s\n", string); 417 | free(string); 418 | } else { 419 | printf(" Native/preferred timing.. n/a\n"); 420 | } 421 | 422 | printf("\n"); 423 | 424 | printf("Standard timings supported\n"); 425 | if (edid->established_timings.timing_720x400_70) 426 | printf(" 720 x 400p @ 70Hz - IBM VGA\n"); 427 | if (edid->established_timings.timing_720x400_88) 428 | printf(" 720 x 400p @ 88Hz - IBM XGA2\n"); 429 | if (edid->established_timings.timing_640x480_60) 430 | printf(" 640 x 480p @ 60Hz - IBM VGA\n"); 431 | if (edid->established_timings.timing_640x480_67) 432 | printf(" 640 x 480p @ 67Hz - Apple Mac II\n"); 433 | if (edid->established_timings.timing_640x480_72) 434 | printf(" 640 x 480p @ 72Hz - VESA\n"); 435 | if (edid->established_timings.timing_640x480_75) 436 | printf(" 640 x 480p @ 75Hz - VESA\n"); 437 | if (edid->established_timings.timing_800x600_56) 438 | printf(" 800 x 600p @ 56Hz - VESA\n"); 439 | if (edid->established_timings.timing_800x600_60) 440 | printf(" 800 x 600p @ 60Hz - VESA\n"); 441 | 442 | if (edid->established_timings.timing_800x600_72) 443 | printf(" 800 x 600p @ 72Hz - VESA\n"); 444 | if (edid->established_timings.timing_800x600_75) 445 | printf(" 800 x 600p @ 75Hz - VESA\n"); 446 | if (edid->established_timings.timing_832x624_75) 447 | printf(" 832 x 624p @ 75Hz - Apple Mac II\n"); 448 | if (edid->established_timings.timing_1024x768_87) 449 | printf(" 1024 x 768i @ 87Hz - VESA\n"); 450 | if (edid->established_timings.timing_1024x768_60) 451 | printf(" 1024 x 768p @ 60Hz - VESA\n"); 452 | if (edid->established_timings.timing_1024x768_70) 453 | printf(" 1024 x 768p @ 70Hz - VESA\n"); 454 | if (edid->established_timings.timing_1024x768_75) 455 | printf(" 1024 x 768p @ 75Hz - VESA\n"); 456 | if (edid->established_timings.timing_1280x1024_75) 457 | printf(" 1280 x 1024p @ 75Hz - VESA\n"); 458 | 459 | for (i = 0; i < ARRAY_SIZE(edid->standard_timing_id); i++) { 460 | const struct edid_standard_timing_descriptor * const desc = 461 | &edid->standard_timing_id[i]; 462 | 463 | if (!memcmp(desc, EDID_STANDARD_TIMING_DESCRIPTOR_INVALID, sizeof(*desc))) 464 | continue; 465 | 466 | printf(" %4u x %4u%c @ %uHz - VESA STD\n", 467 | edid_standard_timing_horizontal_active(desc), 468 | edid_standard_timing_vertical_active(desc), 469 | 'p', 470 | edid_standard_timing_refresh_rate(desc)); 471 | } 472 | 473 | printf("\n"); 474 | } 475 | 476 | 477 | /* CEA861 routines */ 478 | /*! \todo move to cea861.c */ 479 | 480 | static inline void 481 | disp_cea861_audio_data(const struct cea861_audio_data_block * const adb) 482 | { 483 | const uint8_t descriptors = adb->header.length / sizeof(*adb->sad); 484 | 485 | printf("CE audio data (formats supported)\n"); 486 | for (uint8_t i = 0; i < descriptors; i++) { 487 | const struct cea861_short_audio_descriptor * const sad = 488 | (struct cea861_short_audio_descriptor *) &adb->sad[i]; 489 | 490 | switch (sad->audio_format) { 491 | case CEA861_AUDIO_FORMAT_LPCM: 492 | printf(" LPCM %u-channel, %s%s%s\b%s", 493 | sad->channels + 1, 494 | sad->flags.lpcm.bitrate_16_bit ? "16/" : "", 495 | sad->flags.lpcm.bitrate_20_bit ? "20/" : "", 496 | sad->flags.lpcm.bitrate_24_bit ? "24/" : "", 497 | 498 | ((sad->flags.lpcm.bitrate_16_bit + 499 | sad->flags.lpcm.bitrate_20_bit + 500 | sad->flags.lpcm.bitrate_24_bit) > 1) ? " bit depths" : "-bit"); 501 | break; 502 | case CEA861_AUDIO_FORMAT_AC_3: 503 | printf(" AC-3 %u-channel, %4uk max. bit rate", 504 | sad->channels + 1, 505 | (sad->flags.maximum_bit_rate << 3)); 506 | break; 507 | default: 508 | fprintf(stderr, "unknown audio format 0x%02x\n", 509 | sad->audio_format); 510 | continue; 511 | } 512 | 513 | printf(" at %s%s%s%s%s%s%s\b kHz\n", 514 | sad->sample_rate_32_kHz ? "32/" : "", 515 | sad->sample_rate_44_1_kHz ? "44.1/" : "", 516 | sad->sample_rate_48_kHz ? "48/" : "", 517 | sad->sample_rate_88_2_kHz ? "88.2/" : "", 518 | sad->sample_rate_96_kHz ? "96/" : "", 519 | sad->sample_rate_176_4_kHz ? "176.4/" : "", 520 | sad->sample_rate_192_kHz ? "192/" : ""); 521 | } 522 | 523 | printf("\n"); 524 | } 525 | 526 | static inline void 527 | disp_cea861_video_data(const struct cea861_video_data_block * const vdb) 528 | { 529 | printf("CE video identifiers (VICs) - timing/formats supported\n"); 530 | for (uint8_t i = 0; i < vdb->header.length; i++) { 531 | const struct cea861_timing * const timing = 532 | &cea861_timings[vdb->svd[i].video_identification_code]; 533 | 534 | printf(" %s CEA Mode %02u: %4u x %4u%c @ %.fHz\n", 535 | vdb->svd[i].native ? "*" : " ", 536 | vdb->svd[i].video_identification_code, 537 | timing->hactive, timing->vactive, 538 | (timing->mode == INTERLACED) ? 'i' : 'p', 539 | timing->vfreq); 540 | } 541 | 542 | printf("\n"); 543 | } 544 | 545 | static inline void 546 | disp_cea861_vendor_data(const struct cea861_vendor_specific_data_block * vsdb) 547 | { 548 | const uint8_t oui[] = { vsdb->ieee_registration[2], 549 | vsdb->ieee_registration[1], 550 | vsdb->ieee_registration[0] }; 551 | 552 | printf("CEA vendor specific data (VSDB)\n"); 553 | printf(" IEEE registration number. 0x"); 554 | for (uint8_t i = 0; i < ARRAY_SIZE(oui); i++) 555 | printf("%02X", oui[i]); 556 | printf("\n"); 557 | 558 | if (!memcmp(oui, HDMI_OUI, sizeof(oui))) { 559 | const struct hdmi_vendor_specific_data_block * const hdmi = 560 | (struct hdmi_vendor_specific_data_block *) vsdb; 561 | 562 | printf(" CEC physical address..... %u.%u.%u.%u\n", 563 | hdmi->port_configuration_a, 564 | hdmi->port_configuration_b, 565 | hdmi->port_configuration_c, 566 | hdmi->port_configuration_d); 567 | 568 | if (hdmi->header.length >= HDMI_VSDB_EXTENSION_FLAGS_OFFSET) { 569 | printf(" Supports AI (ACP, ISRC).. %s\n", 570 | hdmi->audio_info_frame ? "Yes" : "No"); 571 | printf(" Supports 48bpp........... %s\n", 572 | hdmi->colour_depth_48_bit ? "Yes" : "No"); 573 | printf(" Supports 36bpp........... %s\n", 574 | hdmi->colour_depth_36_bit ? "Yes" : "No"); 575 | printf(" Supports 30bpp........... %s\n", 576 | hdmi->colour_depth_30_bit ? "Yes" : "No"); 577 | printf(" Supports YCbCr 4:4:4..... %s\n", 578 | hdmi->yuv_444_supported ? "Yes" : "No"); 579 | printf(" Supports dual-link DVI... %s\n", 580 | hdmi->dvi_dual_link ? "Yes" : "No"); 581 | } 582 | 583 | if (hdmi->header.length >= HDMI_VSDB_MAX_TMDS_OFFSET) { 584 | if (hdmi->max_tmds_clock) 585 | printf(" Maximum TMDS clock....... %uMHz\n", 586 | hdmi->max_tmds_clock * 5); 587 | else 588 | printf(" Maximum TMDS clock....... n/a\n"); 589 | } 590 | 591 | if (hdmi->header.length >= HDMI_VSDB_LATENCY_FIELDS_OFFSET) { 592 | if (hdmi->latency_fields) { 593 | printf(" Video latency %s........ %ums\n", 594 | hdmi->interlaced_latency_fields ? "(p)" : "...", 595 | (hdmi->video_latency - 1) << 1); 596 | printf(" Audio latency %s........ %ums\n", 597 | hdmi->interlaced_latency_fields ? "(p)" : "...", 598 | (hdmi->audio_latency - 1) << 1); 599 | } 600 | 601 | if (hdmi->interlaced_latency_fields) { 602 | printf(" Video latency (i)........ %ums\n", 603 | hdmi->interlaced_video_latency); 604 | printf(" Audio latency (i)........ %ums\n", 605 | hdmi->interlaced_audio_latency); 606 | } 607 | } 608 | } 609 | 610 | printf("\n"); 611 | } 612 | 613 | static inline void 614 | disp_cea861_speaker_allocation_data(const struct cea861_speaker_allocation_data_block * const sadb) 615 | { 616 | const struct cea861_speaker_allocation * const sa = &sadb->payload; 617 | const uint8_t * const channel_configuration = (uint8_t *) sa; 618 | 619 | printf("CEA speaker allocation data\n"); 620 | printf(" Channel configuration.... %u.%u\n", 621 | (__builtin_popcountll(channel_configuration[0] & 0xe9) << 1) + 622 | (__builtin_popcountll(channel_configuration[0] & 0x14) << 0) + 623 | (__builtin_popcountll(channel_configuration[1] & 0x01) << 1) + 624 | (__builtin_popcountll(channel_configuration[1] & 0x06) << 0), 625 | (channel_configuration[0] & 0x02)); 626 | printf(" Front left/right......... %s\n", 627 | sa->front_left_right ? "Yes" : "No"); 628 | printf(" Front LFE................ %s\n", 629 | sa->front_lfe ? "Yes" : "No"); 630 | printf(" Front center............. %s\n", 631 | sa->front_center ? "Yes" : "No"); 632 | printf(" Rear left/right.......... %s\n", 633 | sa->rear_left_right ? "Yes" : "No"); 634 | printf(" Rear center.............. %s\n", 635 | sa->rear_center ? "Yes" : "No"); 636 | printf(" Front left/right center.. %s\n", 637 | sa->front_left_right_center ? "Yes" : "No"); 638 | printf(" Rear left/right center... %s\n", 639 | sa->rear_left_right_center ? "Yes" : "No"); 640 | printf(" Front left/right wide.... %s\n", 641 | sa->front_left_right_wide ? "Yes" : "No"); 642 | printf(" Front left/right high.... %s\n", 643 | sa->front_left_right_high ? "Yes" : "No"); 644 | printf(" Top center............... %s\n", 645 | sa->top_center ? "Yes" : "No"); 646 | printf(" Front center high........ %s\n", 647 | sa->front_center_high ? "Yes" : "No"); 648 | 649 | printf("\n"); 650 | } 651 | 652 | static void 653 | disp_cea861(const struct edid_extension * const ext) 654 | { 655 | const struct edid_detailed_timing_descriptor *dtd = NULL; 656 | const struct cea861_timing_block * const ctb = 657 | (struct cea861_timing_block *) ext; 658 | const uint8_t offset = offsetof(struct cea861_timing_block, data); 659 | uint8_t index = 0, i; 660 | 661 | /*! \todo handle invalid revision */ 662 | 663 | printf("CEA-861 Information\n"); 664 | printf(" Revision number.......... %u\n", 665 | ctb->revision); 666 | 667 | if (ctb->revision >= 2) { 668 | printf(" IT underscan............. %supported\n", 669 | ctb->underscan_supported ? "S" : "Not s"); 670 | printf(" Basic audio.............. %supported\n", 671 | ctb->basic_audio_supported ? "S" : "Not s"); 672 | printf(" YCbCr 4:4:4.............. %supported\n", 673 | ctb->yuv_444_supported ? "S" : "Not s"); 674 | printf(" YCbCr 4:2:2.............. %supported\n", 675 | ctb->yuv_422_supported ? "S" : "Not s"); 676 | printf(" Native formats........... %u\n", 677 | ctb->native_dtds); 678 | } 679 | 680 | dtd = (struct edid_detailed_timing_descriptor *) ((uint8_t *) ctb + ctb->dtd_offset); 681 | for (i = 0; dtd->pixel_clock; i++, dtd++) { 682 | char *string; 683 | 684 | /*! \todo ensure that we are not overstepping bounds */ 685 | 686 | string = _edid_timing_string(dtd); 687 | printf(" Detailed timing #%u....... %s\n", i + 1, string); 688 | free(string); 689 | 690 | string = _edid_mode_string(dtd); 691 | printf(" Modeline............... %s\n", string); 692 | free(string); 693 | } 694 | 695 | printf("\n"); 696 | 697 | if (ctb->revision >= 3) { 698 | do { 699 | const struct cea861_data_block_header * const header = 700 | (struct cea861_data_block_header *) &ctb->data[index]; 701 | 702 | switch (header->tag) { 703 | case CEA861_DATA_BLOCK_TYPE_AUDIO: 704 | { 705 | const struct cea861_audio_data_block * const db = 706 | (struct cea861_audio_data_block *) header; 707 | 708 | disp_cea861_audio_data(db); 709 | } 710 | break; 711 | case CEA861_DATA_BLOCK_TYPE_VIDEO: 712 | { 713 | const struct cea861_video_data_block * const db = 714 | (struct cea861_video_data_block *) header; 715 | 716 | disp_cea861_video_data(db); 717 | } 718 | break; 719 | case CEA861_DATA_BLOCK_TYPE_VENDOR_SPECIFIC: 720 | { 721 | const struct cea861_vendor_specific_data_block * const db = 722 | (struct cea861_vendor_specific_data_block *) header; 723 | 724 | disp_cea861_vendor_data(db); 725 | } 726 | break; 727 | case CEA861_DATA_BLOCK_TYPE_SPEAKER_ALLOCATION: 728 | { 729 | const struct cea861_speaker_allocation_data_block * const db = 730 | (struct cea861_speaker_allocation_data_block *) header; 731 | 732 | disp_cea861_speaker_allocation_data(db); 733 | } 734 | break; 735 | default: 736 | fprintf(stderr, "unknown CEA-861 data block type 0x%02x\n", 737 | header->tag); 738 | break; 739 | } 740 | 741 | index = index + header->length + sizeof(*header); 742 | } while (index < ctb->dtd_offset - offset); 743 | } 744 | 745 | printf("\n"); 746 | } 747 | 748 | 749 | /* parse edid routines */ 750 | 751 | static const struct edid_extension_handler { 752 | void (* const hex_dump)(const uint8_t * const); 753 | void (* const inf_disp)(const struct edid_extension * const); 754 | } edid_extension_handlers[] = { 755 | [EDID_EXTENSION_CEA] = { dump_cea861, disp_cea861 }, 756 | }; 757 | 758 | static void 759 | parse_edid(const uint8_t * const data) 760 | { 761 | const struct edid * const edid = (struct edid *) data; 762 | const struct edid_extension * const extensions = 763 | (struct edid_extension *) (data + sizeof(*edid)); 764 | 765 | dump_edid1((uint8_t *) edid); 766 | disp_edid1(edid); 767 | 768 | for (uint8_t i = 0; i < edid->extensions; i++) { 769 | const struct edid_extension * const extension = &extensions[i]; 770 | const struct edid_extension_handler * const handler = 771 | &edid_extension_handlers[extension->tag]; 772 | 773 | if (!handler) { 774 | fprintf(stderr, 775 | "WARNING: block %u contains unknown extension (%#04x)\n", 776 | i, extensions[i].tag); 777 | continue; 778 | } 779 | 780 | if (handler->hex_dump) 781 | (*handler->hex_dump)((uint8_t *) extension); 782 | 783 | if (handler->inf_disp) 784 | (*handler->inf_disp)(extension); 785 | } 786 | } 787 | 788 | int 789 | main(int argc, char **argv) 790 | { 791 | uint8_t *buffer = NULL; 792 | FILE *edid = NULL; 793 | long length = 0; 794 | int rv = EXIT_FAILURE; 795 | 796 | if (argc != 2) { 797 | printf("usage: %s \n", argv[0]); 798 | return EXIT_FAILURE; 799 | } 800 | 801 | if ((edid = fopen(argv[1], "rb")) == NULL) { 802 | fprintf(stderr, "unable to open EDID data: %m\n"); 803 | goto out; 804 | } 805 | 806 | fseek(edid, 0, SEEK_END); 807 | length = ftell(edid); 808 | fseek(edid, 0, SEEK_SET); 809 | 810 | if ((buffer = calloc(length, 1)) == NULL) { 811 | fprintf(stderr, "unable to allocate space for edid data\n"); 812 | goto out; 813 | } 814 | 815 | if (fread(buffer, 1, length, edid) != length) { 816 | fprintf(stderr, "unable to read EDID: %m\n"); 817 | goto out; 818 | } 819 | 820 | parse_edid(buffer); 821 | rv = EXIT_SUCCESS; 822 | 823 | out: 824 | if (edid) 825 | fclose(edid); 826 | 827 | free(buffer); 828 | 829 | return rv; 830 | } 831 | 832 | --------------------------------------------------------------------------------