/* * Copyright 2007, 2008 Luc Verhaegen * Copyright 2007, 2008 Matthias Hopf * Copyright 2007, 2008 Egbert Eich * Copyright 2007, 2008 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #define _PARSE_EDID_ #include "common.h" #include "rhd.h" #include "edid.h" #include "xf86DDC.h" #include "rhd_connector.h" #include "rhd_modes.h" #include "rhd_monitor.h" #ifdef ATOM_BIOS # include "rhd_atombios.h" #endif /* From rhd_edid.c */ void RHDMonitorEDIDSet(struct rhdMonitor *Monitor, xf86MonPtr EDID); /* * */ void RHDMonitorPrint(struct rhdMonitor *Monitor) { int i; xf86Msg(X_NONE, " Bandwidth: %dMHz\n", Monitor->Bandwidth / 1000); xf86Msg(X_NONE, " Horizontal timing:\n"); for (i = 0; i < Monitor->numHSync; i++) xf86Msg(X_NONE, " %3.1f - %3.1fkHz\n", Monitor->HSync[i].lo, Monitor->HSync[i].hi); xf86Msg(X_NONE, " Vertical timing:\n"); for (i = 0; i < Monitor->numVRefresh; i++) xf86Msg(X_NONE, " %3.1f - %3.1fHz\n", Monitor->VRefresh[i].lo, Monitor->VRefresh[i].hi); xf86Msg(X_NONE, " DPI: %dx%d\n", Monitor->xDpi, Monitor->yDpi); if (Monitor->ReducedAllowed) xf86Msg(X_NONE, " Allows reduced blanking.\n"); if (Monitor->UseFixedModes) xf86Msg(X_NONE, " Uses Fixed Modes.\n"); if (!Monitor->Modes) xf86Msg(X_NONE, " No modes are provided.\n"); else { DisplayModePtr Mode; xf86Msg(X_NONE, " Attached modes:\n"); for (Mode = Monitor->Modes; Mode; Mode = Mode->next) { xf86Msg(X_NONE, " "); RHDPrintModeline(Mode); } } } #if 0 /* * */ static struct rhdMonitor * rhdMonitorFromConfig(int scrnIndex, MonPtr Config) { struct rhdMonitor *Monitor; DisplayModePtr Mode; int i; Monitor = xnfcalloc(sizeof(struct rhdMonitor), 1); Monitor->Name = xnfstrdup(Config->id); Monitor->scrnIndex = scrnIndex; if (Config->nHsync) { Monitor->numHSync = Config->nHsync; for (i = 0; i < Config->nHsync; i++) { Monitor->HSync[i].lo = Config->hsync[i].lo; Monitor->HSync[i].hi = Config->hsync[i].hi; } } else if (!Monitor->numHSync) { Monitor->numHSync = 3; Monitor->HSync[0].lo = 31.5; Monitor->HSync[0].hi = 31.5; Monitor->HSync[1].lo = 35.15; Monitor->HSync[1].hi = 35.15; Monitor->HSync[2].lo = 35.5; Monitor->HSync[2].hi = 35.5; } if (Config->nVrefresh) { Monitor->numVRefresh = Config->nVrefresh; for (i = 0; i < Config->nVrefresh; i++) { Monitor->VRefresh[i].lo = Config->vrefresh[i].lo; Monitor->VRefresh[i].hi = Config->vrefresh[i].hi; } } else if (!Monitor->numVRefresh) { Monitor->numVRefresh = 1; Monitor->VRefresh[0].lo = 50; Monitor->VRefresh[0].hi = 61; } #ifdef MONREC_HAS_REDUCED if (Config->reducedblanking) Monitor->ReducedAllowed = TRUE; #endif #ifdef MONREC_HAS_BANDWIDTH if (Config->maxPixClock) Monitor->Bandwidth = Config->maxPixClock; #endif for (Mode = Config->Modes; Mode; Mode = Mode->next) Monitor->Modes = RHDModesAdd(Monitor->Modes, RHDModeCopy(Mode)); return Monitor; } #endif /* * */ static struct rhdMonitor * rhdMonitorFromDefault(RHDPtr rhdPtr) { struct rhdMonitor *Monitor; DisplayModePtr Mode; Monitor = xnfcalloc(sizeof(struct rhdMonitor), 1); Monitor->Name = strdup("Default (SVGA)"); Monitor->scrnIndex = rhdPtr->scrnIndex; /* timing for pathetic 14" svga monitors */ Monitor->numHSync = 3; Monitor->HSync[0].lo = 31.5; Monitor->HSync[0].hi = 31.5; Monitor->HSync[1].lo = 35.15; Monitor->HSync[1].hi = 35.15; Monitor->HSync[2].lo = 35.5; Monitor->HSync[2].hi = 35.5; Monitor->numVRefresh = 1; Monitor->VRefresh[0].lo = 50; Monitor->VRefresh[0].hi = 61; return Monitor; } /* * This function tries to handle a configured monitor correctly. * * This either can be forced through the option, or is used when * no monitors are autodetected. */ void RHDConfigMonitorSet(RHDPtr rhdPtr, Bool UseConfig) { int i; for (i = 0; i < RHD_CONNECTORS_MAX; i++) if (rhdPtr->Connector[i] && rhdPtr->Connector[i]->Monitor) break; if (i == RHD_CONNECTORS_MAX) xf86DrvMsg(scrnIndex, X_INFO, "No monitors autodetected; " "attempting to work around this.\n"); if (i == RHD_CONNECTORS_MAX) { rhdPtr->ConfigMonitor = rhdMonitorFromDefault(rhdPtr); DBG(dbgprintf("Created monitor from default: \"%s\":\n", rhdPtr->ConfigMonitor->Name)); RHDMonitorPrint(rhdPtr->ConfigMonitor); }; } /* * Make sure that we keep only a single mode in our list. This mode should * hopefully match our panel at native resolution correctly. */ static void rhdPanelEDIDModesFilter(struct rhdMonitor *Monitor) { DisplayModeRec *Best = Monitor->Modes, *Mode, *Temp; RHDFUNC(Monitor); if (!Best || !Best->next) return; /* don't bother */ /* don't go for preferred, just take the biggest */ for (Mode = Best->next; Mode; Mode = Mode->next) { if (((Best->HDisplay <= Mode->HDisplay) && (Best->VDisplay < Mode->VDisplay)) || ((Best->HDisplay < Mode->HDisplay) && (Best->VDisplay <= Mode->VDisplay))) Best = Mode; } xf86DrvMsg(Monitor->scrnIndex, X_INFO, "Monitor \"%s\": Using Mode \"%s\"" " for native resolution.\n", Monitor->Name, Best->name); /* kill all other modes */ Mode = Monitor->Modes; while (Mode) { Temp = Mode->next; if (Mode != Best) { RHDDebug(Monitor->scrnIndex, "Monitor \"%s\": Discarding Mode \"%s\"\n", Monitor->Name, Mode->name); xfree(Mode->name); xfree(Mode); } Mode = Temp; } Best->next = NULL; Best->prev = NULL; Best->type |= M_T_PREFERRED; Monitor->NativeMode = Best; Monitor->Modes = Monitor->NativeMode; Monitor->numHSync = 1; Monitor->HSync[0].lo = Best->HSync; Monitor->HSync[0].hi = Best->HSync; Monitor->numVRefresh = 1; Monitor->VRefresh[0].lo = Best->VRefresh; Monitor->VRefresh[0].hi = Best->VRefresh; Monitor->Bandwidth = Best->Clock; } /* * */ void rhdMonitorPrintEDID(struct rhdMonitor *Monitor, xf86MonPtr EDID) { xf86DrvMsg(EDID->scrnIndex, X_INFO, "EDID data for %s\n", Monitor->Name); xf86PrintEDID(EDID); } /* * Panels are the most complicated case we need to handle here. * Information can come from several places, and we need to make sure * that we end up with only the native resolution in our table. */ static struct rhdMonitor * rhdMonitorPanel(struct rhdConnector *Connector) { struct rhdMonitor *Monitor; DisplayModeRec *Mode = NULL; xf86MonPtr EDID = NULL; RHDFUNC(Connector); /* has priority over AtomBIOS EDID */ if (Connector->DDC) EDID = xf86DoEDID_DDC2(Connector->scrnIndex, Connector->DDC); #ifdef ATOM_BIOS { RHDPtr rhdPtr = (RHDPtr)Connector->scrnIndex; AtomBiosArgRec data; AtomBiosResult Result; Result = RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS, ATOMBIOS_GET_PANEL_MODE, &data); if (Result == ATOM_SUCCESS) { Mode = data.mode; Mode->type |= M_T_PREFERRED; } if (!EDID) { Result = RHDAtomBiosFunc(rhdPtr,rhdPtr->atomBIOS, ATOMBIOS_GET_PANEL_EDID, &data); if (Result == ATOM_SUCCESS) EDID = xf86InterpretEDID(rhdPtr, data.EDIDBlock); } } #endif Monitor = xnfcalloc(sizeof(struct rhdMonitor), 1); Monitor->scrnIndex = Connector->scrnIndex; Monitor->EDID = EDID; if (Mode) { Monitor->Name = xstrdup("LVDS Panel"); Monitor->Modes = RHDModesAdd(Monitor->Modes, Mode); Monitor->NativeMode = Mode; Monitor->numHSync = 1; Monitor->HSync[0].lo = Mode->HSync; Monitor->HSync[0].hi = Mode->HSync; Monitor->numVRefresh = 1; Monitor->VRefresh[0].lo = Mode->VRefresh; Monitor->VRefresh[0].hi = Mode->VRefresh; Monitor->Bandwidth = Mode->SynthClock; /* Clueless atombios does give us a mode, but doesn't give us a * DPI or a size. It is just perfect, right? */ if (EDID) { if (EDID->features.hsize) Monitor->xDpi = (Mode->HDisplay * 2.54) / ((float) EDID->features.hsize) + 0.5; if (EDID->features.vsize) Monitor->yDpi = (Mode->VDisplay * 2.54) / ((float) EDID->features.vsize) + 0.5; } } else if (EDID) { RHDMonitorEDIDSet(Monitor, EDID); rhdPanelEDIDModesFilter(Monitor); } else { xf86DrvMsg(Connector->scrnIndex, X_ERROR, "%s: No panel mode information found.\n", __func__); xfree(Monitor); return NULL; } /* panel should be driven at native resolution only. */ Monitor->UseFixedModes = TRUE; Monitor->ReducedAllowed = TRUE; if (EDID) rhdMonitorPrintEDID(Monitor, EDID); return Monitor; } /* * rhdMonitorTV(): get TV modes. Currently we can only get this from AtomBIOS. */ static struct rhdMonitor * rhdMonitorTV(struct rhdConnector *Connector) { struct rhdMonitor *Monitor = NULL; #ifdef ATOM_BIOS RHDPtr rhdPtr = RHDPTRI(Connector); DisplayModeRec *Mode = NULL; AtomBiosArgRec arg; RHDFUNC(Connector); arg.tvMode = rhdPtr->tvMode; if (RHDAtomBiosFunc(Connector->scrnIndex, rhdPtr->atomBIOS, ATOM_ANALOG_TV_MODE, &arg) != ATOM_SUCCESS) return NULL; Mode = arg.mode; Mode->type |= M_T_PREFERRED; Monitor = xnfcalloc(sizeof(struct rhdMonitor), 1); Monitor->scrnIndex = Connector->scrnIndex; Monitor->EDID = NULL; Monitor->Name = xstrdup("TV"); Monitor->Modes = RHDModesAdd(Monitor->Modes, Mode); Monitor->NativeMode= Mode; Monitor->numHSync = 1; Monitor->HSync[0].lo = Mode->HSync; Monitor->HSync[0].hi = Mode->HSync; Monitor->numVRefresh = 1; Monitor->VRefresh[0].lo = Mode->VRefresh; Monitor->VRefresh[0].hi = Mode->VRefresh; Monitor->Bandwidth = Mode->SynthClock; /* TV should be driven at native resolution only. */ Monitor->UseFixedModes = TRUE; Monitor->ReducedAllowed = FALSE; /* * hack: the TV encoder takes care of that. * The mode that goes in isn't what comes out. */ Mode->Flags &= ~(V_INTERLACE); #endif return Monitor; } /* * */ struct rhdMonitor * RHDMonitorInit(struct rhdConnector *Connector) { struct rhdMonitor *Monitor = NULL; RHDFUNC(Connector); if (Connector->Type == RHD_CONNECTOR_PANEL) Monitor = rhdMonitorPanel(Connector); else if (Connector->Type == RHD_CONNECTOR_TV) Monitor = rhdMonitorTV(Connector); else if (Connector->DDC) { xf86MonPtr EDID = xf86DoEDID_DDC2(Connector->scrnIndex, Connector->DDC); if (EDID) { Monitor = xnfcalloc(sizeof(struct rhdMonitor), 1); Monitor->scrnIndex = Connector->scrnIndex; Monitor->EDID = EDID; Monitor->NativeMode = NULL; RHDMonitorEDIDSet(Monitor, EDID); rhdMonitorPrintEDID(Monitor, EDID); } } return Monitor; } /* * */ void RHDMonitorDestroy(struct rhdMonitor *Monitor) { DisplayModePtr Mode, Next; for (Mode = Monitor->Modes; Mode;) { Next = Mode->next; xfree(Mode->name); xfree(Mode); Mode = Next; } if (Monitor->EDID) xfree(Monitor->EDID->rawData); xfree(Monitor->EDID); xfree(Monitor->Name); xfree(Monitor); } static unsigned char * VDIFRead(RHDPtr rhdPtr, I2CBusPtr pBus, int start); #define RETRIES 4 static xf86VdifLimitsPtr* get_limits(CARD8 *c); static xf86VdifGammaPtr* get_gamma(CARD8 *c); static xf86VdifTimingPtr* get_timings(CARD8 *c); xf86vdifPtr xf86InterpretVdif(CARD8 *c) { xf86VdifPtr p = (xf86VdifPtr)c; xf86vdifPtr vdif; int i; unsigned long l = 0; if (c == NULL) return NULL; if (p->VDIFId[0] != 'V' || p->VDIFId[1] != 'D' || p->VDIFId[2] != 'I' || p->VDIFId[3] != 'F') return NULL; for ( i = 12; i < p->FileLength; i++) l += c[i]; if ( l != p->Checksum) return NULL; vdif = malloc(sizeof(xf86vdif)); vdif->vdif = p; vdif->limits = get_limits(c); vdif->timings = get_timings(c); vdif->gamma = get_gamma(c); vdif->strings = VDIF_STRING(((xf86VdifPtr)c),0); free(c); return vdif; } static xf86VdifLimitsPtr* get_limits(CARD8 *c) { int num, i, j; xf86VdifLimitsPtr *pp; xf86VdifLimitsPtr p; num = ((xf86VdifPtr)c)->NumberOperationalLimits; pp = malloc(sizeof(xf86VdifLimitsPtr) * (num+1)); p = VDIF_OPERATIONAL_LIMITS(((xf86VdifPtr)c)); j = 0; for ( i = 0; iHeader.ScnTag == VDIF_OPERATIONAL_LIMITS_TAG) pp[j++] = p; VDIF_NEXT_OPERATIONAL_LIMITS(p); } pp[j] = NULL; return pp; } static xf86VdifGammaPtr* get_gamma(CARD8 *c) { int num, i, j; xf86VdifGammaPtr *pp; xf86VdifGammaPtr p; num = ((xf86VdifPtr)c)->NumberOptions; pp = malloc(sizeof(xf86VdifGammaPtr) * (num+1)); p = (xf86VdifGammaPtr)VDIF_OPTIONS(((xf86VdifPtr)c)); j = 0; for ( i = 0; iHeader.ScnTag == VDIF_GAMMA_TABLE_TAG) pp[j++] = p; VDIF_NEXT_OPTIONS(p); } pp[j] = NULL; return pp; } static xf86VdifTimingPtr* get_timings(CARD8 *c) { int num, num_limits; int i,j,k; xf86VdifLimitsPtr lp; xf86VdifTimingPtr *pp; xf86VdifTimingPtr p; num = ((xf86VdifPtr)c)->NumberOperationalLimits; lp = VDIF_OPERATIONAL_LIMITS(((xf86VdifPtr)c)); num_limits = 0; for (i = 0; i < num; i++) { if (lp->Header.ScnTag == VDIF_OPERATIONAL_LIMITS_TAG) num_limits += lp->NumberPreadjustedTimings; VDIF_NEXT_OPERATIONAL_LIMITS(lp); } pp = malloc(sizeof(xf86VdifTimingPtr) * (num_limits+1)); j = 0; lp = VDIF_OPERATIONAL_LIMITS(((xf86VdifPtr) c)); for (i = 0; i < num; i++) { p = VDIF_PREADJUSTED_TIMING(lp); for (k = 0; k < lp->NumberPreadjustedTimings; k++) { if (p->Header.ScnTag == VDIF_PREADJUSTED_TIMING_TAG) pp[j++] = p; VDIF_NEXT_PREADJUSTED_TIMING(p); } VDIF_NEXT_OPERATIONAL_LIMITS(lp); } pp[j] = NULL; return pp; } int DDC_checksum(unsigned char *block, int len) { int i, result = 0; int not_null = 0; for (i=0;iDevName = "ddc2"; dev->SlaveAddr = 0xA0; dev->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */ dev->StartTimeout = 550; dev->BitTimeout = 40; dev->ByteTimeout = 40; dev->AcknTimeout = 40; dev->pI2CBus = pBus; if (!xf86I2CDevInit(dev)) { DBG(dbgprintf("No DDC2 device\n")); return NULL; } } if (start < 0x100) { w_bytes = 1; W_Buffer[0] = start; } else { w_bytes = 2; W_Buffer[0] = start & 0xFF; W_Buffer[1] = (start & 0xFF00) >> 8; } R_Buffer = calloc(1,sizeof(unsigned char)* (len)); if( !R_Buffer) { DBG(dbgprintf("R_Buffer = NULL\n")); return NULL; }; for (i=0; ino_sections)); VDIF_Block = VDIFRead(rhdPtr, pBus, EDID1_LEN * (tmp->no_sections + 1)); tmp->vdif = xf86InterpretVdif(VDIF_Block); return tmp; } static unsigned char* VDIFRead(RHDPtr rhdPtr, I2CBusPtr pBus, int start) { unsigned char * Buffer, *v_buffer = NULL, *v_bufferp = NULL; int i, num = 0; /* read VDIF length in 64 byte blocks */ Buffer = DDCRead_DDC2(rhdPtr, pBus,start,64); if (Buffer == NULL) return NULL; DBG(dbgprintf("number of 64 bit blocks: %i\n",Buffer[0])); if ((num = Buffer[0]) > 0) v_buffer = v_bufferp = malloc(sizeof(unsigned char) * 64 * num); for (i = 0; i < num; i++) { Buffer = DDCRead_DDC2(rhdPtr, pBus,start,64); if (Buffer == NULL) { free (v_buffer); return NULL; } memcpy(v_bufferp,Buffer,63); /* 64th byte is checksum */ free(Buffer); v_bufferp += 63; } return v_buffer; } static void print_vendor(RHDPtr rhdPtr, struct vendor *); static void print_version(RHDPtr rhdPtr, struct edid_version *); static void print_display(RHDPtr rhdPtr, struct disp_features *, struct edid_version *); static void print_established_timings(RHDPtr rhdPtr, struct established_timings *); static void print_std_timings(RHDPtr rhdPtr, struct std_timings *); static void print_detailed_monitor_section(RHDPtr rhdPtr, struct detailed_monitor_section *); static void print_detailed_timings(RHDPtr rhdPtr, struct detailed_timings *); static void print_input_features(RHDPtr rhdPtr, struct disp_features *); static void print_dpms_features(RHDPtr rhdPtr, struct disp_features *, struct edid_version *v); static void print_whitepoint(RHDPtr rhdPtr, struct disp_features *); static void print_number_sections(RHDPtr rhdPtr, int); xf86MonPtr xf86PrintEDID(xf86MonPtr m) { if (!(m)) return NULL; print_vendor(m->rhdPtr,&m->vendor); print_version(m->rhdPtr,&m->ver); print_display(m->rhdPtr,&m->features, &m->ver); print_established_timings(m->rhdPtr,&m->timings1); print_std_timings(m->rhdPtr,m->timings2); print_detailed_monitor_section(m->rhdPtr,m->det_mon); print_number_sections(m->rhdPtr,m->no_sections); return m; } static void print_vendor(RHDPtr rhdPtr, struct vendor *c) { DBG(dbgprintf("Manufacturer: %s Model: %x Serial#: %u\n", (char *)&c->name, c->prod_id, c->serial)); DBG(dbgprintf("Year: %u Week: %u\n", c->year, c->week)); } static void print_version(RHDPtr rhdPtr, struct edid_version *c) { DBG(dbgprintf("EDID Version: %u.%u\n",c->version,c->revision)); } static void print_display(RHDPtr rhdPtr, struct disp_features *disp, struct edid_version *version) { print_input_features(rhdPtr,disp); DBG(dbgprintf("Max H-Image Size [cm]: ")); if (disp->hsize) DBG(dbgprintf("horiz.: %i ",disp->hsize)); else DBG(dbgprintf("H-Size may change, ")); if (disp->vsize) DBG(dbgprintf("vert.: %i\n",disp->vsize)); else DBG(dbgprintf("V-Size may change\n")); DBG(dbgprintf("Gamma: %.2f\n", (double)disp->gamma)); print_dpms_features(rhdPtr,disp,version); print_whitepoint(rhdPtr,disp); } static void print_input_features(RHDPtr rhdPtr, struct disp_features *c) { if (DIGITAL(c->input_type)) { DBG(dbgprintf("Digital Display Input\n")); if (DFP1(c->input_dfp)) DBG(dbgprintf("DFP 1.x compatible TMDS\n")); } else { DBG(dbgprintf("Analog Display Input, ")); DBG(dbgprintf("Input Voltage Level: ")); switch (c->input_voltage) { case V070: DBG(dbgprintf("0.700/0.300 V\n")); break; case V071: DBG(dbgprintf("0.714/0.286 V\n")); break; case V100: DBG(dbgprintf("1.000/0.400 V\n")); break; case V007: DBG(dbgprintf("0.700/0.700 V\n")); break; default: DBG(dbgprintf("undefined\n")); } if (SIG_SETUP(c->input_setup)) DBG(dbgprintf("Signal levels configurable\n")); DBG(dbgprintf("Sync:")); if (SEP_SYNC(c->input_sync)) DBG(dbgprintf(" Separate")); if (COMP_SYNC(c->input_sync)) DBG(dbgprintf(" Composite")); if (SYNC_O_GREEN(c->input_sync)) DBG(dbgprintf(" SyncOnGreen")); if (SYNC_SERR(c->input_sync)) DBG(dbgprintf("Serration on. " "V.Sync Pulse req. if CompSync or SyncOnGreen\n")); else DBG(dbgprintf("\n")); } } static void print_dpms_features(RHDPtr rhdPtr, struct disp_features *c, struct edid_version *v) { if (c->dpms) { DBG(dbgprintf("DPMS capabilities:")); if (DPMS_STANDBY(c->dpms)) DBG(dbgprintf(" StandBy")); if (DPMS_SUSPEND(c->dpms)) DBG(dbgprintf(" Suspend")); if (DPMS_OFF(c->dpms)) DBG(dbgprintf(" Off")); } else DBG(dbgprintf("No DPMS capabilities specified")); switch (c->display_type) { case DISP_MONO: DBG(dbgprintf("; Monochorome/GrayScale Display\n")); break; case DISP_RGB: DBG(dbgprintf("; RGB/Color Display\n")); break; case DISP_MULTCOLOR: DBG(dbgprintf("; Non RGB Multicolor Display\n")); break; default: DBG(dbgprintf("\n")); break; } if (STD_COLOR_SPACE(c->msc)) DBG(dbgprintf("Default color space is primary color space\n")); if (PREFERRED_TIMING_MODE(c->msc)) DBG(dbgprintf("First detailed timing is preferred mode\n")); else if (v->version == 1 && v->revision >= 3) DBG(dbgprintf("First detailed timing not preferred " "mode in violation of standard!")); if (GFT_SUPPORTED(c->msc)) DBG(dbgprintf("GTF timings supported\n")); } static void print_whitepoint(RHDPtr rhdPtr, struct disp_features *disp) { DBG(dbgprintf("redX: %.3f redY: %.3f ", (double)disp->redx,(double)disp->redy)); DBG(dbgprintf("greenX: %.3f greenY: %.3f\n", (double)disp->greenx,(double)disp->greeny)); DBG(dbgprintf("blueX: %.3f blueY: %.3f ", (double)disp->bluex,(double)disp->bluey)); DBG(dbgprintf("whiteX: %.3f whiteY: %.3f\n", (double)disp->whitex,(double)disp->whitey)); } static void print_established_timings(RHDPtr rhdPtr, struct established_timings *t) { unsigned char c; if (t->t1 || t->t2 || t->t_manu) DBG(dbgprintf("Supported VESA Video Modes:\n")); c=t->t1; if (c&0x80) DBG(dbgprintf("720x400@70Hz\n")); if (c&0x40) DBG(dbgprintf("720x400@88Hz\n")); if (c&0x20) DBG(dbgprintf("640x480@60Hz\n")); if (c&0x10) DBG(dbgprintf("640x480@67Hz\n")); if (c&0x08) DBG(dbgprintf("640x480@72Hz\n")); if (c&0x04) DBG(dbgprintf("640x480@75Hz\n")); if (c&0x02) DBG(dbgprintf("800x600@56Hz\n")); if (c&0x01) DBG(dbgprintf("800x600@60Hz\n")); c=t->t2; if (c&0x80) DBG(dbgprintf("800x600@72Hz\n")); if (c&0x40) DBG(dbgprintf("800x600@75Hz\n")); if (c&0x20) DBG(dbgprintf("832x624@75Hz\n")); if (c&0x10) DBG(dbgprintf("1024x768@87Hz (interlaced)\n")); if (c&0x08) DBG(dbgprintf("1024x768@60Hz\n")); if (c&0x04) DBG(dbgprintf("1024x768@70Hz\n")); if (c&0x02) DBG(dbgprintf("1024x768@75Hz\n")); if (c&0x01) DBG(dbgprintf("1280x1024@75Hz\n")); c=t->t_manu; if (c&0x80) DBG(dbgprintf("1152x870@75Hz\n")); DBG(dbgprintf("Manufacturer's mask: %X\n",c&0x7F)); } static void print_std_timings(RHDPtr rhdPtr, struct std_timings *t) { int i; char done = 0; for (i=0;i 256) /* sanity check */ { if (!done) { DBG(dbgprintf("Supported Future Video Modes:\n")); done = 1; } DBG(dbgprintf("#%d: hsize: %i vsize %i refresh: %i vid: %i\n", i, t[i].hsize, t[i].vsize, t[i].refresh, t[i].id)); } } } static void print_detailed_monitor_section(RHDPtr rhdPtr, struct detailed_monitor_section *m) { int i,j; for (i=0;i 0) DBG(dbgprintf(" 2nd GTF parameters: f: %i kHz " "c: %i m: %i k %i j %i\n", m[i].section.ranges.gtf_2nd_f, m[i].section.ranges.gtf_2nd_c, m[i].section.ranges.gtf_2nd_m, m[i].section.ranges.gtf_2nd_k, m[i].section.ranges.gtf_2nd_j)); break; case DS_STD_TIMINGS: for (j = 0; j<5; j++) DBG(dbgprintf("#%i: hsize: %i vsize %i refresh: %i " "vid: %i\n",i,m[i].section.std_t[i].hsize, m[i].section.std_t[j].vsize,m[i].section.std_t[j].refresh, m[i].section.std_t[j].id)); break; case DS_WHITE_P: for (j = 0; j<2; j++) if (m[i].section.wp[j].index != 0) DBG(dbgprintf("White point %i: whiteX: %f, whiteY: %f; gamma: %f\n", m[i].section.wp[j].index,(double)m[i].section.wp[j].white_x, (double)m[i].section.wp[j].white_y, (double)m[i].section.wp[j].white_gamma)); break; case DS_DUMMY: default: break; } } } static void print_detailed_timings(RHDPtr rhdPtr, struct detailed_timings *t) { if (t->clock > 15000000) /* sanity check */ { DBG(dbgprintf("Supported additional Video Mode:\n")); DBG(dbgprintf("clock: %.1f MHz ",(double)t->clock/1000000.0)); DBG(dbgprintf("Image Size: %i x %i mm\n",t->h_size,t->v_size)); DBG(dbgprintf("h_active: %i h_sync: %i h_sync_end %i h_blank_end %i ", t->h_active, t->h_sync_off + t->h_active, t->h_sync_off + t->h_sync_width + t->h_active, t->h_active + t->h_blanking)); DBG(dbgprintf("h_border: %i\n",t->h_border)); DBG(dbgprintf("v_active: %i v_sync: %i v_sync_end %i v_blanking: %i ", t->v_active, t->v_sync_off + t->v_active, t->v_sync_off + t->v_sync_width + t->v_active, t->v_active + t->v_blanking)); DBG(dbgprintf("v_border: %i\n",t->v_border)); if (IS_STEREO(t->stereo)) { DBG(dbgprintf("Stereo: ")); if (IS_RIGHT_STEREO(t->stereo)) { if (!t->stereo_1) DBG(dbgprintf("right channel on sync\n")); else DBG(dbgprintf("left channel on sync\n")); } else if (IS_LEFT_STEREO(t->stereo)) { if (!t->stereo_1) DBG(dbgprintf("right channel on even line\n")); else DBG(dbgprintf("left channel on evel line\n")); } if (IS_4WAY_STEREO(t->stereo)) { if (!t->stereo_1) DBG(dbgprintf("4-way interleaved\n")); else DBG(dbgprintf("side-by-side interleaved")); } } } } static void print_number_sections(RHDPtr rhdPtr, int num) { if (num) DBG(dbgprintf("Number of EDID sections to follow: %i\n",num)); } static void get_vendor_section(Uchar*, struct vendor *); static void get_version_section(Uchar*, struct edid_version *); static void get_display_section(Uchar*, struct disp_features *, struct edid_version *); static void get_established_timing_section(Uchar*, struct established_timings *); static void get_std_timing_section(Uchar*, struct std_timings *, struct edid_version *); static void get_dt_md_section(Uchar *, struct edid_version *, struct detailed_monitor_section *det_mon); static void copy_string(Uchar *, Uchar *); static void get_dst_timing_section(Uchar *, struct std_timings *, struct edid_version *); static void get_monitor_ranges(Uchar *, struct monitor_ranges *); static void get_whitepoint_section(Uchar *, struct whitePoints *); static void get_detailed_timing_section(Uchar*, struct detailed_timings *); static Bool validate_version(RHDPtr rhdPtr, struct edid_version *); xf86MonPtr xf86InterpretEDID(int scrnIndex, Uchar *block) { xf86MonPtr m; RHDPtr rhdPtr = (RHDPtr)scrnIndex; if (!block) return NULL; if (! (m = calloc(sizeof(xf86Monitor),1))) return NULL; m->rhdPtr = rhdPtr; m->rawData = block; get_vendor_section(SECTION(VENDOR_SECTION,block),&m->vendor); get_version_section(SECTION(VERSION_SECTION,block),&m->ver); if (!validate_version(rhdPtr, &m->ver)) goto error; get_display_section(SECTION(DISPLAY_SECTION,block),&m->features, &m->ver); get_established_timing_section(SECTION(ESTABLISHED_TIMING_SECTION,block), &m->timings1); get_std_timing_section(SECTION(STD_TIMING_SECTION,block),m->timings2, &m->ver); get_dt_md_section(SECTION(DET_TIMING_SECTION,block),&m->ver, m->det_mon); m->no_sections = (int)*(char *)SECTION(NO_EDID,block); return (m); error: free(m); return NULL; } static void get_vendor_section(Uchar *c, struct vendor *r) { r->name[0] = L1; r->name[1] = L2; r->name[2] = L3; r->name[3] = '\0'; r->prod_id = PROD_ID; r->serial = SERIAL_NO; r->week = WEEK; r->year = YEAR; } static void get_version_section(Uchar *c, struct edid_version *r) { r->version = VERSION; r->revision = REVISION; } static void get_display_section(Uchar *c, struct disp_features *r, struct edid_version *v) { r->input_type = INPUT_TYPE; if (!DIGITAL(r->input_type)) { r->input_voltage = INPUT_VOLTAGE; r->input_setup = SETUP; r->input_sync = SYNC; } else if (v->version > 1 || v->revision > 2) r->input_dfp = DFP; r->hsize = HSIZE_MAX; r->vsize = VSIZE_MAX; r->gamma = GAMMA; r->dpms = DPMS; r->display_type = DISPLAY_TYPE; r->msc = MSC; r->redx = REDX; r->redy = REDY; r->greenx = GREENX; r->greeny = GREENY; r->bluex = BLUEX; r->bluey = BLUEY; r->whitex = WHITEX; r->whitey = WHITEY; } static void get_established_timing_section(Uchar *c, struct established_timings *r) { r->t1 = T1; r->t2 = T2; r->t_manu = T_MANU; } static void get_std_timing_section(Uchar *c, struct std_timings *r, struct edid_version *v) { int i; for (i=0;iversion == 1 && ver->revision >= 1 && IS_MONITOR_DESC) { switch (MONITOR_DESC_TYPE) { case SERIAL_NUMBER: det_mon[i].type = DS_SERIAL; copy_string(c,det_mon[i].section.serial); break; case ASCII_STR: det_mon[i].type = DS_ASCII_STR; copy_string(c,det_mon[i].section.ascii_data); break; case MONITOR_RANGES: det_mon[i].type = DS_RANGES; get_monitor_ranges(c,&det_mon[i].section.ranges); break; case MONITOR_NAME: det_mon[i].type = DS_NAME; copy_string(c,det_mon[i].section.name); break; case ADD_COLOR_POINT: det_mon[i].type = DS_WHITE_P; get_whitepoint_section(c,det_mon[i].section.wp); break; case ADD_STD_TIMINGS: det_mon[i].type = DS_STD_TIMINGS; get_dst_timing_section(c,det_mon[i].section.std_t, ver); break; case ADD_DUMMY: det_mon[i].type = DS_DUMMY; break; } } else { det_mon[i].type = DT; get_detailed_timing_section(c,&det_mon[i].section.d_timings); } NEXT_DT_MD_SECTION; } } static void copy_string(Uchar *c, Uchar *s) { int i; c = c + 5; for (i = 0; (i < 13 && *c != 0x0A); i++) *(s++) = *(c++); *s = 0; while (i-- && (*--s == 0x20)) *s = 0; } static void get_dst_timing_section(Uchar *c, struct std_timings *t, struct edid_version *v) { int j; c = c + 5; for (j = 0; j < 5; j++) { t[j].hsize = HSIZE1; VSIZE1(t[j].vsize); t[j].refresh = REFRESH_R; t[j].id = STD_TIMING_ID; NEXT_STD_TIMING; } } static void get_monitor_ranges(Uchar *c, struct monitor_ranges *r) { r->min_v = MIN_V; r->max_v = MAX_V; r->min_h = MIN_H; r->max_h = MAX_H; r->max_clock = 0; if(MAX_CLOCK != 0xff) /* is specified? */ r->max_clock = MAX_CLOCK * 10; if (HAVE_2ND_GTF) { r->gtf_2nd_f = F_2ND_GTF; r->gtf_2nd_c = C_2ND_GTF; r->gtf_2nd_m = M_2ND_GTF; r->gtf_2nd_k = K_2ND_GTF; r->gtf_2nd_j = J_2ND_GTF; } else r->gtf_2nd_f = 0; } static void get_whitepoint_section(Uchar *c, struct whitePoints *wp) { wp[1].white_x = WHITEX1; wp[1].white_y = WHITEY1; wp[2].white_x = WHITEX2; wp[2].white_y = WHITEY2; wp[1].index = WHITE_INDEX1; wp[2].index = WHITE_INDEX2; wp[1].white_gamma = WHITE_GAMMA1; wp[2].white_gamma = WHITE_GAMMA2; } static void get_detailed_timing_section(Uchar *c, struct detailed_timings *r) { r->clock = PIXEL_CLOCK; r->h_active = H_ACTIVE; r->h_blanking = H_BLANK; r->v_active = V_ACTIVE; r->v_blanking = V_BLANK; r->h_sync_off = H_SYNC_OFF; r->h_sync_width = H_SYNC_WIDTH; r->v_sync_off = V_SYNC_OFF; r->v_sync_width = V_SYNC_WIDTH; r->h_size = H_SIZE; r->v_size = V_SIZE; r->h_border = H_BORDER; r->v_border = V_BORDER; r->interlaced = INTERLACED; r->stereo = STEREO; r->stereo_1 = STEREO1; r->sync = SYNC_T; r->misc = MISC; } static Bool validate_version(RHDPtr rhdPtr, struct edid_version *r) { if (r->version != 1) return FALSE; if (r->revision > 3) { DBG(dbgprintf("EDID Version 1.%d not yet supported\n",r->revision)); return FALSE; } return TRUE; }