RT-Thread RTOS
An open source embedded real-time operating system
Graphics (DM)

Graphics subsystem (device model)

RT-Thread graphic DM (components/drivers/include/drivers/graphic.h, components/drivers/graphic/graphic.c) wraps display controllers as **struct rt_graphic_device**: multi-**plane** framebuffers, EDID, optional backlight, Linux-style fb ioctls, and legacy **rt_device_graphic_ops** via **rt_graphic_device_switch_primary**.

App-facing pixel formats and **RTGRAPHIC_CTRL_*** live in **drivers/classes/graphic.h**. DM types, planes, and registration APIs are in **graphic.h**.

Implementation split:

File Role
**graphic.c** Device register, control, planes, EDID parse, auto-update timer, OFW backlight
**graphic_simple.c** Synthetic EDID + **rt_graphic_device_simple_register**
**graphic_primary.c** **rt_graphic_device_switch_primary** → set_pixel / draw_hline on primary FB
**framebuffer/fb-simple.c** DT **simple-framebuffer** — Simple framebuffer (simple-framebuffer)

Kconfig

Option Role
**RT_USING_DM** Required parent
**RT_USING_GRAPHIC** Core graphic stack (graphic.c, graphic_primary.c, graphic_simple.c)
**RT_GRAPHIC_FB** Framebuffer drivers (e.g. **simple-framebuffer**)
**RT_GRAPHIC_BACKLIGHT** Backlight class + PWM/GPIO — Backlight devices
**RT_GRAPHIC_LOGO** Boot logo render on register — Boot Logo

Architecture

Controller driver (DSI, LCD, simple-fb, …)
|
| alloc_plane + plane_ops (update / fb_remap / fb_cleanup)
v
struct rt_graphic_device
primary_plane (required, one)
cursor_plane (optional, one)
overlay_nodes (0..N)
edid, ops, backlight, update_timer
|
| rt_graphic_device_register -> rt_device "fbN"
v
Apps: rt_device_find + RTGRAPHIC_CTRL_* / read-write FB
|
| rt_graphic_device_switch_primary(gdev)
v
dev->user_data = rt_device_graphic_ops (graphic_primary.c)
Layer Responsibility
**rt_graphic_plane_ops** Hardware: allocate FB, push pixels to display (**update**), teardown (**fb_cleanup**)
**rt_graphic_device_ops** Device-wide: DPMS, vsync, brightness, vendor **control**
**graphic.c** Standard **rt_device** API, rect update, mode set, hotplug, periodic flush timer

Core structures

<tt>struct rt_graphic_plane</tt>

Field Meaning
**type** **PRIMARY** (one), **CURSOR** (one), **OVERLAY** (list)
**mode / modes[]** **RTGRAPHIC_PIXEL_FORMAT_*** from classes/graphic.h
**framebuffer**, **line_length**, **screen_len**, **framebuffer_len** CPU-visible buffer; may be multi-buffer if **framebuffer_len > screen_len**
**ops** Driver callbacks (see below)

<tt>struct rt_graphic_plane_ops</tt>

Callback When called Contract
**fb_remap** Mode or resolution change Allocate/map FB for **mode** + **rect**; set **plane->framebuffer**
**fb_cleanup** Before remap / del_plane Release HW resources (unmap, unref GPU resource)
**update** **RTGRAPHIC_CTRL_RECT_UPDATE** or auto timer Push **rect** region to display; **width/height == 0** may mean “position only” (cursor)
**fb_pan_display** **RTGRAPHIC_CTRL_PAN_DISPLAY** / open Point scanout at offset inside **framebuffer**
**prop_set** Optional Z-order, rotate, alpha (**enum rt_graphic_plane_prop**)

<tt>struct rt_graphic_device_ops</tt>

Callback Role
**dpms_switch** **RT_GRAPHIC_DPMS_*** — often ties to **rt_graphic_device_update_auto** period
**wait_vsync** **RTGRAPHIC_CTRL_WAIT_VSYNC**
**set_brightness / get_brightness** Panel brightness; may defer to **gdev->backlight**
**get_status** Optional connector status
**current_plane** Override plane used for **GET_INFO** / updates (default: primary)
**control** Vendor-specific commands (optional 3D / acceleration path)

EDID

**struct edid** in **graphic.h** mirrors VESA EDID layout. **RTGRAPHIC_CTRL_GET_EXT** copies **gdev->edid**. On register, if primary plane has no size, **graphic_edid_res()** picks mode from detailed timings.

**rt_graphic_device_simple_edid(gdev, w, h, refresh_hz)** (graphic_simple.c) builds a minimal EDID when hardware has no DDC (fixed panels, simple bring-up).


Registration API

API Role
**rt_graphic_device_alloc_plane(gdev, priv_size, ops, modes, modes_nr, type)** Allocate plane; **priv[0]** tail for driver data
**rt_graphic_device_add_plane(gdev, plane)** Attach primary/cursor/overlay; assigns **plane_ida** id
**rt_graphic_device_register(gdev)** Requires **primary_plane**; names device **fbu** via **RT_DM_IDA**; maps FB from EDID if size zero
**rt_graphic_device_unregister(gdev)** DPMS off, stop timer, del all planes, **rt_device_unregister**
**rt_graphic_device_simple_register(...)** Alloc primary plane + synthetic EDID + register (no full EDID path)
**rt_graphic_device_simple_unregister(gdev)** Alias for **unregister**

Naming: **RT_DM_IDA_INIT(GRAPHIC_FRAMEBUFFER)** → devices **fb0**, **fb1**, …

OFW backlight: during **register**, **graphic_ofw_init** resolves **backlight** phandle on **dev->ofw_node** and opens the backlight device.


Application / framework controls

From **classes/graphic.h** (handled in **graphic.c** **_graphic_control**):

Command Behavior
**RTGRAPHIC_CTRL_RECT_UPDATE** **plane->ops->update(plane, rect)** on current plane
**RTGRAPHIC_CTRL_SET_MODE** **fb_remap** if mode is in **plane->modes[]**
**RTGRAPHIC_CTRL_GET_INFO** Fills **rt_device_graphic_info** from current plane
**RTGRAPHIC_CTRL_POWERON / POWEROFF** DPMS + optional backlight power
**RTGRAPHIC_CTRL_PAN_DISPLAY** Offset within framebuffer (double-buffer)
**RTGRAPHIC_CTRL_WAIT_VSYNC** Driver **wait_vsync**
**RT_DEVICE_CTRL_CURSOR_*** Cursor plane position / bitmap
**FBIOGET_VSCREENINFO / FBIOPUT_VSCREENINFO** Linux fb var screeninfo (when enabled)

Auto flush: **rt_graphic_device_update_auto(gdev, ms)** starts a periodic timer (default idea: RT_GRAPHIC_UPDATE_MS = 16) that calls **update** on primary, overlays, and cursor. **rt_graphic_device_enter / leave** stop/restart the timer around modeset (hotplug).

Hotplug: **rt_graphic_device_hotplug_event(gdev)** re-parses EDID size, **fb_remap** primary, then fires **event_notify**.


Primary display helpers (<tt>graphic_primary.c</tt>)

struct rt_device_graphic_ops *rt_graphic_device_switch_primary(struct rt_graphic_device *gdev);
Definition: graphic.h:93
  • Sets global **primary_gdev** and returns **rt_device_graphic_ops** matching primary plane bpp (8/16/24/32/64).
  • Legacy macro in **graphic.h**: **rt_graphix_ops(dev)** calls **switch_primary** then reads **dev->user_data**.
  • Used by finsh / LVGL / logo code that draws via **set_pixel** / **draw_hline** without **RTGRAPHIC_CTRL_RECT_UPDATE**.

After **register**, call **switch_primary** on the head you want for **rt_graphix_ops**.


Typical controller <tt>probe</tt> (SoC)

static const rt_uint32_t my_modes[] = {
RTGRAPHIC_PIXEL_FORMAT_RGB565,
RTGRAPHIC_PIXEL_FORMAT_ARGB888,
};
static const struct rt_graphic_plane_ops my_plane_ops = {
.fb_remap = my_fb_remap,
.fb_cleanup = my_fb_cleanup,
.update = my_fb_update,
};
static rt_err_t my_probe(struct rt_platform_device *pdev)
{
struct my_drv *priv;
struct rt_graphic_plane *plane;
rt_err_t err;
priv = rt_dm_dev_iomap(pdev->parent.parent, 0); /* or embed in pdev->priv */
/* clocks, resets, regulator — @ref page_device_clk, @ref page_device_reset */
priv->gdev.ops = &my_gdev_ops;
plane = rt_graphic_device_alloc_plane(&priv->gdev, sizeof(*priv),
&my_plane_ops, my_modes, RT_ARRAY_SIZE(my_modes),
RT_GRAPHIC_PLANE_TYPE_PRIMARY);
if (!plane)
return -RT_ENOMEM;
err = rt_graphic_device_add_plane(&priv->gdev, plane);
if (err)
goto err;
/* Fill EDID from DDC or rt_graphic_device_simple_edid() */
err = rt_graphic_device_register(&priv->gdev);
if (err)
goto err;
rt_graphic_device_switch_primary(&priv->gdev);
rt_graphic_device_update_auto(&priv->gdev, RT_GRAPHIC_UPDATE_MS);
return RT_EOK;
}

**simple-framebuffer**: maps fixed **reg** RAM, implements plane ops as CPU memcpy — see Simple framebuffer (simple-framebuffer).


PHY, clocks, backlight

Resource Document
Pixel / link clock Clock framework (CLK)
DSI/DP PHY Generic PHY (Phye)
Panel backlight Backlight devices (backlight DT property)
Coherent FB / cache Hardware cache (hwcache), DMA engine

Bring-up order: regulator → clock → panel init → **register** → backlight **POWERON** → **RTGRAPHIC_CTRL_POWERON**.


Engineer checklist

  1. Implement **rt_graphic_plane_ops** (at minimum **fb_remap**, **update**, **fb_cleanup**).
  2. **add_plane PRIMARY** before **register**; cursor/overlay after if needed.
  3. Populate **edid** or use **simple_register** / **simple_edid**.
  4. **register** → **switch_primary** for legacy drawing APIs.
  5. **remove**: **unregister** (frees planes); release clocks/reset/regulator in reverse order.
  6. Hotplug: call **hotplug_event** after EDID/mode change under **enter/leave**.

Pitfalls

  • Missing update with auto timer off: apps must **RTGRAPHIC_CTRL_RECT_UPDATE** after CPU writes or nothing reaches the panel.
  • Primary plane size 0 at register: core tries EDID **fb_remap** — bogus EDID causes probe failure.
  • Two PRIMARY planes: **add_plane** returns **-RT_EINVAL**.
  • DMA / cache: CPU-drawn FB must use correct MMU attributes; sync cache before **update** if scanout is non-coherent — Hardware cache (hwcache).
  • **switch_primary global**: only one **primary_gdev** — multi-head apps should use **rt_device_find("fb1")** + graphic ctrl, not **rt_graphix_ops** alone.
  • DPMS off without stopping updates: use **rt_graphic_device_update_auto(gdev, 0)** or **enter** during suspend.

Detailed documents

See also

  • components/drivers/include/drivers/graphic.h
  • components/drivers/include/drivers/classes/graphic.h
  • components/drivers/graphic/graphic.c, graphic_simple.c, graphic_primary.c
  • Device model helpers (dm.h)rt_dm_dev_iomap, naming
  • Platform busRT_PLATFORM_DRIVER_EXPORT