This file should show the basic ideas of MY General Graphics Interface for
Linux.

Please note that this definitely is out of date - please use the existing 
sources as examples !!

Please note also that there is a very different approach out there by
Michael Weller (see DEVELOPERS) - you might want to have a look at
it, as from his point of view this concept is broken and pervert.

Version : 0.11
Date    : 24 May 1995

Module-Relation-Chart:

+----------------+             -+
| Monitor-Driver |              |
+-----+          +-----+        |
      | Clock-Chip-Drv |        |
      +-----+          +-----+  +- Graphics kernel mode subsystem
            | Chipset-Driver |  |
+-----------+    +-----------+  |
| Graphic-Device |              |
+----------------+             -+
        ||
        || ioctl- and mmap-interface
        ||
******************             -+
* Graphics - Lib *              +- User mode subsystem
******************             -+


Annotations :
-------------

The driver (bordered with +-|) should be a KLM - this will give it the 
necessary permissions to access the graphics-card.
Moreover only it should do any security-critical things (like calculation
of modes,setting dot-clocks, ...) to keep bad programs/users from damaging
hardware.

The library (****) should connect to this kernel interface from user-mode
and translate between the normal C calling sequence and the format of the
kernel interface.

I personally disliked the idea of an ioctl interface, but together with
mmap and a nopage-exception which maps the VGA-RAM linearly into the
user space it seems to be really great.

I checked out the speed of these methods and found the following :
The ioctl will take about 400 Clock-cycles (6 us).
The nopage-exception is not that expensive anymore 1150 Clocks (15 us).

Interface definition:
---------------------

1.Monitor-Driver:
-----------------

This subsystem is responsible for giving technical information about the
monitor.This will probably be a quite generic driver that will only need
tuning of several parameters (i.e. a config-file that contains horizontal
and vertical frequency range,size,...) per monitor.

It uses a suser only ioctl for setting the monitor-parameters .

It shall export the following functions :
-----------------------------------------

### void GGI_GetMonitorParameters(struct GGI_MonParams * MP);
### int  GGI_CheckMonitorTiming  (struct GGI_Timing * MT);


Where the structs shall contain the information as described in
scrdrv.h.

The most important information contained there are:

struct MonParams:
- Horizontal Frequency range
- Vertical   Frequency range
- Maximum resolution (mask size)
- physical picture size
- manufacturer and model
- type of display (EGA,VGA,color/mono)

struct GGI_Timing:
- Requested values :
  - visible window size
  - virtual resolution,i.e. scrolling range
  - graphics type (Text,8 bit,true color ,...)
  - font size for text mode

- Auto-calculated/-selected values :
  - pixelclock
  - X/Y timings (and sync-polarity)
  - flags like doublescan,magnify,...

I hope I didn't miss too much.

Return values : 
---------------
GGI_GetMonitorParameters - the structure is filled in.
GGI_CheckMonitorTiming: 0  - parameters are accepted.
		     	>0 - error value (see scrdrv.h)
		        <0 - fatal error


2.Clock-Chip-Driver :
---------------------
### struct GGI_Range *GGI_GetClockParameters(void)

will return a pointer to a ascendingly sorted list (0/0 terminated) of 
available clock ranges.
Descrete clocks will be specified by a range x-x entry.

### int GGI_CheckClockTiming(struct GGI_Timing * MT);

will select the next lower suitable clock for the requested mode.
Returns 0,if requested clock is available within a small error range or 
error value if a lower clock was used or no lower clock was found.

In any case the clock field will be set to the exact value found.

### int GGI_SetClockTiming(struct Timing * MT);

will set the chosen clock.
Returs 0 on success, -1 otherwise.

3.Chipset-Driver :
------------------
[Please note that this can be merged together with the clockchipdriver in
 most cases, while e.g. for the S3-based boards it might be desireable to
 separate them, as several boards with the same chipset use different
 clock-chips.]

### void GGI_GetChipsetParameters(struct *GGI_ChipParams);

### int  GGI_CheckChipsetTiming(struct *GGI_Timing);

### int  GGI_SetChipsetTiming(struct *GGI_Timing);


As the Timing has to be calculated with lots of different aspects in mind
by different drivers, here is a table of which driver may alter which fields
in which way :

int	clock;

This field may be set by ALL drivers :

- chipset limiting maximum allowable clock e.g. for DAC,
- monitor limiting clock due to bandwidths,
- clock   choosing from its available values.

Thus the field will be set to a big value initially (by chipset) and then
only decrease.

int	xwidth,xsyncstart,xsyncend,xend;
int	ywidth,ysyncstart,ysyncend,yend;

while x/ywidth will be set by chipdrv before asking mondrv,these and the
others will then only be modified by mondrv,clockdrv does not touch.

int	xsyncpol,ysyncpol;

This is set by mondrv.

enum	GGI_Flags	flags;	/* special things like INTERLACE,DOUBLE_SCAN */

Set by chipdrv.

int	xvisible,yvisible;
int	xvirtual,yvirtual;
enum	GGI_Graphtype 	graphtype;
int	xtextgrid,ytextgrid;

Adjusted by chipdrv.

Table :
			mondrv		clockdrv	chipdrv
x/y-visible	:	x		x		++ adjust
x/y-virtual	:	x		x		++ adjust
x/y-textgrid	:	x		x		+- adjust
clock		:	--		--		=
xwidth,ywidth   :	x		x		=
flags		:	x		x		=
x/y-Timing	:	=		x		x
x/y-syncpol	:	=		x		x
graphtype	:	x		x		x	(error if unsupported)

++/-- mean may adjust in that direction,= may set value, x may not touch

Setting up a graphics-mode should work that way :
===========================-----------------------

1. The user program will ask the Graphics-Lib for a resolution
   (i.e. request a screen of xvisible,yvisible,graphtype,xvirtual,
    yvirtual).

2. The Graphics-Lib will communicate this to the Graphic-Driver,
   which will initiate the remaining steps :

3. Ask the Chipset-Driver, if it can handle the requested mode.
   This is done by only setting x/yvisible,x/yvirtual and graphtype in 
   struct Timing (all others 0).
   If the requested mode cannot be fulfilled exactly,but a superior
   mode is available (e.g. a higher virtual screen size) the struct
   is adjusted accordingly and this mode is set.
   If the mode is not supported (e.g. no 24 bit,size too big for 
   installed RAM,...) return error to application.
   (In this case the struct should contain the description of the
    available mode,that is most similar to the requested one.)

4. Ask CheckMonitorTiming for a suitable timing.

   The monitor driver will then fill in its fields or return an error
   code in case the monitor cannot support the mode.In this case we do 
   GOTO 3.The chipset driver will analyze the error-code and try to
   compensate (e.g. by using the y-magnify feature of standard VGAs to
   overcome a too small y-resolution like in 640x200).

5. Ask CheckClock if the calculated clock is ok.If NO => decrease clock to
   next available one and GOTO 4,else GOTO 6.If there is no lower clock
   available return error LOWEST_CLOCK and GOTO 4.

Now we should have a mode that is supported by the chipset,the monitor 
and the clock-chip (all drivers have retuned OK).

6. Now the mode can be programmed via SetChipsetTiming which will 
   internally call SetClockTiming at a suitable place.

As this verbal description might sound very complicated the following
graph should make the idea more clear :


				   USER-PROGRAM
					||
					\/ Ask for mode
					||
				 GRAPHICS-LIBRARY
					||
					\/ Ask the kernel driver
					||
				 GRAPHICS-DEVICE
					||
					\/ Ask for mode
					||
				  CHIPSET-DRIVER ==> FATAL ERROR
				   ||	    ||
		      Ask 4 timing \/       /\ return error code
				   ||	    ||
				  MONITOR-DRIVER ==> FATAL ERROR
				   ||	    ||
		      Ask 4 clock  \/       /\ return error code
				   ||	    ||
				   CLOCK-DRIVER  ==> FATAL ERROR
					||
					\/ mode OK by all instances
					||
				   SET THE MODE

This means the driver will loop between two stages until they find a
mode both can live with. As it is defined who may alter which variable
in which direction and every stage can abort the process there should be
no recursion.I have implemented a recursion test (just a counter
that will return with a fatal error after e.g. 50 cycles) for safety,
though.

****************************************************************************

After testing it for several weeks I found the ioctl-interface to be a
better soultion for the "communicating with kernel-mode"-problem than I
thought at first - so I think it should be used. Here is the description 
of the functions needed for that in the chipsetdriver :

### GraphIoctl(struct inode *inode, struct file *file, 
                    unsigned int cmd, unsigned long arg)

This function will be directly called by the kernel when an ioctl on
/dev/graphics is requested.It will do all basic operations.

cmd contains the requested operation and arg is a pointer to the arguments.

A sample application of this system that makes it quite easy to choose
between emulation of the request in the library or direct execution by the
driver is included.

Please note that this should only be implemented for otherwise "expensive"
functions (like DrawBox) if there is hardware-support for it. If this is
not the case the function should be left to the library.

If it seems advisable to use the mem-mapped VGA-RAM (as there is no
acceleration support for any of the lower level functions) the driver should 
return ENODRVSUP_???_MMAP to give a hint the library .

For example if there is no support for FillScreen,but acceleration for
DrawBox,the driver should return ENODRVSUP_???_LOWER which will make the 
driver use lower level functions instead of memset(...).

If there is no accelerated support for the appropriate lower-level functions 
the driver would probably use to emulate the request,the driver should
rather return ENODRVSUP_???_MMAP to allow at least optimization on that
level.

Please note that several functions may not fail at all with a ENODRVSUP
(e.g. SetMode) while others may only fail with ENODRVSUP_???_MMAP (e.g.
DrawPixel) as the library cannot know how to "emulate" them except by
using the mmaped VGA-RAM.

### int GraphMmap(struct inode * inode, struct file * file, struct vm_area_struct *vma)

Please have a look at the sample code in my driver on how to implement that,
but be careful - some support from the kernel is still needed.

