ovr_sdk
diff 3rdParty/EDID/edid.cpp @ 0:1b39a1b46319
initial 0.4.4
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Wed, 14 Jan 2015 06:51:16 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/3rdParty/EDID/edid.cpp Wed Jan 14 06:51:16 2015 +0200 1.3 @@ -0,0 +1,446 @@ 1.4 +/* 1.5 + * Copyright 2007 Red Hat, Inc. 1.6 + * 1.7 + * Permission is hereby granted, free of charge, to any person obtaining a 1.8 + * copy of this software and associated documentation files (the "Software"), 1.9 + * to deal in the Software without restriction, including without limitation 1.10 + * on the rights to use, copy, modify, merge, publish, distribute, sub 1.11 + * license, and/or sell copies of the Software, and to permit persons to whom 1.12 + * the Software is furnished to do so, subject to the following conditions: 1.13 + * 1.14 + * The above copyright notice and this permission notice (including the next 1.15 + * paragraph) shall be included in all copies or substantial portions of the 1.16 + * Software. 1.17 + * 1.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1.19 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1.20 + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 1.21 + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 1.22 + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 1.23 + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1.24 + */ 1.25 + 1.26 +/* Author: Soren Sandmann <sandmann@redhat.com> */ 1.27 +#include "edid.h" 1.28 +#include <stdint.h> 1.29 +#include <math.h> 1.30 +#include <memory.h> 1.31 +#include <X11/Xatom.h> 1.32 + 1.33 + 1.34 +static int get_bit(int in, int bit) { 1.35 + return (in & (1 << bit)) >> bit; 1.36 +} 1.37 + 1.38 +static int get_bits(int in, int begin, int end) { 1.39 + int mask = (1 << (end - begin + 1)) - 1; 1.40 + 1.41 + return (in >> begin) & mask; 1.42 +} 1.43 + 1.44 +static bool decode_header(const uint8_t *edid) { 1.45 + if (memcmp(edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0) 1.46 + return true; 1.47 + return false; 1.48 +} 1.49 + 1.50 +static int decode_vendor_and_product_identification(const uint8_t *edid, MonitorInfo *info) { 1.51 + 1.52 + /* Manufacturer Code */ 1.53 + info->manufacturer_code[0] = get_bits(edid[0x08], 2, 6); 1.54 + info->manufacturer_code[1] = get_bits(edid[0x08], 0, 1) << 3; 1.55 + info->manufacturer_code[1] |= get_bits(edid[0x09], 5, 7); 1.56 + info->manufacturer_code[2] = get_bits(edid[0x09], 0, 4); 1.57 + info->manufacturer_code[3] = '\0'; 1.58 + 1.59 + info->manufacturer_code[0] += 'A' - 1; 1.60 + info->manufacturer_code[1] += 'A' - 1; 1.61 + info->manufacturer_code[2] += 'A' - 1; 1.62 + 1.63 + /* Product Code */ 1.64 + info->product_code = edid[0x0b] << 8 | edid[0x0a]; 1.65 + 1.66 + /* Serial Number */ 1.67 + info->serial_number = edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | edid[0x0f] << 24; 1.68 + 1.69 + /* Week and Year */ 1.70 + bool is_model_year = false; 1.71 + switch (edid[0x10]) { 1.72 + case 0x00: 1.73 + info->production_week = -1; 1.74 + break; 1.75 + 1.76 + case 0xff: 1.77 + info->production_week = -1; 1.78 + is_model_year = true; 1.79 + break; 1.80 + 1.81 + default: 1.82 + info->production_week = edid[0x10]; 1.83 + break; 1.84 + } 1.85 + 1.86 + if (is_model_year) { 1.87 + info->production_year = -1; 1.88 + info->model_year = 1990 + edid[0x11]; 1.89 + } else { 1.90 + info->production_year = 1990 + edid[0x11]; 1.91 + info->model_year = -1; 1.92 + } 1.93 + 1.94 + return true; 1.95 +} 1.96 + 1.97 +static bool decode_edid_version(const uint8_t *edid, MonitorInfo *info) { 1.98 + info->major_version = edid[0x12]; 1.99 + info->minor_version = edid[0x13]; 1.100 + return true; 1.101 +} 1.102 + 1.103 +static bool decode_display_parameters(const uint8_t *edid, MonitorInfo *info) { 1.104 + /* Digital vs Analog */ 1.105 + info->is_digital = get_bit(edid[0x14], 7); 1.106 + 1.107 + if (info->is_digital) { 1.108 + static const int bit_depth[8] = { -1, 6, 8, 10, 12, 14, 16, -1 }; 1.109 + static const Interface interfaces[6] = { UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT }; 1.110 + 1.111 + int bits = get_bits(edid[0x14], 4, 6); 1.112 + info->connector.digital.bits_per_primary = bit_depth[bits]; 1.113 + 1.114 + bits = get_bits(edid[0x14], 0, 3); 1.115 + if (bits <= 5) 1.116 + info->connector.digital.interface = interfaces[bits]; 1.117 + else 1.118 + info->connector.digital.interface = UNDEFINED; 1.119 + } else { 1.120 + int bits = get_bits(edid[0x14], 5, 6); 1.121 + static const double levels[][3] = { // 1.122 + { 0.7, 0.3, 1.0 }, // 1.123 + { 0.714, 0.286, 1.0 }, // 1.124 + { 1.0, 0.4, 1.4 }, // 1.125 + { 0.7, 0.0, 0.7 }, // 1.126 + }; 1.127 + 1.128 + info->connector.analog.video_signal_level = levels[bits][0]; 1.129 + info->connector.analog.sync_signal_level = levels[bits][1]; 1.130 + info->connector.analog.total_signal_level = levels[bits][2]; 1.131 + info->connector.analog.blank_to_black = get_bit(edid[0x14], 4); 1.132 + info->connector.analog.separate_hv_sync = get_bit(edid[0x14], 3); 1.133 + info->connector.analog.composite_sync_on_h = get_bit(edid[0x14], 2); 1.134 + info->connector.analog.composite_sync_on_green = get_bit(edid[0x14], 1); 1.135 + info->connector.analog.serration_on_vsync = get_bit(edid[0x14], 0); 1.136 + } 1.137 + 1.138 + /* Screen Size / Aspect Ratio */ 1.139 + if (edid[0x15] == 0 && edid[0x16] == 0) { 1.140 + info->width_mm = -1; 1.141 + info->height_mm = -1; 1.142 + info->aspect_ratio = -1.0; 1.143 + } else if (edid[0x16] == 0) { 1.144 + info->width_mm = -1; 1.145 + info->height_mm = -1; 1.146 + info->aspect_ratio = 100.0 / (edid[0x15] + 99); 1.147 + } else if (edid[0x15] == 0) { 1.148 + info->width_mm = -1; 1.149 + info->height_mm = -1; 1.150 + info->aspect_ratio = 100.0 / (edid[0x16] + 99); 1.151 + info->aspect_ratio = 1 / info->aspect_ratio; /* portrait */ 1.152 + } else { 1.153 + info->width_mm = 10 * edid[0x15]; 1.154 + info->height_mm = 10 * edid[0x16]; 1.155 + } 1.156 + 1.157 + /* Gamma */ 1.158 + if (edid[0x17] == 0xFF) 1.159 + info->gamma = -1.0; 1.160 + else 1.161 + info->gamma = (edid[0x17] + 100.0) / 100.0; 1.162 + 1.163 + /* Features */ 1.164 + info->standby = get_bit(edid[0x18], 7); 1.165 + info->suspend = get_bit(edid[0x18], 6); 1.166 + info->active_off = get_bit(edid[0x18], 5); 1.167 + 1.168 + if (info->is_digital) { 1.169 + info->connector.digital.rgb444 = 1; 1.170 + if (get_bit(edid[0x18], 3)) 1.171 + info->connector.digital.ycrcb444 = 1; 1.172 + if (get_bit(edid[0x18], 4)) 1.173 + info->connector.digital.ycrcb422 = 1; 1.174 + } else { 1.175 + int bits = get_bits(edid[0x18], 3, 4); 1.176 + ColorType color_type[4] = { MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR }; 1.177 + 1.178 + info->connector.analog.color_type = color_type[bits]; 1.179 + } 1.180 + 1.181 + info->srgb_is_standard = get_bit(edid[0x18], 2); 1.182 + 1.183 + /* In 1.3 this is called "has preferred timing" */ 1.184 + info->preferred_timing_includes_native = get_bit(edid[0x18], 1); 1.185 + 1.186 + /* FIXME: In 1.3 this indicates whether the monitor accepts GTF */ 1.187 + info->continuous_frequency = get_bit(edid[0x18], 0); 1.188 + return true; 1.189 +} 1.190 + 1.191 +static double decode_fraction(int high, int low) { 1.192 + double result = 0.0; 1.193 + high = (high << 2) | low; 1.194 + for (int i = 0; i < 10; ++i) 1.195 + result += get_bit(high, i) * pow(2, i - 10); 1.196 + return result; 1.197 +} 1.198 + 1.199 +static bool decode_color_characteristics(const uint8_t *edid, MonitorInfo *info) { 1.200 + info->red_x = decode_fraction(edid[0x1b], get_bits(edid[0x19], 6, 7)); 1.201 + info->red_y = decode_fraction(edid[0x1c], get_bits(edid[0x19], 5, 4)); 1.202 + info->green_x = decode_fraction(edid[0x1d], get_bits(edid[0x19], 2, 3)); 1.203 + info->green_y = decode_fraction(edid[0x1e], get_bits(edid[0x19], 0, 1)); 1.204 + info->blue_x = decode_fraction(edid[0x1f], get_bits(edid[0x1a], 6, 7)); 1.205 + info->blue_y = decode_fraction(edid[0x20], get_bits(edid[0x1a], 4, 5)); 1.206 + info->white_x = decode_fraction(edid[0x21], get_bits(edid[0x1a], 2, 3)); 1.207 + info->white_y = decode_fraction(edid[0x22], get_bits(edid[0x1a], 0, 1)); 1.208 + 1.209 + return true; 1.210 +} 1.211 + 1.212 +static bool decode_established_timings(const uint8_t *edid, MonitorInfo *info) { 1.213 + static const Timing established[][8] = { // 1.214 + { { 800, 600, 60 }, { 800, 600, 56 }, // 1.215 + { 640, 480, 75 }, { 640, 480, 72 }, // 1.216 + { 640, 480, 67 }, { 640, 480, 60 }, // 1.217 + { 720, 400, 88 }, { 720, 400, 70 } }, // 1.218 + { { 1280, 1024, 75 }, { 1024, 768, 75 }, // 1.219 + { 1024, 768, 70 }, { 1024, 768, 60 }, // 1.220 + { 1024, 768, 87 }, { 832, 624, 75 }, // 1.221 + { 800, 600, 75 }, { 800, 600, 72 } }, // 1.222 + { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, // 1.223 + { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1152, 870, 75 } }, // 1.224 + }; 1.225 + 1.226 + int idx = 0; 1.227 + for (int i = 0; i < 3; ++i) { 1.228 + for (int j = 0; j < 8; ++j) { 1.229 + int byte = edid[0x23 + i]; 1.230 + 1.231 + if (get_bit(byte, j) && established[i][j].frequency != 0) 1.232 + info->established[idx++] = established[i][j]; 1.233 + } 1.234 + } 1.235 + return true; 1.236 +} 1.237 + 1.238 +static bool decode_standard_timings(const uint8_t *edid, MonitorInfo *info) { 1.239 + int i; 1.240 + 1.241 + for (i = 0; i < 8; i++) { 1.242 + int first = edid[0x26 + 2 * i]; 1.243 + int second = edid[0x27 + 2 * i]; 1.244 + 1.245 + if (first != 0x01 && second != 0x01) { 1.246 + int w = 8 * (first + 31); 1.247 + int h = 0; 1.248 + 1.249 + switch (get_bits(second, 6, 7)) { 1.250 + case 0x00: 1.251 + h = (w / 16) * 10; 1.252 + break; 1.253 + case 0x01: 1.254 + h = (w / 4) * 3; 1.255 + break; 1.256 + case 0x02: 1.257 + h = (w / 5) * 4; 1.258 + break; 1.259 + case 0x03: 1.260 + h = (w / 16) * 9; 1.261 + break; 1.262 + } 1.263 + 1.264 + info->standard[i].width = w; 1.265 + info->standard[i].height = h; 1.266 + info->standard[i].frequency = get_bits(second, 0, 5) + 60; 1.267 + } 1.268 + } 1.269 + 1.270 + return true; 1.271 +} 1.272 + 1.273 +static void decode_lf_string(const uint8_t *s, int n_chars, char *result) { 1.274 + int i; 1.275 + for (i = 0; i < n_chars; ++i) { 1.276 + if (s[i] == 0x0a) { 1.277 + *result++ = '\0'; 1.278 + break; 1.279 + } else if (s[i] == 0x00) { 1.280 + /* Convert embedded 0's to spaces */ 1.281 + *result++ = ' '; 1.282 + } else { 1.283 + *result++ = s[i]; 1.284 + } 1.285 + } 1.286 +} 1.287 + 1.288 +static void decode_display_descriptor(const uint8_t *desc, MonitorInfo *info) { 1.289 + switch (desc[0x03]) { 1.290 + case 0xFC: 1.291 + decode_lf_string(desc + 5, 13, info->dsc_product_name); 1.292 + break; 1.293 + case 0xFF: 1.294 + decode_lf_string(desc + 5, 13, info->dsc_serial_number); 1.295 + break; 1.296 + case 0xFE: 1.297 + decode_lf_string(desc + 5, 13, info->dsc_string); 1.298 + break; 1.299 + case 0xFD: 1.300 + /* Range Limits */ 1.301 + break; 1.302 + case 0xFB: 1.303 + /* Color Point */ 1.304 + break; 1.305 + case 0xFA: 1.306 + /* Timing Identifications */ 1.307 + break; 1.308 + case 0xF9: 1.309 + /* Color Management */ 1.310 + break; 1.311 + case 0xF8: 1.312 + /* Timing Codes */ 1.313 + break; 1.314 + case 0xF7: 1.315 + /* Established Timings */ 1.316 + break; 1.317 + case 0x10: 1.318 + break; 1.319 + } 1.320 +} 1.321 + 1.322 +static void decode_detailed_timing(const uint8_t *timing, DetailedTiming *detailed) { 1.323 + int bits; 1.324 + StereoType stereo[] = { // 1.325 + NO_STEREO, NO_STEREO, // 1.326 + FIELD_RIGHT, FIELD_LEFT, // 1.327 + TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN, // 1.328 + FOUR_WAY_INTERLEAVED, // 1.329 + SIDE_BY_SIDE // 1.330 + }; 1.331 + 1.332 + detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000; 1.333 + detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4); 1.334 + detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8); 1.335 + detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4); 1.336 + detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8); 1.337 + detailed->h_front_porch = timing[0x08] | get_bits(timing[0x0b], 6, 7) << 8; 1.338 + detailed->h_sync = timing[0x09] | get_bits(timing[0x0b], 4, 5) << 8; 1.339 + detailed->v_front_porch = get_bits(timing[0x0a], 4, 7) | get_bits(timing[0x0b], 2, 3) << 4; 1.340 + detailed->v_sync = get_bits(timing[0x0a], 0, 3) | get_bits(timing[0x0b], 0, 1) << 4; 1.341 + detailed->width_mm = timing[0x0c] | get_bits(timing[0x0e], 4, 7) << 8; 1.342 + detailed->height_mm = timing[0x0d] | get_bits(timing[0x0e], 0, 3) << 8; 1.343 + detailed->right_border = timing[0x0f]; 1.344 + detailed->top_border = timing[0x10]; 1.345 + detailed->interlaced = get_bit(timing[0x11], 7); 1.346 + 1.347 + /* Stereo */ 1.348 + bits = get_bits(timing[0x11], 5, 6) << 1 | get_bit(timing[0x11], 0); 1.349 + detailed->stereo = stereo[bits]; 1.350 + 1.351 + /* Sync */ 1.352 + bits = timing[0x11]; 1.353 + 1.354 + detailed->digital_sync = get_bit(bits, 4); 1.355 + if (detailed->digital_sync) { 1.356 + detailed->connector.digital.composite = !get_bit(bits, 3); 1.357 + if (detailed->connector.digital.composite) { 1.358 + detailed->connector.digital.serrations = get_bit(bits, 2); 1.359 + detailed->connector.digital.negative_vsync = 0; 1.360 + } else { 1.361 + detailed->connector.digital.serrations = 0; 1.362 + detailed->connector.digital.negative_vsync = !get_bit(bits, 2); 1.363 + } 1.364 + detailed->connector.digital.negative_hsync = !get_bit(bits, 0); 1.365 + } else { 1.366 + detailed->connector.analog.bipolar = get_bit(bits, 3); 1.367 + detailed->connector.analog.serrations = get_bit(bits, 2); 1.368 + detailed->connector.analog.sync_on_green = !get_bit(bits, 1); 1.369 + } 1.370 +} 1.371 + 1.372 +static bool decode_descriptors(const uint8_t *edid, MonitorInfo *info) { 1.373 + int timing_idx = 0; 1.374 + for (int i = 0; i < 4; ++i) { 1.375 + int index = 0x36 + i * 18; 1.376 + if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00) { 1.377 + decode_display_descriptor(edid + index, info); 1.378 + } else { 1.379 + decode_detailed_timing(edid + index, &(info->detailed_timings[timing_idx++])); 1.380 + } 1.381 + } 1.382 + info->n_detailed_timings = timing_idx; 1.383 + return true; 1.384 +} 1.385 + 1.386 +static void decode_check_sum(const uint8_t *edid, MonitorInfo *info) { 1.387 + uint8_t check = 0; 1.388 + for (int i = 0; i < 128; ++i) 1.389 + check += edid[i]; 1.390 + info->checksum = check; 1.391 +} 1.392 + 1.393 +MonitorInfo * decode_edid(const uint8_t *edid) { 1.394 + MonitorInfo *info = new MonitorInfo(); 1.395 + decode_check_sum(edid, info); 1.396 + if (decode_header(edid) && // 1.397 + decode_vendor_and_product_identification(edid, info) && // 1.398 + decode_edid_version(edid, info) && // 1.399 + decode_display_parameters(edid, info) && // 1.400 + decode_color_characteristics(edid, info) && // 1.401 + decode_established_timings(edid, info) && // 1.402 + decode_standard_timings(edid, info) && // 1.403 + decode_descriptors(edid, info)) { 1.404 + return info; 1.405 + } else { 1.406 + delete info; 1.407 + return 0; 1.408 + } 1.409 +} 1.410 + 1.411 +static uint8_t * get_property(Display *dpy, RROutput output, Atom atom, int *len) { 1.412 + unsigned char *prop; 1.413 + int actual_format; 1.414 + unsigned long nitems, bytes_after; 1.415 + Atom actual_type; 1.416 + uint8_t *result = NULL; 1.417 + 1.418 + XRRGetOutputProperty(dpy, output, atom, 0, 100, False, False, 1.419 + AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop); 1.420 + 1.421 + if (actual_type == XA_INTEGER && actual_format == 8) { 1.422 + result = new uint8_t[nitems]; 1.423 + memcpy(result, prop, nitems); 1.424 + if (len) 1.425 + *len = nitems; 1.426 + } 1.427 + XFree(prop); 1.428 + return result; 1.429 +} 1.430 + 1.431 +MonitorInfo * read_edid_data(Display * disp, RROutput id) { 1.432 + int len; 1.433 + Atom edid_atom = XInternAtom(disp, "EDID", false); 1.434 + uint8_t *edid = get_property(disp, id, edid_atom, &len); 1.435 + if (!edid) { 1.436 + edid_atom = XInternAtom(disp, "EDID_DATA", false); 1.437 + edid = get_property(disp, id, edid_atom, &len); 1.438 + } 1.439 + 1.440 + MonitorInfo * result = 0; 1.441 + if (edid) { 1.442 + if (len % 128 == 0) { 1.443 + result = decode_edid(edid); 1.444 + } 1.445 + delete[] edid; 1.446 + } 1.447 + 1.448 + return result; 1.449 +}