<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>The Driver Itself </TITLE>
</HEAD>
<BODY BGCOLOR="#efefef" TEXT="black" LINK="blue" VLINK="#551A8B" ALINK="red">

<A HREF="VGADriver.html"><EM> How to add an (S)VGA driver to XFree86</EM></A>
 <b>:</b> <EM>The Driver Itself </EM><BR>
<b>Previous:</b> <A HREF="VGADriver5.html"><EM>The Bank-Switching Functions </EM></A><BR>
<b>Next:</b> <A HREF="VGADriver7.html"><EM>Building The New Server </EM></A>


<HR NOSHADE>
  <H2><A NAME="6"></A>6. The Driver Itself </H2>
<p>Now it's time to get down to the real work - writing the major driver
functions in the files sdc_driver.c.  First, an overview of what the
responsibilities of the driver are:</p>
<p>
<ol>
<li>	   Provide a chipset-descriptor data structure to the server.  This
data structure contains pointers to the driver functions and
some data-structure initialization as well.</li>
<li>	   Provide a driver-local data structure to hold the contents of
the chipset registers.  This data structure will contain a 
generic part and a driver-specific part.  It is used to save the
initial chipset state, and is initialized by the driver to put
the chipset into different modes.</li>
<li>	   Provide an identification function that the server will call to
list the chipsets that the driver is capable of supporting.</li>
<li>	   Provide a probe function that will identify this chipset as
different from all others, and return a positive response if
the chipset this driver supports is installed, and a negative
response otherwise.</li>
<li>	   Provide a function to select dot-clocks available on the board.</li>
<li>	   Provide functions to save, restore, and initialize the driver-
local data structure.</li>
<li>	   Provide a function to set the starting address for display in
the video memory.  This implements the virtual-screen for the
server.</li>
<li>	   Perhaps provide a function for use during VT-switching.</li>
<li>	   Perhaps provide a function to check if each mode is suitable for
the chipset being used.</li>
</ol>
</p>
<p>Before stepping through the driver file in detail, here are some important
issues:</p>
<p>
<ol>
<li>	   If your driver supports both the color and monochrome servers,
you should take care of both cases in the same file.  Most things
are the same - you can differentiate between the two with the
MONOVGA <code>#define</code>.  If the 16 color server is supported,
code specific to it can be enabled with the XF86VGA16
<code>#define</code>.  In most cases  it is sufficient to put
the following near the top of the stub_driver.c file:

<pre>
   #ifdef XF86VGA16
   #define MONOVGA
   #endif
</pre>

</li>
<li>	   The color server uses the SVGA's 8-bit packed-pixel mode.  The
monochrome and vga16 servers uses the VGA's 16-color mode
(4 bit-planes).  Only one plane is enabled for the monochrome
server.</li>
<li>	   It is possible for you to define your monochrome driver so that
no bank-switching is done.  This is not particularly desirable,
as it yields only 64k of viewing area.</li>
</ol>

Keeping these things in mind, you need to find the registers from your
SVGA chipset that control the desired features.  In particular, registers
that control:
<ol>
<li>	   Clock select bits.  The two low-order bits are part of the
standard Miscellaneous Output Register; most SVGA chipsets
will include 1 or 2 more bits, allowing the use of 8 or 16
discrete clocks.</li>
<li>	   Bank selection.  The SVGA chipset will have one or two registers
that control read/write bank selection.</li>
<li>	   CRTC extensions.  The standard VGA registers don't have enough
bits to address large displays.  So the SVGA chipsets have
extension bits.</li>
<li>	   Interlaced mode.  Standard VGA does not support interlaced
displays.  So the SVGA chipset will have a bit somewhere to
control interlaced mode.  Some chipsets require additional
registers to be set up to control interlaced mode</li>
<li>	   Starting address.  The standard VGA only has 16 bits in which
to specify the starting address for the display.  This restricts
the screen size usable by the virtual screen feature.  The SVGA
chipset will usually provide one or more extension bits.</li>
<li>	   Lock registers.  Many SVGA chipset prevent modification of
extended registers unless the registers are first ``unlocked''.
You will need to disable protection of any registers you will
need for other purposes.</li>
<li>	   Any other facilities.  Some chipset may, for example, require
that certain bits be set before you can access extended VGA
memory (beyond the IBM-standard 256k).  Or other facilities;
read through all of the extended register descriptions and see
if anything important leaps out at you.</li>
</ol>
</p>
<p>If you are fortunate, the chipset vendor will include in the databook some
tables of register settings for various BIOS modes.  You can learn a lot
about what manipulations you must do by looking at the various BIOS modes.</p>

<H3><A NAME="7"></A>6.1. Multiple Chipsets And Options </H3>
<p>It is possible, and in fact desirable, to have a single driver support
multiple chipsets from the same vendor.  If there are multiple supported
chipsets, then you would have a series of #define's for them, and a
variable `SDCchipset', which would be used throughout the driver when
distinctions must be made.  See the Trident and PVGA1/WD drivers for
examples (the Tseng ET3000 and ET4000 are counter-examples - these were
implemented before the driver interface allowed for multiple chipsets, so
this example should NOT be followed).  Note that you should only distinguish
versions when your driver needs to do things differently for them.  For
example, suppose the SDC driver supports the SDC-1a, SDC-1b, and SDC-2
chipsets.  The -1a and -1b are essentially the same, but different from the
-2 chipset.  Your driver should support the -1 and -2 chipsets, and not
distinguish between the -1a and -1b.  This will simplify things for the
end user.</p>
<p>In cases where you want to give the user control of driver behavior, or
there are things that cannot be determined without user intervention, you
should use ``option'' flags.  Say that board vendors that use the SDC 
chipsets have the option of providing 8 or 16 clocks.  There's no way you 
can determine this from the chipset probe, so you provide an option flag to 
let the user select the behavior from the XF86Config file.  The option flags
are defined in the file ``xf86_option.h''.  You should look to see if there is 
already a flag that can be reused.  If so, use it in your driver.  If not, 
add a new #define, and define the string-&gt;symbol mapping in the table in 
that file.  To see how option flags are used, look at the ET4000, 
PVGA1/WD, and Trident drivers.</p>

<H3><A NAME="8"></A>6.2. Data Structures </H3>
<p>Once you have an understanding of what is needed from the above description,
it is time to fill in the driver data structures.  First we will deal with
the `vgaSDCRec' structure.  This data structure is the driver-local structure
that holds the SVGA state information.  The first entry in this data structure
is ALWAYS `vgaHWRec std'.  This piece holds the generic VGA portion of the
information.  After that, you will have one `unsigned char' field for each
register that will be manipulated by your driver.  That's all there is to
this data structure.</p>
<p>Next you must initialize the `SDC' structure (type `vgaVideoChipRec').  This
is the global structure that identifies your driver to the server.  Its name
MUST be `SDC', in all caps - i.e. it must match the directory name for your
driver.  This is required so that the Link Kit reconfiguration can identify
all of the requisite directories and global data structures.</p>
<p>The first section of this structure simply holds pointers to the driver
functions.</p>
<p>Next, you must initialize the information about how your chipset does
bank switching.  The following fields must be filled in:
<ol>
<li>	   ChipMapSize - the amount of memory that must be mapped into
the server's address space.  This is almost always 64k (from
0xA0000 to 0xAFFFF).  Some chipsets use a 128k map (from
0xA0000 to 0xBFFFF).  If your chipset gives an option, use the
64k window, as a 128k window rules out using a Hercules or
Monochrome Display Adapter card with the SVGA.</li>
<li>	   ChipSegmentSize - the size of each bank within the ChipMapSize
window.  This is usually also 64k, however, some chipsets split
the mapped window into a read portion and a write portion (for
example the PVGA1/Western Digital chipsets).</li>
<li>	   ChipSegmentShift - the number of bits by which an address will
be shifted right to mask of the bank number.  This is log-base-2
of ChipSegmentSize.</li>
<li>	   ChipSegmentMask - a bitmask used to mask off the address within
a given bank.  This is (ChipSegmentSize-1).</li>
<li>	   ChipReadBottom,ChipReadTop - the addresses within the mapped
window in which read operations can be done.  Usually 0, and 
64k, respectively, except for those chipset that have separate
read and write windows.</li>
<li>	   ChipWriteBottom,ChipWriteTop - same as above, for write operations.</li>
<li>	   ChipUse2Banks - a boolean value for whether this chipset has one
or two bank registers.  This is used to set up the screen-to-screen
operations properly.</li>
</ol>

There are three more fields that must be filled in:
<ol>
<li>	   ChipInterlaceType - this is either VGA_NO_DIVIDE_VERT or
VGA_DIVIDE_VERT.  Some chipsets require that the vertical timing
numbers be divided in half for interlaced modes.  Setting this
flag will take care of that.</li>
<li>	   ChipOptionFlags - this should always be `{0,}' in the data
structure initialization.  This is a bitfield that contains
the Option flags that are valid for this driver.  The appropriate 
bits are initialized at the end of the Probe function.</li>
<li>	   ChipRounding - this gets set to the multiple by which the
virtual width of the display must be rounded for the 256-color
server.  This value is usually 8, but may be 4 or 16 for some
chipsets.</li>
</ol>
</p>

<H3><A NAME="9"></A>6.3. The Ident() function </H3>
<p>The <b>Ident()</b> function is a very simple function.  The server will call
this function repeatedly, until a NULL is returned, when printing out the
list of configured drivers.  The <b>Ident()</b> function should return a chipset
name for a supported chipset.  The function is passed a number which
increments from 0 on each iteration.</p>

<H3><A NAME="10"></A>6.4. The ClockSelect() function </H3>
<p>The <b>ClockSelect()</b> function is used during clock probing (i.e. when no
`Clocks' line is specified in the XF86Config file) to select the dot-clock
indicated by the number passed in the parameter.  The function should
set the chipset's clock-select bits according to the passed-in number.
Two dummy values will be passed in as well (CLK_REG_SAVE, CLK_SAVE_RESTORE).
When CLK_REG_SAVE is passed, the function should save away copies of
any registers that will be modified during clock selection.  When
CLK_REG_RESTORE is passed, the function should restore these registers.
This ensure that the clock-probing cannot corrupt registers.</p>
<p>This function should return FALSE if the passed-in index value is invalid
or if the clock can't be set for some reason.</p>

<H3><A NAME="11"></A>6.5. The Probe() function </H3>
<p>The <b>Probe()</b> function is perhaps the most important, and perhaps the
least intuitive function in the driver.  The Probe function is required
to identify the chipset independent of all other chipsets.  If the user
has specified a `<code>Chipset</code>' line in the XF86Config file, this is a simple
string comparison check.  Otherwise, you must use some other technique
to figure out what chipset is installed.  If you are lucky, the chipset
will have an identification mechanism (ident/version registers, etc), and
this will be documented in the databook.  Otherwise, you will have to
determine some scheme, using the reference materials listed below.</p>
<p>The identification is often done by looking for particular patterns in
register, or for the existence of certain extended registers.  Or with
some boards/chipsets, the requisite information can be obtained by reading
the BIOS for certain signature strings.  The best advise is to study the
existing probe functions, and use the reference documentation.  You
must be certain that your probe is non-destructive - if you modify a
register, it must be saved before, and restored after.</p>
<p>Once the chipset is successfully identified, the <b>Probe()</b> function must
do some other initializations:
<ol>
<li>	   If the user has not specified the `<code>VideoRam</code>' parameter in the
XF86Config file, the amount of installed memory must be determined.</li>
<li>	   If the user has not specified the `<code>Clocks</code>' parameter in the
XF86Config file, the values for the available dot-clocks must
be determined.  This is done by calling the <b>vgaGetClocks()</b>
function, and passing it the number of clocks available and
a pointer to the <b>ClockSelect()</b> function.</li>
<li>	   It is recommended that the `maxClock' field of the server's
`vga256InfoRec' structure be filled in with the maximum
dot-clock rate allowed for this chipset (specified in KHz).
If this is not filled in a probe time, a default (currently
90MHz) will be used.</li>
<li>	   The `chipset' field of the server's `vga256InfoRec' structure
must be initialized to the name of the installed chipset.</li>
<li>	   If the driver will be used with the monochrome server, the
`bankedMono' field of the server's `vga256InfoRec' structure
must be set to indicate whether the monochrome driver supports
banking.</li>
<li>	   If any option flags are used by this driver, the `ChipOptionFlags' 
structure in the `vgaVideoChipRec' must be initialized with the 
allowed option flags using the <b>OFLG_SET()</b> macro.</li>
</ol>
</p>

<H3><A NAME="12"></A>6.6. The EnterLeave() function </H3>
<p>The <b>EnterLeave()</b> function is called whenever the virtual console on which
the server runs is entered or left (for OSs without virtual consoles, the
function is called when the server starts and again when it exits).  The
purpose of this function is to enable and disable I/O permissions (for
OSs where such is required), and to unlock and relock access to ``protected''
registers that the driver must manipulate.  It is a fairly trivial function,
and can be implemented by following the comments in the stub driver.</p>

<H3><A NAME="13"></A>6.7. The Restore() function </H3>
<p>The <b>Restore()</b> function is used for restoring a saved video state.  Note
that `restore' is a bit of a misnomer - this function is used to both
restore a saved state and to install a new one created by the server.  The
<b>Restore()</b> function must complete the following actions:
<ol>
<li>	   Ensure that Bank 0 is selected, and that any other state
information required prior to writing out a new state has been
set up.</li>
<li>	   Call <b>vgaHWRestore()</b> to restore the generic VGA portion of the
state information.  This function is in the vgaHW.c file.</li>
<li>	   Restore the chipset-specific portion of the state information.
This may be done by simply writing out the register, or by
doing a read/modify/write cycle if only certain bits are to
be modified.  Be sure to note the comment in the sample driver
about how to handle clock-select bits.</li>
</ol>
</p>
<H3><A NAME="14"></A>6.8. The Save() function </H3>
<p>The <b>Save()</b> function is used to extract the initial video state information
when the server starts.  The <b>Save()</b> function must complete the following
actions:
<ol>
<li>	   Ensure that Bank 0 is selected.</li>
<li>	   Call <b>vgaHWSave()</b> to extract the generic VGA portion of the state
information.  This function is in the vgaHW.c file.</li>
<li>	   Extract the chipset-specific portion of the state information.</li>
</ol>
</p>
<H3><A NAME="15"></A>6.9. The Init() function </H3>
<p>The <b>Init()</b> function is the second most important function in the driver
(after the <b>Probe()</b> function).  It is used to initialize a data structure
for each of the defined display modes in the server.  This function is
required to initialize the entire `vgaSDCRec' data structure with the
information needed to put the SVGA chipset into the required state.  The
generic VGA portion of the structure is initialized with a call to 
<b>vgaHWInit()</b> (also located in vgaHW.c).</p>
<p>Once the generic portion is initialized, the <b>Init()</b> function can override
any of the generic register initialization, if necessary.  All of the other
fields are filled in with the correct initialization.  The information
about the particular mode being initialized is passed in the `mode'
parameter, a pointer to a `DisplayModeRec' structure.  This can be
dereferenced to determine the needed parameters.</p>
<p>If you only know how to initialize certain bits of the register, do that
here, and make sure that the <b>Restore()</b> function does a read/modify/write
to only manipulate those bits.  Again, refer to the existing drivers
for examples of what happens in this function.</p>

<H3><A NAME="16"></A>6.10. The Adjust() function </H3>
<p>The <b>Adjust()</b> function is another fairly basic function.  It is called
whenever the server needs to adjust the start of the displayed part of
the video memory, due to scrolling of the virtual screen or when changing
the displayed resolution.  All it does is set the starting address on the
chipset to match the specified coordinate.  Follow the comments in the 
stub driver for details on how to implement it.</p>

<H3><A NAME="17"></A>6.11. The ValidMode() function </H3>
<p>The <b>ValidMode()</b> function is required.  It is used to check
for any chipset-dependent reasons why a graphics mode might not be valid.  It
gets called by higher levels of the code after the Probe() stage.  In
many cases no special checking will be required and this function will
simply return TRUE always.</p>

<H3><A NAME="18"></A>6.12. The SaveScreen() function </H3>
<p>The <b>SaveScreen()</b> function is not needed by most chipsets.  This function
would only be required if the extended registers that your driver needs
will be modified when a synchronous reset is performed on the SVGA chipset
(your databook should tell you this).  If you do NOT need this function,
simply don't define it, and put `NoopDDA' in its place in the vgaVideoChipRec
structure initialization (NoopDDA is a generic-use empty function).</p>
<p>If you DO need this function, it is fairly simple to do.  It will be
called twice - once before the reset, and again after.  It will be passed
a parameter of SS_START in the former case, and SS_FINISH in the latter.
All that needs to be done is to save any registers that will be affected
by the reset into static variables on the SS_START call, and then restore
them on the SS_FINISH call.</p>

<H3><A NAME="19"></A>6.13. The GetMode() function </H3>
<p>The <b>GetMode()</b> function is not used as of XFree86 1.3; its place in the
vgaVideoChipRec should be initialized to `NoopDDA'.</p>
<p>At some point in the future, this function will be used to enable the server
and/or a standalone program using the server's driver libraries to do 
interactive video mode adjustments.  This function will read the SVGA
registers and fill in a DisplayModeRec structure with the current video
mode.</p>

<H3><A NAME="20"></A>6.14. The FbInit() function </H3>
<p>The <b>FbInit()</b> function is required for drivers with accelerated graphics
support.  It is used to replace default cfb.banked functions with
accelerated chip-specific versions.  vga256LowlevFuncs is a struct containing
a list of functions which can be replaced.  This struct defined in
vga256.h.  Examples of <b>FbInit()</b> functions can be found in the et4000,
pvga1 and cirrus drivers.</p>
<p>If you do NOT need this function, simply don't define it, and put `NoopDDA'
in its place in the vgaVideoChipRec structure initialization.</p>

 <HR NOSHADE>
<A HREF="VGADriver.html"><EM> How to add an (S)VGA driver to XFree86</EM></A>
 <b>:</b> <EM>The Driver Itself </EM><BR>
<b>Previous:</b> <A HREF="VGADriver5.html"><EM>The Bank-Switching Functions </EM></A><BR>
<b>Next:</b> <A HREF="VGADriver7.html"><EM>Building The New Server </EM></A>



</BODY>
</HTML>
