Recently, RK has implemented a hardware cursor layer processed from the upper layer, with the repository located at https://github.com/JeffyCN/drm-cursor. Previously, hardware cursors like those on the RK3399 used the generic interface of libdrm, but on new platforms such as RK3568 and RK3588, it seems that the kernel does not set up a separate mouse layer.
If you need to use a hardware cursor on the platform, you can use this library to implement it.
The drm cursor mouse pointer implemented here is achieved by hooking the relevant functions for setting the mouse in drm through the EGL interface, avoiding the need to go through libdrm to the kernel.
Here is an explanation of what a cursor is:
Cursors
Similar to planes, many hardware also supports cursors. A cursor is a very small buffer with an image that is blended over the CRTC framebuffer. You can set a different cursor for each CRTC with drmModeSetCursor(3) and move it on the screen with drmModeMoveCursor(3). This allows to move the cursor on the screen without rerendering. If no hardware cursors are supported, you need to rerender for each frame the cursor is moved.
1: Standard Cursor Function Implementation
1.1 Implementation of libdrm Functions
drmModeSetCursor
xf86drmMode.c +412
drmpublic int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle,
uint32_t width, uint32_t height)
{
struct drm_mode_cursor arg;
memclear(arg);
arg.flags = DRM_MODE_CURSOR_BO;
arg.crtc_id = crtcId;
arg.width = width;
arg.height = height;
arg.handle = bo_handle;
return DRM_IOCTL(fd, DRM_IOCTL_MODE_CURSOR, &arg);
}
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER),
drmModeMoveCursor
drmpublic int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y)
{
struct drm_mode_cursor arg;
memclear(arg);
arg.flags = DRM_MODE_CURSOR_MOVE;
arg.crtc_id = crtcId;
arg.x = x;
arg.y = y;
return DRM_IOCTL(fd, DRM_IOCTL_MODE_CURSOR, &arg);
}
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER),
drmModeSetCursor2
drmpublic int drmModeSetCursor2(int fd, uint32_t crtcId, uint32_t bo_handle,
uint32_t width, uint32_t height, int32_t hot_x,
int32_t hot_y)
{
struct drm_mode_cursor2 arg;
memclear(arg);
arg.flags = DRM_MODE_CURSOR_BO;
arg.crtc_id = crtcId;
arg.width = width;
arg.height = height;
arg.handle = bo_handle;
arg.hot_x = hot_x;
arg.hot_y = hot_y;
return DRM_IOCTL(fd, DRM_IOCTL_MODE_CURSOR2, &arg);
}
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_UNLOCKED)
1.2 Implementation of Xorg Functions
The implementation of the cursor in Xorg is as follows:
hw/xfree86/drivers/modesetting/drmmode_display.c
drmmode_set_cursor_position
static void
drmmode_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
drmmode_ptr drmmode = drmmode_crtc->drmmode;
drmModeMoveCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, x, y);
}
drmmode_show_cursor
static Bool
drmmode_show_cursor(xf86CrtcPtr crtc)
{
drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
drmmode_crtc->cursor_up = TRUE;
return drmmode_set_cursor(crtc);
}
1.3 Whether to Use Hardware Cursor
ret = drmModeSetCursor2(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
handle, ms->cursor_width, ms->cursor_height,
cursor->bits->xhot, cursor->bits->yhot);
/* -EINVAL can mean that an old kernel supports drmModeSetCursor but
* not drmModeSetCursor2, though it can mean other things too. */
if (ret == -EINVAL)
ret = drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
handle, ms->cursor_width, ms->cursor_height);
/* -ENXIO normally means that the current drm driver supports neither
* cursor_set nor cursor_set2. Disable hardware cursor support for
* the rest of the session in that case. */
if (ret == -ENXIO) {
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
xf86CursorInfoPtr cursor_info = xf86_config->cursor_info;
cursor_info->MaxWidth = cursor_info->MaxHeight = 0;
drmmode_crtc->drmmode->sw_cursor = TRUE;
}
If the kernel’s IOCTL returns -ENXIO, then Xorg implements a software cursor.
drmmode_crtc->drmmode->sw_cursor = TRUE;
2: RK’s Cursor Hook Implementation
2.1 Function Implementation
int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle,
uint32_t width, uint32_t height)
{
/* Init log file */
drm_get_ctx(fd);
return drm_set_cursor(fd, crtcId, bo_handle, width, height);
}
int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y)
{
return drm_move_cursor(fd, crtcId, x, y);
}
int drmModeSetCursor2(int fd, uint32_t crtcId, uint32_t bo_handle,
uint32_t width, uint32_t height,
int32_t hot_x, int32_t hot_y)
{
return -EINVAL;
}
2.2 Handling of Hooks
This repository ultimately generates the library file libdrm-cursor.so.1.0.0, but the cursor implementation functions are in the libdrm so. Here, RK uses the ld preload configuration.
The method is to add the following in debian/libdrm-cursor.postinst:
echo /usr/lib/*/libdrm-cursor.so.1 >> /etc/ld.so.preload
The linking situation of Xorg is as follows, indicating that the hook has taken effect:
kylin@kylin:~$ ldd /usr/lib/xorg/Xorg | grep drm
/usr/lib/aarch64-linux-gnu/libdrm-cursor.so.1 (0x0000007fb9a31000)
libdrm.so.2 => /lib/aarch64-linux-gnu/libdrm.so.2 (0x0000007fb97d1000)
2.3 drm-cursor Runtime Log
drm_get_ctx(485) atomicdrmAPIenabled
drm_get_ctx(518) maxfps: 60
drm_get_ctx(559) found 0 CRTC: 64(0) (1920x1200) preferplane: 0
drm_get_ctx(559) found 1 CRTC: 83(1) (0x0) preferplane: 0
drm_get_ctx(567) found 2 CRTCs
drm_get_ctx(616) foundplane: 58[primary]crtcs: 0x1 (ARGB)
drm_get_ctx(616) foundplane: 61[cursor ]crtcs: 0x1 (ARGB)
drm_get_ctx(616) foundplane: 65[overlay]crtcs: 0x1 (ARGB)
drm_get_ctx(616) foundplane: 68[overlay]crtcs: 0x1 (ARGB)
drm_get_ctx(616) foundplane: 80[primary]crtcs: 0x2 (ARGB)
drm_get_ctx(616) foundplane: 84[overlay]crtcs: 0x2 (ARGB)
drm_get_ctx(625) usinglibdrm-cursor (1.3.0~20211201)
drmModeSetCursor(1185) fd: 11 crtc: 64 handle: 0 size: 0x0
drm_crtc_bind_plane(692) CRTC[64]: usingcursorplane
drm_crtc_bind_plane(700) CRTC[64]: bindplane: 61
drm_set_cursor(1097) CRTC[64]: requestsettingnewcursor 0 (0x0)
drm_crtc_thread_fn(866) CRTC[64]: threadstarted
drm_crtc_thread_fn(924) CRTC[64]: setnewcursor 0 (0x0)
drmModeMoveCursor(1192) fd: 11 crtc: 64 position: 956,596
drm_move_cursor(1154) CRTC[64]: requestmovingcursorto (956,596) in (1920x1200)
The information indicates: 1. Parsing the configuration file, 2. Finding the CRTC supported by drm, 3. Finding the corresponding plane, 4. Configuring the specified plane as the cursor plane.
2.4 Kernel Runtime Log
To view the kernel drm cursor logs, you need to enable the relevant configuration macros for drm.
The sys node for drm is:
/sys/module/drm/parameters/debug
The corresponding optional values are as follows:
Bit 0 (0x01) will enable CORE messages (drm core code)
Bit 1 (0x02) will enable DRIVER messages (drm controller code)
Bit 2 (0x04) will enable KMS messages (modesetting code)
Bit 3 (0x08) will enable PRIME messages (prime code)
Bit 4 (0x10) will enable ATOMIC messages (atomic code)
Bit 5 (0x20) will enable VBL messages (vblank code) (int)
Since the cursor is implemented through the atomic API, leveraging the debugging of atomic can provide some useful information.
echo 0x10 > /sys/module/drm/parameters/debug # Enable drm ATOMIC debug information
The logs are as follows:
[drm:drm_atomic_state_init] Allocatedatomicstate 000000009ea89924
[drm:drm_atomic_get_plane_state]Added[PLANE:71:Cluster0-win0] 000000001d2516b5stateto 000000009ea89924
[drm:drm_atomic_get_crtc_state]Added[CRTC:85:video_port0] 000000002526233cstateto 000000009ea89924
[drm:drm_atomic_set_fb_for_plane]Set[FB:202]for[PLANE:71:Cluster0-win0]state 000000001d2516b5
[drm:drm_atomic_check_only]checking 000000009ea89924
[drm:drm_atomic_commit]committing 000000009ea89924
[drm:drm_atomic_state_default_clear]Clearingatomicstate 000000009ea89924
[drm:__drm_atomic_state_free]Freeingatomicstate 000000009ea89924
It can be seen that the default uses plane 71, crtc 85, fb202.
Check the information under sys to see if it corresponds normally:
1.cat /sys/kernel/debug/dri/0/framebuffer | grep drm-cursor -B 1
framebuffer[202]:
allocated by = drm-cursor[85]
2.cat /sys/kernel/debug/dri/0/state | grep drm-cursor -B 3
plane[71]: Cluster0-win0
crtc=video_port0
fb=208
allocated by = drm-cursor[85]
3.cat /sys/kernel/debug/dri/0/summary
Cluster0-win0: ACTIVE
win_id: 4
format: AB24little-endian (0x34324241)[AFBC]SDR[0]color_space[0]glb_alpha[0xff]
rotate: xmirror: 0 ymirror: 0 rotate_90: 0 rotate_270: 0
csc: y2r[0]r2y[1]cscmode[1]
zpos: 7
src: pos[0, 0]rect[64 x 64]
dst: pos[0, 0]rect[64 x 64]
buf[0]: addr: 0x0000000001825000pitch: 256 offset: 0
Thus, it can be confirmed that the hardware cursor is started normally, with a size of 64×64.
3: Testing and Debugging
3.1 Configuration File /etc/drm-cursor.conf
# debug=1
# log-file=
# hide=1 # hide cursors
# atomic=0 # disable atomic drm API
# max-fps=60
# allow-overlay=1 # allowing overlay planes
# prefer-afbc=0 # prefer plane with AFBC modifier supported
# num-surfaces=8 # num of egl surfaces to avoid edge moving corruption
# prefer-plane=65
# prefer-planes=61,65
# crtc-blocklist=64,83
debug=1 Default will output logs to /var/log/drm-cursor.log
log-file=/tmp/drm-cursor.log Manually specify log file
hide=1 Whether to hide the mouse
atomic=1 This item is not useful, drm cursor defaults to use atomic API
max-fps=60 The display frame rate of the cursor pointer.
allow-overlay=1 Allow the use of overlay layers. If not set, it defaults to not using overlay, which means only using the cursor layer
prefer-afbc=0 Whether to use the arm's afbc function overlay layer
num-surfaces=8 Number of off-screen buffers created by eglCreateWindowSurface
prefer-plane=65 Default set plane
prefer-planes=61,65 Optional multiple planes
crtc-blocklist=83 Prevent which crtc from allowing the cursor layer
3.2 How to Choose Plane and CRTC
Install libdrm-tests
modeprint rockchip
You can see that the current display is hanging on crtc 64.
modetest -M rockchip -p Check plane
You can see that plane 61 is mounted on the cursor layer of crtc 64.
Test hardware cursor verification:
systemctl stop lightdm
modetest -M rockchip -s 91@64:1920x1200 -C
If a color map appears, it indicates that libdrm can normally use the hardware cursor.
Specify plane:
modetest -M rockchip -P 61@64:1920x1200
If a color map appears, it indicates that libdrm can normally specify the plane.
4: Specific Applications
First, you need to enable the hardware cursor layer in the kernel, find the vp where you need to configure the hardware cursor, and add cursor-win-id. For example, my display is on vp2.


Then you need to install the libdrm-cursor software and libdrm-tests software.
First, use the libdrm-tests software to check the actual CRTC layer in use.
Command: modetest




Here you can see that 68, 85, and 102 are unused CRTCs, and the actual CRTC connected to fb is 133.
So we need to configure the software to block the first three CRTC layers so that the mouse layer can correctly match to 133.
Command: <span>vim /etc/drm-cursor.conf</span>
Before modification:
After modification:
Here we have enabled the hardware cursor layer, so there is no need for the overlay layer, which we commented out, and opened the crtc-blocklist option to block the other unused crtc layers. Restarting the machine will allow the hardware cursor to function normally. The specific id of the crtc layer needs to be determined based on the actual situation using the modetest command.
5: Reference Links
《Linux DRM Developer's Guide》https://landley.net/kdocs/htmldocs/drm.html
《Linux Kernel Doc》 https://www.kernel.org/doc/
《DRM-wikipedia》 https://en.wikipedia.org/wiki/Direct_Rendering_Manager
《Linux GPU Driver Developer's Guide》https://01.org/linuxgraphics/gfx-docs/drm/gpu/index.html
《DRM-KMS Doc》 https://kernel.readthedocs.io/en/latest/gpu/drm-kms.html#mode-setting
《KMS简介》 https://events.static.linuxfound.org/sites/events/files/slides/brezillon-drm-kms.pdf
《EGL Reference Pages》https://www.khronos.org/registry/EGL/sdk/docs/man/
《Linux User and Programmer's Manual》https://www.systutorials.com/docs/linux/man/3-drmModeGetResources/
《Nvidia drm api参考》https://docs.nvidia.com/jetson/l4t-graphics/group__direct__rendering__manager.html
《Linux How to的KMS简介》https://www.linuxhowtos.org/manpages/7/drm-kms.htm
《freedesktop.org的kms文档》https://dri.freedesktop.org/docs/drm/gpu/drm-kms.html
For more articles, please follow the public account: Kirin Embedded