| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- #include "display.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <sys/mman.h>
- #include <sys/ioctl.h>
- #include <poll.h>
- #include <xf86drm.h>
- #include <xf86drmMode.h>
- #include <drm_fourcc.h>
- #include <linux/dma-heap.h>
- #include <linux/dma-buf.h>
- #define MAX_NV12_FB 4
- struct Display {
- char dev_path[64];
- int fd;
- drmModeConnector *connector;
- drmModeEncoder *encoder;
- drmModeCrtc *saved_crtc;
- uint32_t crtc_id;
- uint32_t connector_id;
- int width;
- int height;
- /* The CRTC/mode size actually programmed on the connector. It can differ
- * from width/height (the scanout buffer size) after a monitor hot-plug:
- * the app allocates its output buffers once, so a rebuilt display may end
- * up driving a mode that does not match them. The plane then scales the
- * buffer into the mode instead of failing the whole scanout. */
- int mode_w;
- int mode_h;
- int native_width;
- int native_height;
- /* Per-fd NV12 scanout FBs (the composer already produces NV12; we scan it
- * straight out on the plane and skip RGA colour conversion entirely --
- * NV12->BGRA via RGA is broken on this driver, so it must not be used). */
- struct {
- int key_fd; /* the fd passed to display_commit */
- int owned_fd; /* dup'd fd keeping the dmabuf alive */
- uint32_t handle;
- uint32_t fb_id;
- uint32_t stride;
- } fbs[MAX_NV12_FB];
- int nfbs;
- uint32_t plane_id;
- uint32_t crtc_mode_id;
- uint32_t plane_fb_prop;
- uint32_t plane_crtc_prop;
- uint32_t plane_src_x_prop;
- uint32_t plane_src_y_prop;
- uint32_t plane_src_w_prop;
- uint32_t plane_src_h_prop;
- uint32_t plane_crtc_x_prop;
- uint32_t plane_crtc_y_prop;
- uint32_t plane_crtc_w_prop;
- uint32_t plane_crtc_h_prop;
- uint32_t conn_crtc_prop;
- uint32_t crtc_active_prop;
- uint32_t crtc_mode_id_prop;
- uint32_t conn_color_format_prop;
- uint32_t out_fence_prop;
- int flip_pending;
- int modeset_done;
- drmEventContext drm_ctx;
- /* Other planes sharing our CRTC (e.g. the desktop's RGB scans). They are
- * disabled on every commit so their colours can't blend under our NV12
- * overlay (which makes the wall look purple after a hotplug). */
- uint32_t ext_ids[8];
- uint32_t ext_fb_prop[8];
- uint32_t ext_crtc_prop[8];
- int ext_n;
- };
- /* Connector output format override for the first modeset commit.
- * 0 = auto (leave the driver's own negotiation alone),
- * 1 = force RGB, 2 = force YCbCr444, 3 = force YCbCr422, 4 = force YCbCr420.
- * Forcing RGB makes the VOP take the window through a real Y2R CSC path
- * instead of the YUV passthrough that comes out wrong after a hotplug. */
- static int g_fmt_override = 1;
- int display_set_color_format(int fmt) {
- g_fmt_override = fmt;
- return 0;
- }
- static int dma_heap_alloc(const char *heap_name, size_t size, int *fd) {
- char path[64];
- snprintf(path, sizeof(path), "/dev/dma_heap/%s", heap_name);
- int heap_fd = open(path, O_RDWR | O_CLOEXEC);
- if (heap_fd < 0) heap_fd = open("/dev/dma_heap/system-uncached", O_RDWR | O_CLOEXEC);
- if (heap_fd < 0) return -1;
- struct dma_heap_allocation_data data = { .len = size, .fd_flags = O_RDWR | O_CLOEXEC };
- int ret = ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &data);
- close(heap_fd);
- if (ret < 0) return -1;
- *fd = data.fd;
- return 0;
- }
- static uint32_t find_prop_id(int fd, uint32_t obj_id, uint32_t obj_type, const char *name) {
- drmModeObjectProperties *props = drmModeObjectGetProperties(fd, obj_id, obj_type);
- if (!props) return 0;
- uint32_t id = 0;
- for (uint32_t i = 0; i < props->count_props; i++) {
- drmModePropertyRes *prop = drmModeGetProperty(fd, props->props[i]);
- if (prop) {
- if (strcmp(prop->name, name) == 0) id = prop->prop_id;
- drmModeFreeProperty(prop);
- if (id) break;
- }
- }
- drmModeFreeObjectProperties(props);
- return id;
- }
- static int conn_allowed(int type) {
- const char *only = getenv("RTSP_CONN_ONLY");
- if (only && strcmp(only, "ALL") == 0) return 1; /* legacy: accept any connected output */
- /* Default (and RTSP_CONN_ONLY=HDMI): drive the real HDMI monitor only.
- * These boards report a phantom DSI connector as always-connected even
- * with no panel attached; handing the output to it would leave the real
- * HDMI monitor showing just the desktop. */
- return type == DRM_MODE_CONNECTOR_HDMIA || type == DRM_MODE_CONNECTOR_HDMIB;
- }
- static int drm_get_connector(Display *disp) {
- drmModeRes *res = drmModeGetResources(disp->fd);
- if (!res) { fprintf(stderr, "drmModeGetResources failed\n"); return -1; }
- const char *only = getenv("RTSP_CONN_ONLY");
- for (int i = 0; i < res->count_connectors; i++) {
- drmModeConnector *conn = drmModeGetConnector(disp->fd, res->connectors[i]);
- if (!conn) continue;
- if (conn_allowed(conn->connector_type) &&
- conn->connection == DRM_MODE_CONNECTED && conn->count_modes > 0) {
- disp->connector = conn;
- break;
- }
- drmModeFreeConnector(conn);
- }
- if (!disp->connector) {
- /* No connected output. Only fall back to another connector if it
- * actually has modes; a mode-less (unplugged) connector cannot drive
- * the CRTC and its modes[0] would be read out of bounds below. We fail
- * so the caller can retry until a monitor is plugged in. */
- for (int i = 0; res && i < res->count_connectors && !disp->connector; i++) {
- drmModeConnector *c = drmModeGetConnector(disp->fd, res->connectors[i]);
- if (c && conn_allowed(c->connector_type) && c->count_modes > 0)
- disp->connector = c;
- else if (c) drmModeFreeConnector(c);
- }
- }
- if (!disp->connector || disp->connector->count_modes == 0) {
- if (disp->connector) {
- drmModeFreeConnector(disp->connector);
- disp->connector = NULL;
- }
- fprintf(stderr, "display: no monitor with an active mode (unplugged during boot%s%s); "
- "will retry until one is connected\n",
- only ? "; RTSP_CONN_ONLY=" : "", only ? only : "");
- drmModeFreeResources(res);
- return -1;
- }
- disp->connector_id = disp->connector->connector_id;
- disp->native_width = disp->connector->modes[0].hdisplay;
- disp->native_height = disp->connector->modes[0].vdisplay;
- if (disp->connector->encoder_id) {
- disp->encoder = drmModeGetEncoder(disp->fd, disp->connector->encoder_id);
- if (disp->encoder) disp->crtc_id = disp->encoder->crtc_id;
- }
- if (!disp->encoder) {
- for (int i = 0; i < res->count_encoders; i++) {
- drmModeEncoder *enc = drmModeGetEncoder(disp->fd, res->encoders[i]);
- if (!enc) continue;
- if (enc->encoder_id == disp->connector->encoder_id ||
- (enc->crtc_id > 0 && !disp->encoder)) {
- if (!disp->encoder) { disp->encoder = enc; disp->crtc_id = enc->crtc_id; }
- else drmModeFreeEncoder(enc);
- } else drmModeFreeEncoder(enc);
- }
- }
- if (disp->crtc_id == 0 && res->count_crtcs > 0)
- disp->crtc_id = res->crtcs[0];
- drmModeFreeResources(res);
- return (disp->crtc_id != 0) ? 0 : -1;
- }
- static void page_flip_handler(int fd, unsigned int frame, unsigned int sec,
- unsigned int usec, void *data) {
- (void)fd; (void)frame; (void)sec; (void)usec;
- Display *disp = data;
- disp->flip_pending = 0;
- }
- static int wait_prev_flip(Display *disp, int timeout_ms) {
- if (!disp->flip_pending) return 0;
- while (disp->flip_pending) {
- struct pollfd pfd = { .fd = disp->fd, .events = POLLIN };
- int pret = poll(&pfd, 1, timeout_ms);
- if (pret > 0 && (pfd.revents & POLLIN))
- drmHandleEvent(disp->fd, &disp->drm_ctx);
- else
- return -1;
- }
- return 0;
- }
- Display *display_create(const char *drm_dev) {
- Display *disp = calloc(1, sizeof(Display));
- if (!disp) return NULL;
- strncpy(disp->dev_path, drm_dev ? drm_dev : "/dev/dri/card0", sizeof(disp->dev_path) - 1);
- disp->fd = open(disp->dev_path, O_RDWR | O_CLOEXEC);
- if (disp->fd < 0) { free(disp); return NULL; }
- for (int i = 0; i < MAX_NV12_FB; i++) { disp->fbs[i].key_fd = -1; disp->fbs[i].owned_fd = -1; }
- return disp;
- }
- void display_destroy(Display *disp) {
- if (!disp) return;
- if (disp->saved_crtc) {
- drmModeSetCrtc(disp->fd, disp->saved_crtc->crtc_id,
- disp->saved_crtc->buffer_id,
- disp->saved_crtc->x, disp->saved_crtc->y,
- &disp->connector_id, 1, &disp->saved_crtc->mode);
- drmModeFreeCrtc(disp->saved_crtc);
- }
- for (int i = 0; i < disp->nfbs; i++) {
- if (disp->fbs[i].fb_id) drmModeRmFB(disp->fd, disp->fbs[i].fb_id);
- if (disp->fbs[i].owned_fd >= 0) close(disp->fbs[i].owned_fd);
- }
- if (disp->encoder) drmModeFreeEncoder(disp->encoder);
- if (disp->connector) drmModeFreeConnector(disp->connector);
- if (disp->crtc_mode_id) drmModeDestroyPropertyBlob(disp->fd, disp->crtc_mode_id);
- if (disp->fd >= 0) {
- drmDropMaster(disp->fd);
- close(disp->fd);
- }
- free(disp);
- }
- static int setup_plane(Display *disp) {
- drmModePlaneRes *pres = drmModeGetPlaneResources(disp->fd);
- if (!pres) return -1;
- drmModeRes *res = drmModeGetResources(disp->fd);
- int crtc_index = -1;
- if (res) {
- for (int i = 0; i < res->count_crtcs; i++) {
- if (res->crtcs[i] == disp->crtc_id) { crtc_index = i; break; }
- }
- drmModeFreeResources(res);
- }
- if (crtc_index < 0) {
- fprintf(stderr, "display: CRTC %u not found\n", disp->crtc_id);
- drmModeFreePlaneResources(pres);
- return -1;
- }
- /* On this SoC the primary plane only scans RGB frames, while NV12 is only
- * supported by an overlay plane. We scan the composer's NV12 output
- * straight out on whichever plane advertises NV12 (preferred), falling back
- * to any primary plane otherwise. This avoids an NV12->BGRA RGA conversion
- * that is unsupported on this driver. */
- drmModePlane *chosen = NULL;
- drmModePlane *fallback = NULL;
- for (uint32_t i = 0; i < pres->count_planes; i++) {
- drmModePlane *plane = drmModeGetPlane(disp->fd, pres->planes[i]);
- if (!plane) continue;
- if (!(plane->possible_crtcs & (1u << crtc_index))) { drmModeFreePlane(plane); continue; }
- uint32_t type_id = find_prop_id(disp->fd, plane->plane_id,
- DRM_MODE_OBJECT_PLANE, "type");
- if (!type_id) { drmModeFreePlane(plane); continue; }
- drmModeObjectProperties *props = drmModeObjectGetProperties(disp->fd,
- plane->plane_id, DRM_MODE_OBJECT_PLANE);
- if (!props) { drmModeFreePlane(plane); continue; }
- uint64_t type_val = 0;
- for (uint32_t p = 0; p < props->count_props; p++)
- if (props->props[p] == type_id) type_val = props->prop_values[p];
- drmModeFreeObjectProperties(props);
- int has_nv12 = 0;
- for (uint32_t f = 0; f < plane->count_formats; f++)
- if (plane->formats[f] == DRM_FORMAT_NV12) { has_nv12 = 1; break; }
- if (has_nv12) { chosen = plane; break; }
- if (type_val == DRM_PLANE_TYPE_PRIMARY && !fallback) fallback = plane;
- else drmModeFreePlane(plane);
- }
- if (!chosen) chosen = fallback;
- if (!chosen) {
- drmModeFreePlaneResources(pres);
- fprintf(stderr, "display: no usable plane found for crtc %u\n", disp->crtc_id);
- return -1;
- }
- disp->plane_id = chosen->plane_id;
- disp->plane_fb_prop = find_prop_id(disp->fd, chosen->plane_id,
- DRM_MODE_OBJECT_PLANE, "FB_ID");
- disp->plane_crtc_prop = find_prop_id(disp->fd, chosen->plane_id,
- DRM_MODE_OBJECT_PLANE, "CRTC_ID");
- disp->plane_src_x_prop = find_prop_id(disp->fd, chosen->plane_id,
- DRM_MODE_OBJECT_PLANE, "SRC_X");
- disp->plane_src_y_prop = find_prop_id(disp->fd, chosen->plane_id,
- DRM_MODE_OBJECT_PLANE, "SRC_Y");
- disp->plane_src_w_prop = find_prop_id(disp->fd, chosen->plane_id,
- DRM_MODE_OBJECT_PLANE, "SRC_W");
- disp->plane_src_h_prop = find_prop_id(disp->fd, chosen->plane_id,
- DRM_MODE_OBJECT_PLANE, "SRC_H");
- disp->plane_crtc_x_prop = find_prop_id(disp->fd, chosen->plane_id,
- DRM_MODE_OBJECT_PLANE, "CRTC_X");
- disp->plane_crtc_y_prop = find_prop_id(disp->fd, chosen->plane_id,
- DRM_MODE_OBJECT_PLANE, "CRTC_Y");
- disp->plane_crtc_w_prop = find_prop_id(disp->fd, chosen->plane_id,
- DRM_MODE_OBJECT_PLANE, "CRTC_W");
- disp->plane_crtc_h_prop = find_prop_id(disp->fd, chosen->plane_id,
- DRM_MODE_OBJECT_PLANE, "CRTC_H");
- fprintf(stderr, "display: using plane %u (NV12 scanout)\n", chosen->plane_id);
- drmModeFreePlane(chosen);
- if (fallback && fallback != chosen) drmModeFreePlane(fallback);
- drmModeFreePlaneResources(pres);
- return 0;
- }
- static int build_plane_commit(Display *disp, drmModeAtomicReq *req, uint32_t fb_id) {
- int props = 0;
- props += (disp->plane_fb_prop ?
- (drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_fb_prop, fb_id) >= 0) : 0);
- props += (disp->plane_crtc_prop ?
- (drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_crtc_prop, disp->crtc_id) >= 0) : 0);
- props += (disp->plane_src_x_prop ?
- (drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_src_x_prop, (uint64_t)0) >= 0) : 0);
- props += (disp->plane_src_y_prop ?
- (drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_src_y_prop, (uint64_t)0) >= 0) : 0);
- if (disp->plane_src_w_prop)
- props += (drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_src_w_prop,
- (uint64_t)disp->width << 16) >= 0);
- if (disp->plane_src_h_prop)
- props += (drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_src_h_prop,
- (uint64_t)disp->height << 16) >= 0);
- if (disp->plane_crtc_x_prop)
- props += (drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_crtc_x_prop, (uint64_t)0) >= 0);
- if (disp->plane_crtc_y_prop)
- props += (drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_crtc_y_prop, (uint64_t)0) >= 0);
- if (disp->plane_crtc_w_prop)
- props += (drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_crtc_w_prop, (uint64_t)disp->mode_w) >= 0);
- if (disp->plane_crtc_h_prop)
- props += (drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_crtc_h_prop, (uint64_t)disp->mode_h) >= 0);
- return props;
- }
- static void display_reset_output(Display *disp);
- /* Cache every OTHER plane that can target our CRTC (Xorg/desktop scans).
- * They must stay disabled while we render, or their RGB content blends under
- * our NV12 plane and tints the whole output. */
- static void cache_ext_planes(Display *disp) {
- disp->ext_n = 0;
- drmModePlaneRes *pres = drmModeGetPlaneResources(disp->fd);
- if (!pres) return;
- drmModeRes *res = drmModeGetResources(disp->fd);
- int crtc_index = -1;
- if (res) {
- for (int i = 0; i < res->count_crtcs; i++)
- if (res->crtcs[i] == disp->crtc_id) { crtc_index = i; break; }
- drmModeFreeResources(res);
- }
- if (crtc_index < 0) { drmModeFreePlaneResources(pres); return; }
- for (uint32_t i = 0; i < pres->count_planes && disp->ext_n < 8; i++) {
- drmModePlane *pl = drmModeGetPlane(disp->fd, pres->planes[i]);
- if (!pl) continue;
- if (pl->plane_id != disp->plane_id &&
- (pl->possible_crtcs & (1u << crtc_index))) {
- uint32_t idx = disp->ext_n;
- disp->ext_ids[idx] = pl->plane_id;
- disp->ext_fb_prop[idx] = find_prop_id(disp->fd, pl->plane_id,
- DRM_MODE_OBJECT_PLANE, "FB_ID");
- disp->ext_crtc_prop[idx] = find_prop_id(disp->fd, pl->plane_id,
- DRM_MODE_OBJECT_PLANE, "CRTC_ID");
- if (disp->ext_fb_prop[idx] || disp->ext_crtc_prop[idx])
- disp->ext_n++;
- }
- drmModeFreePlane(pl);
- }
- drmModeFreePlaneResources(pres);
- }
- static void add_ext_planes_off(Display *disp, drmModeAtomicReq *req) {
- for (int i = 0; i < disp->ext_n; i++) {
- if (disp->ext_fb_prop[i])
- drmModeAtomicAddProperty(req, disp->ext_ids[i], disp->ext_fb_prop[i], 0);
- if (disp->ext_crtc_prop[i])
- drmModeAtomicAddProperty(req, disp->ext_ids[i], disp->ext_crtc_prop[i], 0);
- }
- }
- int display_init(Display *disp, int width, int height) {
- if (!disp || disp->fd < 0) return -1;
- if (drmSetMaster(disp->fd))
- fprintf(stderr, "WARN: drmSetMaster: %s\n", strerror(errno));
- drmSetClientCap(disp->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
- drmSetClientCap(disp->fd, DRM_CLIENT_CAP_ATOMIC, 1);
- if (drm_get_connector(disp) < 0) return -1;
- /* Choose the CRTC mode. When the caller already owns scanout buffers of a
- * given size (the app allocates them once at start-up), prefer a connector
- * mode that matches them exactly so the plane never has to scale. Fall
- * back to the connector's preferred mode otherwise. */
- drmModeModeInfo *mode = &disp->connector->modes[0];
- if (width > 0 && height > 0) {
- for (int i = 0; i < disp->connector->count_modes; i++) {
- drmModeModeInfo *m = &disp->connector->modes[i];
- if (m->hdisplay == width && m->vdisplay == height) { mode = m; break; }
- }
- }
- disp->mode_w = mode->hdisplay;
- disp->mode_h = mode->vdisplay;
- /* width/height stay the size of the frames that will be committed, so the
- * NV12 fb is always registered with the buffer's real geometry. */
- disp->width = (width > 0) ? width : disp->mode_w;
- disp->height = (height > 0) ? height : disp->mode_h;
- disp->saved_crtc = drmModeGetCrtc(disp->fd, disp->crtc_id);
- if (setup_plane(disp) < 0) return -1;
- cache_ext_planes(disp);
- disp->conn_crtc_prop = find_prop_id(disp->fd, disp->connector_id,
- DRM_MODE_OBJECT_CONNECTOR, "CRTC_ID");
- disp->conn_color_format_prop = find_prop_id(disp->fd, disp->connector_id,
- DRM_MODE_OBJECT_CONNECTOR, "color_format");
- disp->crtc_active_prop = find_prop_id(disp->fd, disp->crtc_id,
- DRM_MODE_OBJECT_CRTC, "ACTIVE");
- disp->crtc_mode_id_prop = find_prop_id(disp->fd, disp->crtc_id,
- DRM_MODE_OBJECT_CRTC, "MODE_ID");
- disp->out_fence_prop = find_prop_id(disp->fd, disp->crtc_id,
- DRM_MODE_OBJECT_CRTC, "OUT_FENCE_PTR");
- if (drmModeCreatePropertyBlob(disp->fd, mode, sizeof(*mode), &disp->crtc_mode_id)) {
- fprintf(stderr, "drmModeCreatePropertyBlob failed: %s\n", strerror(errno));
- return -1;
- }
- disp->flip_pending = 0;
- memset(&disp->drm_ctx, 0, sizeof(disp->drm_ctx));
- disp->drm_ctx.version = DRM_EVENT_CONTEXT_VERSION;
- disp->drm_ctx.page_flip_handler = page_flip_handler;
- /* The CRTC is activated on the first display_commit (which supplies the
- * NV12 buffer). No RGA / colour conversion is used anywhere in display. */
- display_reset_output(disp);
- fprintf(stderr, "display: init OK buffer %dx%d mode %dx%d crtc=%u plane=%u%s\n",
- disp->width, disp->height, disp->mode_w, disp->mode_h,
- disp->crtc_id, disp->plane_id,
- (disp->width != disp->mode_w || disp->height != disp->mode_h)
- ? " (buffer != mode, plane will scale)" : "");
- return 0;
- }
- /* Add (or find) an NV12 scanout FB for this fd. Returns fb_id or 0. */
- static uint32_t get_nv12_fb(Display *disp, int fd, int stride) {
- for (int i = 0; i < disp->nfbs; i++)
- if (disp->fbs[i].key_fd == fd) return disp->fbs[i].fb_id;
- uint32_t handle = 0;
- if (drmPrimeFDToHandle(disp->fd, fd, &handle)) {
- fprintf(stderr, "display: drmPrimeFDToHandle(nv12) failed: %s\n", strerror(errno));
- return 0;
- }
- uint32_t handles[4] = {handle, handle, 0, 0};
- uint32_t pitches[4] = {(uint32_t)stride, (uint32_t)stride, 0, 0};
- uint32_t offsets[4] = {0, (uint32_t)stride * disp->height, 0, 0};
- uint32_t fb_id = 0;
- if (drmModeAddFB2(disp->fd, disp->width, disp->height, DRM_FORMAT_NV12,
- handles, pitches, offsets, &fb_id, 0)) {
- fprintf(stderr, "display: drmModeAddFB2(NV12) failed: %s\n", strerror(errno));
- return 0;
- }
- int swap_idx = -1;
- if (disp->nfbs < MAX_NV12_FB) {
- swap_idx = disp->nfbs;
- disp->nfbs++;
- } else {
- /* table full: drop the last entry to make room (free its fb/handle) */
- swap_idx = disp->nfbs - 1;
- drmModeRmFB(disp->fd, disp->fbs[swap_idx].fb_id);
- if (disp->fbs[swap_idx].owned_fd >= 0) close(disp->fbs[swap_idx].owned_fd);
- }
- int owned = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- disp->fbs[swap_idx].key_fd = fd;
- disp->fbs[swap_idx].owned_fd = owned;
- disp->fbs[swap_idx].handle = handle;
- disp->fbs[swap_idx].fb_id = fb_id;
- disp->fbs[swap_idx].stride = (uint32_t)stride;
- return fb_id;
- }
- int display_signal_lost(Display *disp) {
- if (!disp || disp->fd < 0) return 1;
- drmModeConnector *conn = drmModeGetConnector(disp->fd, disp->connector_id);
- if (!conn) return 1;
- int lost = !(conn->connection == DRM_MODE_CONNECTED && conn->count_modes > 0);
- drmModeFreeConnector(conn);
- return lost;
- }
- static void display_reset_output(Display *disp) {
- if (!disp || disp->fd < 0) return;
- drmModeAtomicReq *req = drmModeAtomicAlloc();
- if (!req) return;
- if (disp->plane_fb_prop)
- drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_fb_prop, 0);
- if (disp->plane_crtc_prop)
- drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_crtc_prop, 0);
- add_ext_planes_off(disp, req);
- if (disp->crtc_active_prop)
- drmModeAtomicAddProperty(req, disp->crtc_id, disp->crtc_active_prop, 0);
- if (disp->conn_crtc_prop)
- drmModeAtomicAddProperty(req, disp->connector_id, disp->conn_crtc_prop, 0);
- drmModeAtomicCommit(disp->fd, req,
- DRM_MODE_ATOMIC_ALLOW_MODESET | DRM_MODE_ATOMIC_NONBLOCK,
- NULL);
- drmModeAtomicFree(req);
- }
- int display_commit(Display *disp, int nv12_fd, int nv12_stride) {
- if (!disp || disp->fd < 0 || nv12_fd < 0) return -1;
- if (wait_prev_flip(disp, 300) < 0) {
- fprintf(stderr, "display: previous flip not completed, dropping frame\n");
- return -1;
- }
- uint32_t fb_id = get_nv12_fb(disp, nv12_fd, nv12_stride);
- if (!fb_id) return -1;
- drmModeAtomicReq *req = drmModeAtomicAlloc();
- if (!req) return -1;
- int is_first = !disp->modeset_done;
- if (is_first) {
- /* First commit: enable connector + CRTC modeset together with the
- * plane so VOP shows the first NV12 frame immediately. */
- if (disp->conn_crtc_prop)
- drmModeAtomicAddProperty(req, disp->connector_id, disp->conn_crtc_prop, disp->crtc_id);
- if (g_fmt_override > 0 && disp->conn_color_format_prop)
- drmModeAtomicAddProperty(req, disp->connector_id,
- disp->conn_color_format_prop,
- (uint64_t)(g_fmt_override - 1));
- if (disp->crtc_active_prop)
- drmModeAtomicAddProperty(req, disp->crtc_id, disp->crtc_active_prop, 1);
- if (disp->crtc_mode_id_prop)
- drmModeAtomicAddProperty(req, disp->crtc_id, disp->crtc_mode_id_prop, disp->crtc_mode_id);
- build_plane_commit(disp, req, fb_id);
- add_ext_planes_off(disp, req);
- } else {
- /* Page flip: just swap the plane FB. */
- if (disp->plane_fb_prop)
- drmModeAtomicAddProperty(req, disp->plane_id, disp->plane_fb_prop, fb_id);
- add_ext_planes_off(disp, req);
- }
- int out_fence_fd = -1;
- if (disp->out_fence_prop && !is_first) {
- uint64_t fence_ptr = (uint64_t)(unsigned long)&out_fence_fd;
- drmModeAtomicAddProperty(req, disp->crtc_id, disp->out_fence_prop, fence_ptr);
- }
- int ret;
- uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT | (is_first ? DRM_MODE_ATOMIC_ALLOW_MODESET : 0);
- int retries = 5;
- do {
- ret = drmModeAtomicCommit(disp->fd, req, flags, disp);
- if (ret == 0) break;
- if (errno == EBUSY) { usleep(16000); continue; }
- /* A stale state after hot-plug can make a plain page flip EINVAL;
- * schedule the full reset+modeset on the next frame. */
- if (!is_first) disp->modeset_done = 0;
- fprintf(stderr, "display: AtomicCommit failed: %s\n", strerror(errno));
- if (out_fence_fd >= 0) close(out_fence_fd);
- drmModeAtomicFree(req);
- return -1;
- } while (--retries > 0);
- if (ret) {
- fprintf(stderr, "display: AtomicCommit failed after %d retries\n", retries);
- if (out_fence_fd >= 0) close(out_fence_fd);
- drmModeAtomicFree(req);
- return -1;
- }
- drmModeAtomicFree(req);
- disp->modeset_done = 1;
- disp->flip_pending = 1;
- int confirmed = 0;
- if (out_fence_fd >= 0) {
- struct pollfd pfd = { .fd = out_fence_fd, .events = POLLIN };
- int pret = poll(&pfd, 1, 300);
- if (pret > 0 && (pfd.revents & POLLIN)) confirmed = 1;
- close(out_fence_fd);
- }
- if (!confirmed)
- confirmed = (wait_prev_flip(disp, 300) == 0);
- if (!confirmed) {
- fprintf(stderr, "display: flip not confirmed at vblank, frame status unknown\n");
- return -1;
- }
- return 0;
- }
- int display_get_width(Display *disp) { return disp->width; }
- int display_get_height(Display *disp) { return disp->height; }
|