1706f2543Smrg/* 2706f2543Smrg * Copyright 2009 Red Hat, Inc. 3706f2543Smrg * 4706f2543Smrg * Permission is hereby granted, free of charge, to any person obtaining a 5706f2543Smrg * copy of this software and associated documentation files (the "Software") 6706f2543Smrg * to deal in the software without restriction, including without limitation 7706f2543Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub 8706f2543Smrg * license, and/or sell copies of the Software, and to permit persons to whom 9706f2543Smrg * them Software is furnished to do so, subject to the following conditions: 10706f2543Smrg * 11706f2543Smrg * The above copyright notice and this permission notice (including the next 12706f2543Smrg * paragraph) shall be included in all copies or substantial portions of the 13706f2543Smrg * Software. 14706f2543Smrg * 15706f2543Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16706f2543Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTIBILITY, 17706f2543Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18706f2543Smrg * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER 19706f2543Smrg * IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF OR IN 20706f2543Smrg * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21706f2543Smrg * 22706f2543Smrg * Authors: 23706f2543Smrg * Adam Jackson <ajax@redhat.com> 24706f2543Smrg */ 25706f2543Smrg 26706f2543Smrg#include "xorg-config.h" 27706f2543Smrg#include "xf86.h" 28706f2543Smrg#include "xf86Modes.h" 29706f2543Smrg#include "xf86str.h" 30706f2543Smrg#include "edid.h" 31706f2543Smrg#include "xf86DDC.h" 32706f2543Smrg 33706f2543Smrgtypedef void (*did_proc)(int scrnIndex, unsigned char *data, void *closure); 34706f2543Smrg 35706f2543Smrg#define DID_PRODUCT_ID 0x00 36706f2543Smrg#define DID_DISPLAY_PARAMETERS 0x01 37706f2543Smrg#define DID_COLOR_INFO 0x02 38706f2543Smrg#define DID_TIMING_1_DETAILED 0x03 39706f2543Smrg#define DID_TIMING_2_DETAILED 0x04 40706f2543Smrg#define DID_TIMING_3_SHORT 0x05 41706f2543Smrg#define DID_TIMING_4_DMT 0x06 42706f2543Smrg#define DID_TIMING_VESA 0x07 43706f2543Smrg#define DID_TIMING_CEA 0x08 44706f2543Smrg#define DID_TIMING_RANGE_LIMITS 0x09 45706f2543Smrg#define DID_PRODUCT_SERIAL 0x0A 46706f2543Smrg#define DID_ASCII_STRING 0x0B 47706f2543Smrg#define DID_DISPLAY_DEVICE 0x0C 48706f2543Smrg#define DID_POWER_SEQUENCING 0x0D 49706f2543Smrg#define DID_TRANSFER_INFO 0x0E 50706f2543Smrg#define DID_DISPLAY_INTERFACE 0x0F 51706f2543Smrg#define DID_STEREO 0x10 52706f2543Smrg#define DID_VENDOR 0x7F 53706f2543Smrg 54706f2543Smrg#define extract_le16(x, i) ((x[i+1] << 8) + (x[i])) 55706f2543Smrg#define extract_le24(x, i) ((x[i+2] << 16) + (x[i+1] << 8) + (x[i])) 56706f2543Smrg 57706f2543Smrgstatic DisplayModePtr 58706f2543SmrgmodeCalloc(void) 59706f2543Smrg{ 60706f2543Smrg return calloc(1, sizeof(DisplayModeRec)); 61706f2543Smrg} 62706f2543Smrg 63706f2543Smrg/* 64706f2543Smrg * How awesome is it to have two detailed timing formats, neither of which 65706f2543Smrg * are compatible with the format in EDID? So awesome. 66706f2543Smrg */ 67706f2543Smrg 68706f2543Smrgstatic void 69706f2543SmrgdidDetailedTiming1(int i, unsigned char *x, MonPtr mon) 70706f2543Smrg{ 71706f2543Smrg DisplayModePtr m = modeCalloc(); 72706f2543Smrg 73706f2543Smrg if (!m) 74706f2543Smrg return; 75706f2543Smrg 76706f2543Smrg m->Clock = extract_le24(x, 0); 77706f2543Smrg 78706f2543Smrg m->HDisplay = extract_le16(x, 4); 79706f2543Smrg m->HSyncStart = m->HDisplay + (extract_le16(x, 8) & 0x7f); 80706f2543Smrg m->HSyncEnd = m->HSyncStart + extract_le16(x, 10); 81706f2543Smrg m->HTotal = m->HDisplay + extract_le16(x, 6); 82706f2543Smrg m->Flags |= (x[9] & 0x80) ? V_PHSYNC : V_NHSYNC; 83706f2543Smrg 84706f2543Smrg m->VDisplay = extract_le16(x, 12); 85706f2543Smrg m->VSyncStart = m->VDisplay + (extract_le16(x, 16) & 0x7f); 86706f2543Smrg m->VSyncEnd = m->VSyncStart + extract_le16(x, 18); 87706f2543Smrg m->VTotal = m->VDisplay + extract_le16(x, 14); 88706f2543Smrg m->Flags |= (x[17] & 0x80) ? V_PVSYNC : V_NVSYNC; 89706f2543Smrg 90706f2543Smrg m->type = M_T_DRIVER; 91706f2543Smrg if (x[3] & 0x80) 92706f2543Smrg m->type |= M_T_PREFERRED; 93706f2543Smrg 94706f2543Smrg /* XXX double check handling of this */ 95706f2543Smrg if (x[3] & 0x10) 96706f2543Smrg m->Flags |= V_INTERLACE; 97706f2543Smrg 98706f2543Smrg mon->Modes = xf86ModesAdd(mon->Modes, m); 99706f2543Smrg} 100706f2543Smrg 101706f2543Smrg/* XXX no sync bits. what to do? */ 102706f2543Smrgstatic void 103706f2543SmrgdidDetailedTiming2(int i, unsigned char *x, MonPtr mon) 104706f2543Smrg{ 105706f2543Smrg DisplayModePtr mode = modeCalloc(); 106706f2543Smrg 107706f2543Smrg if (!mode) 108706f2543Smrg return; 109706f2543Smrg 110706f2543Smrg mode->Clock = extract_le24(x, 0); 111706f2543Smrg 112706f2543Smrg /* horiz sizes are in character cells, not pixels, hence * 8 */ 113706f2543Smrg mode->HDisplay = ((extract_le16(x, 4) & 0x01ff) + 1) * 8; 114706f2543Smrg mode->HSyncStart = mode->HDisplay + (((x[6] & 0xf0) >> 4) + 1) * 8; 115706f2543Smrg mode->HSyncEnd = mode->HSyncStart + ((x[6] & 0x0f) + 1) * 8; 116706f2543Smrg mode->HTotal = mode->HDisplay + ((x[5] >> 1) + 1) * 8; 117706f2543Smrg 118706f2543Smrg mode->VDisplay = extract_le16(x, 7) & 0x07ff; 119706f2543Smrg mode->VSyncStart = mode->VDisplay + (x[10] >> 4) + 1; 120706f2543Smrg mode->VSyncEnd = mode->VSyncStart + (x[10] & 0x0f) + 1; 121706f2543Smrg mode->VTotal = mode->VDisplay + x[9]; 122706f2543Smrg 123706f2543Smrg mode->status = M_T_DRIVER; 124706f2543Smrg if (x[3] & 0x80) 125706f2543Smrg mode->status |= M_T_PREFERRED; 126706f2543Smrg 127706f2543Smrg /* XXX double check handling of this */ 128706f2543Smrg if (x[3] & 0x10) 129706f2543Smrg mode->Flags |= V_INTERLACE; 130706f2543Smrg 131706f2543Smrg mon->Modes = xf86ModesAdd(mon->Modes, mode); 132706f2543Smrg} 133706f2543Smrg 134706f2543Smrgstatic void 135706f2543SmrgdidShortTiming(int i, unsigned char *x, MonPtr mon) 136706f2543Smrg{ 137706f2543Smrg DisplayModePtr m; 138706f2543Smrg int w, h, r; 139706f2543Smrg 140706f2543Smrg w = (x[1] + 1) * 8; 141706f2543Smrg switch (x[0] & 0x0f) { 142706f2543Smrg case 0: 143706f2543Smrg h = w; 144706f2543Smrg break; 145706f2543Smrg case 1: 146706f2543Smrg h = (w * 4) / 5; 147706f2543Smrg break; 148706f2543Smrg case 2: 149706f2543Smrg h = (w * 3) / 4; 150706f2543Smrg break; 151706f2543Smrg case 3: 152706f2543Smrg h = (w * 9) / 15; 153706f2543Smrg break; 154706f2543Smrg case 4: 155706f2543Smrg h = (w * 9) / 16; 156706f2543Smrg break; 157706f2543Smrg case 5: 158706f2543Smrg h = (w * 10) / 16; 159706f2543Smrg break; 160706f2543Smrg default: 161706f2543Smrg return; 162706f2543Smrg } 163706f2543Smrg r = (x[2] & 0x7f) + 1; 164706f2543Smrg 165706f2543Smrg m = xf86CVTMode(w, h, r, !!(x[0] & 0x10), !!(x[2] & 0x80)); 166706f2543Smrg 167706f2543Smrg m->type = M_T_DRIVER; 168706f2543Smrg if (x[0] & 0x80) 169706f2543Smrg m->type |= M_T_PREFERRED; 170706f2543Smrg 171706f2543Smrg mon->Modes = xf86ModesAdd(mon->Modes, m); 172706f2543Smrg} 173706f2543Smrg 174706f2543Smrgstatic void 175706f2543SmrgdidDMTTiming(int i, unsigned char *x, void *closure) 176706f2543Smrg{ 177706f2543Smrg MonPtr mon = closure; 178706f2543Smrg 179706f2543Smrg mon->Modes = xf86ModesAdd(mon->Modes, 180706f2543Smrg xf86DuplicateMode(DMTModes + *x)); 181706f2543Smrg} 182706f2543Smrg 183706f2543Smrg#define RB 1 184706f2543Smrg#define INT 2 185706f2543Smrgstatic const struct did_dmt { 186706f2543Smrg short w, h, r, f; 187706f2543Smrg} did_dmt[] = { 188706f2543Smrg /* byte 3 */ 189706f2543Smrg { 640, 350, 85, 0 }, 190706f2543Smrg { 640, 400, 85, 0 }, 191706f2543Smrg { 720, 400, 85, 0 }, 192706f2543Smrg { 640, 480, 60, 0 }, 193706f2543Smrg { 640, 480, 72, 0 }, 194706f2543Smrg { 640, 480, 75, 0 }, 195706f2543Smrg { 640, 480, 85, 0 }, 196706f2543Smrg { 800, 600, 56, 0 }, 197706f2543Smrg /* byte 4 */ 198706f2543Smrg { 800, 600, 60, 0 }, 199706f2543Smrg { 800, 600, 72, 0 }, 200706f2543Smrg { 800, 600, 75, 0 }, 201706f2543Smrg { 800, 600, 85, 0 }, 202706f2543Smrg { 800, 600, 120, RB }, 203706f2543Smrg { 848, 480, 60, 0 }, 204706f2543Smrg { 1024, 768, 43, INT }, 205706f2543Smrg { 1024, 768, 60, 0 }, 206706f2543Smrg /* byte 5 */ 207706f2543Smrg { 1024, 768, 70, 0 }, 208706f2543Smrg { 1024, 768, 75, 0 }, 209706f2543Smrg { 1024, 768, 85, 0 }, 210706f2543Smrg { 1024, 768, 120, RB }, 211706f2543Smrg { 1152, 864, 75, 0 }, 212706f2543Smrg { 1280, 768, 60, RB }, 213706f2543Smrg { 1280, 768, 60, 0 }, 214706f2543Smrg { 1280, 768, 75, 0 }, 215706f2543Smrg /* byte 6 */ 216706f2543Smrg { 1280, 768, 85, 0 }, 217706f2543Smrg { 1280, 768, 120, RB }, 218706f2543Smrg { 1280, 800, 60, RB }, 219706f2543Smrg { 1280, 800, 60, 0 }, 220706f2543Smrg { 1280, 800, 75, 0 }, 221706f2543Smrg { 1280, 800, 85, 0 }, 222706f2543Smrg { 1280, 800, 120, RB }, 223706f2543Smrg { 1280, 960, 60, 0 }, 224706f2543Smrg /* byte 7 */ 225706f2543Smrg { 1280, 960, 85, 0 }, 226706f2543Smrg { 1280, 960, 120, RB }, 227706f2543Smrg { 1280, 1024, 60, 0 }, 228706f2543Smrg { 1280, 1024, 75, 0 }, 229706f2543Smrg { 1280, 1024, 85, 0 }, 230706f2543Smrg { 1280, 1024, 120, RB }, 231706f2543Smrg { 1360, 768, 60, 0 }, 232706f2543Smrg { 1360, 768, 120, RB }, 233706f2543Smrg /* byte 8 */ 234706f2543Smrg { 1400, 1050, 60, RB }, 235706f2543Smrg { 1400, 1050, 60, 0 }, 236706f2543Smrg { 1400, 1050, 75, 0 }, 237706f2543Smrg { 1400, 1050, 85, 0 }, 238706f2543Smrg { 1400, 1050, 120, RB }, 239706f2543Smrg { 1440, 900, 60, RB }, 240706f2543Smrg { 1440, 900, 60, 0 }, 241706f2543Smrg { 1440, 900, 75, 0 }, 242706f2543Smrg /* byte 9 */ 243706f2543Smrg { 1440, 900, 85, 0 }, 244706f2543Smrg { 1440, 900, 120, RB }, 245706f2543Smrg { 1600, 1200, 60, 0 }, 246706f2543Smrg { 1600, 1200, 65, 0 }, 247706f2543Smrg { 1600, 1200, 70, 0 }, 248706f2543Smrg { 1600, 1200, 75, 0 }, 249706f2543Smrg { 1600, 1200, 85, 0 }, 250706f2543Smrg { 1600, 1200, 120, RB }, 251706f2543Smrg /* byte a */ 252706f2543Smrg { 1680, 1050, 60, RB }, 253706f2543Smrg { 1680, 1050, 60, 0 }, 254706f2543Smrg { 1680, 1050, 75, 0 }, 255706f2543Smrg { 1680, 1050, 85, 0 }, 256706f2543Smrg { 1680, 1050, 120, RB }, 257706f2543Smrg { 1792, 1344, 60, 0 }, 258706f2543Smrg { 1792, 1344, 75, 0 }, 259706f2543Smrg { 1792, 1344, 120, RB }, 260706f2543Smrg /* byte b */ 261706f2543Smrg { 1856, 1392, 60, 0 }, 262706f2543Smrg { 1856, 1392, 75, 0 }, 263706f2543Smrg { 1856, 1392, 120, RB }, 264706f2543Smrg { 1920, 1200, 60, RB }, 265706f2543Smrg { 1920, 1200, 60, 0 }, 266706f2543Smrg { 1920, 1200, 75, 0 }, 267706f2543Smrg { 1920, 1200, 85, 0 }, 268706f2543Smrg { 1920, 1200, 120, RB }, 269706f2543Smrg /* byte c */ 270706f2543Smrg { 1920, 1440, 60, 0 }, 271706f2543Smrg { 1920, 1440, 75, 0 }, 272706f2543Smrg { 1920, 1440, 120, RB }, 273706f2543Smrg { 2560, 1600, 60, RB }, 274706f2543Smrg { 2560, 1600, 60, 0 }, 275706f2543Smrg { 2560, 1600, 75, 0 }, 276706f2543Smrg { 2560, 1600, 85, 0 }, 277706f2543Smrg { 2560, 1600, 120, RB }, 278706f2543Smrg}; 279706f2543Smrg 280706f2543Smrgstatic void 281706f2543SmrgdidVesaTiming(int scrn, unsigned char *x, MonPtr mon) 282706f2543Smrg{ 283706f2543Smrg int i, j; 284706f2543Smrg 285706f2543Smrg x += 3; 286706f2543Smrg 287706f2543Smrg for (i = 0; i < 10; i++) 288706f2543Smrg for (j = 0; j < 8; j++) 289706f2543Smrg if (x[i] & (1 << j)) { 290706f2543Smrg const struct did_dmt *d = &(did_dmt[i * 8 + j]); 291706f2543Smrg if (d->f == INT) 292706f2543Smrg continue; 293706f2543Smrg mon->Modes = xf86ModesAdd(mon->Modes, 294706f2543Smrg FindDMTMode(d->w, d->h, d->r, 295706f2543Smrg d->f == RB)); 296706f2543Smrg } 297706f2543Smrg 298706f2543Smrg} 299706f2543Smrg 300706f2543Smrgstatic void 301706f2543SmrghandleDisplayIDBlock(int scrnIndex, unsigned char *x, void *closure) 302706f2543Smrg{ 303706f2543Smrg MonPtr mon = closure; 304706f2543Smrg 305706f2543Smrg switch (x[0]) { 306706f2543Smrg case DID_DISPLAY_PARAMETERS: 307706f2543Smrg /* w/h are in decimillimeters */ 308706f2543Smrg mon->widthmm = (extract_le16(x, 3) + 5) / 10; 309706f2543Smrg mon->heightmm = (extract_le16(x, 5) + 5) / 10; 310706f2543Smrg /* XXX pixel count, feature flags, gamma, aspect, color depth */ 311706f2543Smrg break; 312706f2543Smrg 313706f2543Smrg case DID_TIMING_RANGE_LIMITS: 314706f2543Smrg { 315706f2543Smrg int n; 316706f2543Smrg 317706f2543Smrg mon->maxPixClock = max(mon->maxPixClock, extract_le24(x, 6) * 10); 318706f2543Smrg 319706f2543Smrg n = mon->nHsync++; 320706f2543Smrg if (n < MAX_HSYNC) { 321706f2543Smrg mon->hsync[n].lo = x[9]; 322706f2543Smrg mon->hsync[n].hi = x[10]; 323706f2543Smrg } else { 324706f2543Smrg n = MAX_HSYNC; 325706f2543Smrg } 326706f2543Smrg n = mon->nVrefresh++; 327706f2543Smrg if (n < MAX_VREFRESH) { 328706f2543Smrg mon->vrefresh[n].lo = x[13]; 329706f2543Smrg mon->vrefresh[n].hi = x[14]; 330706f2543Smrg } else { 331706f2543Smrg n = MAX_VREFRESH; 332706f2543Smrg } 333706f2543Smrg break; 334706f2543Smrg } 335706f2543Smrg 336706f2543Smrg case DID_TIMING_1_DETAILED: 337706f2543Smrg { 338706f2543Smrg int i; 339706f2543Smrg for (i = 0; i < x[2]; i += 20) 340706f2543Smrg didDetailedTiming1(scrnIndex, x + i + 3, mon); 341706f2543Smrg break; 342706f2543Smrg } 343706f2543Smrg 344706f2543Smrg case DID_TIMING_2_DETAILED: 345706f2543Smrg { 346706f2543Smrg int i; 347706f2543Smrg for (i = 0; i < x[2]; i += 11) 348706f2543Smrg didDetailedTiming2(scrnIndex, x + i + 3, mon); 349706f2543Smrg break; 350706f2543Smrg } 351706f2543Smrg 352706f2543Smrg case DID_TIMING_3_SHORT: 353706f2543Smrg { 354706f2543Smrg int i; 355706f2543Smrg for (i = 0; i < x[2]; i += 3) 356706f2543Smrg didShortTiming(scrnIndex, x + i + 3, mon); 357706f2543Smrg break; 358706f2543Smrg } 359706f2543Smrg 360706f2543Smrg case DID_TIMING_4_DMT: 361706f2543Smrg { 362706f2543Smrg int i; 363706f2543Smrg for (i = 0; i < x[2]; i++) 364706f2543Smrg didDMTTiming(scrnIndex, x + i + 3, mon); 365706f2543Smrg break; 366706f2543Smrg } 367706f2543Smrg 368706f2543Smrg case DID_TIMING_VESA: 369706f2543Smrg didVesaTiming(scrnIndex, x, mon); 370706f2543Smrg break; 371706f2543Smrg 372706f2543Smrg /* XXX pixel format, ar, orientation, subpixel, dot pitch, bit depth */ 373706f2543Smrg case DID_DISPLAY_DEVICE: 374706f2543Smrg 375706f2543Smrg /* XXX interface, links, color encoding, ss, drm */ 376706f2543Smrg case DID_DISPLAY_INTERFACE: 377706f2543Smrg 378706f2543Smrg /* XXX stereo */ 379706f2543Smrg case DID_STEREO: 380706f2543Smrg 381706f2543Smrg /* nothing interesting in these */ 382706f2543Smrg case DID_COLOR_INFO: 383706f2543Smrg case DID_PRODUCT_SERIAL: 384706f2543Smrg case DID_ASCII_STRING: 385706f2543Smrg case DID_POWER_SEQUENCING: 386706f2543Smrg case DID_TRANSFER_INFO: 387706f2543Smrg case DID_VENDOR: 388706f2543Smrg break; 389706f2543Smrg 390706f2543Smrg /* warn about anything else */ 391706f2543Smrg default: 392706f2543Smrg xf86DrvMsg(scrnIndex, X_WARNING, 393706f2543Smrg "Unknown DisplayID block type %hx\n", x[0]); 394706f2543Smrg break; 395706f2543Smrg } 396706f2543Smrg} 397706f2543Smrg 398706f2543Smrgstatic void 399706f2543SmrgforEachDisplayIDBlock(int scrnIndex, unsigned char *did, did_proc proc, 400706f2543Smrg void *closure) 401706f2543Smrg{ 402706f2543Smrg int num_extensions = did[3]; 403706f2543Smrg int section_size = did[1]; 404706f2543Smrg unsigned char *block; 405706f2543Smrg 406706f2543Smrg do { 407706f2543Smrg if ((did[0] & 0xf0) != 0x10) /* not 1.x, abort */ 408706f2543Smrg return; 409706f2543Smrg /* XXX also, checksum */ 410706f2543Smrg 411706f2543Smrg block = did + 4; 412706f2543Smrg 413706f2543Smrg while (section_size > 0) { 414706f2543Smrg int block_size = (block[2] + 2); 415706f2543Smrg 416706f2543Smrg proc(scrnIndex, block, closure); 417706f2543Smrg 418706f2543Smrg section_size -= block_size; 419706f2543Smrg block += block_size; 420706f2543Smrg } 421706f2543Smrg 422706f2543Smrg did += (did[1] + 5); 423706f2543Smrg } while (num_extensions--); 424706f2543Smrg} 425706f2543Smrg 426706f2543Smrg/* 427706f2543Smrg * Fill out MonPtr with xf86MonPtr information. 428706f2543Smrg */ 429706f2543Smrgvoid 430706f2543Smrgxf86DisplayIDMonitorSet(int scrnIndex, MonPtr mon, xf86MonPtr DDC) 431706f2543Smrg{ 432706f2543Smrg if (!mon || !DDC) 433706f2543Smrg return; 434706f2543Smrg 435706f2543Smrg mon->DDC = DDC; 436706f2543Smrg 437706f2543Smrg forEachDisplayIDBlock(scrnIndex, DDC->rawData, handleDisplayIDBlock, mon); 438706f2543Smrg} 439