1/* 2 * Copyright 2009 Red Hat, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software") 6 * to deal in the software without restriction, including without limitation 7 * on the rights to use, copy, modify, merge, publish, distribute, sub 8 * license, and/or sell copies of the Software, and to permit persons to whom 9 * them Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTIBILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER 19 * IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: 23 * Adam Jackson <ajax@redhat.com> 24 */ 25 26#include "xorg-config.h" 27#include "xf86.h" 28#include "xf86Modes.h" 29#include "xf86str.h" 30#include "edid.h" 31#include "xf86DDC.h" 32 33typedef void (*did_proc)(int scrnIndex, unsigned char *data, void *closure); 34 35#define DID_PRODUCT_ID 0x00 36#define DID_DISPLAY_PARAMETERS 0x01 37#define DID_COLOR_INFO 0x02 38#define DID_TIMING_1_DETAILED 0x03 39#define DID_TIMING_2_DETAILED 0x04 40#define DID_TIMING_3_SHORT 0x05 41#define DID_TIMING_4_DMT 0x06 42#define DID_TIMING_VESA 0x07 43#define DID_TIMING_CEA 0x08 44#define DID_TIMING_RANGE_LIMITS 0x09 45#define DID_PRODUCT_SERIAL 0x0A 46#define DID_ASCII_STRING 0x0B 47#define DID_DISPLAY_DEVICE 0x0C 48#define DID_POWER_SEQUENCING 0x0D 49#define DID_TRANSFER_INFO 0x0E 50#define DID_DISPLAY_INTERFACE 0x0F 51#define DID_STEREO 0x10 52#define DID_VENDOR 0x7F 53 54#define extract_le16(x, i) ((x[i+1] << 8) + (x[i])) 55#define extract_le24(x, i) ((x[i+2] << 16) + (x[i+1] << 8) + (x[i])) 56 57static DisplayModePtr 58modeCalloc(void) 59{ 60 return calloc(1, sizeof(DisplayModeRec)); 61} 62 63/* 64 * How awesome is it to have two detailed timing formats, neither of which 65 * are compatible with the format in EDID? So awesome. 66 */ 67 68static void 69didDetailedTiming1(int i, unsigned char *x, MonPtr mon) 70{ 71 DisplayModePtr m = modeCalloc(); 72 73 if (!m) 74 return; 75 76 m->Clock = extract_le24(x, 0); 77 78 m->HDisplay = extract_le16(x, 4); 79 m->HSyncStart = m->HDisplay + (extract_le16(x, 8) & 0x7f); 80 m->HSyncEnd = m->HSyncStart + extract_le16(x, 10); 81 m->HTotal = m->HDisplay + extract_le16(x, 6); 82 m->Flags |= (x[9] & 0x80) ? V_PHSYNC : V_NHSYNC; 83 84 m->VDisplay = extract_le16(x, 12); 85 m->VSyncStart = m->VDisplay + (extract_le16(x, 16) & 0x7f); 86 m->VSyncEnd = m->VSyncStart + extract_le16(x, 18); 87 m->VTotal = m->VDisplay + extract_le16(x, 14); 88 m->Flags |= (x[17] & 0x80) ? V_PVSYNC : V_NVSYNC; 89 90 m->type = M_T_DRIVER; 91 if (x[3] & 0x80) 92 m->type |= M_T_PREFERRED; 93 94 /* XXX double check handling of this */ 95 if (x[3] & 0x10) 96 m->Flags |= V_INTERLACE; 97 98 mon->Modes = xf86ModesAdd(mon->Modes, m); 99} 100 101/* XXX no sync bits. what to do? */ 102static void 103didDetailedTiming2(int i, unsigned char *x, MonPtr mon) 104{ 105 DisplayModePtr mode = modeCalloc(); 106 107 if (!mode) 108 return; 109 110 mode->Clock = extract_le24(x, 0); 111 112 /* horiz sizes are in character cells, not pixels, hence * 8 */ 113 mode->HDisplay = ((extract_le16(x, 4) & 0x01ff) + 1) * 8; 114 mode->HSyncStart = mode->HDisplay + (((x[6] & 0xf0) >> 4) + 1) * 8; 115 mode->HSyncEnd = mode->HSyncStart + ((x[6] & 0x0f) + 1) * 8; 116 mode->HTotal = mode->HDisplay + ((x[5] >> 1) + 1) * 8; 117 118 mode->VDisplay = extract_le16(x, 7) & 0x07ff; 119 mode->VSyncStart = mode->VDisplay + (x[10] >> 4) + 1; 120 mode->VSyncEnd = mode->VSyncStart + (x[10] & 0x0f) + 1; 121 mode->VTotal = mode->VDisplay + x[9]; 122 123 mode->status = M_T_DRIVER; 124 if (x[3] & 0x80) 125 mode->status |= M_T_PREFERRED; 126 127 /* XXX double check handling of this */ 128 if (x[3] & 0x10) 129 mode->Flags |= V_INTERLACE; 130 131 mon->Modes = xf86ModesAdd(mon->Modes, mode); 132} 133 134static void 135didShortTiming(int i, unsigned char *x, MonPtr mon) 136{ 137 DisplayModePtr m; 138 int w, h, r; 139 140 w = (x[1] + 1) * 8; 141 switch (x[0] & 0x0f) { 142 case 0: 143 h = w; 144 break; 145 case 1: 146 h = (w * 4) / 5; 147 break; 148 case 2: 149 h = (w * 3) / 4; 150 break; 151 case 3: 152 h = (w * 9) / 15; 153 break; 154 case 4: 155 h = (w * 9) / 16; 156 break; 157 case 5: 158 h = (w * 10) / 16; 159 break; 160 default: 161 return; 162 } 163 r = (x[2] & 0x7f) + 1; 164 165 m = xf86CVTMode(w, h, r, !!(x[0] & 0x10), !!(x[2] & 0x80)); 166 167 m->type = M_T_DRIVER; 168 if (x[0] & 0x80) 169 m->type |= M_T_PREFERRED; 170 171 mon->Modes = xf86ModesAdd(mon->Modes, m); 172} 173 174static void 175didDMTTiming(int i, unsigned char *x, void *closure) 176{ 177 MonPtr mon = closure; 178 179 mon->Modes = xf86ModesAdd(mon->Modes, 180 xf86DuplicateMode(DMTModes + *x)); 181} 182 183#define RB 1 184#define INT 2 185static const struct did_dmt { 186 short w, h, r, f; 187} did_dmt[] = { 188 /* byte 3 */ 189 { 640, 350, 85, 0 }, 190 { 640, 400, 85, 0 }, 191 { 720, 400, 85, 0 }, 192 { 640, 480, 60, 0 }, 193 { 640, 480, 72, 0 }, 194 { 640, 480, 75, 0 }, 195 { 640, 480, 85, 0 }, 196 { 800, 600, 56, 0 }, 197 /* byte 4 */ 198 { 800, 600, 60, 0 }, 199 { 800, 600, 72, 0 }, 200 { 800, 600, 75, 0 }, 201 { 800, 600, 85, 0 }, 202 { 800, 600, 120, RB }, 203 { 848, 480, 60, 0 }, 204 { 1024, 768, 43, INT }, 205 { 1024, 768, 60, 0 }, 206 /* byte 5 */ 207 { 1024, 768, 70, 0 }, 208 { 1024, 768, 75, 0 }, 209 { 1024, 768, 85, 0 }, 210 { 1024, 768, 120, RB }, 211 { 1152, 864, 75, 0 }, 212 { 1280, 768, 60, RB }, 213 { 1280, 768, 60, 0 }, 214 { 1280, 768, 75, 0 }, 215 /* byte 6 */ 216 { 1280, 768, 85, 0 }, 217 { 1280, 768, 120, RB }, 218 { 1280, 800, 60, RB }, 219 { 1280, 800, 60, 0 }, 220 { 1280, 800, 75, 0 }, 221 { 1280, 800, 85, 0 }, 222 { 1280, 800, 120, RB }, 223 { 1280, 960, 60, 0 }, 224 /* byte 7 */ 225 { 1280, 960, 85, 0 }, 226 { 1280, 960, 120, RB }, 227 { 1280, 1024, 60, 0 }, 228 { 1280, 1024, 75, 0 }, 229 { 1280, 1024, 85, 0 }, 230 { 1280, 1024, 120, RB }, 231 { 1360, 768, 60, 0 }, 232 { 1360, 768, 120, RB }, 233 /* byte 8 */ 234 { 1400, 1050, 60, RB }, 235 { 1400, 1050, 60, 0 }, 236 { 1400, 1050, 75, 0 }, 237 { 1400, 1050, 85, 0 }, 238 { 1400, 1050, 120, RB }, 239 { 1440, 900, 60, RB }, 240 { 1440, 900, 60, 0 }, 241 { 1440, 900, 75, 0 }, 242 /* byte 9 */ 243 { 1440, 900, 85, 0 }, 244 { 1440, 900, 120, RB }, 245 { 1600, 1200, 60, 0 }, 246 { 1600, 1200, 65, 0 }, 247 { 1600, 1200, 70, 0 }, 248 { 1600, 1200, 75, 0 }, 249 { 1600, 1200, 85, 0 }, 250 { 1600, 1200, 120, RB }, 251 /* byte a */ 252 { 1680, 1050, 60, RB }, 253 { 1680, 1050, 60, 0 }, 254 { 1680, 1050, 75, 0 }, 255 { 1680, 1050, 85, 0 }, 256 { 1680, 1050, 120, RB }, 257 { 1792, 1344, 60, 0 }, 258 { 1792, 1344, 75, 0 }, 259 { 1792, 1344, 120, RB }, 260 /* byte b */ 261 { 1856, 1392, 60, 0 }, 262 { 1856, 1392, 75, 0 }, 263 { 1856, 1392, 120, RB }, 264 { 1920, 1200, 60, RB }, 265 { 1920, 1200, 60, 0 }, 266 { 1920, 1200, 75, 0 }, 267 { 1920, 1200, 85, 0 }, 268 { 1920, 1200, 120, RB }, 269 /* byte c */ 270 { 1920, 1440, 60, 0 }, 271 { 1920, 1440, 75, 0 }, 272 { 1920, 1440, 120, RB }, 273 { 2560, 1600, 60, RB }, 274 { 2560, 1600, 60, 0 }, 275 { 2560, 1600, 75, 0 }, 276 { 2560, 1600, 85, 0 }, 277 { 2560, 1600, 120, RB }, 278}; 279 280static void 281didVesaTiming(int scrn, unsigned char *x, MonPtr mon) 282{ 283 int i, j; 284 285 x += 3; 286 287 for (i = 0; i < 10; i++) 288 for (j = 0; j < 8; j++) 289 if (x[i] & (1 << j)) { 290 const struct did_dmt *d = &(did_dmt[i * 8 + j]); 291 if (d->f == INT) 292 continue; 293 mon->Modes = xf86ModesAdd(mon->Modes, 294 FindDMTMode(d->w, d->h, d->r, 295 d->f == RB)); 296 } 297 298} 299 300static void 301handleDisplayIDBlock(int scrnIndex, unsigned char *x, void *closure) 302{ 303 MonPtr mon = closure; 304 305 switch (x[0]) { 306 case DID_DISPLAY_PARAMETERS: 307 /* w/h are in decimillimeters */ 308 mon->widthmm = (extract_le16(x, 3) + 5) / 10; 309 mon->heightmm = (extract_le16(x, 5) + 5) / 10; 310 /* XXX pixel count, feature flags, gamma, aspect, color depth */ 311 break; 312 313 case DID_TIMING_RANGE_LIMITS: 314 { 315 int n; 316 317 mon->maxPixClock = max(mon->maxPixClock, extract_le24(x, 6) * 10); 318 319 n = mon->nHsync++; 320 if (n < MAX_HSYNC) { 321 mon->hsync[n].lo = x[9]; 322 mon->hsync[n].hi = x[10]; 323 } else { 324 n = MAX_HSYNC; 325 } 326 n = mon->nVrefresh++; 327 if (n < MAX_VREFRESH) { 328 mon->vrefresh[n].lo = x[13]; 329 mon->vrefresh[n].hi = x[14]; 330 } else { 331 n = MAX_VREFRESH; 332 } 333 break; 334 } 335 336 case DID_TIMING_1_DETAILED: 337 { 338 int i; 339 for (i = 0; i < x[2]; i += 20) 340 didDetailedTiming1(scrnIndex, x + i + 3, mon); 341 break; 342 } 343 344 case DID_TIMING_2_DETAILED: 345 { 346 int i; 347 for (i = 0; i < x[2]; i += 11) 348 didDetailedTiming2(scrnIndex, x + i + 3, mon); 349 break; 350 } 351 352 case DID_TIMING_3_SHORT: 353 { 354 int i; 355 for (i = 0; i < x[2]; i += 3) 356 didShortTiming(scrnIndex, x + i + 3, mon); 357 break; 358 } 359 360 case DID_TIMING_4_DMT: 361 { 362 int i; 363 for (i = 0; i < x[2]; i++) 364 didDMTTiming(scrnIndex, x + i + 3, mon); 365 break; 366 } 367 368 case DID_TIMING_VESA: 369 didVesaTiming(scrnIndex, x, mon); 370 break; 371 372 /* XXX pixel format, ar, orientation, subpixel, dot pitch, bit depth */ 373 case DID_DISPLAY_DEVICE: 374 375 /* XXX interface, links, color encoding, ss, drm */ 376 case DID_DISPLAY_INTERFACE: 377 378 /* XXX stereo */ 379 case DID_STEREO: 380 381 /* nothing interesting in these */ 382 case DID_COLOR_INFO: 383 case DID_PRODUCT_SERIAL: 384 case DID_ASCII_STRING: 385 case DID_POWER_SEQUENCING: 386 case DID_TRANSFER_INFO: 387 case DID_VENDOR: 388 break; 389 390 /* warn about anything else */ 391 default: 392 xf86DrvMsg(scrnIndex, X_WARNING, 393 "Unknown DisplayID block type %hx\n", x[0]); 394 break; 395 } 396} 397 398static void 399forEachDisplayIDBlock(int scrnIndex, unsigned char *did, did_proc proc, 400 void *closure) 401{ 402 int num_extensions = did[3]; 403 int section_size = did[1]; 404 unsigned char *block; 405 406 do { 407 if ((did[0] & 0xf0) != 0x10) /* not 1.x, abort */ 408 return; 409 /* XXX also, checksum */ 410 411 block = did + 4; 412 413 while (section_size > 0) { 414 int block_size = (block[2] + 2); 415 416 proc(scrnIndex, block, closure); 417 418 section_size -= block_size; 419 block += block_size; 420 } 421 422 did += (did[1] + 5); 423 } while (num_extensions--); 424} 425 426/* 427 * Fill out MonPtr with xf86MonPtr information. 428 */ 429void 430xf86DisplayIDMonitorSet(int scrnIndex, MonPtr mon, xf86MonPtr DDC) 431{ 432 if (!mon || !DDC) 433 return; 434 435 mon->DDC = DDC; 436 437 forEachDisplayIDBlock(scrnIndex, DDC->rawData, handleDisplayIDBlock, mon); 438} 439