kolibrios-fun/drivers/video/radeonhd/rhd_monitor.c

1336 lines
37 KiB
C
Raw Normal View History

/*
* Copyright 2007, 2008 Luc Verhaegen <lverhaegen@novell.com>
* Copyright 2007, 2008 Matthias Hopf <mhopf@novell.com>
* Copyright 2007, 2008 Egbert Eich <eich@novell.com>
* 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; i<num; i++) {
if (p->Header.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; i<num; i++)
{
if (p->Header.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;i<len;i++)
{
not_null |= block[i];
result += block[i];
}
if (result & 0xFF) DBG(dbgprintf("DDC checksum not correct\n"));
if (!not_null) DBG(dbgprintf("DDC read all Null\n"));
/* catch the trivial case where all bytes are 0 */
if (!not_null) return 1;
return (result&0xFF);
}
static unsigned char *
DDCRead_DDC2(RHDPtr rhdPtr, I2CBusPtr pBus, int start, int len)
{
I2CDevPtr dev;
unsigned char W_Buffer[2];
int w_bytes;
unsigned char *R_Buffer;
int i;
RHDFUNC(rhdPtr);
// xf86LoaderReqSymLists(i2cSymbols, NULL);
if (!(dev = xf86I2CFindDev(pBus, 0x00A0)))
{
dev = xf86CreateI2CDevRec();
dev->DevName = "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; i<RETRIES; i++)
{
if (xf86I2CWriteRead(dev, W_Buffer,w_bytes, R_Buffer,len))
{
if (!DDC_checksum(R_Buffer,len))
return R_Buffer;
else
DBG(dbgprintf("Checksum error in EDID block\n"));
}
else
DBG(dbgprintf("Error reading EDID block\n"));
}
xf86DestroyI2CDevRec(dev,TRUE);
free(R_Buffer);
return NULL;
}
static unsigned char*
EDID1Read_DDC2(RHDPtr rhdPtr, I2CBusPtr pBus)
{
return DDCRead_DDC2(rhdPtr, pBus, 0, EDID1_LEN);
}
xf86MonPtr
xf86DoEDID_DDC2(RHDPtr rhdPtr, I2CBusPtr pBus)
{
unsigned char *EDID_block = NULL;
unsigned char *VDIF_Block = NULL;
xf86MonPtr tmp = NULL;
RHDFUNC(rhdPtr);
EDID_block = EDID1Read_DDC2(rhdPtr,pBus);
if (EDID_block)
{
tmp = xf86InterpretEDID(rhdPtr,EDID_block);
}
else
{
DBG(dbgprintf("No EDID block returned\n"));
return NULL;
}
if (!tmp)
{
DBG(dbgprintf("Cannot interpret EDID block\n"));
return tmp;
}
DBG(dbgprintf("Sections to follow: %d\n",tmp->no_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<STD_TIMINGS;i++)
{
if (t[i].hsize > 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<DET_TIMINGS;i++)
{
switch (m[i].type)
{
case DT:
print_detailed_timings(rhdPtr,&m[i].section.d_timings);
break;
case DS_SERIAL:
DBG(dbgprintf("Serial No: %s\n",m[i].section.serial));
break;
case DS_ASCII_STR:
DBG(dbgprintf(" %s\n",m[i].section.ascii_data));
break;
case DS_NAME:
DBG(dbgprintf("Monitor name: %s\n",m[i].section.name));
break;
case DS_RANGES:
DBG(dbgprintf("Ranges: V min: %i V max: %i Hz, H min: %i H max: %i kHz,",
m[i].section.ranges.min_v, m[i].section.ranges.max_v,
m[i].section.ranges.min_h, m[i].section.ranges.max_h));
if (m[i].section.ranges.max_clock != 0)
DBG(dbgprintf(" PixClock max %i MHz\n",m[i].section.ranges.max_clock));
else
DBG(dbgprintf("\n"));
if (m[i].section.ranges.gtf_2nd_f > 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;i<STD_TIMINGS;i++)
{
if (VALID_TIMING)
{
r[i].hsize = HSIZE1;
VSIZE1(r[i].vsize);
r[i].refresh = REFRESH_R;
r[i].id = STD_TIMING_ID;
}
else
{
r[i].hsize = r[i].vsize = r[i].refresh = r[i].id = 0;
}
NEXT_STD_TIMING;
}
}
static void
get_dt_md_section(Uchar *c, struct edid_version *ver,
struct detailed_monitor_section *det_mon)
{
int i;
for (i=0;i<DET_TIMINGS;i++) {
if (ver->version == 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;
}