Framebuffer Control Operations

Name

Control Operations -- managing a framebuffer

Synopsis

#include <cyg/io/framebuf.h>
      

int cyg_fb_on(cyg_fb* fbdev);

int cyg_fb_off(cyg_fb* fbdev);

int cyg_fb_ioctl(cyg_fb* fbdev, cyg_uint16 key, void* data, size_t* len);

int CYG_FB_ON(FRAMEBUF);

int CYG_FB_OFF(FRAMEBUF);

int CYG_FB_IOCTL(FRAMEBUF, cyg_uint16 key, void* data, size_t* len);

Description

The main operations on a framebuffer are drawing and colour management. However on most hardware it is also necessary to switch the display on before the user can see anything, and application code should be able to control when this happens. There are also miscellaneous operations such as manipulating the backlight or moving the viewpoint. These do not warrant dedicated functions, especially since the functionality will only be available on some hardware, so an ioctl interface is used.

Switching the Display On or Off

With most hardware nothing will be visible until there is a call to cyg_fb_on or an invocation of the CYG_FB_ON macro. This will initialize the framebuffer control circuitry, start sending the data signals to the display unit, and switch on the display if necessary. The exact initialization semantics are left to the framebuffer device driver. In some cases the hardware may already be partially or fully initialized by a static constructor or by boot code that ran before eCos.

There are some circumstances in which initialization can fail, and this is indicated by a POSIX error code such as ENODEV. An example would be plug and play hardware where the framebuffer device is not detected at run-time. Another example is hardware which can operate in several modes, with separate cyg_fb structures for each mode, if the hardware is already in use for a different mode. A return value of 0 indicates success.

Some but not all hardware allows the framebuffer memory and, if present, the palette to be manipulated before the device is switched on. That way the user does not see random noise on the screen during system startup. The flag CYG_FB_FLAGS0_MUST_BE_ON should be checked:

static void
init_screen(cyg_fb_colour background)
{
    int result;

#if (! (CYG_FB_FLAGS0(FRAMEBUF) & CYG_FB_FLAGS0_MUST_BE_ON))
    CYG_FB_FILL_BLOCK(FRAMEBUF, 0, 0,
                      CYG_FB_WIDTH(FRAMEBUF), CYG_FB_HEIGHT(FRAMEBUF),
                      background);
#endif

    result = CYG_FB_ON(FRAMEBUF);
    if (0 != result) {
        <handle unusual error condition>
    }

#if (CYG_FB_FLAGS0(FRAMEBUF) & CYG_FB_FLAGS0_MUST_BE_ON)
    CYG_FB_FILL_BLOCK(FRAMEBUF, 0, 0,
                      CYG_FB_WIDTH(FRAMEBUF), CYG_FB_HEIGHT(FRAMEBUF),
                      background);
#endif
}
    

Obviously if the application has already manipulated framebuffer memory or the palette but then the cyg_fb_on operation fails, the system is left in an undefined state.

It is also possible to switch a framebuffer device off, using the function cyg_fb_off or the macro CYG_FB_OFF, although this functionality is rarely used in embedded systems. The exact semantics of switching a device off are implementation-defined, but typically it involves shutting down the display, stopping the data signals to the display, and halting the control circuitry. The framebuffer memory and the palette are left in an undefined state, and application code should assume that both need full reinitializing when the device is switched back on. Some hardware may also provide a blank operation which typically just manipulates the display, not the whole framebuffer device. Normally cyg_fb_on returns 0. The API allows for a POSIX error code as with cyg_fb_on, but switching a device off is not an operation that is likely to fail.

If a framebuffer device can operate in several modes, represented by several cyg_fb structures and macro identifiers, then switching modes requires turning the current device off before turning the next one one.

Miscellaneous Control Operations

Some hardware functionality such as an LCD panel backlight is common but not universal. Supporting these does not warrant dedicated functions. Instead a catch-all ioctl interface is provided, with the arguments just passed straight to the device driver. This approach also allows for future expansion and for device-specific operations. cyg_fb_ioctl and CYG_FB_IOCTL take four arguments: a cyg_fb structure or framebuffer identifier; a key that specifies the operation to be performed; an arbitrary pointer, which should usually be a pointer to a data structure specific to the key; and a length field. Key values from 0 to 0x7fff are generic. Key values from 0x8000 onwards are reserved for the individual framebuffer device drivers, for device-specific functionality. The length field should be set to the size of the data structure, and may get updated by the device driver.

With most ioctl operations the device can indicate whether or not it supports the functionality by one of the flags, for example:

void
backlight_off(cyg_fb* fb)
{
    if (fb->fb_flags0 & CYG_FB_FLAGS0_BACKLIGHT) {
        cyg_fb_ioctl_backlight  new_setting;
        size_t                  len = sizeof(cyg_fb_ioctl_backlight);
        int                     result;

        new_setting.fbbl_current = 0;
        result = cyg_fb_ioctl(fb, CYG_FB_IOCTL_BACKLIGHT_SET,
                              &new_setting, &len);
        if (0 != result) {
            …
        }
    }
}
    

The operation returns zero for success or a POSIX error code on failure, for example ENOSYS if the device driver does not implement the requested functionality.

Viewport

# define CYG_FB_IOCTL_VIEWPORT_GET_POSITION     0x0100
# define CYG_FB_IOCTL_VIEWPORT_SET_POSITION     0x0101

typedef struct cyg_fb_ioctl_viewport {
    cyg_ucount16    fbvp_x;     // position of top-left corner of the viewport within
    cyg_ucount16    fbvp_y;     // the framebuffer
    cyg_ucount16    fbvp_when;  // set-only, now or vert retrace
} cyg_fb_ioctl_viewport;
      

On some targets the framebuffer device has a higher resolution than the display. Only a subset of the pixels, the viewport, is currently visible. Application code can exploit this functionality to achieve certain effects, for example smooth scrolling. Framebuffers which support this functionality will have the CYG_FB_FLAGS0_VIEWPORT flag set. The viewport dimensions are available as additional parameters to the normal framebuffer width and height.

The current position of the viewport can be obtained using an CYG_FB_IOCTL_VIEWPORT_GET_POSITION ioctl operation. The data argument should be a pointer to a cyg_fb_ioctl_viewport structure. On return the fbvp_x and fbvp_y fields will be filled in. To move the viewport use CYG_FB_IOCTL_VIEWPORT_SET_POSITION with fbvp_x and fbvp_y set to the top left corner of the new viewport within the framebuffer, and fbvp_when set to either CYG_FB_UPDATE_NOW or CYG_FB_UPDATE_VERTICAL_RETRACE. If the device driver cannot easily synchronize to a vertical retrace period then this last field is ignored.

void
move_viewport(cyg_fb* fb, int dx, int dy)
{
#ifdef CYGHWR_IO_FRAMEBUF_FUNCTIONALITY_VIEWPORT
    cyg_fb_ioctl_viewport viewport;
    int len = sizeof(cyg_fb_ioctl_viewport);
    int result;

    result = cyg_fb_ioctl(fb, CYG_FB_IOCTL_VIEWPORT_GET_POSITION,
                        &viewport, &len);
    if (result != 0) {
        …
    }
    if (((int)viewport.fbvp_x + dx) < 0) {
        viewport.fbvp_x = 0;
    } else if ((viewport.fbvp_x + dx + fb->fb_viewport_width) > fb->fb_width) {
        viewport.fbvp_x = fb->fb_width - fb->fb_viewport_width;
    } else {
        viewport.fbvp_x += dx;
    }
    if (((int)viewport.fbvp_y + dy) < 0) {
        viewport.fbvp_y = 0;
    } else if ((viewport.fbvp_y + dy + fb->fb_viewport_height) > fb->fb_height) {
        viewport.fbvp_y = fb->fb_height - fb->fb_viewport_height;
    } else {
        viewport.fbvp_y += dy;
    }
    result = cyg_fb_ioctl(fb, CYG_FB_IOCTL_VIEWPORT_SET_POSITION,
                          &viewport, &len);
    if (result != 0) {
        …
    }
#else
    CYG_UNUSED_PARAM(cyg_fb*, fb);
    CYG_UNUSED_PARAM(int, dx);
    CYG_UNUSED_PARAM(int, dy);
#endif
}
      

If an attempt is made to move the viewport beyond the boundaries of the framebuffer then the resulting behaviour is undefined. Some hardware may behave reasonably, wrapping around as appropriate, but portable code cannot assume this. The above code fragment is careful to clip the viewport to the framebuffer dimensions.

Page Flipping

# define CYG_FB_IOCTL_PAGE_FLIPPING_GET_PAGES   0x0200
# define CYG_FB_IOCTL_PAGE_FLIPPING_SET_PAGES   0x0201

typedef struct cyg_fb_ioctl_page_flip {
    cyg_uint32      fbpf_number_pages;
    cyg_uint32      fbpf_visible_page;
    cyg_uint32      fbpf_drawable_page;
    cyg_ucount16    fbpf_when;  // set-only, now or vert retrace
} cyg_fb_ioctl_page_flip;
      

On some targets the framebuffer has enough memory for several pages, only one of which is visible at a time. This allows the application to draw into one page while displaying another. Once drawing is complete the display is flipped to the newly drawn page, and the previously displayed page is now available for updating. This technique is used for smooth animation, especially in games. The flag CYG_FB_FLAGS0_PAGE_FLIPPING indicates support for this functionality.

CYG_FB_IOCTL_PAGE_FLIPPING_GET_PAGES can be used to get the current settings of the page flipping support. The data argument should be a pointer to a cyg_fb_ioctl_page_flip structure. The resulting fbpf_number_pages field indicates the total number of pages available: 2 is common, but more pages are possible. fbpf_visible_page gives the page that is currently visible to the user, and will be between 0 and (fbpf_number_pages - 1). Similarly fbpf_drawable_page gives the page that is currently visible. It is implementation-defined whether or not the visible and drawable page can be the same one.

CYG_FB_IOCTL_PAGE_FLIPPING_SET_PAGES can be used to change the visible and drawable page. The fbpf_number_pages field is ignored. fbpf_visible_page and fbpf_drawable_page give the new settings. fbpf_when should be one of CYG_FB_UPDATE_NOW or CYG_FB_UPDATE_VERTICAL_RETRACE, but may be ignored by some device drivers.

#if !(CYG_FB_FLAGS0(FRAMEBUF) & CYG_FB_FLAGS0_PAGE_FLIPPING)
# error Current framebuffer device does not support page flipping
#endif

static cyg_uint32 current_visible = 0;

static void
page_flip_init(cyg_fb_colour background)
{
    cyg_fb_ioctl_page_flip flip;
    size_t len = sizeof(cyg_fb_ioctl_page_flip);

    flip.fbpf_visible_page  = current_visible;
    flip.fbpf_drawable_page = 1 - current_visible;
    flip.fbpf_when          = CYG_FB_UPDATE_NOW;
    CYG_FB_IOCTL(FRAMEBUF, CYG_FB_IOCTL_PAGE_FLIPPING_SET_PAGES,
                 &flip, &len);
    CYG_FB_FILL_BLOCK(FRAMEBUF, 0, 0,
                      CYG_FB_WIDTH(FRAMEBUF), CYG_FB_HEIGHT(FRAMEBUF),
                      background);
    flip.fbpf_visible_page  = 1 - current_visible;
    flip.fbpf_drawable_page = current_visible;
    CYG_FB_IOCTL(FRAMEBUF, CYG_FB_IOCTL_PAGE_FLIPPING_SET_PAGES,
                 &flip, &len);
    CYG_FB_FILL_BLOCK(FRAMEBUF, 0, 0,
                      CYG_FB_WIDTH(FRAMEBUF), CYG_FB_HEIGHT(FRAMEBUF),
                      background);
    current_visible = 1 - current_visible;
}

static void
page_flip_toggle(void)
{
    cyg_fb_ioctl_page_flip flip;
    size_t len = sizeof(cyg_fb_ioctl_page_flip);

    flip.fbpf_visible_page  = 1 - current_visible;
    flip.fbpf_drawable_page = current_visible;
    CYG_FB_IOCTL(FRAMEBUF, CYG_FB_IOCTL_PAGE_FLIPPING_SET_PAGES,
                 &flip, &len);
    current_visible = 1 - current_visible;
}
      

A page flip typically just changes a couple of pointers within the hardware and device driver. No attempt is made to synchronize the contents of the pages, that is left to higher-level code.

Blanking the Screen

# define CYG_FB_IOCTL_BLANK_GET                 0x0300
# define CYG_FB_IOCTL_BLANK_SET                 0x0301

typedef struct cyg_fb_ioctl_blank {
    cyg_bool        fbbl_on;
} cyg_fb_ioctl_blank;
      

Some hardware allows the display to be switched off or blanked without shutting down the entire framebuffer device, greatly reducing power consumption. The current blanking state can be obtained using CYG_FB_IOCTL_BLANK_GET and the state can be updated using CYG_FB_IOCTL_BLANK_SET. The data argument should be a pointer to a cyg_fb_ioctl_blank structure. Support for this functionality is indicated by the CYG_FB_FLAGS0_BLANK flag.

static cyg_bool
display_blanked(cyg_fb_* fb)
{
    cyg_fb_ioctl_blank blank;
    size_t len = sizeof(cyg_fb_ioctl_blank);
    
    if (! (fb->fb_flags0 & CYG_FB_FLAGS0_BLANK)) {
        return false;
    }
    (void) cyg_fb_ioctl(fb, CYG_FB_IOCTL_BLANK_GET, &blank, &len);
    return !blank.fbbl_on;
}
      

Controlling the Backlight

# define CYG_FB_IOCTL_BACKLIGHT_GET             0x0400
# define CYG_FB_IOCTL_BACKLIGHT_SET             0x0401

typedef struct cyg_fb_ioctl_backlight {
    cyg_ucount32    fbbl_current;
    cyg_ucount32    fbbl_max;
} cyg_fb_ioctl_backlight;
      

Many LCD panels provide some sort of backlight, making the display easier to read at the cost of increased power consumption. Support for this is indicated by the CYG_FB_FLAGS0_BACKLIGHT flag. CYG_FB_IOCTL_BACKLIGHT_GET can be used to get both the current setting and the maximum value. If the maximum is 1 then the backlight can only be switched on or off. Otherwise it is possible to control the intensity.

static void
set_backlight_50_percent(void)
{
#if (CYG_FB_FLAGS0(FRAMEBUF) & CYG_FB_FLAGS0_BACKLIGHT)
    cyg_fb_ioctl_backlight backlight;
    size_t len = sizeof(cyg_fb_ioctl_backlight);

    CYG_FB_IOCTL(FRAMEBUF, CYG_FB_IOCTL_BACKLIGHT_GET, &backlight, &len);
    backlight.fbbl_current = (backlight.fbbl_max + 1) >> 1;    
    CYG_FB_IOCTL(FRAMEBUF, CYG_FB_IOCTL_BACKLIGHT_SET, &backlight, &len);
#endif
}