Display Driver Writers Guide

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.

The Kernel Graphic Interface for 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.

struct kgi_display;

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.

KGI internal fields

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.

Mandatory fields

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)

void (*disable)(struct kgi_display *dpy)

void (*reset)(struct kgi_display *dpy)

struct kgi_card *card

int (*check_mode)(struct kgi_display *dpy, struct kgi_mode *mode, enum kgi_tm_cmd cmd)

void (*set_mode)(struct kgi_display *dpy, struct kgi_mode *mode)

void (*show_cursor)(struct kgi_display *dpy, int x, int y)

void (*hide_cursor)(struct kgi_display *dpy)

void (*undo_cursor)(struct kgi_display *dpy)

void (*show_pointer)(struct kgi_display *dpy)
void (*hide_pointer)(struct kgi_display *dpy)
void (*undo_pointer)(struct kgi_display *dpy)

struct kgi_mode mode;

ggi_mode *defmode;

kgi_pixmap fb;

Optional fields

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

void (*set_font)(struct kgi_display *dpy, struct kgi_font *f, int from, int to)

void (*set_clut)(struct kgi_display *dpy, struct kgi_clut *c, int from, int to)

void (*set_frame)(struct kgi_display *dpy, int frame)

void (*set_split)(struct kgi_display *dpy, ggi_uint line)

void (*set_origin)(struct kgi_display *dpy, ggi_uint x, ggi_uint y)

void (*set_pointer_shape)(struct kgi_display *dpy, struct kgi_pointer64x64 *shape)

Display registration

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)

int kgi_unregister_display(struct kgi_display *dpy, struct kgi_display *alias)

Modularized Display Drivers used by GGI

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.

Generic I/O Functions

[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 {

};

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);


extern uint8 DAC_in8(struct kgi_display *dpy, uint8 reg);

extern void DAC_outs8(struct kgi_display *dpy, uint8 reg, void *buf, ggi_uint cnt);

extern void DAC_ins8(struct kgi_display *dpy, uint8 reg, void *buf, ggi_uint cnt);

extern void CLK_out8(struct kgi_display *dpy, uint8 val, uint8 reg);

extern uint8 CLK_in8(struct kgi_display *dpy, uint8 reg);

NOTE: Future extensions here will include VESA DDC support and probably a more generic I/O abstraction.

The Subsystem Interface

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.

int kgim_<subsystem>_init(struct kgi_card *, struct kgi_display *);
void kgim_<ubsystem>_done(struct kgi_display *);

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
  • detect the chipset (if possible) and do any neccessary initialization so that all chipset services, especially I/O, work after this function returned successful
  • register the neccessary I/O ressources used. Due to the current lack of full ressource bookkeeping with Linux, this means mainly the I/O and IRQ ressources used.
  • set the card->chipinfo pointer to a valid chipset info structure. This struct must be writable and must not be reallocated or freeed afterwards!
monitor
  • detect the monitor, read the parameters, do all the other initialization neccessary.
  • set the card->monitor field to point to a valid monitor info structure.
ramdac
  • detect the ramdac properly
  • initialize internal state
  • set the card->ramdac info pointer to point to a valid ramdac structure
clock chip
  • init driver state
  • set the card->clock info pointer to point to a valid clock info structure.

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.

int kgim_<subsystem>_check_mode(struct kgi_display *, struct kgi_mode *, enum kgi_tm_cmd);
int kgim_monitor_check_mode(struct kgi_display *, struct kgi_mode *, enum kgi_tm_cmd *);
void kgim_<subsystem>_set_mode(struct kgi_display *, struct kgi_mode *);

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.

PROPOSING A VIDEO MODE

Basically the kgim_check_mode() does the following steps when a mode has to be proposed (cmd == CMD_PROPOSE):

  1. set the undefined fields in mode to 0 to wipe out undefined behavior
  2. Enter a loop that allows the drivers to communicate and agree on the question if a mode can be done or not. As soon as one subsystem reports the mode cannot be done, kgim_check_mode() exits returning the error reported by the subsystem.
  3. If the loop is passed, the mode is possible on the given hardware and the mode structure holds all the neccessary information to set this mode.

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
  • do nothing
CMD_PROPOSE ramdac
  • set the mode->lclk ratio (the load clock rate divided by the dot clock rate)
  • set the mode->clk ratio (the clock rate to be provided by the clock chip divided by the dot clock rate)

Propose values that give maximum performance. The ratios mul and div fields must not have common divisors.

CMD_PROPOSE chipset
  • alter the visible and virtual size so that hardware acceleration is possible or hardware constraints are met, but only to greater values.
  • if only one of the mode->request.virtual.x or mode->request.virtual.y fields is given, extend the other one to its maximum possible value.
  • check if the chipset can support the lclk and clk ratios proposed by the DAC. You may adjust them just to what the chipset can do.
  • set the mode->x.width, mode->y.width and mode->mag fields to values that are needed with this mode. Use magnification features when resolution is very low (e.g. 300x200). Note that all timing parameters are in dots and not pixels. Adjust the mode->request.text field so that it represents the magnification used.NOTE: the mode->mag field will go away and mode->request.text will be named mode->request.dots with later versions, because the text size will be used to describe the dots per pixel.
CMD_PROPOSE monitor
  • set the timing paramters mode->x and mode->y according to the mode->x.width and mode->y.width fields which must not be altered.
  • propose a DCLK value convinient for the monitor and enter it in the mode->dclk field.
  • set *cmd to CMD_RAISE if the other drivers should raise the DCLK rate or CMD_LOWER if the other drivers should lower the DCLK rate.

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
  • if neccessary, adjust mode->dclk to meet the chipset AC specifications.Take care you consider the mode->clk and mode->lclk fields properly!
CMD_LOWER
or
CMD_RAISE
ramdac
  • if neccessary, adjust the mode->dclk field to meet the ramdac electrical specifications.
  • adjust the mode->x.total and mode->y.total timing fields to ensure proper operation (e.g. when the DAC has a hardware cursor and has to count the lines/dots, the HSYNC portion needs to have a certain length).
CMD_LOWER
or
CMD_RAISE
chipset
  • adjust the timing values (mode->x and mode->y) to what the hardware can do. Usually there are constraints that limit it to multiples of 8, for example.
CMD_LOWER
or
CMD_RAISE
clock
  • if neccessary, adjust mode->dclk to meet the chipset AC specifications.Take care you consider the mode->clk and mode->lclk fields properly!
CMD_LOWER
or
CMD_RAISE
monitor
  • refine the timing parameters to meet the specifications, but don't change mode->dclk.

Now you may either

  • alter *cmd to be CMD_CHECK, don't touch the other fields then.

or you

  • save the current timing in a static local variable and propose an alternate timing by setting the mode->x, mode->y and mode->dclk field (not altering the mode->x.width and mode->y.width fields)
  • alter *cmd to be CMD_LOWER or CMD_RAISE to start a second refinement cycle.
  • if you do this, make sure you copy the best mode settings back to *mode and set *cmd to CMD_CHECK during the second cycle.

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
  • check the clock chip specifications are met and the clock chip can provide the requested DCLK rate (considering the mode->clk ratio!).
CMD_CHECK ramdac
  • make sure the DACs chip specifications are met and that it can do the mode requested
CMD_CHECK chipset
  • make sure the chipsets specifications and operating conditions are met when this mode is set
  • if a text mode is requested, enter pointers to the proper frame buffer operations in mode->mem and mode->io.You may use those provided by the kernel here. NOTE: This may change slightly.
  • Enter the physical description of the framebuffer in mode->fb, mode->fb_size and mode->fsize.
CMD_CHECK monitor
  • enter the size of the visible image in mm in mode->request.size (if available)
  • make sure the monitor specification is met when operating this mode.

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 VIDEO MODE

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
  • none
ramdac
  • set_clut() if possible with the mode set
  • show_pointer(), hide_pointer() and undo_pointer() if a hardware cursor is supported in this mode
  • show_cursor(), hide_cursor() and undo_cursor() if a hardware cursor is supported in this mode (additionally to the hardware pointer).
chipset
  • set_font(), if textured pixles (fonts) are supported in this mode and they are setable.
  • set_origin() if virtual frames are supported in this mode.
  • set_split() if the splitline feature is supported in this mode.
  • set_frame() if multiple frames are supported in this mode.
  • show_cursor(), hide_cursor() and undo_cursor() if a hardware cursor is possible and the DAC didn't provide one already. If a textmode is being set, software alternatives must be provided.
  • show_pointer(), hide_pointer() and undo_cursor() if a hardware cursor is possible with the chipset and the DAC didn't provide one already. If a textmode is being set, software alternatives must be provided.
monitor
  • none

[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]


Copyright (C) 1997 by Steffen Seeger - All rights of this work, especially publishing, translation and all kinds of reproduction are reserved by the author(s). You may, however, print a copy for personal use provided that the name(s) of the author(s) are included with the copy.