written by Steffen
Seeger
last update was on $Date: 1997/12/14 21:55:27 $
This document should give you a short introduction
to the relevant parts of the KGI interface, describe
the interface for the GGI modularized drivers,
and provide some hints for actually writing display drivers.
Exactly one display driver has to be registered for every real world image that can - or should - be controlled independently. Note that the term 'display driver' here only refers to a description of the display hardware and associated methods to access the display. It is especially not used to refer to the binary form of the driver - the module - that is loaded to extent the kernel. The difference becomes clear when you pay attention to the fact that a module may register an arbitrary number of display, input and terminal drivers. If you plan to write a driver yourself or to modify one, is suggested you have a copy of ggi/kgi.h at hand and make sure you get a general overview of the data structures used by the KGI manager before you start coding.
To do the registration, the driver has to fill in a 'registration form' which is a struct kgi_display. This struct is declared in ggi/kgi.h and collects information about the display and the 'knowledge' how to access it. There are three different kinds of fields in that struct.
The first kind is maintained by the KGI manager and has to be initialized to certain fixed values:
ggi_uint id = -1; | This is a number from 0..MAX_NR_DISPLAYS-1 identifying the display. If the display is not registered, this number should be -1. |
ggi_uint refcnt = 0; | This is a count of the references made to this display. If it is zero no references are made at all and the driver could destroy this struct or unload. |
ulong conflict_text ulong conflict_graph ulong conflict_setup = 0xFFFFFFFF |
These bit fields hold the information which displays need to be disabled when this display is to be accessed for access in text-mode, in graphics-mode or for setup. NOTE: As of version 0.0.9, these fields are not used yet and may be subject to change. |
struct kgi_device *dev = NULL; |
This field points to the device currently mapped on the display, if there is no current device, dev is NULL. |
The second kind are the following fields which are mandatory and must be provided with every display by the display driver. With other words, NULL pointers or undefined states there will have ugly consequences as long as not stated otherwise.
void (*enable)(struct kgi_display *dpy)
Enable the display dpy for access. This is not used nor specified yet. Enter a NULL here.
void (*disable)(struct kgi_display *dpy)
Disable the display dpy for access. This is not used nor specified yet. Enter a NULL here.
void (*reset)(struct kgi_display *dpy)
This has to reset the display referred to by dpy to it's power-on mode. This may differ from a set_mode() call with the power-on mode as there might be other things to do than setting the mode. Power on mode should be a mode that, by convention, would not cause damage to any part if e.g. the monitor of a display is changed. When reset, the display should enter a state that allows a boot display driver provided by the kernel to handle the display with minimal support. dpy is guaranteed not to be NULL and being registered by the driver.
struct kgi_card *card
This provides some information about the hardware detected and its vendors to allow for easier troubleshooting and bug-hunting. This field must not be NULL except for boot display drivers provided by the kernel.
int (*check_mode)(struct kgi_display *dpy, struct kgi_mode *mode, enum kgi_tm_cmd cmd)
cmd determines the operations to be carried out and is guaranteed to be either CMD_PROPOSE or CMD_CHECK. The parameters mode and dpy are and guarantueed not to be NULL.
If cmd is CMD_PROPOSE, all fields in the '*mode' struct except for the 'request' field are undefined. The fields in 'request' describe the mode to be proposed. At least one of the the virtual.x and virtual.y fields is guaranteed to have a nonzero value. If both are set, at least the specified virtual screen size is required; if only one is set, the other one should be maximized according to the displays capabilities but be not less than the visible size required. NOTE: The GGI-0.0.8 drivers don't implement this right now. Take care to implement this!
If cmd is CMD_CHECK, an error code must be returned if this mode is not possible with the display referred to by dpy for any reason, such as too low refresh rate, lack of memory, etc.
If the display does not support virtual frame buffers, that is there is no set_origin() method supplied, check_mode() has to ensure the mode has equal virtual and visible frame buffer sizes.
void (*set_mode)(struct kgi_display *dpy, struct kgi_mode *mode)
This should set the display referred to by 'dpy' into the mode 'mode'. This requires to do the actual hardware setup as well as to update the 'dpy' struct, especially the show, hide and undo methods for the cursor and pointer marks as well as the framebuffer struct and operations. NOTE: Make sure you actually do this, not doing so will cause errors you will hardly find! As the check_mode() function has been called and returned with no error before set_mode() is called, mode is guaranteed to be possible on this display and the set_mode() operation must not fail. The set_mode() function need not be supplied if the display driver can do only one mode and check_mode() returns the current mode of the display when asked to propose a mode.
void (*show_cursor)(struct kgi_display *dpy, int x, int y)
This should show a cursor mark on the screen. The position is given in dots relative to the origin of the visible image. If this function needs to modify the framebuffer, the undo_cursor() function has to be provided and is guaranteed to be called before show_cursor() is called next time. This function is mandatory for textmodes and optional for other (graphical) modes for which it should only be provided when the cursor mark is done in hardware; that is, without modifying the framebuffer.
void (*hide_cursor)(struct kgi_display *dpy)
This has to hide the cursor if it is displayed or do nothing if the cursor is not visible.
void (*undo_cursor)(struct kgi_display *dpy)
This function must be provided when show_cursor() modifies the framebuffer to show the cursor on the screen. If the cursor is done in hardware, this field must be initialized to NULL. The purpose of this function is to undo framebuffer modifications, so it is the same as the hide_cursor() function
void (*show_pointer)(struct kgi_display *dpy)
void (*hide_pointer)(struct kgi_display *dpy)
void (*undo_pointer)(struct kgi_display *dpy)
These should do the equivalent actions as the show_cursor(), hide_cursor() and undo_cursor() functions, but with the pointer mark. Note that the cursor and pointer mark may overlap and the original framebuffer state needs to be restored independent of the calling sequence of the four show/hide functions. Also these functions may depend on the mode set and set_mode() has to care about this.
struct kgi_mode mode;
The current mode the display is in. It's up to the driver to maintain this field correctly.
ggi_mode *defmode;
This pointer must not be NULL and should describe the default mode to use with this display. NOTE: This currently *needs* to be a textmode!
kgi_pixmap fb;
This is the framebuffer of the current frame. It must be valid after a reset() or set_mode() call.This means that the correct framebuffer operations, framebuffer geometry and buffer pointer have to be supplied. Currently the framebuffer operations need to be blocking, meaning that the framebuffer must have been altered according to the function being called.
Any other field is optional and has to be initialized with 0 or NULL if not stated otherwise. The optional fields are:
void *driver_data
A display driver may store a pointer to display specific data for every display it registered here. This is only useful for drivers that have to support more than one display and that want to use the same functions to access the displays, e.g. because only the I/O bases differ.
void (*set_font)(struct kgi_display *dpy, struct kgi_font *f, int from, int to)
This sets the display-internal font table postitions in the range from from to (including) to to the same as in the font f.
void (*set_clut)(struct kgi_display *dpy, struct kgi_clut *c, int from, int to)
This sets the display-internal color-look-up-table in the range from from to (including) to to the same as in the CLUT c.
void (*set_frame)(struct kgi_display *dpy, int frame)
Set the display to display the frame frame. This routine has to update the fb field of the dpy-struct. Note: this has not been fully specified yet.
void (*set_split)(struct kgi_display *dpy, ggi_uint line)
Activates the split-line feature for frame 0 and sets the splitline to line line. The line coordinate is given in dots relative to the origin of the virtual image.
void (*set_origin)(struct kgi_display *dpy, ggi_uint x, ggi_uint y)
This sets the origin of the visible area of the current frame to be at the dot coordinates (x,y) of the virtual image. Note that the cursor and pointer coordinates are given relative to the origin and may need to be updated too!
void (*set_pointer_shape)(struct kgi_display *dpy, struct kgi_pointer64x64 *shape)
This should set the pointer shape of a hardware pointer mask. It doesn't make sense to implent this if there is not hardware cursor at all. The pointer shape is defined as follows:
enum kgi_pointer_mode {
pmWindows, pmX11, pmThreeColor
};
struct kgi_pointer64x64 {
enum kgi_pointer_mode mode;
uint8 xor_mask[512];
uint8 and_mask[512];
ggi_uint hotx, hoty;
ggi_color col[3];
};
and_mask and xor_mask are two 64x64 bitmasks. Given (x,y) is the coordinate of a pixel within the 64x64 pointer area, thepixel number is pnum = y*64 + x and the corresponding bit is given by the expressions {xor,and}_mask[(pnum >> 3)] & (1 << (pnum & 7)). (hotx,hoty) denotes the pointer hot spot. Depending on mode, xor_mask and and_mask, a pixel of the pointer shape will be displayed as:[NOTE: document under construction, please be patient. Thanks.]
Mode | pointer shape | |||||||||||||||
pmX11 |
|
|||||||||||||||
pmThreeColor |
|
|||||||||||||||
pmWindows |
|
A display has to be registered using the function kgi_register_display(). Any display driver should first, if possible, successfully detect the hardware it is designed for and do further hardware access only if the registration did not fail. If it failed, there must not be any further access to the display hardware by this driver.
To allow proper boot on any given machine, a simple 'text16-framebuffer-only' display is implemented in file:linux/drivers/char/<arch>/display.c that has to export framebuffer operations for a color and mono text16-framebuffer in I/O memory space and in normal memory space. Thus there are four framebuffer operations exported which are named text16_{mono,color}_{io,mem}. During boot on i386, a framebuffer-only display is set up for a monochrome and/or color display adapter; depending on what is detected.
Now let's have a look at the functions of the KGI to handle displays, which are currently two functions used to register and unregister displays. They are implemented in drivers/char/kgi.c
int kgi_register_display(struct kgi_display *dpy, int id)
This registers dpy under the ID id. If id is negative, the first free one is assigned. Basically there are two cases to use this function: (a) to register a full driver, possibly to replace a boot driver and (b) to register a boot driver. This is only allowed for the display_init() function called during KGI initialization. If a boot driver is replaced, the replaced struct kgi_display is destroyed using text16_kfree_display(). The return value indicates if the registration was successful or the reason for failing. The possibly returned error codes are:
EOK | registration was successful. The KGI manager keeps a reference to the registered display internally, so you must not reallocate or free the registered stucture. |
-ENOMEM | There was no free ID found. |
-EINVAL | The calling parameters were invalid, e.g. registering a second boot display under the same ID as a previously registered one. |
-EBUSY | The new driver can't support the modes served by the old one. |
int kgi_unregister_display(struct kgi_display *dpy, struct kgi_display *alias)
This is used to unregister a previously registered display. There might be an alias display driver provided that handles devices at minimal functionality. Note that this alias must be allocated using text16_kmalloc_display().The return values indicates if the unregistration failed or has been done. NOTE: A loadable module that registered a driver before must not be unloaded before all display drivers it has registered are unregistered successfully! The possibly returned error codes are:
EOK | Unregistration was successful. This guarantees that no references to the driver are made by the KGI manager any more. |
-EBUSY | This indicates that no alias driver was provided or that the alias provided is invalid. This could happen when, for example, the alias cannot do the modes served by the current driver and there are devices that are attached to the display to be unregistered. |
-EINVAL | The calling parameters are invalid, e.g. you tried to unregister a NULL display. |
Though it is possible to write display drivers within one single source file, we have choosen to split up the display drivers into separate soruce code files each of which handles one essential part of the graphics hardware. If you plan to write a driver yourself, we strongly suggest you use this technique and extend the current drivers because it may save you a lot of time spent bug-hunting otherwise. NOTE: The internal module interface will probably change to allow multihead support even at module level, however, we should port the existing drivers first and adopt them step by step.
The modularized display drivers reside in the ggi/driver directory and are split according to the components of current display hardware driver designs which are
Each subsystem is given a subdirectory of its own, which in turn host directories for the several hardware vendors that produce these parts. Philosophy is to write a driver once per chip, any combinations of different pieces are handled by the cleverly choosen mode negotiation. For example, if there is Vendor A who uses an S3 Vision964 chip on his card together with a TI ramdac, one would write a driver for the Vision964 chip and the TI ramdac. Suppose vendor B has now a card using a Vision964 and a Bt485 ramdac, one would only have to rewrite the Bt485 driver. And, the display drivers for both cards will benefit from any bugfixes that are made for example to the Vision964 chip driver. So, before you start coding, have a look what parts of the card you want to write a driver for are already supported.
Before you start coding, it is strongly recommended you get the specification of the parts you want to write drivers for from the hardware vendor. It cannot be emphasized enough, but the best information comes mostly direct from the source. Once you have this, and you are sure you understand how to program this part, try to look for compatible devices that might be supported already and check if simple modifications suffice to make it work with the new part. Only if this fails, start writing your own driver. The first thing to do is to write header files for the hardware registers. Yes, C allows you to code magic numbers to write to ports strait out of the head, but what is easier to understand a year after you wrote the code and have to fix a bug: DAC_out8(dpy, DAC_C0_EXT_UNLOCK | DAC_C0_SLEEP_MODE, DAC_CONTROL0); or DAC_out8(dpy, 0x81, 0x6); ? And, a second pro to do this is that you will get a good overview of the registers and possible traps you may fall into. Then start the real driver and keep things as simple as possible.
The key to understand the module design is the kernel interface driver implemented in ggi/driver/kernel/linux/i386.c For the i386 architecture, this implements the neccessary interfacing to make a loadable module an provides the neccessary memory for the struct kgi_display and struct kgi_card for registration of the display driver. All fields are initialized with reasonable default values, especially the mode to use when resetting the display is set to be a VGA compatible one. NOTE: This is likely to change in the future when we allow several drivers per module, also the reset mode maybecome a property of the display!
The internal module interface is declared in ggi/module.h and the main functions are described below. This is also the only file (beside chip specific header files) that you are allowed to include from the drivers (except kernel interface). Any violations against this rule are historic and will be removed soon.
[NOTE: This should be made more consistent (read: have a better abstraction). So things might change slightly here, but we will try to keep things easy so that you can use automatic find&replace to upgrade drivers. Also, description is Intel specific, so any help to improve this is very welcome.] To keep the drivers as portable as possible, only the I/O abstractions defined in ggi/system.h have to be used to access the hardware. The basic abstraction here is a I/O region, which describes the ressources used by the hardware in a physical address space. There may be many different address spaces, to keep things easy we adopt the following naming conventions:
To allow for proper ressource management, the drivers have to check and claim or allocate every ressource thay occupy in a certain address space (except for PCI configuration space). To do so, information about the ressources used has to be stored in a struct ..._region, which holds the following fields:
struct <io-space>_region {
<io-space>_addr base; /* start of the region */
ggi_uint size; /* size of the region */
<io-space>_addr decode; /* address lines decoded */
char *name; /* name of the device/region */
<io-space>_vaddr baseptr; /* virtual address to use with in/out */
};
base and decode are the physical address lines decoded by the hardware, while baseptr is the address to be used as a base offset for the in/out functions. NOTE: this is not fully implemented yet for io regions, so check ggi/system.h.
These have to be provided by the chipset driver because this is the only driver that is allowed to use low level I/O. The purpose of these generic I/O functions is to provide hardware independent access to subsystems. The currently defined functions are:
extern void DAC_out8(struct kgi_display *dpy, uint8 val, uint8 reg);
Write value to the DAC register dac_reg. The register dac_reg is the state the corresponding RegisterSelect lines of the chipset have to be driven to.
extern uint8 DAC_in8(struct kgi_display *dpy, uint8 reg);
Read the DAC register dac_reg and return the value read. Again, dac_reg is the state of the RegisterSelect lines the chipset has to drive.
extern void DAC_outs8(struct kgi_display *dpy, uint8 reg, void *buf, ggi_uint cnt);
This writes cnt values given in *buf to the DAC register reg .
extern void DAC_ins8(struct kgi_display *dpy, uint8 reg, void *buf, ggi_uint cnt);
This reads count values from the DAC register dac_reg and stores the values read in *buf.
extern void CLK_out8(struct kgi_display *dpy, uint8 val, uint8 reg);
This should write val to clock chip register reg.Register 0x00 is defined to set the ClockSelect lines of the chipset to the state given by value.
extern uint8 CLK_in8(struct kgi_display *dpy, uint8 reg);
This reports the state of the ClockSelect lines.
NOTE: Future extensions here will include VESA DDC support and probably a more generic I/O abstraction.
Per subsystem, several actions have to be provided. However, to give you a better understanding how the drivers will work together, we will describe the module loading and operation in detail. It is suggested you have a copy of ggi/driver/kernel/i386.c at hand and read the corresponding code parts.
These functions are used for initializing and shutting down the subsystems properly. The ..._init() functions have to do the following steps and ensure the substem is fully operational after ..._init() returned wihtout error:
Initialization starts in init_module() which is called when the driver gets loaded using the insmod utility. This first tries to initialize the several subsystems and shuts down properly exiting with an error code in case this fails. The calling sequence is carefully choosen and will probably not be changed in the future. Each kgim_<subsystem>_init() function is passed a pointer to the display to be registered and the card info to be used with this display. Here is a summary of the calling sequencee and the actions to be done by the several subsystems:
Subsystem | Actions |
chipset |
|
monitor |
|
ramdac |
|
clock chip |
|
After successful subsystem wakeup, the driver checks that the mode used when the display is to be reset is accepted by all drivers. To do so, it uses the kgim_check_mode() function implemented in file:ggi/driver/kernel/i386.c Mode checking is the only complicated thing to understand in the driver design and will be explained lateron. Once the power-on mode is checked, the display struct is initialized completely and the hardware is reset. Then the driver attempts to register the display driver using the kgi_register_display() function under the ID display_number which is 0 by default but can be passed as an argument to the insmod command when loading the driver. Now the driver is fully operational and can be used to map devices. NOTE: Unloading is still not possible yet, sorry for this, but we have to implement this first. When being unloaded, the display is reset and the subsystems are shut down properly using the kgim_<subsystem>_done() functions. The calling sequence is reverse to initialization and each function should do the neccessary things to shutdown the driver for the corresponding subsystem.
Checking and setting a mode is quite tricky, because we allow the exact timing to be calculated 'on-the-fly' during mode checking. As you can see, the monitor driver has a different calling interface than the other subsystems, because it controls the mode negotiation. The check_mode() functions are called from kgim_check_mode() which is exported as the check_mode() function for the display provided by the module.
Basically the kgim_check_mode() does the following steps when a mode has to be proposed (cmd == CMD_PROPOSE):
To understand better what happens during mode check, it is suggested you try to read through the code of some existing drivers. We will explain the mode calculation process in deftail below, giving the actions each subsystem is responsible for. So here is the calling sequence of the first iteration:
cmd | Subsystem | Action |
CMD_PROPOSE | clock |
|
CMD_PROPOSE | ramdac |
Propose values that give maximum performance. The ratios mul and div fields must not have common divisors. |
CMD_PROPOSE | chipset |
|
CMD_PROPOSE | monitor |
You will usually choose to raise it for fixed horizontal refresh frequency monitors, because you can compensate higher dot clock rates with a larger blank section; and choose to start at maximum DCLK value for multisync monitors, because they will likely to be able to handle lower horizontal refresh rates. |
Now all timing and chipset relevant fields of the *mode struct are initialized with values that might work for the present hardware However, the DCLK rate proposed by the monitor might be out of bounds for e.g. the chipset and the DAC might require a different clock rate from the clock chip because it must do internal clock doubling. So, we go for a second iteration that allows each subsystem to adjust the timing values to meet the vendor specification given in the hardware documentation. A field may be altered only in a way that ensures that the dotclock, horizontal refresh and vertical refresh rates drop or raise, according to the value of cmd which is either CMD_LOWER or CMD_RAISE. The following table lists the calling sequence and which driver may alter which field:
cmd | Subsystem | Action |
CMD_LOWER or CMD_RAISE |
clock |
|
CMD_LOWER or CMD_RAISE |
ramdac |
|
CMD_LOWER or CMD_RAISE |
chipset |
|
CMD_LOWER or CMD_RAISE |
clock |
|
CMD_LOWER or CMD_RAISE |
monitor |
Now you may either
or you
|
Now all drivers had a chance to contribute their limits and take part in the mode negotioation. To make sure all drivers are satisfied with this, we ask all drivers check whether they can do the proposed mode. If this fails, the mode is not possible and the driver who detects this first should return an error code that alloees to identify why. NOTE: These error codes are not specified yet. Just return -E(<subsystem>, UNKNOWN). Each driver should make sure it can set the mode checked and operate it properly, because it hereby accepts this mode and the corresponding kgim_<subsystem>_set_mode() call must not fail for accepted modes. Each driver may enter some 'hints' for its set_mode() function, e.g. clock index values and so on. However, only when cmd equals CMD_CHECK these hints should be entered, because the KGI manager does also call the check_mode() function of a display just to make shure it can do a certain mode. So, here is the final iteration:
cmd | Subsystem | Action |
CMD_CHECK | clock |
|
CMD_CHECK | ramdac |
|
CMD_CHECK | chipset |
|
CMD_CHECK | monitor |
|
Once this is passed, the struct kgi_mode obtained contains all parameter neccessary to set a certain videomode and all driver can do this mode at hopefully maximum performance.
Setting a videomode is not that complicated once the timing parameters and the DCLK, LCLK and CLK ratios are known. As setting a mode heavily involves the chipset, we call the kgim_chipset_set_mode() function directly, which in turn has to call the other kgim_<subsystem>_set_mode() functions when appropriate actions have been taken (e.g. the clock was disabled or the timing signal generation was stopped).
Anyhow, the kgim_<subsystem>_set_mode() functions should be restricted to do only the neccessary things as fast as possible. Especailly extensive calculation of 'best' register settings must not be done here. Do this during the kgim_<subsystem>_check_mode() function and store the obtained values in the subsystem specific fields of the struct kgi_mode. The several subsystems must also update certain fields in the struct kgi_display passed. Function pointers shoudl be set to NULL if not supported and not mandatory in the mode being set.
Subsystem | Fields to set in *dpy |
clock |
|
ramdac |
|
chipset |
|
monitor |
|
[NOTE: this is under construction, expect updates here when the /dev/graph?? devices are implemented! Thanks for your patience. Any comments are welcome, just mail Steffen Seeger]