]> git.lizzy.rs Git - plan9front.git/commitdiff
aux/vga: display switching for Intel adapters
authorftrvxmtrx <ftrvxmtrx@gmail.com>
Thu, 1 Jan 2015 15:19:24 +0000 (16:19 +0100)
committerftrvxmtrx <ftrvxmtrx@gmail.com>
Thu, 1 Jan 2015 15:19:24 +0000 (16:19 +0100)
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
sys/src/cmd/aux/vga/vesa.c

index ca2ad6da4da73fded893d010d469fae72c9e4dfe..ff62768bdcd38cf2592cbcb15a5e6652295308ad 100644 (file)
@@ -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.
index b04b8cbb780d234c868cadce030cab6abfea99f8..699b2f8bf15c91382c027db795158d8b65f65996 100644 (file)
@@ -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<<i))
+                       Bprint(&stdout, "%d ", i+1);
+       Bprint(&stdout, "\n");
+
+       printitem("vesa", "dsp act");
+       for(i = 0; i < 8; i++)
+               if(vbe->dspact & (1<<i))
+                       Bprint(&stdout, "%d ", i+1);
+       Bprint(&stdout, "\n");
 }
 
 uchar*
@@ -805,10 +872,12 @@ vbegetmode(Vbe *vbe)
 }
 
 int
-vbesetmode(Vbe *vbe, int id)
+vbesetmode(Vbe *vbe, int id, int display)
 {
        Ureg u;
 
+       if(setdisplay(vbe, display) < 0)
+               return -1;
        vbesetup(vbe, &u, 0x4F02);
        u.bx = id;
        if(id != 3)
@@ -826,7 +895,7 @@ vesatextmode(void)
        }
        if(vbecheck(vbe) < 0)
                error("vbecheck: %r\n");
-       if(vbesetmode(vbe, 3) < 0)
+       if(vbesetmode(vbe, 3, 0) < 0)
                error("vbesetmode: %r\n");
 }
 
@@ -859,6 +928,29 @@ vbeddcedid(Vbe *vbe, Edid *e)
        return 0;
 }
 
+int
+setdisplay(Vbe *vbe, int display)
+{
+       Ureg u;
+       int cx;
+
+       if(display == 0)
+               return 0;
+
+       cx = 1<<(display-1);
+       if(vbe->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)
 {