deal with some strange vesa modes. Reference: /n/atom/patch/applied/vesanotsquare Date: Thu Apr 30 15:15:15 CES 2015 Signed-off-by: quanstro@quanstro.net --- /sys/src/cmd/aux/vga/main.c Thu Apr 30 15:14:55 2015 +++ /sys/src/cmd/aux/vga/main.c Thu Apr 30 15:14:56 2015 @@ -163,6 +163,7 @@ Vga *vga; fmtinstall('H', encodefmt); + quotefmtinstall(); Binit(&stdout, 1, OWRITE); bios = getenv("vgactlr"); --- /sys/src/cmd/aux/vga/error.c Thu Apr 30 15:14:58 2015 +++ /sys/src/cmd/aux/vga/error.c Thu Apr 30 15:14:59 2015 @@ -15,7 +15,7 @@ int n; sequencer(0, 1); - n = sprint(buf, "%s: ", argv0); + n = snprint(buf, sizeof buf, "%s: ", argv0); va_start(arg, format); out = vseprint(buf+n, buf+sizeof(buf)-n, format, arg); va_end(arg); --- /sys/src/cmd/aux/vga/vesa.c Thu Apr 30 15:15:02 2015 +++ /sys/src/cmd/aux/vga/vesa.c Thu Apr 30 15:15:03 2015 @@ -27,8 +27,10 @@ int memfd; /* /dev/realmem */ uchar *mem; /* copy of memory; 1MB */ uchar *isvalid; /* 1byte per 4kB in mem */ - uchar *buf; uchar *modebuf; + int dspcon; /* connected displays bitmask */ + int dspact; /* active displays bitmask */ + void (*scale)(Vga*, Ctlr*); }; struct Vmode @@ -93,6 +95,8 @@ static Vbe *vbe; static Edid edid; +extern Mode *vesamodes[]; + Vbe *mkvbe(void); int vbecheck(Vbe*); uchar *vbemodes(Vbe*); @@ -105,14 +109,21 @@ void vesaddc(void); int vbeddcedid(Vbe *vbe, Edid *e); void printedid(Edid*); +int setdisplay(Vbe *vbe, int display); +int getdisplay(Vbe *vbe); +void fixbios(Vbe*); +uchar* vbesetup(Vbe*, Ureg*, int); +int vbecall(Vbe*, Ureg*); int dbvesa(Vga* vga) { - vbe = mkvbe(); if(vbe == nil){ - fprint(2, "mkvbe: %r\n"); - return 0; + vbe = mkvbe(); + if(vbe == nil){ + fprint(2, "mkvbe: %r\n"); + return 0; + } } if(vbecheck(vbe) < 0){ fprint(2, "dbvesa: %r\n"); @@ -130,76 +141,183 @@ return 1; } +static void +addattr(Mode *m, char *attr, char *val) +{ + Attr *a; + + a = alloc(sizeof(*a)); + a->attr = attr; + a->val = val; + a->next = m->attr; + m->attr = a; +} + Mode* -dbvesamode(char *mode) +edidmode(Vmode *vm) +{ + Mode *m; + Modelist *l; + + for(l = edid.modelist; l != nil; l = l->next){ + if(l->x != vm->dx || l->y != vm->dy) + continue; + m = l; + return m; + } + return nil; +} + +Mode* +vesamode0(Vmode *vm) { int i; + static Mode m; + + for(i=0; vesamodes[i] != nil; i++){ + if(vesamodes[i]->x != vm->dx || vesamodes[i]->y != vm->dy) + continue; + return vesamodes[i]; + } + m.x = vm->dx; + m.y = vm->dy; + m.ht = m.x; + m.shb = m.x; + m.ehb = m.x; + m.shs = m.x; + m.ehs = m.x; + m.vt = m.y; + m.vrs = m.y; + m.vre = m.y; + m.frequency = m.ht * m.vt * 60; + return &m; +} + +Mode* +mkmode(Vmode *vm, char *scale, int display) +{ + int width; + Mode *m, *v, *e; + + e = edidmode(vm); + if(e == nil && edid.modelist != nil){ + fprint(2, "dbvesamode: mode %dx%dx0 not in edid info\n", vm->dx, vm->dy); + return nil; + } + v = vesamode0(vm); + if(v == nil){ + fprint(2, "dbvesamode: mode %dx%dx0 not in vesa info\n", vm->dx, vm->dy); + return nil; + } + if(e != nil && v != nil && (e->x != v->x || e->y != v->y)){ + fprint(2, "dbvesamode: mode %dx%dx0 !~ %dx%dx%d not in vesa info", + e->x, e->y, v->x, v->y, vm->depth); + return nil; + } + + m = alloc(sizeof *m); + if(e != nil) + *m = *e; + else + *m = *v; + + strcpy(m->type, "vesa"); + strcpy(m->size, vm->name); + strcpy(m->chan, vm->chan); + m->z = vm->depth; + + addattr(m, "id", smprint("%#x", vm->id)); + if(scale != nil) + addattr(m, "scale", smprint("%s", scale)); + if(display != 0) + addattr(m, "display", smprint("%d", display)); + if((width = vm->bpl * 8 / m->z) > m->x) + addattr(m, "virtx", smprint("%d", width)); + + return m; +} + +Mode* +dbvesamode(char *mode) +{ + int i, n, display, oldmode, olddisplay; uchar *p, *ep; Vmode vm; - Mode *m; - + char *scale, *f[6]; + if(vbe == nil) return nil; - p = vbemodes(vbe); - if(p == nil) - return nil; - for(ep=p+1024; (p[0]!=0xFF || p[1]!=0xFF) && ptype, "vesa"); - strcpy(m->size, vm.name); - strcpy(m->chan, vm.chan); - m->frequency = 100; - m->x = vm.dx; - m->y = vm.dy; - m->z = vm.depth; - m->ht = m->x; - m->shb = m->x; - m->ehb = m->x; - m->shs = m->x; - m->ehs = m->x; - m->vt = m->y; - m->vrs = m->y; - m->vre = m->y; - - m->attr = alloc(sizeof(Attr)); - m->attr->attr = "id"; - m->attr->val = alloc(32); - sprint(m->attr->val, "0x%x", vm.id); - return m; } static void snarf(Vga* vga, Ctlr* ctlr) { - if(!vbe) + if(vbe == nil) vbe = mkvbe(); - if(vbe) + if(vbe != nil) vga->vesa = ctlr; vbesnarf(vbe, vga); vga->linear = 1; - ctlr->flag |= Hlinear|Ulinear; + ctlr->flag |= Hlinear|Ulinear|Fsnarf; +} + +static void +options(Vga *vga, Ctlr *ctlr) +{ + char *v; + + if(v = dbattr(vga->mode->attr, "virtx")){ + vga->virtx = atoi(v); + vga->virty = vga->mode->y; + vga->panning = 0; + } + ctlr->flag |= Foptions; } static void @@ -210,7 +328,8 @@ if(vbesetmode(vbe, atoi(dbattr(vga->mode->attr, "id"))) < 0){ ctlr->flag |= Ferror; fprint(2, "vbesetmode: %r\n"); - } + }else if(vbe->scale != nil) + vbe->scale(vga, ctlr); } static void @@ -220,7 +339,7 @@ char did[0x200]; uchar *p, *ep; - if(!vbe){ + if(vbe == nil){ Bprint(&stdout, "no vesa bios\n"); return; } @@ -228,7 +347,7 @@ memset(did, 0, sizeof did); vbeprintinfo(vbe); p = vbemodes(vbe); - if(p){ + if(p != nil){ for(ep=p+1024; (p[0]!=0xFF || p[1]!=0xFF) && pmode->attr, "scale"); + if(scale == nil) + cx = 0; + else if(strcmp(scale, "scalefull") == 0) + cx = 4; + else{ + ctlr->flag |= Ferror; + fprint(2, "vbescale: unsupported mode %s\n", scale); + return; + } + + vbesetup(vbe, &u, 0x5F61); + u.bx = 0; + u.cx = cx; /* horizontal */ + u.dx = cx; /* vertical */ + if(vbecall(vbe, &u) < 0 && (u.ax&0xFFFF) != 0x5F) + fprint(2, "vbescale: %r\n"); +} + +static void +nvidiascale(Vga* vga, Ctlr* ctlr) +{ + Ureg u; + int cx; + char *scale; + + if(vbe == nil) + error("no vesa bios\n"); + + scale = dbattr(vga->mode->attr, "scale"); + if(scale == nil) + cx = 1; + else if(strcmp(scale, "scaleaspect") == 0) + cx = 3; + else if(strcmp(scale, "scalefull") == 0) + cx = 0; + else{ + ctlr->flag |= Ferror; + fprint(2, "vbescale: unsupported mode %s\n", scale); + return; + } + + vbesetup(vbe, &u, 0x4F14); + u.bx = 0x102; + u.cx = cx; + if(vbecall(vbe, &u) < 0) + fprint(2, "vbescale: %r\n"); +} + Ctlr vesa = { "vesa", /* name */ snarf, /* snarf */ - 0, /* options */ - 0, /* init */ + options, /* options */ + nil, /* init */ load, /* load */ dump, /* dump */ }; @@ -278,19 +457,36 @@ 0 }; +enum { + AttrSupported = 1<<0, + AttrTTY = 1<<2, + AttrColor = 1<<3, + AttrGraphics = 1<<4, + AttrNotVGA = 1<<5, + AttrNotWinVGA = 1<<6, + AttrLinear = 1<<7, + AttrDoublescan = 1<<8, + AttrInterlace = 1<<9, + AttrTriplebuf = 1<<10, + AttrStereo = 1<<11, + AttrDualAddr = 1<<12, + + AttrRequired = AttrSupported | AttrGraphics | AttrLinear, +}; + static Flag modeattributesflags[] = { - 1<<0, "supported", - 1<<2, "tty", - 1<<3, "color", - 1<<4, "graphics", - 1<<5, "not-vga", - 1<<6, "no-windowed-vga", - 1<<7, "linear", - 1<<8, "double-scan", - 1<<9, "interlace", - 1<<10, "triple-buffer", - 1<<11, "stereoscopic", - 1<<12, "dual-start-addr", + AttrSupported, "supported", + AttrTTY, "tty", + AttrColor, "color", + AttrGraphics, "graphics", + AttrNotVGA, "not-vga", + AttrNotWinVGA, "no-windowed-vga", + AttrLinear, "linear", + AttrDoublescan, "double-scan", + AttrInterlace, "interlace", + AttrTriplebuf, "triple-buffer", + AttrStereo, "stereoscopic", + AttrDualAddr, "dual-start-addr", 0 }; @@ -307,8 +503,26 @@ 0 }; +enum { + ModText = 0, + ModCGA, + ModHercules, + ModPlanar, + ModPacked, + ModNonChain4, + ModDirect, + ModYUV, +}; + static char *modelstr[] = { - "text", "cga", "hercules", "planar", "packed", "non-chain4", "direct", "YUV" + [ModText] "text", + [ModCGA] "cga", + [ModHercules] "hercules", + [ModPlanar] "planar", + [ModPacked] "packed", + [ModNonChain4] "non-chain4", + [ModDirect] "direct", + [ModYUV] "YUV", }; static void @@ -334,72 +548,104 @@ return nil; vbe->mem = alloc(MemSize); vbe->isvalid = alloc(MemSize/PageSize); - vbe->buf = alloc(PageSize); vbe->modebuf = alloc(PageSize); + fixbios(vbe); return vbe; } static void -loadpage(Vbe *vbe, int p) +loadpage(Vbe *vbe, int p, int wr) +{ + if(p >= MemSize/PageSize) + return; + if(vbe->isvalid[p] == 0) + if(pread(vbe->memfd, vbe->mem+p*PageSize, PageSize, p*PageSize) != PageSize) + error("read /dev/realmodemem: %r\n"); + vbe->isvalid[p] = 1+wr; +} + +static void +flushpage(Vbe *vbe, int p) { - if(p >= MemSize/PageSize || vbe->isvalid[p]) + if(p >= MemSize/PageSize || vbe->isvalid[p]!=2) return; - if(pread(vbe->memfd, vbe->mem+p*PageSize, PageSize, p*PageSize) != PageSize) - error("read /dev/realmodemem: %r\n"); + if(pwrite(vbe->memfd, vbe->mem+p*PageSize, PageSize, p*PageSize) != PageSize) + error("write /dev/realmodemem: %r\n"); vbe->isvalid[p] = 1; } static void* +getmem(Vbe *vbe, int off, int wr) +{ + if(off == 0 || off >= MemSize) + return nil; + loadpage(vbe, off/PageSize, wr); + if(off % PageSize) + loadpage(vbe, (off/PageSize)+1, wr); + return vbe->mem+off; +} + +static void* unfarptr(Vbe *vbe, uchar *p) { int seg, off; seg = WORD(p+2); off = WORD(p); - if(seg==0 && off==0) - return nil; off += seg<<4; - if(off >= MemSize) - return nil; - loadpage(vbe, off/PageSize); - loadpage(vbe, off/PageSize+1); /* just in case */ - return vbe->mem+off; + return getmem(vbe, off, 0); } uchar* vbesetup(Vbe *vbe, Ureg *u, int ax) { - memset(vbe->buf, 0, PageSize); + uchar *p; + memset(u, 0, sizeof *u); u->ax = ax; u->es = (RealModeBuf>>4)&0xF000; u->di = RealModeBuf&0xFFFF; - return vbe->buf; + p = getmem(vbe, RealModeBuf, 1); + memset(p, 0, PageSize); + return p; +} + +void +vbeflush(Vbe *vbe) +{ + int p; + + for(p=0; pisvalid, 0, MemSize/PageSize); } int vbecall(Vbe *vbe, Ureg *u) { u->trap = 0x10; - if(pwrite(vbe->memfd, vbe->buf, PageSize, RealModeBuf) != PageSize) - error("write /dev/realmodemem: %r\n"); + + vbeflush(vbe); + if(pwrite(vbe->rmfd, u, sizeof *u, 0) != sizeof *u) error("write /dev/realmode: %r\n"); if(pread(vbe->rmfd, u, sizeof *u, 0) != sizeof *u) error("read /dev/realmode: %r\n"); - if(pread(vbe->memfd, vbe->buf, PageSize, RealModeBuf) != PageSize) - error("read /dev/realmodemem: %r\n"); + + getmem(vbe, RealModeBuf, 0); + if((u->ax&0xFFFF) != 0x004F){ werrstr("VBE error %#.4lux", u->ax&0xFFFF); return -1; } - memset(vbe->isvalid, 0, MemSize/PageSize); return 0; } int vbecheck(Vbe *vbe) { + char *oem; uchar *p; Ureg u; @@ -411,6 +657,23 @@ werrstr("invalid vesa signature %.4H %.4H\n", p, p+4); return -1; } + oem = unfarptr(vbe, p+6); + if(strncmp(oem, "Intel", 5) == 0){ + vbe->scale = intelscale; + + /* detect connected display devices */ + vbesetup(vbe, &u, 0x5F64); + u.bx = 0x200; + vbecall(vbe, &u); + vbe->dspcon = u.cx >> 8; /* CH = connected, CL = available? */ + + /* detect active display devices */ + vbesetup(vbe, &u, 0x5F64); + u.bx = 0x100; + vbecall(vbe, &u); + vbe->dspact = u.cx; + }else if(memcmp(oem, "NVIDIA", 6) == 0) + vbe->scale = nvidiascale; return 0; } @@ -427,6 +690,7 @@ if(memcmp(p, "VESA", 4) != 0 || p[5] < 2) return -1; vga->apz = WORD(p+18)*0x10000UL; + vbeddcedid(vbe, &edid); return 0; } @@ -434,6 +698,7 @@ vbeprintinfo(Vbe *vbe) { uchar *p; + int i; Ureg u; p = vbesetup(vbe, &u, 0x4F00); @@ -460,6 +725,18 @@ printitem("vesa", "mem"); Bprint(&stdout, "%lud\n", WORD(p+18)*0x10000UL); + + printitem("vesa", "dsp con"); + for(i = 0; i < 8; i++) + if(vbe->dspcon & (1<dspact & (1<chan]; @@ -496,7 +773,23 @@ m->dx = WORD(p+18); m->dy = WORD(p+20); m->depth = p[25]; - m->model = p[27] < nelem(modelstr) ? modelstr[p[27]] : "unknown"; + if((m->attr & AttrRequired) != AttrRequired + || m->dx * m->dy * m->depth < 0 || m->bpl <= 0){ + werrstr("mode unsupported"); + return -1; + } + mod = p[27]; + switch(mod){ + default: + werrstr("mode unsupported"); + return -1; + case ModCGA: + case ModHercules: + case ModPacked: + case ModDirect: + m->model = modelstr[mod]; + break; + } m->r = p[31]; m->g = p[33]; m->b = p[35]; @@ -510,7 +803,8 @@ snprint(m->name, sizeof m->name, "%dx%dx%d", m->dx, m->dy, m->depth); if(m->depth <= 8) { - snprint(m->chan, sizeof m->chan, "m%d", m->depth); + snprint(m->chan, sizeof m->chan, "%c%d", + (m->attr & AttrColor) ? 'm' : 'k', m->depth); return 0; } @@ -612,7 +906,7 @@ { if(vbe == nil){ vbe = mkvbe(); - if(!vbe) + if(vbe == nil) error("mkvbe: %r\n"); } if(vbecheck(vbe) < 0) @@ -638,7 +932,7 @@ { uchar *p; Ureg u; - + p = vbesetup(vbe, &u, 0x4F15); u.bx = 0x0001; if(vbecall(vbe, &u) < 0) @@ -649,12 +943,12 @@ } return 0; } - + void printedid(Edid *e) { Modelist *l; - + printitem("edid", "mfr"); Bprint(&stdout, "%s\n", e->mfr); printitem("edid", "serialstr"); @@ -681,7 +975,7 @@ 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", @@ -695,7 +989,6 @@ int rr; Modelist **lp; -//m.z = 8; // BUG rr = (m.frequency+m.ht*m.vt/2)/(m.ht*m.vt); snprint(m.name, sizeof m.name, "%dx%dx%d@%dHz", m.x, m.y, m.z, rr); @@ -837,8 +1130,6 @@ return 0; } -extern Mode *vesamodes[]; - int vesalookup(Mode *m, char *name) { @@ -982,7 +1273,7 @@ */ estab = (p[0]<<16) | (p[1]<<8) | p[2]; p += 3; - + for(i=0, m=1<<23; i>=1) if(estab & m) if(vesalookup(&mode, estabtime[i]) == 0) @@ -1001,9 +1292,7 @@ /* * Detailed Timings */ -fprint(2, "dt\n"); for(i=0; i<4; i++, p+=18) { -fprint(2, "%.8H\n", p); 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); @@ -1018,7 +1307,6 @@ case 0xFE: /* ascii string (13-byte ascii, 0A terminated) */ break; case 0xFD: /* monitor range limits */ - print("fd %.18H\n", p); e->rrmin = p[5]; e->rrmax = p[6]; e->hrmin = p[7]*1000; @@ -1045,4 +1333,73 @@ assert(p == (uchar*)v+8+10+2+5+10+3+16+72); return 0; +} + +int +getdisplay(Vbe *vbe) +{ + int i; + + for(i = 0; i < 8; i++) + if(vbe->dspact & 1<dspcon & 1<dspcon & cx){ + /* switch to common mode before trying */ + vbesetmode(vbe, 3); + + vbesetup(vbe, &u, 0x5F64); + u.bx = 0; + u.cx = cx; + vbecall(vbe, &u); + if(u.ax == 0x5f) + return 0; + werrstr("setdisplay: VBE error %#.4lux", u.ax); + }else + werrstr("setdisplay: %d not connected", display); + return -1; +} + +void +fixbios(Vbe *vbe) +{ + uchar *p; + int i; + + /* + * Intel(r) Cantiga Graphics Chip Accelerated VGA BIOS 1.0 has + * a wrong entry in mode alias table at c000:7921 for mode 0x16b + * (1440x900x32) wrongly replacing it with mode 0x162 (768x480x32). + */ + p = getmem(vbe, 0xc7921, 0); + if(p != nil && p[0] == 0x01 && p[1] == 0xff && p[2] == 0xff){ + for(i=1; i<64; i++){ + p = getmem(vbe, 0xc7921 + 3*i, 0); + if(p == nil || p[0] == 0xff) + break; + if(p[0] == 0x6b && p[1] == 0x6b && p[2] == 0x62){ + p = getmem(vbe, 0xc7921 + 3*i, 1); + p[2] = 0x6b; /* fix */ + break; + } + } + } }