Video Drivers Development D1130370375 Auriel (82.182.149.46) #Source: #! aux/vga: /sys/src/cmd/aux/vga/* #! kernel drivers: /sys/src/9/pc/vga* # #See also: # * /sys/src/cmd/aux/vga/notes.txt # * /n/sources/contrib/vga/notes (more recent but obsolete?) # #Extracted from the 9fans archives: #[http://lists.cse.psu.edu/archives/9fans/2002-February/015741.html] # #The following is a sort of theory of operation for aux/vga and the #kernel vga drivers. # #--- aux/vga and basic kernel drivers # #Aux/vga consists of a number of modules each of which conforms to an #interface called a Ctlr. The Ctlr provides functions snarf, options, #init, load, and dump, which are explained in more detail below. #Video cards are internally represented as just a collection of #Ctlrs. When we want to run one of the functions (snarf, etc.) on the #whole card, we run it on each Ctlr piece in turn. # #In the beginning of aux/vga, it was common for video cards to mix #and match different VGA controller chips, RAMDACs, clock generators, #and sometimes even hardware cursors. The original use for vgadb was #to provide a recipe for how to deal with each card. The ordering in #the ctlr sections was followed during initialization, so that if you #said ctlr 0xC0076="Tseng Laboratories, Inc. 03/04/94 V8.00N" #link=vga clock=ics2494a-324 ctlr=et4000-w32p ramdac=stg1602-135 when #aux/vga wanted to run, say, snarf on this card it would call the #snarf routines for the vga, ics2494a, et4000, and stg1602 Ctlrs, in #that order. The special Ctlrs vga and ibm8514 take care of the #generic VGA register set and the extensions to that register set #introduced by the IBM 8514 chip. Pretty much all graphics cards #these days still use the VGA register set with some extensions. The #only exceptions currently in vgadb are the Ticket to Ride IV and the #Neomagic (both LCD cards). The S3 line of chips tends to have the #IBM 8514 extensions. # #This "mix and match" diversity has settled down a bit, with one chip #now usually handling everything. As a result, vgadb entries have #become a bit more formulaic, usually listing only the vga link, a #controller, and a hardware cursor. For example: ctlr #0xC0039="CL-GD540" link=vga ctlr=clgd542x hwgc=clgd542xhwgc # #On to the controller functions themselves. The functions mentioned #earlier are supposed to do the following. # #void snarf(Vga *vga, Ctlr *ctlr) Read the ctlr's registers into #memory, storing them either in the vga structure (if there is an #appropriate place) or into a privately allocated structure, a #pointer to which can be stored in vga->private (sic). (The use of #vga->private rather than ctlr->private betrays the fact that private #data has only been added after we got down to having cards with #basically a single controller.) # #void options(Vga *vga, Ctlr *ctlr) This step prepares to edit the #in-memory copy of the registers to implement the mode given in #vga->mode. It's really the first half of init, and is often empty. #Basically, something goes here if you need to influence one of the #other init routines and can't depend on being called before it. For #example, the virge Ctlr rounds the pixel line width up to a multiple #of 16 in its options routine. This is necessary because the vga Ctlr #uses the pixel line width. If we set it in virge.init, vga.init #would already have used the wrong value. # #void init(Vga *vga, Ctlr *ctlr) Edit the in-memory copy of the #registers to implement the mode given in vga->mode. # #void load(Vga *vga, Ctlr *ctlr) Write all the ctlr's registers, #using the in-memory values. This is the function actually used to #switch modes. # #void dump(Vga *vga, Ctlr *ctlr) Print (to the Biobuf stdout) a #description of all the in-memory controller state. This includes the #in-memory copy of the registers but often includes other calculated #state like the intended clock frequencies, etc. # #Now we have enough framework to explain what aux/vga does. It's #easiest to present it as a commented recipe. # #1. We sniff around in the BIOS memory looking for a match to any of #the strings given in vgadb. (In the future, we intend also to use #the PCI configuration registers to identify cards.) # #2. Having identified the card and thus made the list of controller #structures, we snarf the registers and, if the -p flag was given, #dump them. # #3. If the -i or -l flag is given, aux/vga then locates the desired #mode in the vgadb and copies it into the vga structure. It then does #any automatic frequency calculations if they need doing. (See the #discussion of defaultclock in vgadb(6).) # #For a good introduction to video modes, read Eric Raymond's XFree86 #Video Timings HOWTO, which, although marked as obsolete for XFree86, #is still a good introduction to what's going on between the video #card and the monitor. #[http://en.tldp.org/HOWTO/XFree86-Video-Timings-HOWTO/] # #4. Having copied the vgadb mode parameters into the vga structure, #aux/vga calls the options and then the init routines to twiddle the #in-memory registers appropriately. # #5. Now we are almost ready to switch video modes. We dump the #registers to stdout if we're being verbose. # #6. We tell the kernel (via the "type" vga ctl message) what sort of #video card to look for. Specifically, the kernel locates the named #kernel vga driver and runs its enable function. # #7. If we're using a frame buffer in direct-mapped linear mode (see #the section below), we express this intent with a "linear"; vga ctl #message. In response, the kernel calls the vga driver's linear #function. This should map the video memory into the kernel's address #space. Conventionally, it also creates a named memory segment for #use with segattach so that user-level programs can get at the video #memory. If there is a separate memory-mapped i/o space, it too is #mapped and named. These segments are only used for debugging, #specifically for debugging the hardware acceleration routines from #user space before putting them into the kernel. # #8. We tell the kernel the layout of video memory in a "size" ctl #message. The arguments are the screen image resolution and the pixel #channel format string. # #9. Everything is set; we disable the video card, call the loads to #actally set the real registers, and reenable the card. # #At this point there should be a reasonable picture on the screen. It #will be of random memory contents and thus could be mostly garbage, #but there should be a distinct image on the screen rather than, say, #funny changing patterns due to having used an incorrect sync #frequency. # #10. We write "drawinit" into #v/vgactl, which will initialize the #screen and make console output from now on appear on the graphics #screen instead of being written to the CGA text video memory (as has #been happening). This calls the kernel driver's drawinit function, #whose only job is to initialize hardware accelerated fills and #scrolls and hardware blanking if desired. # #11. We write "hwgc " into #v/vgactl, which calls the #enable function on the named kernel hwgc driver. (Plan 9 does not #yet support software graphics cursors.) # #12. We set the actual screen size with an "actualsize" ctl message. #The virtual screen size (which was used in the "size" message in #step 8) controls how the video memory is laid out; the actual screen #size is how much fits on your monitor at a time. Virtual screen size #is sometimes larger than actual screen size, either to implement #panning (which is really confusing and not recommended) or to round #pixel lines up to some boundary, as is done on the ViRGE and Matrox #cards. The only reason the kernel needs to know the actual screen #size is to make sure the mouse cursor stays on the actual screen. # #13. If we're being verbose, we dump the vga state again. # #--- hardware acceleration and blanking # #Hardware drawing acceleration is accomplished by calling the #kernel-driver-provided fill and scroll routines rather than doing #the memory operations ourselves. For >8-bit pixel depths, hardware #acceleration is noticeably needed. For typical Plan 9 applications, #accelerating fill and scroll has been fast enough that we haven't #worried about doing anything else. # #The kernel driver's drawinit function should sniff the card and #decide whether it can use accelerated fill and scroll functions. If #so, it fills in the scr->fill and scr->scroll function pointers with #functions that implement the following: # #int fill(VGAscr *scr, Rectangle r, ulong val); Set every pixel in #the given rectangle to val. Val is a bit pattern already formatted #for the screen's pixel format (rather than being an RGBA quadruple). #Do not return until the operation has completed (meaning video #memory has been updated). Usually this means a busy wait looping for #a bit in some status register. Although slighty inefficient, the net #effect is still much faster than doing the work ourselves. It's a #good idea to break out of the busy loop after a large number of #iterations, so that if the driver or the card gets confused we don't #lock up the system waiting for the bit. Look at any of the #accelerated drivers for the conventional method. # #int scroll(VGAscr *scr, Rectangle r, Rectangle sr); Set the pixels #in rectangle r with the pixels in sr. r and sr are allowed to #overlap, and the correct thing must be done, just like memmove. Like #fill, scroll must not return until the operation has completed. # #Russ Cox has a user-level scaffold for #testing fill and scroll routines before putting them into the #kernel. You can mail him for them. # #Finally, drawinit can set scr->blank to a hardware blanking #function. On 8-bit displays we can set the colormap to all black to #get a sort of blanking, but for true-color displays we need help #from the hardware. # #int blank(VGAscr *vga, int isblank); If isblank is set, blank the #screen. Otherwise, restore it. Implementing this function on #CRT-based cards is known to mess up the registers coming out of the #blank. We've had better luck with LCD-based cards although still not #great luck. But there it is. # #--- linear mode and soft screens # #In the bad old days, the entire address space was only 1MB, but #video memory (640x480x1) was only 37.5kB, so everything worked out. #It got its own 64kB segment and everyone was happy. When screens got #deeper and then bigger, the initial solution was to use the 64kB #segment as a window onto a particular part of video memory. The #offset of the window was controlled by setting a register on the #card. This works okay but is a royal pain, especially if you're #trying to copy from one area of the screen to another and they don't #fit in the same window. When we are forced to cope with cards that #require accessing memory through the 64kB window, we allocate our #own copy of the screen (a so-called soft screen) in normal RAM, make #changes there, and then flush the changed portions of memory to #video RAM through the window. To do this, we call the kernel #driver-provided page routine: # #int pageset(VGAscr *scr, int page); Set the base offset of the video #window to point page*64kB into video memory. # #With the advent of 32-bit address spaces, we can map all of video #memory and avoid the soft screen. We call this running the card in #linear mode, because the whole video memory is mapped linearly into #our address space. Aux/vga is in charge of deciding whether to do #this. (In turn, aux/vga more or less respects vgadb, which controls #it by having or not having "linear=1" in the controller entry.) If #not, aux/vga doesn't do anything special, and we use a soft screen. #If so, aux/vga writes "linear" and an address space size into vgactl #in step #7 above. In response the kernel calls the kernel driver's #linear function, whose job was described in step #7. # #Most drivers only implement one or the other interface: if you've #got linear mode, you might as well use it and ignore the paging #capabilities of the card. Paging is typically implemented only when #necessary. # #--- from here # #If you want to write a VGA driver, it's fairly essential that you #get documentation for the video chipset. In a pinch, you might be #able to get by with the XFree86 driver for the chipset instead. (The #NVidia driver was written this way.) Another alternative is to use #documentation for a similar but earlier chipset and then tweak #registers until you figure out what is different. (The SuperSavage #parts of the virge driver got written this way, starting with the #Savage4 parts, which in turn were written by referring to the #Savage4 documentation and the Virge parts.) # #Even if you do get documentation, the XFree86 driver is good to have #to double check. Sometimes the documentation is incomplete, #misleading, or just plain wrong, whereas the XFree86 drivers, #complicated beasts though they are, are known to work most of the #time. # #Another useful method for making sure you understand what is going #on is dumping the card's registers under another system like XFree86 #or Microsoft Windows. The Plan 9 updates page contains an ANSI/POSIX #port of aux/vga that is useful only for dumping registers on various #systems. It has been used under Linux, FreeBSD, and Windows 95/98. #It's not clear what to do on systems like Windows NT or Windows 2000 #that both have reasonable memory protection and are hardware #programmer-unfriendly. # #If you're going to write a driver, it's much easier with a real Plan #9 network or at least with a do-everything cpu/auth/file server #terminal, so that you can have an editor and compiler going on a #usable machine while you continually frotz and reboot the machine #with the newfangled video card. Booting this latter machine from the #network rather than its own disk makes life easier for you (you #don't have to explicitly copy aux/vga from the compiling machine to #the testing machine) and doesn't wreak havoc on the testing #machine's local kfs. # #It's nice sometimes to have a command-line utility to poke at the #vga registers you care about. We have one that perhaps we can clean #up and make available. Otherwise, it's not hard to roll your own. # #The first step in writing an aux/vga driver is to write the snarf #and dump routines for the controller. Then you can run aux/vga -p #and see whether the values you are getting match what you expect #from the documentation you have. # #A good first resolution to try to get working is 640x480x8, as it #can use one of the standard clock modes rather than require #excessive clock fiddling. # #/sys/src/cmd/aux/vga/template.c is a template for a new vga #controller driver. There is no kernel template but any of the #current drivers is a decent template. /sys/src/9/pc/vga3dfx.c is the #smallest one that supports linear addressing mode. #