From f1b1e0d637e5a280aebbdf01b905b90320ad7f43 Mon Sep 17 00:00:00 2001 From: ftrvxmtrx Date: Thu, 1 Jan 2015 16:19:24 +0100 Subject: [PATCH] aux/vga: display switching for Intel adapters Magic was discovered by abusing INT 10 on several machines while switching cables back and forth and watching the end result. --- sys/man/8/vga | 26 +++++++- sys/src/cmd/aux/vga/vesa.c | 124 ++++++++++++++++++++++++++++++++----- 2 files changed, 132 insertions(+), 18 deletions(-) diff --git a/sys/man/8/vga b/sys/man/8/vga index ca2ad6da4..ff62768bd 100644 --- a/sys/man/8/vga +++ b/sys/man/8/vga @@ -99,7 +99,7 @@ as the VGA database rather than .PP .I Mode is of the form -.IB X x Y x Z ,S +.IB X x Y x Z[,S][,#N] , where .IR X , .IR Y , @@ -112,6 +112,10 @@ is scaling mode, either or .IR scaleaspect ; not specifying it disables scaling altogether. +.I #N +is used to switch to a specific display using its index +.IR N . +.PP The mode must appear in .B /lib/vgadb as a value for one of the monitor entries. @@ -150,7 +154,7 @@ There are no entries for the .B vesa monitor in .IR vgadb . -For a list of available VESA modes, use +For a list of available VESA modes and connected displays, use .IP .EX aux/vga -m vesa -p @@ -172,6 +176,20 @@ Change the display resolution: aux/vga -l 1600x1200x8 .EE .PP +Show connected and active displays: +.IP +.EX +aux/vga -m vesa -p | grep dsp +.EE +.PP +Switch to display +.I 4 +and load a specific mode: +.IP +.EX +aux/vga -m vesa -l '1920x1080x16,#4' +.EE +.PP Print the current VGA controller registers. It is usually best to redirect the output of a .B -p @@ -219,7 +237,11 @@ new VGA controller or monitor setting. It is useful in such cases to have the above command for setting the controller to a known state at your fingertips. +.PP Scaling modes currently work with Intel and NVIDIA video adapters only, using VESA. Intel doesn't support .I scaleaspect mode. +.PP +Display switching currently works with Intel video adapters +only, using VESA. diff --git a/sys/src/cmd/aux/vga/vesa.c b/sys/src/cmd/aux/vga/vesa.c index b04b8cbb7..699b2f8bf 100644 --- a/sys/src/cmd/aux/vga/vesa.c +++ b/sys/src/cmd/aux/vga/vesa.c @@ -26,7 +26,9 @@ struct Vbe uchar *mem; /* copy of memory; 1MB */ uchar *isvalid; /* 1byte per 4kB in mem */ uchar *modebuf; - void (*scale)(Vga*, Ctlr*); + int dspcon; /* connected displays bitmask */ + int dspact; /* active displays bitmask */ + void (*scale)(Vga*, Ctlr*, int); }; struct Vmode @@ -98,7 +100,7 @@ int vbecheck(Vbe*); uchar *vbemodes(Vbe*); int vbemodeinfo(Vbe*, int, Vmode*); int vbegetmode(Vbe*); -int vbesetmode(Vbe*, int); +int vbesetmode(Vbe*, int, int); void vbeprintinfo(Vbe*); void vbeprintmodeinfo(Vbe*, int, char*); int vbesnarf(Vbe*, Vga*); @@ -108,6 +110,7 @@ void printedid(Edid*); void fixbios(Vbe*); uchar* vbesetup(Vbe*, Ureg*, int); int vbecall(Vbe*, Ureg*); +int setdisplay(Vbe *vbe, int display); int dbvesa(Vga* vga) @@ -139,19 +142,31 @@ dbvesa(Vga* vga) Mode* dbvesamode(char *size) { - int i, width; + int i, width, nargs; uchar *p, *ep; Attr *a; Vmode vm; Mode *m; Modelist *l; - char *scale; + char *args[4], *scale, *display; if(vbe == nil) return nil; - if(scale = strchr(size, ',')) - *scale++ = 0; + scale = nil; + display = nil; + nargs = getfields(size, args, 4, 0, ","); + if(nargs > 1){ + if(args[1][0] == '#'){ + display = &args[1][1]; + if(nargs > 2) + scale = args[2]; + }else if(args[1][0] == 's'){ + scale = args[1]; + if(nargs > 2) + display = &args[2][1]; + } + } if(strncmp(size, "0x", 2) == 0){ if(vbemodeinfo(vbe, strtol(size+2, nil, 16), &vm) == 0) @@ -232,6 +247,17 @@ havemode: m->attr = a; } + /* display id */ + if(display != nil){ + a = alloc(sizeof(Attr)); + a->attr = "display"; + a->val = alloc(2); + strncpy(a->val, display, 2); + + a->next = m->attr; + m->attr = a; + } + /* account for framebuffer stride */ width = vm.bpl * 8 / m->z; if(width > m->x){ @@ -275,13 +301,24 @@ options(Vga *vga, Ctlr *ctlr) static void load(Vga* vga, Ctlr* ctlr) { + int mode, display; + char *ds; + if(vbe == nil) error("no vesa bios\n"); - if(vbesetmode(vbe, atoi(dbattr(vga->mode->attr, "id"))) < 0){ + mode = atoi(dbattr(vga->mode->attr, "id")); + ds = dbattr(vga->mode->attr, "display"); + display = ds == nil ? 0 : atoi(ds); + + /* need to reset scaling before switching displays */ + if(vbe->scale != nil) + vbe->scale(vga, ctlr, 1); + + if(vbesetmode(vbe, mode, display) < 0){ ctlr->flag |= Ferror; fprint(2, "vbesetmode: %r\n"); }else if(vbe->scale != nil) - vbe->scale(vga, ctlr); + vbe->scale(vga, ctlr, 0); } static void @@ -314,7 +351,7 @@ dump(Vga*, Ctlr*) } static void -intelscale(Vga* vga, Ctlr* ctlr) +intelscale(Vga* vga, Ctlr* ctlr, int reset) { Ureg u; int cx; @@ -334,17 +371,20 @@ intelscale(Vga* vga, Ctlr* ctlr) fprint(2, "vbescale: unsupported mode %s\n", scale); return; } + if(reset) + cx = 4; 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"); + vbecall(vbe, &u); + if(u.ax != 0x5f) + fprint(2, "vbescale: %#.4lux", u.ax); } static void -nvidiascale(Vga* vga, Ctlr* ctlr) +nvidiascale(Vga* vga, Ctlr* ctlr, int reset) { Ureg u; int cx; @@ -365,6 +405,8 @@ nvidiascale(Vga* vga, Ctlr* ctlr) fprint(2, "vbescale: unsupported mode %s\n", scale); return; } + if(reset) + cx = 0; vbesetup(vbe, &u, 0x4F14); u.bx = 0x102; @@ -609,9 +651,21 @@ vbecheck(Vbe *vbe) return -1; } oem = unfarptr(vbe, p+6); - if(memcmp(oem, "Intel", 5) == 0) + if(strncmp(oem, "Intel", 5) == 0){ vbe->scale = intelscale; - else if(memcmp(oem, "NVIDIA", 6) == 0) + + /* 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; } @@ -644,6 +698,7 @@ vbeprintinfo(Vbe *vbe) { uchar *p; Ureg u; + int i; p = vbesetup(vbe, &u, 0x4F00); strcpy((char*)p, "VBE2"); @@ -669,6 +724,18 @@ vbeprintinfo(Vbe *vbe) 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<dspcon & cx){ + 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 printedid(Edid *e) { -- 2.44.0