Overview — eCos Support for Framebuffer Devices


Framebuffer devices are the most common way for a computer system to display graphical output to users. There are immense variations in the implementations of such devices. CYGPKG_IO_FRAMEBUF provides an abstraction layer for use by application code and other packages. It defines an API for manipulating framebuffers, mapping this API on to functionality provided by the appropriate device driver. It also defines the interface which such device drivers should implement. For simple hardware it provides default implementations of much of this interface, greatly reducing the effort needed to write a device driver.

This package does not constitute a graphics library. It does not implement functionality like drawing text or arbitrary lines, let alone any kind of windowing system. Instead it operates at the lower level of individual pixels and blocks of pixels, in addition to control operations such as hardware initialization. Some applications may use the framebuffer API directly. Others will instead use a higher-level graphics library, and it is that library which uses the framebuffer API.

It is assumed that users are already familiar with the fundamentals of computer graphics, and no attempt is made here to explain terms like display depth, palette or pixel.


This package is work-in-progress. The support for 1bpp, 2bpp and 4bpp display depths is incomplete. For double-buffered displays the code does not yet maintain a bounding box of the updated parts of the display. The package has also been designed to allow for expansion with new functionality.


CYGPKG_IO_FRAMEBUF only contains hardware-independent code. It should be complemented by one or more framebuffer device drivers appropriate for the target platform. These drivers may be specific to the platform, or they may be more generic with platform-specific details such as the framebuffer memory base address provided by the platform HAL. When creating a configuration for a given target the device driver(s) will always be included automatically (assuming one has been written or ported). However by default this driver will be inactive and will not get built, so does not add any unnecessary size overhead for applications which do not require graphics. To activate the device driver CYGPKG_IO_FRAMEBUF must be added explicitly to the configuration, for example using ecosconfig add framebuf. After this the full framebuffer API will be available to other packages and to application code.

This package contains very few configuration options. Instead it is left to device drivers or higher-level code to provide appropriate configurability. One option, CYGFUN_IO_FRAMEBUF_INSTALL_DEFAULT_PALETTE, relates to the initialization of paletted displays.

There are a number of calculated and inferred configuration options and a number of interfaces. These provide information such as whether or not there is a backlight. The most important one is CYGDAT_IO_FRAMEBUF_DEVICES, which holds a list of framebuffer identifiers for use with the macro-based API. If there is a single framebuffer device driver which supports one display in either landscape or portrait mode, the configuration option may hold a value like 240x320x8 320x240x8r90.

Application Programmer Interfaces

Framebuffer devices require a difficult choice between flexibility and performance. On the one hand the API should be able to support multiple devices driving separate displays, or a single device operating in different modes at different times. On the other hand graphics tends to involve very large amounts of I/O: even something as simple as drawing a background image can involve setting many thousands of pixels. Efficiency requires avoiding all possible overheads including function calls. Instead the API should make extensive use of macros or inline functions. Ideally details of the framebuffer device such as the stride would be known constants at compile-time, giving the compiler as much opportunity as possible to optimize the code. Clearly this is difficult if multiple framebuffer devices are in use or if the device mode may get changed at run-time.

To meet the conflicting requirements the generic framebuffer package provides two APIs: a fast macro API which requires selecting a single framebuffer device at compile or configure time; and a slower function API without this limitation. The two are very similar, for example:

#include <cyg/io/framebuf.h>

clear_screen(cyg_fb* fb, cyg_fb_colour colour)
    cyg_fb_fill_block(fb, 0, 0,
                      fb->fb_width, fb->fb_height,

or the equivalent macro version:

#include <cyg/io/framebuf.h>

#define FRAMEBUF 240x320x8

clear_screen(cyg_fb_colour colour)

The function-based API works in terms of cyg_fb structures, containing all the information needed to manipulate the device. Each framebuffer device driver will export one or more of these structures, for example cyg_alaia_fb_240x320x8, and the driver documentation should list the variable names. The macro API works in terms of identifiers such as 240x320x8, and by a series of substitutions the main macro gets expanded to the appropriate device-specific code, usually inline. Again the device driver documentation should list the supported identifiers. In addition the configuration option CYGDAT_IO_FRAMEBUF_DEVICES will contain the full list. By convention the identifier will be specified by a #define'd symbol such as FRAMEBUF, or in the case of graphics libraries by a configuration option.

If a platform has multiple framebuffer devices connected to different displays then there will be separate cyg_fb structures and macro identifiers for each one. In addition some devices can operate in multiple modes. For example a PC VGA card can operate in a monochome 640x480 mode, an 8bpp 320x200 mode, and many other modes, but only one of these can be active at a time. The different modes are also represented by different cyg_fb structures and identifiers, effectively treating the modes as separate devices. It is the responsibility of higher-level code to ensure that only one mode is in use at a time.

It is possible to use the macro API with more than one device, basically by compiling the code twice with different values of FRAMEBUF, taking appropriate care to avoid identifier name clashes. This gives the higher performance of the macros at the cost of increased code size.

All of the framebuffer API, including exports of the device-specific cyg_fb structures, is available through a single header file <cyg/io/framebuf.h>. The API follows a number of conventions. Coordinates (0,0) correspond to the top-left corner of the display. All functions and macros which take a pair of coordinates have x first, y second. For block operations these coordinates are followed by width, then height. Coordinates and dimensions use cyg_ucount16 variables, which for any processor should be the most efficient unsigned data type with at least 16 bits - usually plain unsigned integers. Colours are identified by cyg_fb_colour variables, again usually unsigned integers.

To allow for the different variants of the English language, the API allows for a number of alternate spellings. Colour and color can be used interchangeably, so there are data types cyg_fb_colour and cyg_fb_color, and functions cyg_fb_make_colour and cyg_fb_make_color. Similarly gray is accepted as a variant of grey so the predefined colours CYG_FB_DEFAULT_PALETTE_LIGHTGREY and CYG_FB_DEFAULT_PALETTE_LIGHTGRAY are equivalent.

The API is split into the following categories:

getting information about a given framebuffer device such as width, height and depth. Colours management is complicated so has its own category.
operations such as switching the display on and off, and more device-specific ones such as manipulating the backlight.
determining the colour format (monochrome, paletted, …), manipulating the palette, or constructing true colours.
primitives for manipulating pixels and blocks of pixels.
efficiently iterating over blocks of pixels.

Thread Safety

The framebuffer API never performs any locking so is not thread-safe. Instead it assumes that higher-level code such as a graphics library performs any locking that may be needed. Adding a mutex lock and unlock around every drawing primitive, including pixel writes, would be prohibitively expensive.

It is also assumed that the framebuffer will only be updated from thread context. With most hardware it will also be possible to access a framebuffer from DSR or ISR context, but this should be avoided in portable code.