merge
This commit is contained in:
commit
b4fcd2d68f
6 changed files with 570 additions and 482 deletions
414
sys/src/cmd/aux/vga/edid.c
Normal file
414
sys/src/cmd/aux/vga/edid.c
Normal file
|
@ -0,0 +1,414 @@
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <bio.h>
|
||||||
|
#include <ndb.h>
|
||||||
|
|
||||||
|
#include "pci.h"
|
||||||
|
#include "vga.h"
|
||||||
|
#include "edid.h"
|
||||||
|
|
||||||
|
static Modelist*
|
||||||
|
addmode(Modelist *l, Mode m)
|
||||||
|
{
|
||||||
|
int rr;
|
||||||
|
Modelist **lp;
|
||||||
|
|
||||||
|
rr = (m.frequency+m.ht*m.vt/2)/(m.ht*m.vt);
|
||||||
|
snprint(m.name, sizeof m.name, "%dx%d@%dHz", m.x, m.y, rr);
|
||||||
|
|
||||||
|
for(lp=&l; *lp; lp=&(*lp)->next){
|
||||||
|
if(strcmp((*lp)->name, m.name) == 0){
|
||||||
|
(*lp)->Mode = m;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*lp = alloc(sizeof(**lp));
|
||||||
|
(*lp)->Mode = m;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse VESA EDID information. Based on the VESA
|
||||||
|
* Extended Display Identification Data standard, Version 3,
|
||||||
|
* November 13, 1997. See /public/doc/vesa/edidv3.pdf.
|
||||||
|
*
|
||||||
|
* This only handles 128-byte EDID blocks. Until I find
|
||||||
|
* a monitor that produces 256-byte blocks, I'm not going
|
||||||
|
* to try to decode them.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Established timings block. There is a bitmap
|
||||||
|
* that says whether each mode is supported. Most
|
||||||
|
* of these have VESA definitions. Those that don't are marked
|
||||||
|
* as such, and we ignore them (the lookup fails).
|
||||||
|
*/
|
||||||
|
static char *estabtime[] = {
|
||||||
|
"720x400@70Hz", /* non-VESA: IBM, VGA */
|
||||||
|
"720x400@88Hz", /* non-VESA: IBM, XGA2 */
|
||||||
|
"640x480@60Hz",
|
||||||
|
"640x480@67Hz", /* non-VESA: Apple, Mac II */
|
||||||
|
"640x480@72Hz",
|
||||||
|
"640x480@75Hz",
|
||||||
|
"800x600@56Hz",
|
||||||
|
"800x600@60Hz",
|
||||||
|
|
||||||
|
"800x600@72Hz",
|
||||||
|
"800x600@75Hz",
|
||||||
|
"832x624@75Hz", /* non-VESA: Apple, Mac II */
|
||||||
|
"1024x768i@87Hz", /* non-VESA: IBM */
|
||||||
|
"1024x768@60Hz",
|
||||||
|
"1024x768@70Hz",
|
||||||
|
"1024x768@75Hz",
|
||||||
|
"1280x1024@75Hz",
|
||||||
|
|
||||||
|
"1152x870@75Hz", /* non-VESA: Apple, Mac II */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode the EDID detailed timing block. See pp. 20-21 of the standard.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
decodedtb(Mode *m, uchar *p)
|
||||||
|
{
|
||||||
|
int ha, hb, hso, hspw, rr, va, vb, vso, vspw;
|
||||||
|
/* int hbord, vbord, dxmm, dymm, hbord, vbord; */
|
||||||
|
|
||||||
|
memset(m, 0, sizeof *m);
|
||||||
|
|
||||||
|
m->frequency = ((p[1]<<8) | p[0]) * 10000;
|
||||||
|
|
||||||
|
ha = ((p[4] & 0xF0)<<4) | p[2]; /* horizontal active */
|
||||||
|
hb = ((p[4] & 0x0F)<<8) | p[3]; /* horizontal blanking */
|
||||||
|
va = ((p[7] & 0xF0)<<4) | p[5]; /* vertical active */
|
||||||
|
vb = ((p[7] & 0x0F)<<8) | p[6]; /* vertical blanking */
|
||||||
|
hso = ((p[11] & 0xC0)<<2) | p[8]; /* horizontal sync offset */
|
||||||
|
hspw = ((p[11] & 0x30)<<4) | p[9]; /* horizontal sync pulse width */
|
||||||
|
vso = ((p[11] & 0x0C)<<2) | ((p[10] & 0xF0)>>4); /* vertical sync offset */
|
||||||
|
vspw = ((p[11] & 0x03)<<4) | (p[10] & 0x0F); /* vertical sync pulse width */
|
||||||
|
|
||||||
|
/* dxmm = (p[14] & 0xF0)<<4) | p[12]; /* horizontal image size (mm) */
|
||||||
|
/* dymm = (p[14] & 0x0F)<<8) | p[13]; /* vertical image size (mm) */
|
||||||
|
/* hbord = p[15]; /* horizontal border (pixels) */
|
||||||
|
/* vbord = p[16]; /* vertical border (pixels) */
|
||||||
|
|
||||||
|
m->x = ha;
|
||||||
|
m->y = va;
|
||||||
|
|
||||||
|
m->ht = ha+hb;
|
||||||
|
m->shb = ha+hso;
|
||||||
|
m->ehb = ha+hso+hspw;
|
||||||
|
|
||||||
|
m->vt = va+vb;
|
||||||
|
m->vrs = va+vso;
|
||||||
|
m->vre = va+vso+vspw;
|
||||||
|
|
||||||
|
if(p[17] & 0x80) /* interlaced */
|
||||||
|
m->interlace = 'v';
|
||||||
|
|
||||||
|
if(p[17] & 0x60) /* some form of stereo monitor mode; no support */
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sync signal description. I have no idea how to properly handle the
|
||||||
|
* first three cases, which I think are aimed at things other than
|
||||||
|
* canonical SVGA monitors.
|
||||||
|
*/
|
||||||
|
switch((p[17] & 0x18)>>3) {
|
||||||
|
case 0: /* analog composite sync signal*/
|
||||||
|
case 1: /* bipolar analog composite sync signal */
|
||||||
|
/* p[17] & 0x04 means serration: hsync during vsync */
|
||||||
|
/* p[17] & 0x02 means sync pulse appears on RGB not just G */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: /* digital composite sync signal */
|
||||||
|
/* p[17] & 0x04 means serration: hsync during vsync */
|
||||||
|
/* p[17] & 0x02 means hsync positive outside vsync */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: /* digital separate sync signal; the norm */
|
||||||
|
m->vsync = (p[17] & 0x04) ? '+' : '-';
|
||||||
|
m->hsync = (p[17] & 0x02) ? '+' : '-';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* p[17] & 0x01 is another stereo bit, only referenced if p[17] & 0x60 != 0 */
|
||||||
|
|
||||||
|
rr = (m->frequency+m->ht*m->vt/2) / (m->ht*m->vt);
|
||||||
|
|
||||||
|
snprint(m->name, sizeof m->name, "%dx%d@%dHz", m->x, m->y, rr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vesalookup(Mode *m, char *name)
|
||||||
|
{
|
||||||
|
Mode **p;
|
||||||
|
|
||||||
|
for(p=vesamodes; *p; p++)
|
||||||
|
if(strcmp((*p)->name, name) == 0) {
|
||||||
|
*m = **p;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
decodesti(Mode *m, uchar *p)
|
||||||
|
{
|
||||||
|
int x, y, rr;
|
||||||
|
char str[20];
|
||||||
|
|
||||||
|
x = (p[0]+31)*8;
|
||||||
|
switch((p[1]>>6) & 3){
|
||||||
|
default:
|
||||||
|
case 0:
|
||||||
|
y = x;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
y = (x*4)/3;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
y = (x*5)/4;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
y = (x*16)/9;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rr = (p[1] & 0x1F) + 60;
|
||||||
|
|
||||||
|
sprint(str, "%dx%d@%dHz", x, y, rr);
|
||||||
|
return vesalookup(m, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
parseedid128(Edid *e, void *v)
|
||||||
|
{
|
||||||
|
static uchar magic[8] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
|
||||||
|
uchar *p, *q, sum;
|
||||||
|
int dpms, estab, i, m, vid;
|
||||||
|
Mode mode;
|
||||||
|
|
||||||
|
memset(e, 0, sizeof *e);
|
||||||
|
|
||||||
|
p = (uchar*)v;
|
||||||
|
if(memcmp(p, magic, 8) != 0) {
|
||||||
|
werrstr("bad edid header");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum = 0;
|
||||||
|
for(i=0; i<128; i++)
|
||||||
|
sum += p[i];
|
||||||
|
if(sum != 0) {
|
||||||
|
werrstr("bad edid checksum");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
p += 8;
|
||||||
|
|
||||||
|
assert(p == (uchar*)v+8); /* assertion offsets from pp. 12-13 of the standard */
|
||||||
|
/*
|
||||||
|
* Manufacturer name is three 5-bit ascii letters, packed
|
||||||
|
* into a big endian [sic] short in big endian order. The high bit is unused.
|
||||||
|
*/
|
||||||
|
i = (p[0]<<8) | p[1];
|
||||||
|
p += 2;
|
||||||
|
e->mfr[0] = 'A'-1 + ((i>>10) & 0x1F);
|
||||||
|
e->mfr[1] = 'A'-1 + ((i>>5) & 0x1F);
|
||||||
|
e->mfr[2] = 'A'-1 + (i & 0x1F);
|
||||||
|
e->mfr[3] = '\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Product code is a little endian short.
|
||||||
|
*/
|
||||||
|
e->product = (p[1]<<8) | p[0];
|
||||||
|
p += 2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Serial number is a little endian long, 0x01010101 = unused.
|
||||||
|
*/
|
||||||
|
e->serial = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
|
||||||
|
p += 4;
|
||||||
|
if(e->serial == 0x01010101)
|
||||||
|
e->serial = 0;
|
||||||
|
|
||||||
|
e->mfrweek = *p++;
|
||||||
|
e->mfryear = 1990 + *p++;
|
||||||
|
|
||||||
|
assert(p == (uchar*)v+8+10);
|
||||||
|
/*
|
||||||
|
* Structure version is next two bytes: major.minor.
|
||||||
|
*/
|
||||||
|
e->version = *p++;
|
||||||
|
e->revision = *p++;
|
||||||
|
|
||||||
|
assert(p == (uchar*)v+8+10+2);
|
||||||
|
/*
|
||||||
|
* Basic display parameters / features.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Video input definition byte: 0x80 tells whether it is
|
||||||
|
* an analog or digital screen; we ignore the other bits.
|
||||||
|
* See p. 15 of the standard.
|
||||||
|
*/
|
||||||
|
vid = *p++;
|
||||||
|
if(vid & 0x80)
|
||||||
|
e->flags |= Fdigital;
|
||||||
|
|
||||||
|
e->dxcm = *p++;
|
||||||
|
e->dycm = *p++;
|
||||||
|
e->gamma = 100 + *p++;
|
||||||
|
dpms = *p++;
|
||||||
|
if(dpms & 0x80)
|
||||||
|
e->flags |= Fdpmsstandby;
|
||||||
|
if(dpms & 0x40)
|
||||||
|
e->flags |= Fdpmssuspend;
|
||||||
|
if(dpms & 0x20)
|
||||||
|
e->flags |= Fdpmsactiveoff;
|
||||||
|
if((dpms & 0x18) == 0x00)
|
||||||
|
e->flags |= Fmonochrome;
|
||||||
|
if(dpms & 0x01)
|
||||||
|
e->flags |= Fgtf;
|
||||||
|
|
||||||
|
assert(p == (uchar*)v+8+10+2+5);
|
||||||
|
/*
|
||||||
|
* Color characteristics currently ignored.
|
||||||
|
*/
|
||||||
|
p += 10;
|
||||||
|
|
||||||
|
assert(p == (uchar*)v+8+10+2+5+10);
|
||||||
|
/*
|
||||||
|
* Established timings: a bitmask of 19 preset timings.
|
||||||
|
*/
|
||||||
|
estab = (p[0]<<16) | (p[1]<<8) | p[2];
|
||||||
|
p += 3;
|
||||||
|
|
||||||
|
for(i=0, m=1<<23; i<nelem(estabtime); i++, m>>=1)
|
||||||
|
if(estab & m)
|
||||||
|
if(vesalookup(&mode, estabtime[i]) == 0)
|
||||||
|
e->modelist = addmode(e->modelist, mode);
|
||||||
|
|
||||||
|
assert(p == (uchar*)v+8+10+2+5+10+3);
|
||||||
|
/*
|
||||||
|
* Standard Timing Identifications: eight 2-byte selectors
|
||||||
|
* of more standard timings.
|
||||||
|
*/
|
||||||
|
for(i=0; i<8; i++, p+=2)
|
||||||
|
if(decodesti(&mode, p+2*i) == 0)
|
||||||
|
e->modelist = addmode(e->modelist, mode);
|
||||||
|
|
||||||
|
assert(p == (uchar*)v+8+10+2+5+10+3+16);
|
||||||
|
/*
|
||||||
|
* Detailed Timings
|
||||||
|
*/
|
||||||
|
for(i=0; i<4; i++, p+=18) {
|
||||||
|
if(p[0] || p[1]) { /* detailed timing block: p[0] or p[1] != 0 */
|
||||||
|
if(decodedtb(&mode, p) == 0)
|
||||||
|
e->modelist = addmode(e->modelist, mode);
|
||||||
|
} else if(p[2]==0) { /* monitor descriptor block */
|
||||||
|
switch(p[3]) {
|
||||||
|
case 0xFF: /* monitor serial number (13-byte ascii, 0A terminated) */
|
||||||
|
if(q = memchr(p+5, 0x0A, 13))
|
||||||
|
*q = '\0';
|
||||||
|
memset(e->serialstr, 0, sizeof(e->serialstr));
|
||||||
|
strncpy(e->serialstr, (char*)p+5, 13);
|
||||||
|
break;
|
||||||
|
case 0xFE: /* ascii string (13-byte ascii, 0A terminated) */
|
||||||
|
break;
|
||||||
|
case 0xFD: /* monitor range limits */
|
||||||
|
e->rrmin = p[5];
|
||||||
|
e->rrmax = p[6];
|
||||||
|
e->hrmin = p[7]*1000;
|
||||||
|
e->hrmax = p[8]*1000;
|
||||||
|
if(p[9] != 0xFF)
|
||||||
|
e->pclkmax = p[9]*10*1000000;
|
||||||
|
break;
|
||||||
|
case 0xFC: /* monitor name (13-byte ascii, 0A terminated) */
|
||||||
|
if(q = memchr(p+5, 0x0A, 13))
|
||||||
|
*q = '\0';
|
||||||
|
memset(e->name, 0, sizeof(e->name));
|
||||||
|
strncpy(e->name, (char*)p+5, 13);
|
||||||
|
break;
|
||||||
|
case 0xFB: /* extra color point data */
|
||||||
|
break;
|
||||||
|
case 0xFA: /* extra standard timing identifications */
|
||||||
|
for(i=0; i<6; i++)
|
||||||
|
if(decodesti(&mode, p+5+2*i) == 0)
|
||||||
|
e->modelist = addmode(e->modelist, mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(p == (uchar*)v+8+10+2+5+10+3+16+72);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Flag edidflags[] = {
|
||||||
|
Fdigital, "digital",
|
||||||
|
Fdpmsstandby, "standby",
|
||||||
|
Fdpmssuspend, "suspend",
|
||||||
|
Fdpmsactiveoff, "activeoff",
|
||||||
|
Fmonochrome, "monochrome",
|
||||||
|
Fgtf, "gtf",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
printflags(Flag *f, int b)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; f[i].bit; i++)
|
||||||
|
if(f[i].bit & b)
|
||||||
|
Bprint(&stdout, " %s", f[i].desc);
|
||||||
|
Bprint(&stdout, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
printedid(Edid *e)
|
||||||
|
{
|
||||||
|
Modelist *l;
|
||||||
|
|
||||||
|
printitem("edid", "mfr");
|
||||||
|
Bprint(&stdout, "%s\n", e->mfr);
|
||||||
|
printitem("edid", "serialstr");
|
||||||
|
Bprint(&stdout, "%s\n", e->serialstr);
|
||||||
|
printitem("edid", "name");
|
||||||
|
Bprint(&stdout, "%s\n", e->name);
|
||||||
|
printitem("edid", "product");
|
||||||
|
Bprint(&stdout, "%d\n", e->product);
|
||||||
|
printitem("edid", "serial");
|
||||||
|
Bprint(&stdout, "%lud\n", e->serial);
|
||||||
|
printitem("edid", "version");
|
||||||
|
Bprint(&stdout, "%d.%d\n", e->version, e->revision);
|
||||||
|
printitem("edid", "mfrdate");
|
||||||
|
Bprint(&stdout, "%d.%d\n", e->mfryear, e->mfrweek);
|
||||||
|
printitem("edid", "size (cm)");
|
||||||
|
Bprint(&stdout, "%dx%d\n", e->dxcm, e->dycm);
|
||||||
|
printitem("edid", "gamma");
|
||||||
|
Bprint(&stdout, "%.2f\n", e->gamma/100.);
|
||||||
|
printitem("edid", "vert (Hz)");
|
||||||
|
Bprint(&stdout, "%d-%d\n", e->rrmin, e->rrmax);
|
||||||
|
printitem("edid", "horz (Hz)");
|
||||||
|
Bprint(&stdout, "%d-%d\n", e->hrmin, e->hrmax);
|
||||||
|
printitem("edid", "pclkmax");
|
||||||
|
Bprint(&stdout, "%lud\n", e->pclkmax);
|
||||||
|
printitem("edid", "flags");
|
||||||
|
printflags(edidflags, e->flags);
|
||||||
|
|
||||||
|
for(l=e->modelist; l; l=l->next){
|
||||||
|
printitem("edid", l->name);
|
||||||
|
Bprint(&stdout, "\n\t\tclock=%g\n"
|
||||||
|
"\t\tshb=%d ehb=%d ht=%d\n"
|
||||||
|
"\t\tvrs=%d vre=%d vt=%d\n"
|
||||||
|
"\t\thsync=%c vsync=%c %s\n",
|
||||||
|
l->frequency/1.e6,
|
||||||
|
l->shb, l->ehb, l->ht,
|
||||||
|
l->vrs, l->vre, l->vt,
|
||||||
|
l->hsync?l->hsync:'?',
|
||||||
|
l->vsync?l->vsync:'?',
|
||||||
|
l->interlace?"interlace=v" : "");
|
||||||
|
}
|
||||||
|
}
|
50
sys/src/cmd/aux/vga/edid.h
Normal file
50
sys/src/cmd/aux/vga/edid.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
typedef struct Modelist Modelist;
|
||||||
|
typedef struct Edid Edid;
|
||||||
|
typedef struct Flag Flag;
|
||||||
|
|
||||||
|
struct Edid {
|
||||||
|
char mfr[4]; /* manufacturer */
|
||||||
|
char serialstr[16]; /* serial number as string (in extended data) */
|
||||||
|
char name[16]; /* monitor name as string (in extended data) */
|
||||||
|
ushort product; /* product code, 0 = unused */
|
||||||
|
ulong serial; /* serial number, 0 = unused */
|
||||||
|
uchar version; /* major version number */
|
||||||
|
uchar revision; /* minor version number */
|
||||||
|
uchar mfrweek; /* week of manufacture, 0 = unused */
|
||||||
|
int mfryear; /* year of manufacture, 0 = unused */
|
||||||
|
uchar dxcm; /* horizontal image size in cm. */
|
||||||
|
uchar dycm; /* vertical image size in cm. */
|
||||||
|
int gamma; /* gamma*100 */
|
||||||
|
int rrmin; /* minimum vertical refresh rate */
|
||||||
|
int rrmax; /* maximum vertical refresh rate */
|
||||||
|
int hrmin; /* minimum horizontal refresh rate */
|
||||||
|
int hrmax; /* maximum horizontal refresh rate */
|
||||||
|
ulong pclkmax; /* maximum pixel clock */
|
||||||
|
int flags;
|
||||||
|
Modelist *modelist; /* list of supported modes */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Modelist
|
||||||
|
{
|
||||||
|
Mode;
|
||||||
|
Modelist *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Flag {
|
||||||
|
int bit;
|
||||||
|
char *desc;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
Fdigital = 1<<0, /* is a digital display */
|
||||||
|
Fdpmsstandby = 1<<1, /* supports DPMS standby mode */
|
||||||
|
Fdpmssuspend = 1<<2, /* supports DPMS suspend mode */
|
||||||
|
Fdpmsactiveoff = 1<<3, /* supports DPMS active off mode */
|
||||||
|
Fmonochrome = 1<<4, /* is a monochrome display */
|
||||||
|
Fgtf = 1<<5, /* supports VESA GTF: see /public/doc/vesa/gtf10.pdf */
|
||||||
|
};
|
||||||
|
Flag edidflags[];
|
||||||
|
void printflags(Flag *f, int b);
|
||||||
|
|
||||||
|
int parseedid128(Edid *e, void *v);
|
||||||
|
void printedid(Edid *e);
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
#include "vga.h"
|
#include "vga.h"
|
||||||
|
#include "edid.h"
|
||||||
|
|
||||||
typedef struct Reg Reg;
|
typedef struct Reg Reg;
|
||||||
typedef struct Dpll Dpll;
|
typedef struct Dpll Dpll;
|
||||||
|
@ -142,12 +143,15 @@ struct Igfx {
|
||||||
Reg ppstatus;
|
Reg ppstatus;
|
||||||
|
|
||||||
/* G45 */
|
/* G45 */
|
||||||
|
Reg gmbus[6]; /* GMBUSx */
|
||||||
|
|
||||||
Reg sdvoc;
|
Reg sdvoc;
|
||||||
Reg sdvob;
|
Reg sdvob;
|
||||||
|
|
||||||
/* common */
|
/* common */
|
||||||
Reg adpa;
|
Reg adpa;
|
||||||
Reg lvds;
|
Reg lvds;
|
||||||
|
Edid *lvdsedid;
|
||||||
|
|
||||||
Reg vgacntrl;
|
Reg vgacntrl;
|
||||||
};
|
};
|
||||||
|
@ -305,6 +309,8 @@ devtype(Igfx *igfx)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void snarfedid(Igfx*);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
snarf(Vga* vga, Ctlr* ctlr)
|
snarf(Vga* vga, Ctlr* ctlr)
|
||||||
{
|
{
|
||||||
|
@ -326,16 +332,14 @@ snarf(Vga* vga, Ctlr* ctlr)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
vgactlpci(igfx->pci);
|
vgactlpci(igfx->pci);
|
||||||
|
if((igfx->pci->mem[4].bar & 1) == 0)
|
||||||
|
error("%s: no pio bar\n", ctlr->name);
|
||||||
|
igfx->pio = igfx->pci->mem[4].bar & ~1;
|
||||||
if(1){
|
if(1){
|
||||||
vgactlw("type", ctlr->name);
|
vgactlw("type", ctlr->name);
|
||||||
igfx->mmio = segattach(0, "igfxmmio", 0, igfx->pci->mem[0].size);
|
igfx->mmio = segattach(0, "igfxmmio", 0, igfx->pci->mem[0].size);
|
||||||
if(igfx->mmio == (u32int*)-1)
|
if(igfx->mmio == (u32int*)-1)
|
||||||
error("%s: segattach mmio failed: %r\n", ctlr->name);
|
igfx->mmio = nil; /* use pio */
|
||||||
} else {
|
|
||||||
if((igfx->pci->mem[4].bar & 1) == 0)
|
|
||||||
error("%s: no pio bar\n", ctlr->name);
|
|
||||||
igfx->pio = igfx->pci->mem[4].bar & ~1;
|
|
||||||
igfx->mmio = nil;
|
|
||||||
}
|
}
|
||||||
vga->private = igfx;
|
vga->private = igfx;
|
||||||
}
|
}
|
||||||
|
@ -356,6 +360,10 @@ snarf(Vga* vga, Ctlr* ctlr)
|
||||||
igfx->sdvob = snarfreg(igfx, 0x061140);
|
igfx->sdvob = snarfreg(igfx, 0x061140);
|
||||||
igfx->sdvoc = snarfreg(igfx, 0x061160);
|
igfx->sdvoc = snarfreg(igfx, 0x061160);
|
||||||
|
|
||||||
|
for(x=0; x<5; x++)
|
||||||
|
igfx->gmbus[x] = snarfreg(igfx, 0x5100 + x*4);
|
||||||
|
igfx->gmbus[5] = snarfreg(igfx, 0x5120);
|
||||||
|
|
||||||
igfx->pfit[0].ctrl = snarfreg(igfx, 0x061230);
|
igfx->pfit[0].ctrl = snarfreg(igfx, 0x061230);
|
||||||
y = (igfx->pfit[0].ctrl.v >> 29) & 3;
|
y = (igfx->pfit[0].ctrl.v >> 29) & 3;
|
||||||
if(igfx->pipe[y].pfit == nil)
|
if(igfx->pipe[y].pfit == nil)
|
||||||
|
@ -441,6 +449,8 @@ snarf(Vga* vga, Ctlr* ctlr)
|
||||||
for(x=0; x<igfx->npipe; x++)
|
for(x=0; x<igfx->npipe; x++)
|
||||||
snarfpipe(igfx, x);
|
snarfpipe(igfx, x);
|
||||||
|
|
||||||
|
snarfedid(igfx);
|
||||||
|
|
||||||
ctlr->flag |= Fsnarf;
|
ctlr->flag |= Fsnarf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,7 +624,7 @@ inittrans(Trans *t, Mode *m)
|
||||||
/* trans/pipe timing */
|
/* trans/pipe timing */
|
||||||
t->ht.v = (m->ht - 1)<<16 | (m->x - 1);
|
t->ht.v = (m->ht - 1)<<16 | (m->x - 1);
|
||||||
t->hb.v = t->ht.v;
|
t->hb.v = t->ht.v;
|
||||||
t->hs.v = (m->ehs - 1)<<16 | (m->shs - 1);
|
t->hs.v = (m->ehb - 1)<<16 | (m->shb - 1);
|
||||||
t->vt.v = (m->vt - 1)<<16 | (m->y - 1);
|
t->vt.v = (m->vt - 1)<<16 | (m->y - 1);
|
||||||
t->vb.v = t->vt.v;
|
t->vb.v = t->vt.v;
|
||||||
t->vs.v = (m->vre - 1)<<16 | (m->vrs - 1);
|
t->vs.v = (m->vre - 1)<<16 | (m->vrs - 1);
|
||||||
|
@ -1165,6 +1175,81 @@ dump(Vga* vga, Ctlr* ctlr)
|
||||||
dumpreg(ctlr->name, "sdvoc", igfx->sdvoc);
|
dumpreg(ctlr->name, "sdvoc", igfx->sdvoc);
|
||||||
|
|
||||||
dumpreg(ctlr->name, "vgacntrl", igfx->vgacntrl);
|
dumpreg(ctlr->name, "vgacntrl", igfx->vgacntrl);
|
||||||
|
|
||||||
|
if(igfx->lvdsedid != nil)
|
||||||
|
printedid(igfx->lvdsedid);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GMBUSCP = 0, /* Clock/Port selection */
|
||||||
|
GMBUSCS = 1, /* Command/Status */
|
||||||
|
GMBUSST = 2, /* Status Register */
|
||||||
|
GMBUSDB = 3, /* Data Buffer Register */
|
||||||
|
GMBUSIM = 4, /* Interrupt Mask */
|
||||||
|
GMBUSIX = 5, /* Index Register */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
gmbusread(Igfx *igfx, int portsel, int addr, uchar *data, int len)
|
||||||
|
{
|
||||||
|
u32int x, y;
|
||||||
|
int n, t;
|
||||||
|
|
||||||
|
if(igfx->gmbus[GMBUSCP].a == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
wr(igfx, igfx->gmbus[GMBUSCP].a, portsel);
|
||||||
|
wr(igfx, igfx->gmbus[GMBUSIX].a, 0);
|
||||||
|
|
||||||
|
/* bus cycle without index and stop, byte count, slave address, read */
|
||||||
|
wr(igfx, igfx->gmbus[GMBUSCS].a, 1<<30 | 5<<25 | len<<16 | addr<<1 | 1);
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
while(len > 0){
|
||||||
|
x = 0;
|
||||||
|
for(t=0; t<100; t++){
|
||||||
|
x = rr(igfx, igfx->gmbus[GMBUSST].a);
|
||||||
|
if(x & (1<<11))
|
||||||
|
break;
|
||||||
|
sleep(5);
|
||||||
|
}
|
||||||
|
if((x & (1<<11)) == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
t = 4 - (x & 3);
|
||||||
|
if(t > len)
|
||||||
|
t = len;
|
||||||
|
len -= t;
|
||||||
|
|
||||||
|
y = rr(igfx, igfx->gmbus[GMBUSDB].a);
|
||||||
|
switch(t){
|
||||||
|
case 4:
|
||||||
|
data[n++] = y & 0xff, y >>= 8;
|
||||||
|
case 3:
|
||||||
|
data[n++] = y & 0xff, y >>= 8;
|
||||||
|
case 2:
|
||||||
|
data[n++] = y & 0xff, y >>= 8;
|
||||||
|
case 1:
|
||||||
|
data[n++] = y & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
snarfedid(Igfx *igfx)
|
||||||
|
{
|
||||||
|
uchar buf[128];
|
||||||
|
|
||||||
|
if(igfx->type != TypeG45)
|
||||||
|
return;
|
||||||
|
if(gmbusread(igfx, 3, 0x50, buf, sizeof(buf)) != sizeof(buf))
|
||||||
|
return;
|
||||||
|
igfx->lvdsedid = malloc(sizeof(Edid));
|
||||||
|
if(parseedid128(igfx->lvdsedid, buf) != 0){
|
||||||
|
free(igfx->lvdsedid);
|
||||||
|
igfx->lvdsedid = nil;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ctlr igfx = {
|
Ctlr igfx = {
|
||||||
|
|
|
@ -15,6 +15,7 @@ OFILES=\
|
||||||
cyber938x.$O\
|
cyber938x.$O\
|
||||||
data.$O\
|
data.$O\
|
||||||
db.$O\
|
db.$O\
|
||||||
|
edid.$O\
|
||||||
error.$O\
|
error.$O\
|
||||||
et4000.$O\
|
et4000.$O\
|
||||||
et4000hwgc.$O\
|
et4000hwgc.$O\
|
||||||
|
@ -66,7 +67,8 @@ OFILES=\
|
||||||
|
|
||||||
HFILES=\
|
HFILES=\
|
||||||
pci.h\
|
pci.h\
|
||||||
vga.h
|
vga.h\
|
||||||
|
edid.h\
|
||||||
|
|
||||||
UPDATE=\
|
UPDATE=\
|
||||||
mkfile\
|
mkfile\
|
||||||
|
@ -75,7 +77,6 @@ UPDATE=\
|
||||||
/lib/vgadb\
|
/lib/vgadb\
|
||||||
riva_tbl.h\
|
riva_tbl.h\
|
||||||
|
|
||||||
|
|
||||||
</sys/src/cmd/mkone
|
</sys/src/cmd/mkone
|
||||||
|
|
||||||
geode.$O: geode_modes.h
|
geode.$O: geode_modes.h
|
||||||
|
|
|
@ -6,11 +6,10 @@ typedef struct Ureg Ureg;
|
||||||
|
|
||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
#include "vga.h"
|
#include "vga.h"
|
||||||
|
#include "edid.h"
|
||||||
|
|
||||||
typedef struct Vbe Vbe;
|
typedef struct Vbe Vbe;
|
||||||
typedef struct Vmode Vmode;
|
typedef struct Vmode Vmode;
|
||||||
typedef struct Modelist Modelist;
|
|
||||||
typedef struct Edid Edid;
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -47,44 +46,6 @@ struct Vmode
|
||||||
ulong paddr;
|
ulong paddr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Edid {
|
|
||||||
char mfr[4]; /* manufacturer */
|
|
||||||
char serialstr[16]; /* serial number as string (in extended data) */
|
|
||||||
char name[16]; /* monitor name as string (in extended data) */
|
|
||||||
ushort product; /* product code, 0 = unused */
|
|
||||||
ulong serial; /* serial number, 0 = unused */
|
|
||||||
uchar version; /* major version number */
|
|
||||||
uchar revision; /* minor version number */
|
|
||||||
uchar mfrweek; /* week of manufacture, 0 = unused */
|
|
||||||
int mfryear; /* year of manufacture, 0 = unused */
|
|
||||||
uchar dxcm; /* horizontal image size in cm. */
|
|
||||||
uchar dycm; /* vertical image size in cm. */
|
|
||||||
int gamma; /* gamma*100 */
|
|
||||||
int rrmin; /* minimum vertical refresh rate */
|
|
||||||
int rrmax; /* maximum vertical refresh rate */
|
|
||||||
int hrmin; /* minimum horizontal refresh rate */
|
|
||||||
int hrmax; /* maximum horizontal refresh rate */
|
|
||||||
ulong pclkmax; /* maximum pixel clock */
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
Modelist *modelist; /* list of supported modes */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Modelist
|
|
||||||
{
|
|
||||||
Mode;
|
|
||||||
Modelist *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
Fdigital = 1<<0, /* is a digital display */
|
|
||||||
Fdpmsstandby = 1<<1, /* supports DPMS standby mode */
|
|
||||||
Fdpmssuspend = 1<<2, /* supports DPMS suspend mode */
|
|
||||||
Fdpmsactiveoff = 1<<3, /* supports DPMS active off mode */
|
|
||||||
Fmonochrome = 1<<4, /* is a monochrome display */
|
|
||||||
Fgtf = 1<<5, /* supports VESA GTF: see /public/doc/vesa/gtf10.pdf */
|
|
||||||
};
|
|
||||||
|
|
||||||
#define WORD(p) ((p)[0] | ((p)[1]<<8))
|
#define WORD(p) ((p)[0] | ((p)[1]<<8))
|
||||||
#define LONG(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
|
#define LONG(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
|
||||||
#define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8
|
#define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8
|
||||||
|
@ -93,8 +54,6 @@ enum {
|
||||||
static Vbe *vbe;
|
static Vbe *vbe;
|
||||||
static Edid *edid;
|
static Edid *edid;
|
||||||
|
|
||||||
extern Mode *vesamodes[];
|
|
||||||
|
|
||||||
Vbe *mkvbe(void);
|
Vbe *mkvbe(void);
|
||||||
int vbecheck(Vbe*);
|
int vbecheck(Vbe*);
|
||||||
uchar *vbemodes(Vbe*);
|
uchar *vbemodes(Vbe*);
|
||||||
|
@ -106,12 +65,11 @@ void vbeprintmodeinfo(Vbe*, int, char*);
|
||||||
int vbesnarf(Vbe*, Vga*);
|
int vbesnarf(Vbe*, Vga*);
|
||||||
void vesaddc(void);
|
void vesaddc(void);
|
||||||
int vbeddcedid(Vbe *vbe, Edid *e);
|
int vbeddcedid(Vbe *vbe, Edid *e);
|
||||||
void printedid(Edid*);
|
|
||||||
void fixbios(Vbe*);
|
|
||||||
uchar* vbesetup(Vbe*, Ureg*, int);
|
uchar* vbesetup(Vbe*, Ureg*, int);
|
||||||
int vbecall(Vbe*, Ureg*);
|
int vbecall(Vbe*, Ureg*);
|
||||||
int setdisplay(Vbe *vbe, int display);
|
int setdisplay(Vbe *vbe, int display);
|
||||||
int getdisplay(Vbe *vbe);
|
int getdisplay(Vbe *vbe);
|
||||||
|
void fixbios(Vbe*);
|
||||||
|
|
||||||
int
|
int
|
||||||
dbvesa(Vga* vga)
|
dbvesa(Vga* vga)
|
||||||
|
@ -463,12 +421,6 @@ Ctlr softhwgc = {
|
||||||
* VESA bios extension
|
* VESA bios extension
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef struct Flag Flag;
|
|
||||||
struct Flag {
|
|
||||||
int bit;
|
|
||||||
char *desc;
|
|
||||||
};
|
|
||||||
|
|
||||||
static Flag capabilityflag[] = {
|
static Flag capabilityflag[] = {
|
||||||
0x01, "8-bit-dac",
|
0x01, "8-bit-dac",
|
||||||
0x02, "not-vga",
|
0x02, "not-vga",
|
||||||
|
@ -544,18 +496,6 @@ static char *modelstr[] = {
|
||||||
[ModYUV] "YUV",
|
[ModYUV] "YUV",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
printflags(Flag *f, int b)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for(i=0; f[i].bit; i++)
|
|
||||||
if(f[i].bit & b)
|
|
||||||
Bprint(&stdout, " %s", f[i].desc);
|
|
||||||
Bprint(&stdout, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
Vbe*
|
Vbe*
|
||||||
mkvbe(void)
|
mkvbe(void)
|
||||||
{
|
{
|
||||||
|
@ -928,16 +868,6 @@ vesatextmode(void)
|
||||||
error("vbesetmode: %r\n");
|
error("vbesetmode: %r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Flag edidflags[] = {
|
|
||||||
Fdigital, "digital",
|
|
||||||
Fdpmsstandby, "standby",
|
|
||||||
Fdpmssuspend, "suspend",
|
|
||||||
Fdpmsactiveoff, "activeoff",
|
|
||||||
Fmonochrome, "monochrome",
|
|
||||||
Fgtf, "gtf",
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
int parseedid128(Edid *e, void *v);
|
int parseedid128(Edid *e, void *v);
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -1000,405 +930,6 @@ setdisplay(Vbe *vbe, int display)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
printedid(Edid *e)
|
|
||||||
{
|
|
||||||
Modelist *l;
|
|
||||||
|
|
||||||
printitem("edid", "mfr");
|
|
||||||
Bprint(&stdout, "%s\n", e->mfr);
|
|
||||||
printitem("edid", "serialstr");
|
|
||||||
Bprint(&stdout, "%s\n", e->serialstr);
|
|
||||||
printitem("edid", "name");
|
|
||||||
Bprint(&stdout, "%s\n", e->name);
|
|
||||||
printitem("edid", "product");
|
|
||||||
Bprint(&stdout, "%d\n", e->product);
|
|
||||||
printitem("edid", "serial");
|
|
||||||
Bprint(&stdout, "%lud\n", e->serial);
|
|
||||||
printitem("edid", "version");
|
|
||||||
Bprint(&stdout, "%d.%d\n", e->version, e->revision);
|
|
||||||
printitem("edid", "mfrdate");
|
|
||||||
Bprint(&stdout, "%d.%d\n", e->mfryear, e->mfrweek);
|
|
||||||
printitem("edid", "size (cm)");
|
|
||||||
Bprint(&stdout, "%dx%d\n", e->dxcm, e->dycm);
|
|
||||||
printitem("edid", "gamma");
|
|
||||||
Bprint(&stdout, "%.2f\n", e->gamma/100.);
|
|
||||||
printitem("edid", "vert (Hz)");
|
|
||||||
Bprint(&stdout, "%d-%d\n", e->rrmin, e->rrmax);
|
|
||||||
printitem("edid", "horz (Hz)");
|
|
||||||
Bprint(&stdout, "%d-%d\n", e->hrmin, e->hrmax);
|
|
||||||
printitem("edid", "pclkmax");
|
|
||||||
Bprint(&stdout, "%lud\n", e->pclkmax);
|
|
||||||
printitem("edid", "flags");
|
|
||||||
printflags(edidflags, e->flags);
|
|
||||||
|
|
||||||
for(l=e->modelist; l; l=l->next){
|
|
||||||
printitem("edid", l->name);
|
|
||||||
Bprint(&stdout, "\n\t\tclock=%g\n"
|
|
||||||
"\t\tshb=%d ehb=%d ht=%d\n"
|
|
||||||
"\t\tvrs=%d vre=%d vt=%d\n"
|
|
||||||
"\t\thsync=%c vsync=%c %s\n",
|
|
||||||
l->frequency/1.e6,
|
|
||||||
l->shb, l->ehb, l->ht,
|
|
||||||
l->vrs, l->vre, l->vt,
|
|
||||||
l->hsync?l->hsync:'?',
|
|
||||||
l->vsync?l->vsync:'?',
|
|
||||||
l->interlace?"interlace=v" : "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Modelist*
|
|
||||||
addmode(Modelist *l, Mode m)
|
|
||||||
{
|
|
||||||
int rr;
|
|
||||||
Modelist **lp;
|
|
||||||
|
|
||||||
rr = (m.frequency+m.ht*m.vt/2)/(m.ht*m.vt);
|
|
||||||
snprint(m.name, sizeof m.name, "%dx%d@%dHz", m.x, m.y, rr);
|
|
||||||
|
|
||||||
if(m.shs == 0)
|
|
||||||
m.shs = m.shb;
|
|
||||||
if(m.ehs == 0)
|
|
||||||
m.ehs = m.ehb;
|
|
||||||
if(m.vbs == 0)
|
|
||||||
m.vbs = m.vrs;
|
|
||||||
if(m.vbe == 0)
|
|
||||||
m.vbe = m.vbs+1;
|
|
||||||
|
|
||||||
for(lp=&l; *lp; lp=&(*lp)->next){
|
|
||||||
if(strcmp((*lp)->name, m.name) == 0){
|
|
||||||
(*lp)->Mode = m;
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*lp = alloc(sizeof(**lp));
|
|
||||||
(*lp)->Mode = m;
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Parse VESA EDID information. Based on the VESA
|
|
||||||
* Extended Display Identification Data standard, Version 3,
|
|
||||||
* November 13, 1997. See /public/doc/vesa/edidv3.pdf.
|
|
||||||
*
|
|
||||||
* This only handles 128-byte EDID blocks. Until I find
|
|
||||||
* a monitor that produces 256-byte blocks, I'm not going
|
|
||||||
* to try to decode them.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Established timings block. There is a bitmap
|
|
||||||
* that says whether each mode is supported. Most
|
|
||||||
* of these have VESA definitions. Those that don't are marked
|
|
||||||
* as such, and we ignore them (the lookup fails).
|
|
||||||
*/
|
|
||||||
static char *estabtime[] = {
|
|
||||||
"720x400@70Hz", /* non-VESA: IBM, VGA */
|
|
||||||
"720x400@88Hz", /* non-VESA: IBM, XGA2 */
|
|
||||||
"640x480@60Hz",
|
|
||||||
"640x480@67Hz", /* non-VESA: Apple, Mac II */
|
|
||||||
"640x480@72Hz",
|
|
||||||
"640x480@75Hz",
|
|
||||||
"800x600@56Hz",
|
|
||||||
"800x600@60Hz",
|
|
||||||
|
|
||||||
"800x600@72Hz",
|
|
||||||
"800x600@75Hz",
|
|
||||||
"832x624@75Hz", /* non-VESA: Apple, Mac II */
|
|
||||||
"1024x768i@87Hz", /* non-VESA: IBM */
|
|
||||||
"1024x768@60Hz",
|
|
||||||
"1024x768@70Hz",
|
|
||||||
"1024x768@75Hz",
|
|
||||||
"1280x1024@75Hz",
|
|
||||||
|
|
||||||
"1152x870@75Hz", /* non-VESA: Apple, Mac II */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Decode the EDID detailed timing block. See pp. 20-21 of the standard.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
decodedtb(Mode *m, uchar *p)
|
|
||||||
{
|
|
||||||
int ha, hb, hso, hspw, rr, va, vb, vso, vspw;
|
|
||||||
/* int dxmm, dymm, hbord, vbord; */
|
|
||||||
|
|
||||||
memset(m, 0, sizeof *m);
|
|
||||||
|
|
||||||
m->frequency = ((p[1]<<8) | p[0]) * 10000;
|
|
||||||
|
|
||||||
ha = ((p[4] & 0xF0)<<4) | p[2]; /* horizontal active */
|
|
||||||
hb = ((p[4] & 0x0F)<<8) | p[3]; /* horizontal blanking */
|
|
||||||
va = ((p[7] & 0xF0)<<4) | p[5]; /* vertical active */
|
|
||||||
vb = ((p[7] & 0x0F)<<8) | p[6]; /* vertical blanking */
|
|
||||||
hso = ((p[11] & 0xC0)<<2) | p[8]; /* horizontal sync offset */
|
|
||||||
hspw = ((p[11] & 0x30)<<4) | p[9]; /* horizontal sync pulse width */
|
|
||||||
vso = ((p[11] & 0x0C)<<2) | ((p[10] & 0xF0)>>4); /* vertical sync offset */
|
|
||||||
vspw = ((p[11] & 0x03)<<4) | (p[10] & 0x0F); /* vertical sync pulse width */
|
|
||||||
|
|
||||||
/* dxmm = (p[14] & 0xF0)<<4) | p[12]; /* horizontal image size (mm) */
|
|
||||||
/* dymm = (p[14] & 0x0F)<<8) | p[13]; /* vertical image size (mm) */
|
|
||||||
/* hbord = p[15]; /* horizontal border (pixels) */
|
|
||||||
/* vbord = p[16]; /* vertical border (pixels) */
|
|
||||||
|
|
||||||
m->x = ha;
|
|
||||||
m->y = va;
|
|
||||||
|
|
||||||
m->ht = ha+hb;
|
|
||||||
m->shs = ha;
|
|
||||||
m->shb = ha+hso;
|
|
||||||
m->ehb = ha+hso+hspw;
|
|
||||||
m->ehs = ha+hb;
|
|
||||||
|
|
||||||
m->vt = va+vb;
|
|
||||||
m->vbs = va;
|
|
||||||
m->vrs = va+vso;
|
|
||||||
m->vre = va+vso+vspw;
|
|
||||||
m->vbe = va+vb;
|
|
||||||
|
|
||||||
if(p[17] & 0x80) /* interlaced */
|
|
||||||
m->interlace = 'v';
|
|
||||||
|
|
||||||
if(p[17] & 0x60) /* some form of stereo monitor mode; no support */
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sync signal description. I have no idea how to properly handle the
|
|
||||||
* first three cases, which I think are aimed at things other than
|
|
||||||
* canonical SVGA monitors.
|
|
||||||
*/
|
|
||||||
switch((p[17] & 0x18)>>3) {
|
|
||||||
case 0: /* analog composite sync signal*/
|
|
||||||
case 1: /* bipolar analog composite sync signal */
|
|
||||||
/* p[17] & 0x04 means serration: hsync during vsync */
|
|
||||||
/* p[17] & 0x02 means sync pulse appears on RGB not just G */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: /* digital composite sync signal */
|
|
||||||
/* p[17] & 0x04 means serration: hsync during vsync */
|
|
||||||
/* p[17] & 0x02 means hsync positive outside vsync */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: /* digital separate sync signal; the norm */
|
|
||||||
m->vsync = (p[17] & 0x04) ? '+' : '-';
|
|
||||||
m->hsync = (p[17] & 0x02) ? '+' : '-';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* p[17] & 0x01 is another stereo bit, only referenced if p[17] & 0x60 != 0 */
|
|
||||||
|
|
||||||
rr = (m->frequency+m->ht*m->vt/2) / (m->ht*m->vt);
|
|
||||||
|
|
||||||
snprint(m->name, sizeof m->name, "%dx%d@%dHz", m->x, m->y, rr);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
vesalookup(Mode *m, char *name)
|
|
||||||
{
|
|
||||||
Mode **p;
|
|
||||||
|
|
||||||
for(p=vesamodes; *p; p++)
|
|
||||||
if(strcmp((*p)->name, name) == 0) {
|
|
||||||
*m = **p;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
decodesti(Mode *m, uchar *p)
|
|
||||||
{
|
|
||||||
int x, y, rr;
|
|
||||||
char str[20];
|
|
||||||
|
|
||||||
x = (p[0]+31)*8;
|
|
||||||
switch((p[1]>>6) & 3){
|
|
||||||
default:
|
|
||||||
case 0:
|
|
||||||
y = x;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
y = (x*4)/3;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
y = (x*5)/4;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
y = (x*16)/9;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
rr = (p[1] & 0x1F) + 60;
|
|
||||||
|
|
||||||
sprint(str, "%dx%d@%dHz", x, y, rr);
|
|
||||||
return vesalookup(m, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
parseedid128(Edid *e, void *v)
|
|
||||||
{
|
|
||||||
static uchar magic[8] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
|
|
||||||
uchar *p, *q, sum;
|
|
||||||
int dpms, estab, i, m, vid;
|
|
||||||
Mode mode;
|
|
||||||
|
|
||||||
memset(e, 0, sizeof *e);
|
|
||||||
|
|
||||||
p = (uchar*)v;
|
|
||||||
if(memcmp(p, magic, 8) != 0) {
|
|
||||||
werrstr("bad edid header");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sum = 0;
|
|
||||||
for(i=0; i<128; i++)
|
|
||||||
sum += p[i];
|
|
||||||
if(sum != 0) {
|
|
||||||
werrstr("bad edid checksum");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += 8;
|
|
||||||
|
|
||||||
assert(p == (uchar*)v+8); /* assertion offsets from pp. 12-13 of the standard */
|
|
||||||
/*
|
|
||||||
* Manufacturer name is three 5-bit ascii letters, packed
|
|
||||||
* into a big endian [sic] short in big endian order. The high bit is unused.
|
|
||||||
*/
|
|
||||||
i = (p[0]<<8) | p[1];
|
|
||||||
p += 2;
|
|
||||||
e->mfr[0] = 'A'-1 + ((i>>10) & 0x1F);
|
|
||||||
e->mfr[1] = 'A'-1 + ((i>>5) & 0x1F);
|
|
||||||
e->mfr[2] = 'A'-1 + (i & 0x1F);
|
|
||||||
e->mfr[3] = '\0';
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Product code is a little endian short.
|
|
||||||
*/
|
|
||||||
e->product = (p[1]<<8) | p[0];
|
|
||||||
p += 2;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Serial number is a little endian long, 0x01010101 = unused.
|
|
||||||
*/
|
|
||||||
e->serial = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
|
|
||||||
p += 4;
|
|
||||||
if(e->serial == 0x01010101)
|
|
||||||
e->serial = 0;
|
|
||||||
|
|
||||||
e->mfrweek = *p++;
|
|
||||||
e->mfryear = 1990 + *p++;
|
|
||||||
|
|
||||||
assert(p == (uchar*)v+8+10);
|
|
||||||
/*
|
|
||||||
* Structure version is next two bytes: major.minor.
|
|
||||||
*/
|
|
||||||
e->version = *p++;
|
|
||||||
e->revision = *p++;
|
|
||||||
|
|
||||||
assert(p == (uchar*)v+8+10+2);
|
|
||||||
/*
|
|
||||||
* Basic display parameters / features.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* Video input definition byte: 0x80 tells whether it is
|
|
||||||
* an analog or digital screen; we ignore the other bits.
|
|
||||||
* See p. 15 of the standard.
|
|
||||||
*/
|
|
||||||
vid = *p++;
|
|
||||||
if(vid & 0x80)
|
|
||||||
e->flags |= Fdigital;
|
|
||||||
|
|
||||||
e->dxcm = *p++;
|
|
||||||
e->dycm = *p++;
|
|
||||||
e->gamma = 100 + *p++;
|
|
||||||
dpms = *p++;
|
|
||||||
if(dpms & 0x80)
|
|
||||||
e->flags |= Fdpmsstandby;
|
|
||||||
if(dpms & 0x40)
|
|
||||||
e->flags |= Fdpmssuspend;
|
|
||||||
if(dpms & 0x20)
|
|
||||||
e->flags |= Fdpmsactiveoff;
|
|
||||||
if((dpms & 0x18) == 0x00)
|
|
||||||
e->flags |= Fmonochrome;
|
|
||||||
if(dpms & 0x01)
|
|
||||||
e->flags |= Fgtf;
|
|
||||||
|
|
||||||
assert(p == (uchar*)v+8+10+2+5);
|
|
||||||
/*
|
|
||||||
* Color characteristics currently ignored.
|
|
||||||
*/
|
|
||||||
p += 10;
|
|
||||||
|
|
||||||
assert(p == (uchar*)v+8+10+2+5+10);
|
|
||||||
/*
|
|
||||||
* Established timings: a bitmask of 19 preset timings.
|
|
||||||
*/
|
|
||||||
estab = (p[0]<<16) | (p[1]<<8) | p[2];
|
|
||||||
p += 3;
|
|
||||||
|
|
||||||
for(i=0, m=1<<23; i<nelem(estabtime); i++, m>>=1)
|
|
||||||
if(estab & m)
|
|
||||||
if(vesalookup(&mode, estabtime[i]) == 0)
|
|
||||||
e->modelist = addmode(e->modelist, mode);
|
|
||||||
|
|
||||||
assert(p == (uchar*)v+8+10+2+5+10+3);
|
|
||||||
/*
|
|
||||||
* Standard Timing Identifications: eight 2-byte selectors
|
|
||||||
* of more standard timings.
|
|
||||||
*/
|
|
||||||
for(i=0; i<8; i++, p+=2)
|
|
||||||
if(decodesti(&mode, p+2*i) == 0)
|
|
||||||
e->modelist = addmode(e->modelist, mode);
|
|
||||||
|
|
||||||
assert(p == (uchar*)v+8+10+2+5+10+3+16);
|
|
||||||
/*
|
|
||||||
* Detailed Timings
|
|
||||||
*/
|
|
||||||
for(i=0; i<4; i++, p+=18) {
|
|
||||||
if(p[0] || p[1]) { /* detailed timing block: p[0] or p[1] != 0 */
|
|
||||||
if(decodedtb(&mode, p) == 0)
|
|
||||||
e->modelist = addmode(e->modelist, mode);
|
|
||||||
} else if(p[2]==0) { /* monitor descriptor block */
|
|
||||||
switch(p[3]) {
|
|
||||||
case 0xFF: /* monitor serial number (13-byte ascii, 0A terminated) */
|
|
||||||
if(q = memchr(p+5, 0x0A, 13))
|
|
||||||
*q = '\0';
|
|
||||||
memset(e->serialstr, 0, sizeof(e->serialstr));
|
|
||||||
strncpy(e->serialstr, (char*)p+5, 13);
|
|
||||||
break;
|
|
||||||
case 0xFE: /* ascii string (13-byte ascii, 0A terminated) */
|
|
||||||
break;
|
|
||||||
case 0xFD: /* monitor range limits */
|
|
||||||
e->rrmin = p[5];
|
|
||||||
e->rrmax = p[6];
|
|
||||||
e->hrmin = p[7]*1000;
|
|
||||||
e->hrmax = p[8]*1000;
|
|
||||||
if(p[9] != 0xFF)
|
|
||||||
e->pclkmax = p[9]*10*1000000;
|
|
||||||
break;
|
|
||||||
case 0xFC: /* monitor name (13-byte ascii, 0A terminated) */
|
|
||||||
if(q = memchr(p+5, 0x0A, 13))
|
|
||||||
*q = '\0';
|
|
||||||
memset(e->name, 0, sizeof(e->name));
|
|
||||||
strncpy(e->name, (char*)p+5, 13);
|
|
||||||
break;
|
|
||||||
case 0xFB: /* extra color point data */
|
|
||||||
break;
|
|
||||||
case 0xFA: /* extra standard timing identifications */
|
|
||||||
for(i=0; i<6; i++)
|
|
||||||
if(decodesti(&mode, p+5+2*i) == 0)
|
|
||||||
e->modelist = addmode(e->modelist, mode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(p == (uchar*)v+8+10+2+5+10+3+16+72);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
fixbios(Vbe *vbe)
|
fixbios(Vbe *vbe)
|
||||||
{
|
{
|
||||||
|
@ -1424,3 +955,4 @@ fixbios(Vbe *vbe)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,8 +112,8 @@ typedef struct Mode {
|
||||||
int vrs; /* Vertical Retrace Start (Crt10) */
|
int vrs; /* Vertical Retrace Start (Crt10) */
|
||||||
int vre; /* Vertical Retrace End (Crt11) */
|
int vre; /* Vertical Retrace End (Crt11) */
|
||||||
|
|
||||||
int vbs; /* optional Vertical Blank Start */
|
int vbs; /* optional Vertical Blank Start */
|
||||||
int vbe; /* optional Vertical Blank End */
|
int vbe; /* optional Vertical Blank End */
|
||||||
|
|
||||||
ulong videobw;
|
ulong videobw;
|
||||||
|
|
||||||
|
@ -252,6 +252,9 @@ extern int dbctlr(char*, Vga*);
|
||||||
extern Mode* dbmode(char*, char*, char*);
|
extern Mode* dbmode(char*, char*, char*);
|
||||||
extern void dbdumpmode(Mode*);
|
extern void dbdumpmode(Mode*);
|
||||||
|
|
||||||
|
/* edid.c */
|
||||||
|
extern Mode* edidmode(uchar *, int);
|
||||||
|
|
||||||
/* error.c */
|
/* error.c */
|
||||||
extern void error(char*, ...);
|
extern void error(char*, ...);
|
||||||
extern void trace(char*, ...);
|
extern void trace(char*, ...);
|
||||||
|
@ -438,6 +441,9 @@ extern int dbvesa(Vga*);
|
||||||
extern Mode *dbvesamode(char*);
|
extern Mode *dbvesamode(char*);
|
||||||
extern void vesatextmode(void);
|
extern void vesatextmode(void);
|
||||||
|
|
||||||
|
/* vesadb.c */
|
||||||
|
extern Mode *vesamodes[];
|
||||||
|
|
||||||
/* virge.c */
|
/* virge.c */
|
||||||
extern Ctlr virge;
|
extern Ctlr virge;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue