1The following is a sort of theory of operation for aux/vga and the 2kernel vga drivers. 3 4--- aux/vga and basic kernel drivers 5 6Aux/vga consists of a number of modules each of which conforms to an 7interface called a Ctlr. The Ctlr provides functions snarf, options, 8init, load, and dump, which are explained in more detail below. Video 9cards are internally represented as just a collection of Ctlrs. When 10we want to run one of the functions (snarf, etc.) on the whole card, 11we run it on each Ctlr piece in turn. 12 13In the beginning of aux/vga, it was common for video cards to mix and 14match different VGA controller chips, RAMDACs, clock generators, and 15sometimes even hardware cursors. The original use for vgadb was to 16provide a recipe for how to deal with each card. The ordering in the 17ctlr sections was followed during initialization, so that if you said 18 ctlr 19 0xC0076="Tseng Laboratories, Inc. 03/04/94 V8.00N" 20 link=vga 21 clock=ics2494a-324 22 ctlr=et4000-w32p 23 ramdac=stg1602-135 24when aux/vga wanted to run, say, snarf on this card it would call the 25snarf routines for the vga, ics2494a, et4000, and stg1602 Ctlrs, in 26that order. The special Ctlrs vga and ibm8514 take care of the 27generic VGA register set and the extensions to that register set 28introduced by the IBM 8514 chip. Pretty much all graphics cards these 29days still use the VGA register set with some extensions. The only 30exceptions currently in vgadb are the Ticket to Ride IV and the 31Neomagic (both LCD cards). The S3 line of chips tends to have the IBM 328514 extensions. 33 34This "mix and match" diversity has settled down a bit, with one chip 35now usually handling everything. As a result, vgadb entries have 36become a bit more formulaic, usually listing only the vga link, a 37controller, and a hardware cursor. For example: 38 ctlr 39 0xC0039="CL-GD540" 40 link=vga 41 ctlr=clgd542x 42 hwgc=clgd542xhwgc 43 44On to the controller functions themselves. The functions mentioned 45earlier are supposed to do the following. 46 47void snarf(Vga *vga, Ctlr *ctlr) 48 Read the ctlr's registers into memory, storing them 49 either in the vga structure (if there is an appropriate 50 place) or into a privately allocated structure, a pointer 51 to which can be stored in vga->private [sic]. 52 [The use of vga->private rather than ctlr->private betrays 53 the fact that private data has only been added after we got 54 down to having cards with basically a single controller.] 55 56void options(Vga *vga, Ctlr *ctlr) 57 This step prepares to edit the in-memory copy of the 58 registers to implement the mode given in vga->mode. 59 It's really the first half of init, and is often empty. 60 Basically, something goes here if you need to influence 61 one of the other init routines and can't depend on being 62 called before it. For example, the virge Ctlr rounds 63 the pixel line width up to a multiple of 16 in its options routine. 64 This is necessary because the vga Ctlr uses the pixel line 65 width. If we set it in virge.init, vga.init would already 66 have used the wrong value. 67 68void init(Vga *vga, Ctlr *ctlr) 69 Edit the in-memory copy of the registers to implement 70 the mode given in vga->mode. 71 72void load(Vga *vga, Ctlr *ctlr) 73 Write all the ctlr's registers, using the in-memory values. 74 This is the function actually used to switch modes. 75 76void dump(Vga *vga, Ctlr *ctlr) 77 Print (to the Biobuf stdout) a description of all the 78 in-memory controller state. This includes the in-memory 79 copy of the registers but often includes other calculated 80 state like the intended clock frequencies, etc. 81 82Now we have enough framework to explain what aux/vga does. It's 83easiest to present it as a commented recipe. 84 851. We sniff around in the BIOS memory looking for a match to 86any of the strings given in vgadb. (In the future, we intend also to 87use the PCI configuration registers to identify cards.) 88 892. Having identified the card and thus made the list of controller 90structures, we snarf the registers and, if the -p flag was 91given, dump them. 92 933. If the -i or -l flag is given, aux/vga then locates the desired 94mode in the vgadb and copies it into the vga structure. It then does 95any automatic frequency calculations if they need doing. (See the 96discussion of defaultclock in vgadb(6).) 97 98For a good introduction to video modes, read Eric Raymond's XFree86 99Video Timings HOWTO, which, although marked as obsolete for XFree86, 100is still a good introduction to what's going on between the video card 101and the monitor. 102http://www.linuxdoc.org/HOWTO/XFree86-Video-Timings-HOWTO/ 103 1044. Having copied the vgadb mode parameters into the vga structure, 105aux/vga calls the options and then the init routines to twiddle the 106in-memory registers appropriately. 107 1085. Now we are almost ready to switch video modes. We dump the 109registers to stdout if we're being verbose. 110 1116. We tell the kernel (via the "type" vga ctl message) what sort of 112video card to look for. Specifically, the kernel locates the named 113kernel vga driver and runs its enable function. 114 1157. If we're using a frame buffer in direct-mapped linear mode (see 116the section below), we express this intent with a "linear" vga ctl 117message. In response, the kernel calls the vga driver's linear 118function. This should map the video memory into the kernel's address 119space. Conventionally, it also creates a named memory segment for use 120with segattach so that uesr-level programs can get at the video 121memory. If there is a separate memory-mapped i/o space, it too is 122mapped and named. These segments are only used for debugging, 123specifically for debugging the hardware acceleration routines from 124user space before putting them into the kernel. 125 1268. We tell the kernel the layout of video memory in a "size" ctl 127message. The arguments are the screen image resolution and the pixel 128channel format string. 129 1309. Everything is set; we disable the video card, call the loads to 131actally set the real registers, and reenable the card. 132 133At this point there should be a reasonable picture on the screen. It 134will be of random memory contents and thus could be mostly garbage, 135but there should be a distinct image on the screen rather than, say, 136funny changing patterns due to having used an incorrect sync 137frequency. 138 13910. We write "drawinit" into #v/vgactl, which will initialize the 140screen and make console output from now on appear on the graphics 141screen instead of being written to the CGA text video memory (as has 142been happening). This calls the kernel driver's drawinit function, 143whose only job is to initialize hardware accelerated fills and scrolls 144and hardware blanking if desired. 145 14611. We write "hwgc <hwgcname>" into #v/vgactl, which calls the enable 147function on the named kernel hwgc driver. (Plan 9 does not yet support 148software graphics cursors.) 149 15012. We set the actual screen size with an "actualsize" ctl message. 151The virtual screen size (which was used in the "size" message in step 1528) controls how the video memory is laid out; the actual screen size 153is how much fits on your monitor at a time. Virtual screen size is 154sometimes larger than actual screen size, either to implement panning 155(which is really confusing and not recommended) or to round pixel 156lines up to some boundary, as is done on the ViRGE and Matrox cards. 157The only reason the kernel needs to know the actual screen size is to 158make sure the mouse cursor stays on the actual screen. 159 16013. If we're being verbose, we dump the vga state again. 161 162--- hardware acceleration and blanking 163 164Hardware drawing acceleration is accomplished by calling the 165kernel-driver-provided fill and scroll routines rather than 166doing the memory operations ourselves. For >8-bit pixel depths, 167hardware acceleration is noticeably needed. For typical Plan 9 168applications, accelerating fill and scroll has been fast enough that we haven't 169worried about doing anything else. 170 171The kernel driver's drawinit function should sniff the card 172and decide whether it can use accelerated fill and scroll functions. 173If so, it fills in the scr->fill and scr->scroll function pointers 174with functions that implement the following: 175 176int fill(VGAscr *scr, Rectangle r, ulong val); 177 Set every pixel in the given rectangle to val. 178 Val is a bit pattern already formatted for the screen's 179 pixel format (rather than being an RGBA quadruple). 180 Do not return until the operation has completed 181 (meaning video memory has been updated). 182 Usually this means a busy wait looping for a bit 183 in some status register. Although slighty inefficient, 184 the net effect is still much faster than doing the work 185 ourselves. It's a good idea to break out of the busy 186 loop after a large number of iterations, so that 187 if the driver or the card gets confused we don't 188 lock up the system waiting for the bit. Look at 189 any of the accelerated drivers for the conventional 190 method. 191 192int scroll(VGAscr *scr, Rectangle r, Rectangle sr); 193 Set the pixels in rectangle r with the pixels in sr. 194 r and sr are allowed to overlap, and the correct 195 thing must be done, just like memmove. 196 Like fill, scroll must not return until the operation 197 has completed. 198 199Russ Cox <rsc@plan9.bell-labs.com> has a user-level scaffold 200for testing fill and scroll routines before putting them into 201the kernel. You can mail him for them. 202 203Finally, drawinit can set scr->blank to a hardware blanking 204function. On 8-bit displays we can set the colormap to all 205black to get a sort of blanking, but for true-color displays 206we need help from the hardware. 207 208int blank(VGAscr *vga, int isblank); 209 If isblank is set, blank the screen. Otherwise, restore it. 210 Implementing this function on CRT-based cards is known to 211 mess up the registers coming out of the blank. 212 We've had better luck with LCD-based cards although 213 still not great luck. But there it is. 214 215--- linear mode and soft screens 216 217In the bad old days, the entire address space was only 1MB, but video 218memory (640x480x1) was only 37.5kB, so everything worked out. It got 219its own 64kB segment and everyone was happy. When screens got deeper 220and then bigger, the initial solution was to use the 64kB segment as a 221window onto a particular part of video memory. The offset of the 222window was controlled by setting a register on the card. This works 223okay but is a royal pain, especially if you're trying to copy from one 224area of the screen to another and they don't fit in the same window. 225When we are forced to cope with cards that require accessing memory 226through the 64kB window, we allocate our own copy of the screen (a 227so-called soft screen) in normal RAM, make changes there, and then 228flush the changed portions of memory to video RAM through the window. 229To do this, we call the kernel driver-provided page routine: 230 231int pageset(VGAscr *scr, int page); 232 Set the base offset of the video window to point 233 page*64kB into video memory. 234 235With the advent of 32-bit address spaces, we can map all of video 236memory and avoid the soft screen. We call this running the card 237in linear mode, because the whole video memory is mapped linearly 238into our address space. Aux/vga is in charge of deciding 239whether to do this. (In turn, aux/vga more or less respects 240vgadb, which controls it by having or not having "linear=1" in 241the controller entry.) If not, aux/vga doesn't do anything special, 242and we use a soft screen. If so, aux/vga writes "linear" and 243an address space size into vgactl in step #7 above. In response 244the kernel calls the kernel driver's linear function, whose 245job was described in step #7. 246 247Most drivers only implement one or the other interface: if you've 248got linear mode, you might as well use it and ignore the paging 249capabilties of the card. Paging is typically implemented only 250when necessary. 251 252--- from here 253 254If you want to write a VGA driver, it's fairly essential that you get 255documentation for the video chipset. In a pinch, you might be able to 256get by with the XFree86 driver for the chipset instead. (The NVidia 257driver was written this way.) Another alternative is to use 258documentation for a similar but earlier chipset and then tweak 259registers until you figure out what is different. (The SuperSavage 260parts of the virge driver got written this way, starting with the 261Savage4 parts, which in turn were written by referring to the Savage4 262documentation and the Virge parts.) 263 264Even if you do get documentation, the XFree86 driver is good to 265have to double check. Sometimes the documentation is incomplete, 266misleading, or just plain wrong, whereas the XFree86 drivers, 267complicated beasts though they are, are known to work most of the time. 268 269Another useful method for making sure you understand what is going on 270is dumping the card's registers under another system like XFree86 or 271Microsoft Windows. The Plan 9 updates page contains an ANSI/POSIX 272port of aux/vga that is useful only for dumping registers on various 273systems. It has been used under Linux, FreeBSD, and Windows 95/98. 274It's not clear what to do on systems like Windows NT or Windows 2000 275that both have reasonable memory protection and are hardware 276programmer-unfriendly. 277 278If you're going to write a driver, it's much easier with a real 279Plan 9 network or at least with a do-everything cpu/auth/file server 280terminal, so that you can have an editor and compiler going on a 281usable machine while you continually frotz and reboot the machine 282with the newfangled video card. Booting this latter machine from 283the network rather than its own disk makes life easier for you 284(you don't have to explicitly copy aux/vga from the compiling machine to 285the testing machine) and doesn't wreak havoc on the testing machine's 286local kfs. 287 288It's nice sometimes to have a command-line utility to poke 289at the vga registers you care about. We have one that perhaps 290we can clean up and make available. Otherwise, it's not hard 291to roll your own. 292 293The first step in writing an aux/vga driver is to write the 294snarf and dump routines for the controller. Then you can 295run aux/vga -p and see whether the values you are getting 296match what you expect from the documentation you have. 297 298A good first resolution to try to get working is 640x480x8, 299as it can use one of the standard clock modes rather than 300require excessive clock fiddling. 301 302/sys/src/cmd/aux/vga/template.c is a template for a new 303vga controller driver. There is no kernel template 304but any of the current drivers is a decent template. 305/sys/src/9/pc/vga3dfx.c is the smallest one that supports 306linear addressing mode. 307 308