1 1.18 thorpej /* $NetBSD: edid.c,v 1.18 2022/09/25 21:27:39 thorpej Exp $ */ 2 1.1 gdamore 3 1.1 gdamore /*- 4 1.1 gdamore * Copyright (c) 2006 Itronix Inc. 5 1.1 gdamore * All rights reserved. 6 1.1 gdamore * 7 1.1 gdamore * Written by Garrett D'Amore for Itronix Inc. 8 1.1 gdamore * 9 1.1 gdamore * Redistribution and use in source and binary forms, with or without 10 1.1 gdamore * modification, are permitted provided that the following conditions 11 1.1 gdamore * are met: 12 1.1 gdamore * 1. Redistributions of source code must retain the above copyright 13 1.1 gdamore * notice, this list of conditions and the following disclaimer. 14 1.1 gdamore * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 gdamore * notice, this list of conditions and the following disclaimer in the 16 1.1 gdamore * documentation and/or other materials provided with the distribution. 17 1.1 gdamore * 3. The name of Itronix Inc. may not be used to endorse 18 1.1 gdamore * or promote products derived from this software without specific 19 1.1 gdamore * prior written permission. 20 1.1 gdamore * 21 1.1 gdamore * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND ANY EXPRESS 22 1.1 gdamore * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 1.1 gdamore * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 1.1 gdamore * ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY 25 1.1 gdamore * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 1.1 gdamore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 1.1 gdamore * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 1.1 gdamore * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 1.1 gdamore * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 1.1 gdamore * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 1.1 gdamore * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 1.1 gdamore */ 33 1.1 gdamore 34 1.1 gdamore #include <sys/cdefs.h> 35 1.18 thorpej __KERNEL_RCSID(0, "$NetBSD: edid.c,v 1.18 2022/09/25 21:27:39 thorpej Exp $"); 36 1.1 gdamore 37 1.16 mlelstv #ifdef _KERNEL 38 1.1 gdamore #include <sys/param.h> 39 1.1 gdamore #include <sys/systm.h> 40 1.1 gdamore #include <sys/device.h> 41 1.1 gdamore #include <sys/kernel.h> 42 1.16 mlelstv #else 43 1.16 mlelstv #include <stdlib.h> 44 1.16 mlelstv #include <inttypes.h> 45 1.16 mlelstv #include <string.h> 46 1.16 mlelstv #include <errno.h> 47 1.16 mlelstv #include <stdio.h> 48 1.16 mlelstv #endif 49 1.16 mlelstv 50 1.1 gdamore #include <dev/videomode/videomode.h> 51 1.1 gdamore #include <dev/videomode/ediddevs.h> 52 1.1 gdamore #include <dev/videomode/edidreg.h> 53 1.1 gdamore #include <dev/videomode/edidvar.h> 54 1.1 gdamore #include <dev/videomode/vesagtf.h> 55 1.1 gdamore 56 1.1 gdamore #define EDIDVERBOSE 1 57 1.1 gdamore #define DIVIDE(x,y) (((x) + ((y) / 2)) / (y)) 58 1.1 gdamore 59 1.7 jdc /* These are reversed established timing order */ 60 1.1 gdamore static const char *_edid_modes[] = { 61 1.3 gdamore "1280x1024x75", 62 1.3 gdamore "1024x768x75", 63 1.3 gdamore "1024x768x70", 64 1.3 gdamore "1024x768x60", 65 1.3 gdamore "1024x768x87i", 66 1.7 jdc "832x624x74", /* rounding error, 74.55 Hz aka "832x624x75" */ 67 1.3 gdamore "800x600x75", 68 1.3 gdamore "800x600x72", 69 1.3 gdamore "800x600x60", 70 1.3 gdamore "800x600x56", 71 1.3 gdamore "640x480x75", 72 1.3 gdamore "640x480x72", 73 1.3 gdamore "640x480x67", 74 1.3 gdamore "640x480x60", 75 1.7 jdc "720x400x87", /* rounding error, 87.85 Hz aka "720x400x88" */ 76 1.7 jdc "720x400x70", 77 1.1 gdamore }; 78 1.1 gdamore 79 1.1 gdamore #ifdef EDIDVERBOSE 80 1.1 gdamore struct edid_vendor { 81 1.1 gdamore const char *vendor; 82 1.1 gdamore const char *name; 83 1.1 gdamore }; 84 1.1 gdamore 85 1.1 gdamore struct edid_product { 86 1.1 gdamore const char *vendor; 87 1.1 gdamore uint16_t product; 88 1.1 gdamore const char *name; 89 1.1 gdamore }; 90 1.1 gdamore 91 1.1 gdamore #include <dev/videomode/ediddevs_data.h> 92 1.1 gdamore #endif /* EDIDVERBOSE */ 93 1.1 gdamore 94 1.1 gdamore static const char * 95 1.1 gdamore edid_findvendor(const char *vendor) 96 1.1 gdamore { 97 1.1 gdamore #ifdef EDIDVERBOSE 98 1.1 gdamore int n; 99 1.1 gdamore 100 1.1 gdamore for (n = 0; n < edid_nvendors; n++) 101 1.1 gdamore if (memcmp(edid_vendors[n].vendor, vendor, 3) == 0) 102 1.10 christos return edid_vendors[n].name; 103 1.1 gdamore #endif 104 1.1 gdamore return NULL; 105 1.1 gdamore } 106 1.1 gdamore 107 1.1 gdamore static const char * 108 1.1 gdamore edid_findproduct(const char *vendor, uint16_t product) 109 1.1 gdamore { 110 1.1 gdamore #ifdef EDIDVERBOSE 111 1.1 gdamore int n; 112 1.1 gdamore 113 1.6 tsutsui for (n = 0; n < edid_nproducts; n++) 114 1.10 christos if (edid_products[n].product == product && 115 1.10 christos memcmp(edid_products[n].vendor, vendor, 3) == 0) 116 1.11 christos return edid_products[n].name; 117 1.1 gdamore #endif /* EDIDVERBOSE */ 118 1.1 gdamore return NULL; 119 1.1 gdamore 120 1.1 gdamore } 121 1.1 gdamore 122 1.1 gdamore static void 123 1.1 gdamore edid_strchomp(char *ptr) 124 1.1 gdamore { 125 1.1 gdamore for (;;) { 126 1.1 gdamore switch (*ptr) { 127 1.10 christos case '\0': 128 1.1 gdamore return; 129 1.1 gdamore case '\r': 130 1.1 gdamore case '\n': 131 1.10 christos *ptr = '\0'; 132 1.1 gdamore return; 133 1.1 gdamore } 134 1.1 gdamore ptr++; 135 1.1 gdamore } 136 1.1 gdamore } 137 1.1 gdamore 138 1.1 gdamore int 139 1.1 gdamore edid_is_valid(uint8_t *d) 140 1.1 gdamore { 141 1.1 gdamore int sum = 0, i; 142 1.1 gdamore uint8_t sig[8] = EDID_SIGNATURE; 143 1.16 mlelstv 144 1.1 gdamore if (memcmp(d, sig, 8) != 0) 145 1.1 gdamore return EINVAL; 146 1.1 gdamore 147 1.1 gdamore for (i = 0; i < 128; i++) 148 1.1 gdamore sum += d[i]; 149 1.1 gdamore if ((sum & 0xff) != 0) 150 1.1 gdamore return EINVAL; 151 1.1 gdamore 152 1.1 gdamore return 0; 153 1.1 gdamore } 154 1.1 gdamore 155 1.1 gdamore void 156 1.1 gdamore edid_print(struct edid_info *edid) 157 1.1 gdamore { 158 1.1 gdamore int i; 159 1.1 gdamore 160 1.1 gdamore if (edid == NULL) 161 1.1 gdamore return; 162 1.1 gdamore printf("Vendor: [%s] %s\n", edid->edid_vendor, edid->edid_vendorname); 163 1.1 gdamore printf("Product: [%04X] %s\n", edid->edid_product, 164 1.1 gdamore edid->edid_productname); 165 1.1 gdamore printf("Serial number: %s\n", edid->edid_serial); 166 1.1 gdamore printf("Manufactured %d Week %d\n", 167 1.1 gdamore edid->edid_year, edid->edid_week); 168 1.1 gdamore printf("EDID Version %d.%d\n", edid->edid_version, 169 1.1 gdamore edid->edid_revision); 170 1.1 gdamore printf("EDID Comment: %s\n", edid->edid_comment); 171 1.1 gdamore 172 1.1 gdamore printf("Video Input: %x\n", edid->edid_video_input); 173 1.1 gdamore if (edid->edid_video_input & EDID_VIDEO_INPUT_DIGITAL) { 174 1.1 gdamore printf("\tDigital"); 175 1.1 gdamore if (edid->edid_video_input & EDID_VIDEO_INPUT_DFP1_COMPAT) 176 1.1 gdamore printf(" (DFP 1.x compatible)"); 177 1.1 gdamore printf("\n"); 178 1.1 gdamore } else { 179 1.1 gdamore printf("\tAnalog\n"); 180 1.1 gdamore switch (EDID_VIDEO_INPUT_LEVEL(edid->edid_video_input)) { 181 1.1 gdamore case 0: 182 1.1 gdamore printf("\t-0.7, 0.3V\n"); 183 1.1 gdamore break; 184 1.1 gdamore case 1: 185 1.1 gdamore printf("\t-0.714, 0.286V\n"); 186 1.1 gdamore break; 187 1.1 gdamore case 2: 188 1.1 gdamore printf("\t-1.0, 0.4V\n"); 189 1.1 gdamore break; 190 1.1 gdamore case 3: 191 1.1 gdamore printf("\t-0.7, 0.0V\n"); 192 1.1 gdamore break; 193 1.1 gdamore } 194 1.1 gdamore if (edid->edid_video_input & EDID_VIDEO_INPUT_BLANK_TO_BLACK) 195 1.1 gdamore printf("\tBlank-to-black setup\n"); 196 1.1 gdamore if (edid->edid_video_input & EDID_VIDEO_INPUT_SEPARATE_SYNCS) 197 1.17 andvar printf("\tSeparate syncs\n"); 198 1.1 gdamore if (edid->edid_video_input & EDID_VIDEO_INPUT_COMPOSITE_SYNC) 199 1.1 gdamore printf("\tComposite sync\n"); 200 1.1 gdamore if (edid->edid_video_input & EDID_VIDEO_INPUT_SYNC_ON_GRN) 201 1.1 gdamore printf("\tSync on green\n"); 202 1.1 gdamore if (edid->edid_video_input & EDID_VIDEO_INPUT_SERRATION) 203 1.1 gdamore printf("\tSerration vsync\n"); 204 1.1 gdamore } 205 1.1 gdamore 206 1.1 gdamore printf("Gamma: %d.%02d\n", 207 1.1 gdamore edid->edid_gamma / 100, edid->edid_gamma % 100); 208 1.1 gdamore 209 1.1 gdamore printf("Max Size: %d cm x %d cm\n", 210 1.1 gdamore edid->edid_max_hsize, edid->edid_max_vsize); 211 1.1 gdamore 212 1.1 gdamore printf("Features: %x\n", edid->edid_features); 213 1.1 gdamore if (edid->edid_features & EDID_FEATURES_STANDBY) 214 1.1 gdamore printf("\tDPMS standby\n"); 215 1.1 gdamore if (edid->edid_features & EDID_FEATURES_SUSPEND) 216 1.1 gdamore printf("\tDPMS suspend\n"); 217 1.1 gdamore if (edid->edid_features & EDID_FEATURES_ACTIVE_OFF) 218 1.1 gdamore printf("\tDPMS active-off\n"); 219 1.1 gdamore switch (EDID_FEATURES_DISP_TYPE(edid->edid_features)) { 220 1.1 gdamore case EDID_FEATURES_DISP_TYPE_MONO: 221 1.1 gdamore printf("\tMonochrome\n"); 222 1.1 gdamore break; 223 1.1 gdamore case EDID_FEATURES_DISP_TYPE_RGB: 224 1.1 gdamore printf("\tRGB\n"); 225 1.1 gdamore break; 226 1.1 gdamore case EDID_FEATURES_DISP_TYPE_NON_RGB: 227 1.1 gdamore printf("\tMulticolor\n"); 228 1.1 gdamore break; 229 1.1 gdamore case EDID_FEATURES_DISP_TYPE_UNDEFINED: 230 1.1 gdamore printf("\tUndefined monitor type\n"); 231 1.1 gdamore break; 232 1.1 gdamore } 233 1.1 gdamore if (edid->edid_features & EDID_FEATURES_STD_COLOR) 234 1.1 gdamore printf("\tStandard color space\n"); 235 1.1 gdamore if (edid->edid_features & EDID_FEATURES_PREFERRED_TIMING) 236 1.1 gdamore printf("\tPreferred timing\n"); 237 1.1 gdamore if (edid->edid_features & EDID_FEATURES_DEFAULT_GTF) 238 1.1 gdamore printf("\tDefault GTF supported\n"); 239 1.1 gdamore 240 1.1 gdamore printf("Chroma Info:\n"); 241 1.1 gdamore printf("\tRed X: 0.%03d\n", edid->edid_chroma.ec_redx); 242 1.1 gdamore printf("\tRed Y: 0.%03d\n", edid->edid_chroma.ec_redy); 243 1.1 gdamore printf("\tGrn X: 0.%03d\n", edid->edid_chroma.ec_greenx); 244 1.1 gdamore printf("\tGrn Y: 0.%03d\n", edid->edid_chroma.ec_greeny); 245 1.1 gdamore printf("\tBlu X: 0.%03d\n", edid->edid_chroma.ec_bluex); 246 1.1 gdamore printf("\tBlu Y: 0.%03d\n", edid->edid_chroma.ec_bluey); 247 1.1 gdamore printf("\tWht X: 0.%03d\n", edid->edid_chroma.ec_whitex); 248 1.1 gdamore printf("\tWht Y: 0.%03d\n", edid->edid_chroma.ec_whitey); 249 1.1 gdamore 250 1.1 gdamore if (edid->edid_have_range) { 251 1.1 gdamore printf("Range:\n"); 252 1.1 gdamore printf("\tHorizontal: %d - %d kHz\n", 253 1.1 gdamore edid->edid_range.er_min_hfreq, 254 1.1 gdamore edid->edid_range.er_max_hfreq); 255 1.1 gdamore printf("\tVertical: %d - %d Hz\n", 256 1.1 gdamore edid->edid_range.er_min_vfreq, 257 1.1 gdamore edid->edid_range.er_max_vfreq); 258 1.1 gdamore printf("\tMax Dot Clock: %d MHz\n", 259 1.1 gdamore edid->edid_range.er_max_clock); 260 1.1 gdamore if (edid->edid_range.er_have_gtf2) { 261 1.1 gdamore printf("\tGTF2 hfreq: %d\n", 262 1.1 gdamore edid->edid_range.er_gtf2_hfreq); 263 1.1 gdamore printf("\tGTF2 C: %d\n", edid->edid_range.er_gtf2_c); 264 1.1 gdamore printf("\tGTF2 M: %d\n", edid->edid_range.er_gtf2_m); 265 1.1 gdamore printf("\tGTF2 J: %d\n", edid->edid_range.er_gtf2_j); 266 1.1 gdamore printf("\tGTF2 K: %d\n", edid->edid_range.er_gtf2_k); 267 1.1 gdamore } 268 1.1 gdamore } 269 1.1 gdamore printf("Video modes:\n"); 270 1.1 gdamore for (i = 0; i < edid->edid_nmodes; i++) { 271 1.8 jdc printf("\t%dx%d @ %dHz", 272 1.1 gdamore edid->edid_modes[i].hdisplay, 273 1.1 gdamore edid->edid_modes[i].vdisplay, 274 1.1 gdamore DIVIDE(DIVIDE(edid->edid_modes[i].dot_clock * 1000, 275 1.10 christos edid->edid_modes[i].htotal), edid->edid_modes[i].vtotal)); 276 1.8 jdc printf(" (%d %d %d %d %d %d %d", 277 1.8 jdc edid->edid_modes[i].dot_clock, 278 1.8 jdc edid->edid_modes[i].hsync_start, 279 1.8 jdc edid->edid_modes[i].hsync_end, 280 1.8 jdc edid->edid_modes[i].htotal, 281 1.8 jdc edid->edid_modes[i].vsync_start, 282 1.8 jdc edid->edid_modes[i].vsync_end, 283 1.8 jdc edid->edid_modes[i].vtotal); 284 1.8 jdc printf(" %s%sH %s%sV)\n", 285 1.8 jdc edid->edid_modes[i].flags & VID_PHSYNC ? "+" : "", 286 1.8 jdc edid->edid_modes[i].flags & VID_NHSYNC ? "-" : "", 287 1.8 jdc edid->edid_modes[i].flags & VID_PVSYNC ? "+" : "", 288 1.8 jdc edid->edid_modes[i].flags & VID_NVSYNC ? "-" : ""); 289 1.1 gdamore } 290 1.1 gdamore if (edid->edid_preferred_mode) 291 1.1 gdamore printf("Preferred mode: %dx%d @ %dHz\n", 292 1.1 gdamore edid->edid_preferred_mode->hdisplay, 293 1.1 gdamore edid->edid_preferred_mode->vdisplay, 294 1.1 gdamore DIVIDE(DIVIDE(edid->edid_preferred_mode->dot_clock * 1000, 295 1.10 christos edid->edid_preferred_mode->htotal), 296 1.10 christos edid->edid_preferred_mode->vtotal)); 297 1.13 jmcneill 298 1.13 jmcneill printf("Number of extension blocks: %d\n", edid->edid_ext_block_count); 299 1.1 gdamore } 300 1.1 gdamore 301 1.1 gdamore static const struct videomode * 302 1.1 gdamore edid_mode_lookup_list(const char *name) 303 1.1 gdamore { 304 1.1 gdamore int i; 305 1.1 gdamore 306 1.1 gdamore for (i = 0; i < videomode_count; i++) 307 1.1 gdamore if (strcmp(name, videomode_list[i].name) == 0) 308 1.1 gdamore return &videomode_list[i]; 309 1.1 gdamore return NULL; 310 1.1 gdamore } 311 1.1 gdamore 312 1.9 jdc static struct videomode * 313 1.9 jdc edid_search_mode(struct edid_info *edid, const struct videomode *mode) 314 1.9 jdc { 315 1.9 jdc int refresh, i; 316 1.9 jdc 317 1.9 jdc refresh = DIVIDE(DIVIDE(mode->dot_clock * 1000, 318 1.9 jdc mode->htotal), mode->vtotal); 319 1.9 jdc for (i = 0; i < edid->edid_nmodes; i++) { 320 1.9 jdc if (mode->hdisplay == edid->edid_modes[i].hdisplay && 321 1.9 jdc mode->vdisplay == edid->edid_modes[i].vdisplay && 322 1.9 jdc refresh == DIVIDE(DIVIDE( 323 1.9 jdc edid->edid_modes[i].dot_clock * 1000, 324 1.10 christos edid->edid_modes[i].htotal), edid->edid_modes[i].vtotal)) { 325 1.10 christos return &edid->edid_modes[i]; 326 1.9 jdc } 327 1.9 jdc } 328 1.9 jdc return NULL; 329 1.9 jdc } 330 1.9 jdc 331 1.1 gdamore static int 332 1.1 gdamore edid_std_timing(uint8_t *data, struct videomode *vmp) 333 1.1 gdamore { 334 1.1 gdamore unsigned x, y, f; 335 1.1 gdamore const struct videomode *lookup; 336 1.1 gdamore char name[80]; 337 1.1 gdamore 338 1.1 gdamore if ((data[0] == 1 && data[1] == 1) || 339 1.1 gdamore (data[0] == 0 && data[1] == 0) || 340 1.1 gdamore (data[0] == 0x20 && data[1] == 0x20)) 341 1.1 gdamore return 0; 342 1.1 gdamore 343 1.1 gdamore x = EDID_STD_TIMING_HRES(data); 344 1.1 gdamore switch (EDID_STD_TIMING_RATIO(data)) { 345 1.1 gdamore case EDID_STD_TIMING_RATIO_16_10: 346 1.1 gdamore y = x * 10 / 16; 347 1.1 gdamore break; 348 1.1 gdamore case EDID_STD_TIMING_RATIO_4_3: 349 1.1 gdamore y = x * 3 / 4; 350 1.1 gdamore break; 351 1.1 gdamore case EDID_STD_TIMING_RATIO_5_4: 352 1.1 gdamore y = x * 4 / 5; 353 1.1 gdamore break; 354 1.1 gdamore case EDID_STD_TIMING_RATIO_16_9: 355 1.1 gdamore default: 356 1.1 gdamore y = x * 9 / 16; 357 1.1 gdamore break; 358 1.1 gdamore } 359 1.1 gdamore f = EDID_STD_TIMING_VFREQ(data); 360 1.1 gdamore 361 1.1 gdamore /* first try to lookup the mode as a DMT timing */ 362 1.10 christos snprintf(name, sizeof(name), "%dx%dx%d", x, y, f); 363 1.1 gdamore if ((lookup = edid_mode_lookup_list(name)) != NULL) { 364 1.1 gdamore *vmp = *lookup; 365 1.10 christos } else { 366 1.10 christos /* failing that, calculate it using gtf */ 367 1.1 gdamore /* 368 1.1 gdamore * Hmm. I'm not using alternate GTF timings, which 369 1.1 gdamore * could, in theory, be present. 370 1.1 gdamore */ 371 1.1 gdamore vesagtf_mode(x, y, f, vmp); 372 1.1 gdamore } 373 1.1 gdamore return 1; 374 1.1 gdamore } 375 1.1 gdamore 376 1.1 gdamore static int 377 1.1 gdamore edid_det_timing(uint8_t *data, struct videomode *vmp) 378 1.1 gdamore { 379 1.1 gdamore unsigned hactive, hblank, hsyncwid, hsyncoff; 380 1.1 gdamore unsigned vactive, vblank, vsyncwid, vsyncoff; 381 1.1 gdamore uint8_t flags; 382 1.1 gdamore 383 1.1 gdamore flags = EDID_DET_TIMING_FLAGS(data); 384 1.1 gdamore 385 1.1 gdamore /* we don't support stereo modes (for now) */ 386 1.1 gdamore if (flags & (EDID_DET_TIMING_FLAG_STEREO | 387 1.8 jdc EDID_DET_TIMING_FLAG_STEREO_MODE)) 388 1.1 gdamore return 0; 389 1.1 gdamore 390 1.1 gdamore vmp->dot_clock = EDID_DET_TIMING_DOT_CLOCK(data) / 1000; 391 1.1 gdamore 392 1.1 gdamore hactive = EDID_DET_TIMING_HACTIVE(data); 393 1.1 gdamore hblank = EDID_DET_TIMING_HBLANK(data); 394 1.1 gdamore hsyncwid = EDID_DET_TIMING_HSYNC_WIDTH(data); 395 1.1 gdamore hsyncoff = EDID_DET_TIMING_HSYNC_OFFSET(data); 396 1.1 gdamore 397 1.1 gdamore vactive = EDID_DET_TIMING_VACTIVE(data); 398 1.1 gdamore vblank = EDID_DET_TIMING_VBLANK(data); 399 1.1 gdamore vsyncwid = EDID_DET_TIMING_VSYNC_WIDTH(data); 400 1.1 gdamore vsyncoff = EDID_DET_TIMING_VSYNC_OFFSET(data); 401 1.1 gdamore 402 1.9 jdc /* Borders are contained within the blank areas. */ 403 1.1 gdamore 404 1.1 gdamore vmp->hdisplay = hactive; 405 1.1 gdamore vmp->htotal = hactive + hblank; 406 1.1 gdamore vmp->hsync_start = hactive + hsyncoff; 407 1.1 gdamore vmp->hsync_end = vmp->hsync_start + hsyncwid; 408 1.1 gdamore 409 1.1 gdamore vmp->vdisplay = vactive; 410 1.1 gdamore vmp->vtotal = vactive + vblank; 411 1.1 gdamore vmp->vsync_start = vactive + vsyncoff; 412 1.1 gdamore vmp->vsync_end = vmp->vsync_start + vsyncwid; 413 1.1 gdamore 414 1.1 gdamore vmp->flags = 0; 415 1.1 gdamore 416 1.1 gdamore if (flags & EDID_DET_TIMING_FLAG_INTERLACE) 417 1.1 gdamore vmp->flags |= VID_INTERLACE; 418 1.1 gdamore if (flags & EDID_DET_TIMING_FLAG_HSYNC_POSITIVE) 419 1.1 gdamore vmp->flags |= VID_PHSYNC; 420 1.1 gdamore else 421 1.1 gdamore vmp->flags |= VID_NHSYNC; 422 1.1 gdamore 423 1.1 gdamore if (flags & EDID_DET_TIMING_FLAG_VSYNC_POSITIVE) 424 1.1 gdamore vmp->flags |= VID_PVSYNC; 425 1.1 gdamore else 426 1.1 gdamore vmp->flags |= VID_NVSYNC; 427 1.1 gdamore 428 1.1 gdamore return 1; 429 1.1 gdamore } 430 1.1 gdamore 431 1.14 macallan static void bump_preferred_mode(struct edid_info *edid, struct videomode *m) 432 1.14 macallan { 433 1.14 macallan /* 434 1.14 macallan * XXX 435 1.14 macallan * Iiyama 4800 series monitors may have their native resolution in the 436 1.14 macallan * 2nd detailed timing descriptor instead of the 1st. Try to detect 437 1.14 macallan * that here and pick the native mode anyway. 438 1.14 macallan */ 439 1.14 macallan if (edid->edid_preferred_mode == NULL) { 440 1.14 macallan edid->edid_preferred_mode = m; 441 1.16 mlelstv } else if ((strncmp((char*)edid->edid_vendor, "IVM", 3) == 0) && 442 1.14 macallan (edid->edid_product == 0x4800) && 443 1.14 macallan (edid->edid_preferred_mode->dot_clock < m->dot_clock)) 444 1.14 macallan edid->edid_preferred_mode = m; 445 1.14 macallan } 446 1.14 macallan 447 1.1 gdamore static void 448 1.1 gdamore edid_block(struct edid_info *edid, uint8_t *data) 449 1.1 gdamore { 450 1.1 gdamore int i; 451 1.9 jdc struct videomode mode, *exist_mode; 452 1.1 gdamore 453 1.1 gdamore if (EDID_BLOCK_IS_DET_TIMING(data)) { 454 1.11 christos if (!edid_det_timing(data, &mode)) 455 1.10 christos return; 456 1.10 christos /* Does this mode already exist? */ 457 1.10 christos exist_mode = edid_search_mode(edid, &mode); 458 1.10 christos if (exist_mode != NULL) { 459 1.10 christos *exist_mode = mode; 460 1.14 macallan bump_preferred_mode(edid, exist_mode); 461 1.10 christos } else { 462 1.10 christos edid->edid_modes[edid->edid_nmodes] = mode; 463 1.14 macallan bump_preferred_mode(edid, 464 1.14 macallan &edid->edid_modes[edid->edid_nmodes]); 465 1.10 christos edid->edid_nmodes++; 466 1.1 gdamore } 467 1.1 gdamore return; 468 1.1 gdamore } 469 1.1 gdamore 470 1.1 gdamore switch (EDID_BLOCK_TYPE(data)) { 471 1.1 gdamore case EDID_DESC_BLOCK_TYPE_SERIAL: 472 1.10 christos memcpy(edid->edid_serial, data + EDID_DESC_ASCII_DATA_OFFSET, 473 1.1 gdamore EDID_DESC_ASCII_DATA_LEN); 474 1.10 christos edid->edid_serial[sizeof(edid->edid_serial) - 1] = 0; 475 1.1 gdamore break; 476 1.1 gdamore 477 1.1 gdamore case EDID_DESC_BLOCK_TYPE_ASCII: 478 1.15 maxv memset(edid->edid_comment, 0, sizeof(edid->edid_comment)); 479 1.10 christos memcpy(edid->edid_comment, data + EDID_DESC_ASCII_DATA_OFFSET, 480 1.1 gdamore EDID_DESC_ASCII_DATA_LEN); 481 1.10 christos edid->edid_comment[sizeof(edid->edid_comment) - 1] = 0; 482 1.1 gdamore break; 483 1.1 gdamore 484 1.1 gdamore case EDID_DESC_BLOCK_TYPE_RANGE: 485 1.1 gdamore edid->edid_have_range = 1; 486 1.10 christos edid->edid_range.er_min_vfreq = EDID_DESC_RANGE_MIN_VFREQ(data); 487 1.10 christos edid->edid_range.er_max_vfreq = EDID_DESC_RANGE_MAX_VFREQ(data); 488 1.10 christos edid->edid_range.er_min_hfreq = EDID_DESC_RANGE_MIN_HFREQ(data); 489 1.10 christos edid->edid_range.er_max_hfreq = EDID_DESC_RANGE_MAX_HFREQ(data); 490 1.10 christos edid->edid_range.er_max_clock = EDID_DESC_RANGE_MAX_CLOCK(data); 491 1.10 christos if (!EDID_DESC_RANGE_HAVE_GTF2(data)) 492 1.10 christos break; 493 1.10 christos edid->edid_range.er_have_gtf2 = 1; 494 1.10 christos edid->edid_range.er_gtf2_hfreq = 495 1.10 christos EDID_DESC_RANGE_GTF2_HFREQ(data); 496 1.10 christos edid->edid_range.er_gtf2_c = EDID_DESC_RANGE_GTF2_C(data); 497 1.10 christos edid->edid_range.er_gtf2_m = EDID_DESC_RANGE_GTF2_M(data); 498 1.10 christos edid->edid_range.er_gtf2_j = EDID_DESC_RANGE_GTF2_J(data); 499 1.10 christos edid->edid_range.er_gtf2_k = EDID_DESC_RANGE_GTF2_K(data); 500 1.1 gdamore break; 501 1.1 gdamore 502 1.1 gdamore case EDID_DESC_BLOCK_TYPE_NAME: 503 1.1 gdamore /* copy the product name into place */ 504 1.1 gdamore memcpy(edid->edid_productname, 505 1.1 gdamore data + EDID_DESC_ASCII_DATA_OFFSET, 506 1.1 gdamore EDID_DESC_ASCII_DATA_LEN); 507 1.1 gdamore break; 508 1.1 gdamore 509 1.1 gdamore case EDID_DESC_BLOCK_TYPE_STD_TIMING: 510 1.1 gdamore data += EDID_DESC_STD_TIMING_START; 511 1.1 gdamore for (i = 0; i < EDID_DESC_STD_TIMING_COUNT; i++) { 512 1.1 gdamore if (edid_std_timing(data, &mode)) { 513 1.9 jdc /* Does this mode already exist? */ 514 1.9 jdc exist_mode = edid_search_mode(edid, &mode); 515 1.9 jdc if (exist_mode == NULL) { 516 1.9 jdc edid->edid_modes[edid->edid_nmodes] = 517 1.9 jdc mode; 518 1.9 jdc edid->edid_nmodes++; 519 1.9 jdc } 520 1.1 gdamore } 521 1.1 gdamore data += 2; 522 1.1 gdamore } 523 1.1 gdamore break; 524 1.1 gdamore 525 1.1 gdamore case EDID_DESC_BLOCK_TYPE_COLOR_POINT: 526 1.1 gdamore /* XXX: not implemented yet */ 527 1.1 gdamore break; 528 1.1 gdamore } 529 1.1 gdamore } 530 1.1 gdamore 531 1.1 gdamore /* 532 1.1 gdamore * Gets EDID version in BCD, e.g. EDID v1.3 returned as 0x0103 533 1.1 gdamore */ 534 1.2 gdamore int 535 1.2 gdamore edid_parse(uint8_t *data, struct edid_info *edid) 536 1.1 gdamore { 537 1.1 gdamore uint16_t manfid, estmodes; 538 1.1 gdamore const struct videomode *vmp; 539 1.1 gdamore int i; 540 1.1 gdamore const char *name; 541 1.5 macallan int max_dotclock = 0; 542 1.5 macallan int mhz; 543 1.1 gdamore 544 1.1 gdamore if (edid_is_valid(data) != 0) 545 1.2 gdamore return -1; 546 1.1 gdamore 547 1.1 gdamore /* get product identification */ 548 1.1 gdamore manfid = EDID_VENDOR_ID(data); 549 1.1 gdamore edid->edid_vendor[0] = EDID_MANFID_0(manfid); 550 1.1 gdamore edid->edid_vendor[1] = EDID_MANFID_1(manfid); 551 1.1 gdamore edid->edid_vendor[2] = EDID_MANFID_2(manfid); 552 1.1 gdamore edid->edid_vendor[3] = 0; /* null terminate for convenience */ 553 1.1 gdamore 554 1.1 gdamore edid->edid_product = data[EDID_OFFSET_PRODUCT_ID] + 555 1.1 gdamore (data[EDID_OFFSET_PRODUCT_ID + 1] << 8); 556 1.1 gdamore 557 1.16 mlelstv name = edid_findvendor((char *)edid->edid_vendor); 558 1.10 christos if (name != NULL) 559 1.10 christos strlcpy(edid->edid_vendorname, name, 560 1.10 christos sizeof(edid->edid_vendorname)); 561 1.10 christos else 562 1.10 christos edid->edid_vendorname[0] = '\0'; 563 1.1 gdamore 564 1.16 mlelstv name = edid_findproduct((char *)edid->edid_vendor, edid->edid_product); 565 1.10 christos if (name != NULL) 566 1.10 christos strlcpy(edid->edid_productname, name, 567 1.10 christos sizeof(edid->edid_productname)); 568 1.10 christos else 569 1.10 christos edid->edid_productname[0] = '\0'; 570 1.1 gdamore 571 1.10 christos snprintf(edid->edid_serial, sizeof(edid->edid_serial), "%08x", 572 1.1 gdamore EDID_SERIAL_NUMBER(data)); 573 1.1 gdamore 574 1.15 maxv edid->edid_comment[0] = '\0'; 575 1.15 maxv 576 1.1 gdamore edid->edid_week = EDID_WEEK(data); 577 1.1 gdamore edid->edid_year = EDID_YEAR(data); 578 1.1 gdamore 579 1.1 gdamore /* get edid revision */ 580 1.1 gdamore edid->edid_version = EDID_VERSION(data); 581 1.1 gdamore edid->edid_revision = EDID_REVISION(data); 582 1.1 gdamore 583 1.1 gdamore edid->edid_video_input = EDID_VIDEO_INPUT(data); 584 1.1 gdamore edid->edid_max_hsize = EDID_MAX_HSIZE(data); 585 1.1 gdamore edid->edid_max_vsize = EDID_MAX_VSIZE(data); 586 1.1 gdamore 587 1.1 gdamore edid->edid_gamma = EDID_GAMMA(data); 588 1.1 gdamore edid->edid_features = EDID_FEATURES(data); 589 1.1 gdamore 590 1.1 gdamore edid->edid_chroma.ec_redx = EDID_CHROMA_REDX(data); 591 1.1 gdamore edid->edid_chroma.ec_redy = EDID_CHROMA_REDX(data); 592 1.1 gdamore edid->edid_chroma.ec_greenx = EDID_CHROMA_GREENX(data); 593 1.1 gdamore edid->edid_chroma.ec_greeny = EDID_CHROMA_GREENY(data); 594 1.1 gdamore edid->edid_chroma.ec_bluex = EDID_CHROMA_BLUEX(data); 595 1.1 gdamore edid->edid_chroma.ec_bluey = EDID_CHROMA_BLUEY(data); 596 1.1 gdamore edid->edid_chroma.ec_whitex = EDID_CHROMA_WHITEX(data); 597 1.1 gdamore edid->edid_chroma.ec_whitey = EDID_CHROMA_WHITEY(data); 598 1.1 gdamore 599 1.13 jmcneill edid->edid_ext_block_count = EDID_EXT_BLOCK_COUNT(data); 600 1.13 jmcneill 601 1.1 gdamore /* lookup established modes */ 602 1.4 macallan edid->edid_nmodes = 0; 603 1.5 macallan edid->edid_preferred_mode = NULL; 604 1.1 gdamore estmodes = EDID_EST_TIMING(data); 605 1.7 jdc /* Iterate in esztablished timing order */ 606 1.7 jdc for (i = 15; i >= 0; i--) { 607 1.1 gdamore if (estmodes & (1 << i)) { 608 1.1 gdamore vmp = edid_mode_lookup_list(_edid_modes[i]); 609 1.1 gdamore if (vmp != NULL) { 610 1.1 gdamore edid->edid_modes[edid->edid_nmodes] = *vmp; 611 1.1 gdamore edid->edid_nmodes++; 612 1.1 gdamore } 613 1.5 macallan #ifdef DIAGNOSTIC 614 1.5 macallan else 615 1.5 macallan printf("no data for est. mode %s\n", 616 1.5 macallan _edid_modes[i]); 617 1.5 macallan #endif 618 1.1 gdamore } 619 1.1 gdamore } 620 1.1 gdamore 621 1.1 gdamore /* do standard timing section */ 622 1.1 gdamore for (i = 0; i < EDID_STD_TIMING_COUNT; i++) { 623 1.9 jdc struct videomode mode, *exist_mode; 624 1.1 gdamore if (edid_std_timing(data + EDID_OFFSET_STD_TIMING + i * 2, 625 1.1 gdamore &mode)) { 626 1.9 jdc /* Does this mode already exist? */ 627 1.9 jdc exist_mode = edid_search_mode(edid, &mode); 628 1.9 jdc if (exist_mode == NULL) { 629 1.9 jdc edid->edid_modes[edid->edid_nmodes] = mode; 630 1.9 jdc edid->edid_nmodes++; 631 1.9 jdc } 632 1.1 gdamore } 633 1.1 gdamore } 634 1.1 gdamore 635 1.1 gdamore /* do detailed timings and descriptors */ 636 1.1 gdamore for (i = 0; i < EDID_BLOCK_COUNT; i++) { 637 1.1 gdamore edid_block(edid, data + EDID_OFFSET_DESC_BLOCK + 638 1.1 gdamore i * EDID_BLOCK_SIZE); 639 1.1 gdamore } 640 1.1 gdamore 641 1.1 gdamore edid_strchomp(edid->edid_vendorname); 642 1.1 gdamore edid_strchomp(edid->edid_productname); 643 1.1 gdamore edid_strchomp(edid->edid_serial); 644 1.1 gdamore edid_strchomp(edid->edid_comment); 645 1.1 gdamore 646 1.5 macallan /* 647 1.5 macallan * XXX 648 1.5 macallan * some monitors lie about their maximum supported dot clock 649 1.5 macallan * by claiming to support modes which need a higher dot clock 650 1.5 macallan * than the stated maximum. 651 1.5 macallan * For sanity's sake we bump it to the highest dot clock we find 652 1.5 macallan * in the list of supported modes 653 1.5 macallan */ 654 1.5 macallan for (i = 0; i < edid->edid_nmodes; i++) 655 1.5 macallan if (edid->edid_modes[i].dot_clock > max_dotclock) 656 1.5 macallan max_dotclock = edid->edid_modes[i].dot_clock; 657 1.5 macallan 658 1.16 mlelstv #ifdef _KERNEL 659 1.12 skrll aprint_debug("max_dotclock according to supported modes: %d\n", 660 1.5 macallan max_dotclock); 661 1.16 mlelstv #endif 662 1.5 macallan 663 1.5 macallan mhz = (max_dotclock + 999) / 1000; 664 1.5 macallan 665 1.5 macallan if (edid->edid_have_range) { 666 1.5 macallan if (mhz > edid->edid_range.er_max_clock) 667 1.5 macallan edid->edid_range.er_max_clock = mhz; 668 1.5 macallan } else 669 1.5 macallan edid->edid_range.er_max_clock = mhz; 670 1.5 macallan 671 1.2 gdamore return 0; 672 1.1 gdamore } 673 1.1 gdamore 674