Here are some tips on writing a new chipset svgalib driver.
They don't require knowledge of direct vga programming, even though that 
helps.
The instructions assume: 
1- A super vga card, that is a card which is extended, but also fully 
compatible to standard vga. Of the cards available today (in the mass market),
all are, with the possible exception of Permedia chipsets.
2- A PCI (or AGP) connected card. This is useful for detection of the card,
and for finding the linear aperture, as well as knowing it is always there.
3- Knowledge of the extended features of the chipset: Usually either as the
spec, or a source of a driver for the chipset (XFree86).

as a start, get the following:
1- vgadoc4b.zip[1] (or a later version), includes information on the vga
   hardware registers, as well as on many chipsets.
2- the latest svgalib[2]. 
3- If the card is supported by XFree86, either the link kit (Xlkit.tgz[3]) 
   if the card is supported by the XF86_SVGA server, or the XFree86 source 
   ([4]), if it is supported by a special server.

1. in the src subdir of the svgalib distribution, cp skeleton.c to some other
   name, chipset.c, where chipset is the name of the chipset you intend to
   support.

2. now you have to fill the blanks. The functions that need to be are
sk_setpage
sk_saveregs
sk_setregs
sk_initializemode
sk_unlock
sk_test
sk_setdisplaystart
sk_setlogicalwidth
sk_init

3. When that is done, change all sk_ in the driver to chipset_ (where 
   chipset is the name of the chipset you write the driver for). Then
   the driver needs to be integrated into svgalib. The files that need
   to be edited are:
Makefile.cfg
src/Makefile
src/driver.h
src/vga.c
src/vga.h
   (It's easy to see what changes are needed in those files, by simply 
    seeing how its done for another driver).

4. Now is the most interesting time - debugging. Usually (in my experience,
   in all cases), the driver won't work right immediately. Here are a few 
   debugging tips:

Setting modes is made of two things: setting the timings, and setting the
memory organisation. You can usually tell which is the problem, by noticing
if the wrong screen is seen as if some/all pixels are not set to the color
they should (memory organisation), or the display looks "stormy" (timings 
problem). If the problem is timing problem, a digital monitor taht displays
horizontal and vertical timings helps. If the horizontal freq is right, but 
vertical is wrong, then the problem is in the vertical timing. If both horiz
and vert are wrong, then either the clock frequency, or the horizontal 
frequency is wrong.

If the problem is in memory organisation, It might work in linear mode, so try
running testlinear and lineart.

If the driver fails to restore text mode properly, then usually starting X
will work. It won't help restoring text mode, but then you might be able
to see the output of the program (using /dev/vcs?, /dev/vcsa?).

The program mode3 (in lrmi-0.6m subdir) might be able to restore text mode,
corrupted by a bad driver.

The same mode3 might be used for debugging as follows: if a mode, say 
800x600x256 does not work properly, but mode3 does work, you might try the
following run vgatest, select 11 (for 800x600x256), and while the wrong mode
is displayed, press d (to recieve a registers dump).
then, try mode3 259 ; utils/dumpreg ; mode3, and compare the register dump
of vesa mode 259 (800x600x256), with the output from your driver. Try finding
out what do the bits that are different mean.  


Here are some more details on writing the necessary functions:
examples given are from the banshee.c driver, written according to the spec
available from 3dfx, and the (hypotethical) milleniumII.c driver, written 
according to the XFree86 source.

_setpage:

This is a simple function. A mistake in this function is indicated by:
all linear programs work fine, while in paged memory examples, the problem 
is that blocks (full width * ??? lines) are moved.

For millenium, from freebe (this part is unavailable in X source, 
since X uses only linear mode).
{
	outw(0x3de,(page<<8)|4);
};

For Banshee:
{
   page<<=1;
   outl(banshee_io_base+0x2c,(inl(banshee_io_base+0x2c)&0xfff00000)|(page)|(page<<10));
}


_saveregs, _setregs

This functions should save and restore the state of the svga card, such that 
it can be restored to a previous state, no matter what we do. A mistake is 
indicated by failure to restore text mode properly.

We should save all registers that we change, or might be changed by other 
programs (X, SVGATextMode, etc.), here's the banshee_saveregs:

typedef struct {
   unsigned int pllCtrl0, pllCtrl1, dacMode, dacAddr,
      		vidProcCfg, vidScreenSize, vgaInit0,
                vgaInit1, vidDesktopStartAddr,vidDesktopOverlayStride;
} *HWRecPtr;

static int banshee_saveregs(unsigned char regs[])
{ 
  HWRecPtr save;

  banshee_unlock();
  
  save=(HWRecPtr)(regs+62);
  
  regs[BANSHEEREG_SAVE(0)]=__svgalib_inCR(0x1a);
  regs[BANSHEEREG_SAVE(1)]=__svgalib_inCR(0x1b);
  save->pllCtrl0=inl(banshee_io_base+0x40);
  save->pllCtrl1=inl(banshee_io_base+0x44);
  save->dacMode=inl(banshee_io_base+0x4c);
  save->dacAddr=inl(banshee_io_base+0x50);
  save->vidProcCfg=inl(banshee_io_base+0x5c);
  save->vidScreenSize=inl(banshee_io_base+0x98);
  save->vgaInit0=inl(banshee_io_base+0x28);
  save->vgaInit1=inl(banshee_io_base+0x2c);
  save->vidDesktopStartAddr=inl(banshee_io_base+0xe4);
  save->vidDesktopOverlayStride=inl(banshee_io_base+0xe8);
  
  return BANSHEE_TOTAL_REGS - VGA_TOTAL_REGS;
}

If we use the X source, it usually in the functions XXXSave and XXXRestore.
It is only needed to translate from XFree86 notation to svgalib notation, 
and to remember that while the X functions need to save the vga state (usually
by calling the vga function to do it, in svgalib the chipset_saveregs/setregs
don't need to do that, but only save extended vga info).
For MilleniumII, the interesting X functions are: MGA3026Save and 
MGA3026Restore, here a translation of a part of MGA3026Restore to 
mill_setregs:

MGA3026Restore:
	for (i = 0; i < 6; i++)
		outw(0x3DE, (restore->ExtVga[i] << 8) | i);

	/* restore DAC regs */
	for (i = 0; i < sizeof(MGADACregs); i++)
		outMGA1064(MGADACregs[i], restore->DACreg[i]);

translates to (assuming sizeof(MGADACregs)==32)
	for (i = 0; i < 6; i++)
		outw(0x3DE, (restore[60+i] << 8) | i);

	/* restore DAC regs */
	for (i = 0; i < 32; i++){
		OUTREG8(RAMDAC_OFFSET + MGA1064_INDEX, MGADACregs[i]);
		OUTREG8(RAMDAC_OFFSET + MGA1064_DATA, restore[66+i]);
	};


_initializemode - This is the trickiest function. If you use the XFree86 
source, try to translate the appropriate function from X to svgalib (in
the milleniumII case, MGA1064Init(mode)). For examples of this translation,
see how its done for nv3.c compared to nv/nv3driver.c from X, or apm.c compared
to apm/apm_driver.c.
If you are working from specs, it usually explained in the spec.

_unlock:
simple function. The skeleton includes the vga unlocking part. If you have
specs, they usually explain how to unlock the extended features, if you use
the X source, its usually in the EnterLeave function.

_test - If you are supporting a single chipset of a single manufacturer,
on a PCI/AGP, the skeleton driver includes a test for this, just make sure to
set VENDOR_ID and CARD_ID properly. Otherwise, either the specs explain
how to recognize that you have the right chipset, or its in the function 
XXXProbe of the XFree86 source.


_setdisplaystart, _setlogicalwidth:
vga includes this settings, but with limited range (start<65536, 
width<2048 bytes). An svga chipset either defines extra bits, for increasing
the range, or completely new registers for these values.
In the first case, the skeleton driver includes setting the vga part, and
the extra bits are either defined in the specs, or in the XXXAdjust function
in XFree86 (see mx.c for example of this). In the second case, remove the
vga setting part from the functions (see for example rage.c).
Note that none of the demo programs test this functions, so if you want to 
check if they work, the only example I know of is seejpeg (try to see an image
larger than the screen)


_init
should not need much changes, except for checking memory size (in the spec, or
in XXXProbe), and maybe setting MMIO (if other functions use it).




1- http://home.worldonline.dk/finth
2- http://www.cs.bgu.ac.il/~zivav/svgalib
3- ftp://ftp.xfree86.org/pub/XFree86/current/binaries/Linux-ix86-libc5/Xlkit.tgz
    (or mirrors)
4- ftp://ftp.xfree86.org/pub/XFree86/current/source/X333servonly.tgz
    (or mirrors)
