eCos Reference Manual
Prev |  | Next

# 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
    }

Prev | Home | Next
Framebuffer Parameters | Up | Framebuffer Colours