Skip to content

Instantly share code, notes, and snippets.

@ardera
Last active June 9, 2020 11:41
Show Gist options
  • Save ardera/884b3ba034f357a37b78978319815a8d to your computer and use it in GitHub Desktop.
Save ardera/884b3ba034f357a37b78978319815a8d to your computer and use it in GitHub Desktop.
Reproduction of a `drmModeSetPlane` issue of the raspbian linux kernel (https://github.com/raspberrypi/linux/issues/3661). This time with two planes being drawn into concurrently.
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
static int find_crtc_comma_connector_comma_mode_and_overlay_plane(
int drm_fd,
uint32_t *connector_id_out,
drmModeModeInfo **mode_out,
uint32_t *crtc_id_out,
uint32_t *plane_id_out
) {
drmModeConnector *connector;
drmModePlaneRes *plane_res;
drmModeModeInfo *mode;
drmModeEncoder *encoder;
drmModeRes *resources;
uint32_t connector_id;
uint32_t encoder_id;
uint32_t crtc_id;
uint32_t plane_id;
drmSetClientCap(drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
resources = drmModeGetResources(drm_fd);
if (resources == NULL) {
fprintf(stderr, "drmModeGetResources failed: %s\n", strerror(errno));
return errno;
}
encoder_id = 0;
connector_id = 0;
for (int i = 0; (i < resources->count_connectors) && ((encoder_id == 0) || (connector_id == 0)); i++) {
connector = drmModeGetConnector(drm_fd, resources->connectors[i]);
if (connector->connection == DRM_MODE_CONNECTED) {
connector_id = connector->connector_id;
encoder_id = connector->encoder_id;
mode = NULL;
for (int j = 0; (j < connector->count_modes) && (mode == NULL); j++) {
drmModeModeInfo *mode_cursor = &connector->modes[j];
if (mode_cursor->type & DRM_MODE_TYPE_PREFERRED) {
mode = mode_cursor;
}
}
break;
}
drmModeFreeConnector(connector);
}
if (encoder_id == 0) {
fprintf(stderr, "could not find a connected connector!\n");
return EINVAL;
}
crtc_id = 0;
for (int i = 0; (i < resources->count_encoders) && (crtc_id == 0); i++) {
encoder = drmModeGetEncoder(drm_fd, resources->encoders[i]);
if (encoder->encoder_id == encoder_id) {
crtc_id = encoder->crtc_id;
}
drmModeFreeEncoder(encoder);
}
if (crtc_id == 0) {
fprintf(stderr, "could not find a suitable crtc!\n");
return EINVAL;
}
drmModeFreeResources(resources);
plane_id = 0;
plane_res = drmModeGetPlaneResources(drm_fd);
for (int i = 0; (i < plane_res->count_planes) && (plane_id == 0); i++) {
drmModePlane *plane = drmModeGetPlane(drm_fd, plane_res->planes[i]);
drmModeObjectProperties *props = drmModeObjectGetProperties(drm_fd, plane->plane_id, DRM_MODE_OBJECT_ANY);
for (int j = 0; (j < props->count_props) && (plane_id == 0); j++) {
drmModePropertyRes *prop = drmModeGetProperty(drm_fd, props->props[j]);
if ((strcmp(prop->name, "type") == 0) && (props->prop_values[j] == DRM_PLANE_TYPE_OVERLAY)) {
plane_id = plane->plane_id;
}
drmModeFreeProperty(prop);
}
drmModeFreeObjectProperties(props);
drmModeFreePlane(plane);
}
drmModeFreePlaneResources(plane_res);
*connector_id_out = connector_id;
*mode_out = mode;
*crtc_id_out = crtc_id;
*plane_id_out = plane_id;
return 0;
}
static int create_fb(
int drm_fd,
int width, int height,
uint32_t *bo_handle_out,
uint32_t *stride_out,
uint32_t *size_out,
uint32_t *fb_id_out
) {
struct drm_mode_create_dumb create_req;
int ok;
memset(&create_req, 0, sizeof(create_req));
create_req.width = width;
create_req.height = height;
create_req.bpp = 32;
ok = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_req);
if (ok < 0) {
fprintf(stderr, "Could not create dumb buffer: %s\n", strerror(errno));
return errno;
}
if (bo_handle_out)
*bo_handle_out = create_req.handle;
if (stride_out)
*stride_out = create_req.pitch;
if (size_out)
*size_out = create_req.size;
ok = drmModeAddFB(
drm_fd,
width,
height,
24,
32,
create_req.pitch,
create_req.handle,
fb_id_out
);
if (ok < 0) {
fprintf(stderr, "Could not add dumb buffer as FB: %s\n", strerror(-errno));
return errno;
}
return 0;
}
int main(int argc, char **argv) {
drmModeModeInfo *mode;
struct timespec t0, t1;
uint32_t connector_id, crtc_id, plane_id, fb_ids[4];
int width, height;
int ok, drm_fd, current_fb_id;
drm_fd = open("/dev/dri/card1", O_RDWR);
if (drm_fd < 0) {
fprintf(stderr, "Could not open DRM device\n");
return EXIT_FAILURE;
}
ok = find_crtc_comma_connector_comma_mode_and_overlay_plane(drm_fd, &connector_id, &mode, &crtc_id, &plane_id);
if (ok != 0) return EXIT_FAILURE;
width = mode->hdisplay;
height = mode->vdisplay;
ok = create_fb(drm_fd, width, height, NULL, NULL, NULL, fb_ids + 0);
if (ok != 0) return EXIT_FAILURE;
ok = create_fb(drm_fd, width, height, NULL, NULL, NULL, fb_ids + 1);
if (ok != 0) return EXIT_FAILURE;
ok = create_fb(drm_fd, width, height, NULL, NULL, NULL, fb_ids + 2);
if (ok != 0) return EXIT_FAILURE;
ok = create_fb(drm_fd, width, height, NULL, NULL, NULL, fb_ids + 3);
if (ok != 0) return EXIT_FAILURE;
ok = drmModeSetCrtc(drm_fd, crtc_id, fb_ids[current_fb_id], 0, 0, &connector_id, 1, mode);
if (ok) {
fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
return EXIT_FAILURE;
}
current_fb_id = 0;
while (1) {
clock_gettime(CLOCK_MONOTONIC, &t0);
ok = drmModePageFlip(
drm_fd,
crtc_id,
fb_ids[current_fb_id],
DRM_MODE_PAGE_FLIP_EVENT,
NULL
);
if (ok < 0) {
fprintf(stderr, "Could not schedule page flip: %s\n", strerror(errno));
return EXIT_FAILURE;
}
ok = drmModeSetPlane(
drm_fd,
plane_id,
crtc_id,
fb_ids[current_fb_id + 2],
0,
0, 0,
width, height,
0, 0,
((uint16_t) width) << 16, ((uint16_t) height) << 16
);
if (ok < 0) {
fprintf(stderr, "Could not set new fb: %s\n", strerror(errno));
return EXIT_FAILURE;
}
current_fb_id ^= 1;
clock_gettime(CLOCK_MONOTONIC, &t1);
uint64_t delta = ((t1.tv_sec - t0.tv_sec) * 1000000000L) + (t1.tv_nsec - t0.tv_nsec);
printf("drmModeSetPlane took %09lluns\n", delta);
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment