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