/* This is part of the NEWTRACK eyetracking software, (c) 2004 by */ /* Eric Auer. NEWTRACK is free software; you can redistribute it */ /* and modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* NEWTRACK is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty */ /* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /* See the GNU General Public License for more details. */ /* You should have received a copy of the GNU General Public */ /* License (license.txt) along with this program; if not, check */ /* www.gnu.org or write to the Free Software Foundation, Inc., 59 */ /* Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /* screen output functions with dual PCI/AGP VGA support */ #include "tracker.h" /* includes all the headers */ uint16 ScreenSeg = 0xb800; /* accessed by screen.c put_string, too */ int current_screen = -1; /* 0 or 1, -1 for unknown */ int lines_on_screen = 0; /* negative for fake dual screen mode */ /* Supports: PCI+PCI, AGP+PCI, anycolor + anymono (non-PCI dualhead mode). */ uint32 agp_bridge = 0; /* PCI id of AGP bridge, 0 if none */ uint32 agp_vga = 0; /* PCI id of AGP or 2nd PCI VGA (0 for non-PCI...) */ uint32 pci_vga = 0; /* PCI id of 1st PCI VGA (0 for non-PCI dualhead) */ uint32 pci_configs[6]; /* 1st bridge/agp/pci, 2nd bridge/agp/pci */ #define PCIaddr 0xcf8 /* PCI configuration space: 32 bit address port */ #define PCIdata 0xcfc /* PCI configuration space: 32 bit data port */ int detect_crtc (uint16 port) /* check if a 6845 CRTC chip is present */ { int cursor_location_low; int index, found; found = 0; index = in8 (port); /* save */ out8 (port, 0x0e); /* select cursor location low */ cursor_location_low = in8 (port+1); /* read it */ out8 (port, 0x0e); /* select cursor location low */ out8 (port+1, cursor_location_low ^ 0x0a); /* some test value */ /* 0xaa does not work: in ancient MONO cards, 2 MSB stick to 0 ! */ out8 (port, 0x0e); /* select cursor location low */ if ( (cursor_location_low ^ in8 (port+1)) == 0x0a) { /* read it */ found++; /* yes, there was a writeable reg. here! */ }; out8 (port, 0x0e); /* select cursor location low */ out8 (port+1, cursor_location_low); /* restore value */ out8 (port, index); /* restore */ return found; /* nonzero if CRTC found */ } /* detect_crtc */ void dual_screen_init(void) { int86regs r; int busses, bus, dev; uint32 pcisel, pcival, pci_id; lines_on_screen = peekb (0x40, 0x84) + 1; /* max. row number from BIOS */ if (lines_on_screen < 20) lines_on_screen = 25; current_screen = 0; /* mark init as done! */ pci_vga = agp_vga = agp_bridge = 0; /* start in non-PCI mode */ r.d.eax = 0xb101; /* PCI BIOS 2.0c+ install check */ r.d.edi = 0; (void) intr (0x1a, &r, &r); /* this can eat up to 1k of stack space! */ if ( (r.h.ah != 0) || (r.d.edx != 0x20494350 /* " ICP" */) ) { printf ("Sorry, PCI BIOS 2.0c or newer required for dual-head.\n"); #if 1 goto probe_for_mono; #else lines_on_screen = -lines_on_screen; return; #endif } if (!(r.h.al & 1)) { printf ("Only PCI config mechanism 1 supported for dual-head.\n"); #if 1 goto probe_for_mono; #else lines_on_screen = -lines_on_screen; return; #endif } busses = r.h.cl + 1; for (bus = 0; bus < busses; bus++) { for (dev = 0; dev < 32; dev++) { pcisel = (dev<<11) | (bus<<16) | 0x80000000; /* only interested in function 0 of each card */ out32 (PCIaddr, pcisel); /* reg 0: ident */ pci_id = in32 (PCIdata); if ((!pci_id) || (pci_id == 0xffffffff)) continue; /* no device, try next */ out32 (PCIaddr, pcisel + 8); /* reg 8: class.sub.interface.rev */ pcival = in32 (PCIdata) & 0xffffff00; /* ignore revision */ if (pcival == 0x03000000) { /* VGA type device? */ if (!bus) { /* PCI bus? */ if (pci_vga) { if (agp_vga) { printf ("Additional PCI VGA (0.%d) ignored: %04x:%04x\n", dev, pci_id>>16, pci_id & 0xffff); } else { agp_vga = pcisel; /* 2nd PCI VGA */ printf ("Second PCI VGA (0.%d): %04x:%04x\n", dev, pci_id>>16, pci_id & 0xffff); } } else { pci_vga = pcisel; /* 1st PCI VGA */ printf ("First PCI VGA (0.%d): %04x:%04x\n", dev, pci_id>>16, pci_id & 0xffff); } } else { /* else it is AGP */ if (agp_vga) { /* e.g. already 2 PCI VGA */ printf("Additional AGP VGA (%d.%d) ignored: %04x:%04x\n", bus, dev, pci_id>>16, pci_id & 0xffff); } else { agp_vga = pcisel; printf("Found AGP VGA (%d.%d): %04x:%04x\n", bus, dev, pci_id>>16, pci_id & 0xffff); } } /* if bus ... */ } /* VGA */ } /* for devices */ } /* for busses */ bus = (agp_vga >> 16) & 0xff; if (bus) { /* if AGP card is not on primary bus (i.e. is really AGP) */ for (dev = 0; dev < 32; dev++) { pcisel = (dev<<11) | (0<<16) | 0x80000000; out32 (PCIaddr, pcisel); /* reg 0: ident */ pci_id = in32 (PCIdata); if ((!pci_id) || (pci_id == 0xffffffff)) continue; /* no device, try next */ out32 (PCIaddr, pcisel + 0x0c); /* reg E (C): header type */ pcival = (in32 (PCIdata) >> 16) & 0x7f; if (pcival == 1) { /* if PCI-to-PCI bridge... */ out32 (PCIaddr, pcisel + 0x18); /* reg 18: busses */ pcival = (in32 (PCIdata) >> 8) & 0xff; if (pcival == bus) { agp_bridge = pcisel; printf("Found bridge to AGP (0.%d): %04x:%04x\n", dev, pci_id>>16, pci_id & 0xffff); } /* right bridge */ } /* if bridge */ } /* for devices */ } /* need bridge */ probe_for_mono: /* we can still try legacy mode */ if ( (!pci_vga) || (!agp_vga) || (pci_vga == agp_vga) ) { #if 1 uint16 probe1, probe2, result1, result2; probe1 = peek (0xb000, 0); /* test for mono text buffer */ probe2 = peek (0xb800, 0); /* test for color text buffer */ poke (0xb000, 0, probe1 ^ 0x55aa); poke (0xb800, 0, probe2 ^ 0xaa55); result1 = peek (0xb000, 0) ^ probe1; result2 = peek (0xb800, 0) ^ probe2; poke (0xb000, 0, probe1); /* restore contents */ poke (0xb800, 0, probe2); if ( (result1 == 0x55aa) && (result2 == 0xaa55) && detect_crtc (0x3b4) && detect_crtc (0x3d4) ) { /* 3b4 is mono(-mode), 3d4 is color(-mode). */ printf ("Did not find 2 PCI or newer VGA devices, but did find\n"); printf ("Both color and mono text buffers accessible. Will be\n"); printf ("Using classic VGA + mono text style dual-head mode.\n"); /* not only that, we even found 2 CRTC chips */ pci_vga = agp_vga = agp_bridge = 0; /* do not touch this anymore */ return; } #if 0 printf ("Mono buf=%d crtc=%d Color buf=%d crtc=%d\n", result1 == 0x55aa, detect_crtc (0x3b4), result2 == 0xaa55, detect_crtc (0x3d4) ); #endif printf ("Did not find 2 PCI or newer VGA devices.\n"); printf ("Did not find both color and mono text buffers either.\n"); printf ("Using interlaced mode, simulating dual-head on a single screen.\n"); #else printf ("Did not find 2 PCI or newer VGA devices. Dual-head with VGA\n"); printf ("combined with ISA mono not available - using single-head.\n"); #endif lines_on_screen = -lines_on_screen; return; } /* not at least 2 devices */ if (agp_bridge) { out32 (PCIaddr, agp_bridge + 0x3c); /* reg 3E (3C): bridge config */ pcival = in32 (PCIdata) >> 16; pci_configs[3] = pci_configs[0] = pcival; /* AGP bridge control */ } else { pci_configs[0] = 0; /* no bridge involved */ pci_configs[3] = 0; /* no bridge involved */ } out32 (PCIaddr, agp_vga + 4); /* reg 4: command (config) */ pcival = in32 (PCIdata) & 0xffff; pci_configs[4] = pci_configs[1] = pcival; /* AGP device control */ out32 (PCIaddr, pci_vga + 4); /* reg 4: command (config) */ pcival = in32 (PCIdata) & 0xffff; pci_configs[5] = pci_configs[2] = pcival; /* PCI device control */ if ( ( (pci_configs[1] & 3) == (pci_configs[2] & 3) ) || ( (pci_configs[1] & 3) & (pci_configs[2] & 3) ) ) { printf ("Both of the selected VGA devices were %s (%d, %d).\n", (pci_configs[1] & 3) ? "active" : "off", (pci_configs[1] & 3), (pci_configs[2] & 3) ); printf ("Confusing. Using fake dual-head only.\n"); printf("Found: bridge [%x]@%x+3E AGP [%x]@%x+04 PCI [%x]@%x+04\n", pci_configs[0], agp_bridge, pci_configs[1], agp_vga, pci_configs[2], pci_vga); agp_bridge = 0; agp_vga = 0; pci_vga = 0; lines_on_screen = -lines_on_screen; return; } /* both devices are in same state or have overlapping enable bits */ if (pci_configs[2] & 3) { /* was PCI device on? */ #if 0 /* PCI is active already */ pci_configs[0] &= ~8; /* block at least AGP VGA */ pci_configs[1] &= ~0x1ff; /* de-activate AGP device */ pci_configs[2] |= 0x07; /* I/O, MEM, busmaster ON */ #endif pci_configs[3] |= 0x0c; /* VGA enable, ISA enable */ pci_configs[4] |= 0x07; /* AGP */ /* I/O, MEM, busmaster ON */ pci_configs[5] &= ~0x1ff; /* PCI */ /* de-activate PCI device */ } else { /* AGP device was on! */ #if 0 /* AGP is active already */ pci_configs[0] |= 0x0c; /* VGA enable, ISA enable */ pci_configs[1] |= 0x07; /* I/O, MEM, busmaster ON */ pci_configs[2] &= ~0x1ff; /* de-activate PCI device */ #endif pci_configs[3] &= ~0x0c; /* block AGP VGA (bridge) */ pci_configs[4] &= ~0x1ff; /* AGP */ /* de-activate AGP device */ pci_configs[5] |= 0x07; /* PCI */ /* I/O, MEM, busmaster ON */ /* bus master */ } /* creation of secondary configuration */ printf("Using bridge [%x/%x]@%x+3E AGP [%x/%x]@%x+04 PCI [%x/%x]@%x+04\n", pci_configs[0], pci_configs[3], agp_bridge, pci_configs[1], pci_configs[4], agp_vga, pci_configs[2], pci_configs[5], pci_vga); if (peek (0x40, 0x17) & 0x10) { /* Scroll lock active? */ printf ("Scroll lock on: Will only use 1st screen in interlaced mode.\n"); lines_on_screen = -lines_on_screen; /* force to fake mode! */ return; } /* scroll lock cheat mode */ } /* dual_screen_init */ /* activate access of either screen 0 or 1 */ void dual_screen_select(int screen) { uint32 pcival; int base; base = 3 * screen; if ( (screen < 0) || (screen > 1) ) return; /* impossible screen */ if (lines_on_screen < 0) { if (-lines_on_screen & 1) { /* odd -> has unused line */ uint16 idx, cols; cols = peek (0x40, 0x4a); /* as in get_resolution_y() */ idx = cols * 2 * ((-lines_on_screen) - 1); /* offset of last line in buffer */ for (cols--; cols; cols--) /* zap all columns in last line */ poke (0xb800, idx + cols + cols, ' ' | (7 << 8)); } current_screen = screen; /* UPDATE this (for lazy switching)... */ return; /* interlaced mode */ } /* fake mode */ if ( (!pci_vga) || (!agp_vga) ) { /* non-PCI dual head mode */ ScreenSeg = 0xb800; if (screen == 1) ScreenSeg = 0xb000; /* 0: color 1: mono */ if (lines_on_screen > 25) lines_on_screen = 25; /* a bit kludgy! */ /* use mono screen as secondary display in "legacy" dual head mode. */ current_screen = screen; /* UPDATE this (for lazy switching)... */ return; /* rest is done in screen.c */ } if (pci_configs[base] & 8) { /* does transition enable AGP VGA? */ out32 (PCIaddr, pci_vga + 4); /* reg 4: command */ pcival = in32 (PCIdata); /* high word stays untouched */ pcival &= 0xffff0000; pcival |= pci_configs[base+2]; /* disable PCI VGA first */ out32 (PCIaddr, pci_vga + 4); /* reg 4: command */ out32 (PCIdata, pcival); } else { /* else: transition disables AGP VGA */ out32 (PCIaddr, agp_vga + 4); /* reg 4: command */ pcival = in32 (PCIdata); /* high word stays untouched */ pcival &= 0xffff0000; pcival |= pci_configs[base+1]; /* disable AGP VGA first */ out32 (PCIaddr, agp_vga + 4); /* reg 4: command */ out32 (PCIdata, pcival); } /* transition disables AGP VGA */ if (agp_bridge) { /* any real AGP affected? */ out32 (PCIaddr, agp_bridge + 0x3c); /* reg 3E (3c): bridge ctrl */ pcival = pci_configs[base]; pcival <<= 16; /* move to high word */ pcival |= in32 (PCIdata) & 0xffff; /* do not touch low word... */ out32 (PCIaddr, agp_bridge + 0x3c); /* reg 3E (3c): bridge ctrl */ out32 (PCIdata, pcival); /* update bridge state */ } /* AGP bridge control has to be updated */ if (pci_configs[base] & 8) { /* do we enable AGP VGA? */ out32 (PCIaddr, agp_vga + 4); /* reg 4: command */ pcival = in32 (PCIdata); /* high word preserved */ pcival &= 0xffff0000; pcival |= pci_configs[base+1]; /* enable AGP VGA now */ out32 (PCIaddr, agp_vga + 4); /* reg 4: command */ out32 (PCIdata, pcival); } else { /* else: we disable AGP VGA */ out32 (PCIaddr, pci_vga + 4); /* reg 4: command */ pcival = in32 (PCIdata); /* high word preserved */ pcival &= 0xffff0000; pcival |= pci_configs[base+2]; /* enable PCI VGA now */ out32 (PCIaddr, pci_vga + 4); /* reg 4: command */ out32 (PCIdata, pcival); } /* transition disables AGP VGA */ current_screen = screen; /* UPDATE this (for lazy switching)... */ } /* dual_screen_select */ /* enable can be -1 (disable), 0 (keep) or 1 (enable) */ /* waits for retrace if enable is 1 */ /* screen 0 is the "screen which was found active", screen 1 is the */ /* operator screen which will be always 80x25 in the first versions */ uint32 select_screen(int screen, int enable) { if (current_screen < 0) { dual_screen_init (); dual_screen_select (0); } if (current_screen != screen) dual_screen_select (screen); /* Visibility stuff is not for mono, but not needed there either */ /* because mono is only used for operator screen where we do not */ /* (should not!) change visibility. So we have this security jump. */ if ( ( (!agp_vga) || (!pci_vga) ) && (screen != 0) ) goto protect_innocent_mono; /* And protect innocent color from */ /* being hit by things which were meant to affect only mono...! */ if (enable != 0) { /* screen visibility changing? */ uint8 seqval; if (screen == 0) { /* hide cursor on subject screen */ out8 (0x3d4, 0x0a); /* CRTC register A: cursor start */ seqval = in8 (0x3d5); seqval |= 0x20; /* set "disable hardware cursor" bit */ out8 (0x3d4, 0x0a); /* CRTC register A: cursor start */ out8 (0x3d5, seqval); /* cursor is now invisible */ } out8 (0x3c4, 1); /* check sequencer register 1, clocking mode */ seqval = in8 (0x3c5) & ~0x20; /* zap "screen refresh off" bit */ seqval |= (enable > 0) ? 0 : 0x20; /* to disable screen visibility: set "screen refresh off" bit */ if (enable > 0) { /* only for SHOWING things the timing is critical */ while (!(in8 (0x3da) & 8)) { /* just wait until VRETRACE starts */ }; } /* if transition to enable */ out8 (0x3c4, 1); /* clocking mode again */ out8 (0x3c5, seqval); /* set desired visibility */ } protect_innocent_mono: return get_ytime ();; } /* select_screen */ /* later versions of this can return either char OR pixel values */ /* pixel values will only be used for graphics modes, though */ /* fake is nonzero for fake dual head mode */ int get_resolution_y(int * x, int * fake) /* return lines... */ { if (current_screen < 0) dual_screen_init (); *x = peek (0x40, 0x4a); /* fetch columns on screen from BIOS */ if (*x < 40) /* maybe some graphics mode...? */ *x = 80; /* assume 80 text mode columns */ *fake = (lines_on_screen < 0) ? 1 : 0; return (lines_on_screen > 0) ? lines_on_screen /* normal mode: dual-head */ : (-lines_on_screen/2); /* fake dual head: half Y resolution */ } /* get_resolution_y */