From edc84300b5e5d8b15a87fbb4aee0c83e1a80e9e6 Mon Sep 17 00:00:00 2001 From: Vasyl Vavrychuk Date: Mon, 25 Apr 2022 10:59:51 +0300 Subject: [PATCH 12/17] Implement drm backend Limitation is that splash screen is drawn only on the first connector. drm-backend backport from: https://patchwork.yoctoproject.org/project/yocto/cover/20220425075954.10427-1-vasyl.vavrychuk@opensynergy.com/ Signed-off-by: Vasyl Vavrychuk --- Makefile.am | 12 +++- configure.ac | 9 +++ psplash-drm.c | 179 +++++++++++++++++--------------------------------- psplash-drm.h | 17 +++++ psplash.c | 52 +++++++++++---- 5 files changed, 135 insertions(+), 134 deletions(-) create mode 100644 psplash-drm.h diff --git a/Makefile.am b/Makefile.am index 375b926..c3d4f03 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,12 +8,20 @@ psplash_SOURCES = psplash.c psplash.h psplash-fb.c psplash-fb.h \ psplash-poky-img.h psplash-bar-img.h $(FONT_NAME)-font.h \ psplash-draw.c psplash-draw.h BUILT_SOURCES = psplash-poky-img.h psplash-bar-img.h +psplash_CPPFLAGS = +psplash_LDFLAGS = psplash_write_SOURCES = psplash-write.c psplash.h +if ENABLE_DRM +psplash_SOURCES += psplash-drm.c psplash-drm.h +psplash_CPPFLAGS += $(LIBDRM_CFLAGS) -DENABLE_DRM +psplash_LDFLAGS += $(LIBDRM_LIBS) +endif + if HAVE_SYSTEMD -psplash_CPPFLAGS = $(SYSTEMD_CFLAGS) -DHAVE_SYSTEMD -psplash_LDFLAGS= $(SYSTEMD_LIBS) +psplash_CPPFLAGS += $(SYSTEMD_CFLAGS) -DHAVE_SYSTEMD +psplash_LDFLAGS += $(SYSTEMD_LIBS) bin_PROGRAMS += psplash-systemd psplash_systemd_CPPFLAGS = $(SYSTEMD_CFLAGS) psplash_systemd_LDFLAGS= $(SYSTEMD_LIBS) diff --git a/configure.ac b/configure.ac index 2a7da91..2e5c4f5 100644 --- a/configure.ac +++ b/configure.ac @@ -12,6 +12,15 @@ if test "x$GCC" = "xyes"; then GCC_FLAGS="-g -Wall -Wextra" fi +AC_ARG_ENABLE(drm, + AS_HELP_STRING([--enable-drm], [enable drm backend (default is 'no')])) + +AS_IF([test "x$enable_drm" = "xyes"], [ + PKG_CHECK_MODULES(LIBDRM, libdrm) +]) + +AM_CONDITIONAL([ENABLE_DRM], [test "x$enable_drm" = "xyes"]) + AC_ARG_WITH([systemd], AS_HELP_STRING([--with-systemd], [Build with systemd support])) diff --git a/psplash-drm.c b/psplash-drm.c index c9a9f5c..30850ed 100644 --- a/psplash-drm.c +++ b/psplash-drm.c @@ -26,10 +26,8 @@ * provided by some external library. */ -#define _GNU_SOURCE #include #include -#include #include #include #include @@ -39,6 +37,7 @@ #include #include #include +#include "psplash-drm.h" struct modeset_dev; static int modeset_find_crtc(int fd, drmModeRes *res, drmModeConnector *conn, @@ -48,8 +47,6 @@ static int modeset_setup_dev(int fd, drmModeRes *res, drmModeConnector *conn, struct modeset_dev *dev); static int modeset_open(int *out, const char *node); static int modeset_prepare(int fd); -static void modeset_draw(void); -static void modeset_cleanup(int fd); /* * When the linux kernel detects a graphics-card on your machine, it loads the @@ -153,7 +150,7 @@ struct modeset_dev { uint32_t stride; uint32_t size; uint32_t handle; - uint8_t *map; + void *map; drmModeModeInfo mode; uint32_t fb; @@ -187,7 +184,7 @@ static int modeset_prepare(int fd) { drmModeRes *res; drmModeConnector *conn; - unsigned int i; + int i; struct modeset_dev *dev; int ret; @@ -338,11 +335,10 @@ static int modeset_find_crtc(int fd, drmModeRes *res, drmModeConnector *conn, struct modeset_dev *dev) { drmModeEncoder *enc; - unsigned int i, j; - int32_t crtc; + int i, j, crtc; struct modeset_dev *iter; - /* first try the currently conected encoder+crtc */ + /* first try the currently connected encoder+crtc */ if (conn->encoder_id) enc = drmModeGetEncoder(fd, conn->encoder_id); else @@ -352,7 +348,7 @@ static int modeset_find_crtc(int fd, drmModeRes *res, drmModeConnector *conn, if (enc->crtc_id) { crtc = enc->crtc_id; for (iter = modeset_list; iter; iter = iter->next) { - if (iter->crtc == crtc) { + if (iter->crtc == (uint32_t)crtc) { crtc = -1; break; } @@ -389,7 +385,7 @@ static int modeset_find_crtc(int fd, drmModeRes *res, drmModeConnector *conn, /* check that no other device already uses this CRTC */ crtc = res->crtcs[j]; for (iter = modeset_list; iter; iter = iter->next) { - if (iter->crtc == crtc) { + if (iter->crtc == (uint32_t)crtc) { crtc = -1; break; } @@ -503,6 +499,12 @@ err_destroy: return ret; } +static void psplash_drm_flip(PSplashCanvas *canvas, int sync) +{ + (void)canvas; + (void)sync; +} + /* * Finally! We have a connector with a suitable CRTC. We know which mode we want * to use and we have a framebuffer of the correct size that we can write to. @@ -532,155 +534,89 @@ err_destroy: * also often not guaranteed. So instead, we only use one connector per CRTC. * * Before calling drmModeSetCrtc() we also save the current CRTC configuration. - * This is used in modeset_cleanup() to restore the CRTC to the same mode as was - * before we changed it. + * This is used in psplash_drm_destroy() to restore the CRTC to the same mode as + * was before we changed it. * If we don't do this, the screen will stay blank after we exit until another * application performs modesetting itself. */ -int main(int argc, char **argv) +PSplashDRM* psplash_drm_new(int angle, int dev_id) { - int ret, fd; - const char *card; + PSplashDRM *drm = NULL; + int ret; + char card[] = "/dev/dri/card0"; struct modeset_dev *iter; - /* check which DRM device to open */ - if (argc > 1) - card = argv[1]; - else - card = "/dev/dri/card0"; + if ((drm = malloc(sizeof(*drm))) == NULL) { + perror("malloc"); + goto error; + } + drm->canvas.priv = drm; + drm->canvas.flip = psplash_drm_flip; + + if (dev_id > 0 && dev_id < 10) { + // Conversion from integer to ascii. + card[13] = dev_id + 48; + } fprintf(stderr, "using card '%s'\n", card); /* open the DRM device */ - ret = modeset_open(&fd, card); + ret = modeset_open(&drm->fd, card); if (ret) - goto out_return; + goto error; /* prepare all connectors and CRTCs */ - ret = modeset_prepare(fd); + ret = modeset_prepare(drm->fd); if (ret) - goto out_close; + goto error; /* perform actual modesetting on each found connector+CRTC */ for (iter = modeset_list; iter; iter = iter->next) { - iter->saved_crtc = drmModeGetCrtc(fd, iter->crtc); - ret = drmModeSetCrtc(fd, iter->crtc, iter->fb, 0, 0, + iter->saved_crtc = drmModeGetCrtc(drm->fd, iter->crtc); + ret = drmModeSetCrtc(drm->fd, iter->crtc, iter->fb, 0, 0, &iter->conn, 1, &iter->mode); if (ret) fprintf(stderr, "cannot set CRTC for connector %u (%d): %m\n", iter->conn, errno); } - /* draw some colors for 5seconds */ - modeset_draw(); - - /* cleanup everything */ - modeset_cleanup(fd); - - ret = 0; - -out_close: - close(fd); -out_return: - if (ret) { - errno = -ret; - fprintf(stderr, "modeset failed with error %d: %m\n", errno); - } else { - fprintf(stderr, "exiting\n"); - } - return ret; + drm->canvas.data = modeset_list->map; + drm->canvas.width = modeset_list->width; + drm->canvas.height = modeset_list->height; + drm->canvas.bpp = 32; + drm->canvas.stride = modeset_list->stride; + drm->canvas.angle = angle; + drm->canvas.rgbmode = RGB888; + + return drm; +error: + psplash_drm_destroy(drm); + return NULL; } /* - * A short helper function to compute a changing color value. No need to - * understand it. - */ - -static uint8_t next_color(bool *up, uint8_t cur, unsigned int mod) -{ - uint8_t next; - - next = cur + (*up ? 1 : -1) * (rand() % mod); - if ((*up && next < cur) || (!*up && next > cur)) { - *up = !*up; - next = cur; - } - - return next; -} - -/* - * modeset_draw(): This draws a solid color into all configured framebuffers. - * Every 100ms the color changes to a slightly different color so we get some - * kind of smoothly changing color-gradient. - * - * The color calculation can be ignored as it is pretty boring. So the - * interesting stuff is iterating over "modeset_list" and then through all lines - * and width. We then set each pixel individually to the current color. - * - * We do this 50 times as we sleep 100ms after each redraw round. This makes - * 50*100ms = 5000ms = 5s so it takes about 5seconds to finish this loop. - * - * Please note that we draw directly into the framebuffer. This means that you - * will see flickering as the monitor might refresh while we redraw the screen. - * To avoid this you would need to use two framebuffers and a call to - * drmModeSetCrtc() to switch between both buffers. - * You can also use drmModePageFlip() to do a vsync'ed pageflip. But this is - * beyond the scope of this document. - */ - -static void modeset_draw(void) -{ - uint8_t r, g, b; - bool r_up, g_up, b_up; - unsigned int i, j, k, off; - struct modeset_dev *iter; - - srand(time(NULL)); - r = rand() % 0xff; - g = rand() % 0xff; - b = rand() % 0xff; - r_up = g_up = b_up = true; - - for (i = 0; i < 50; ++i) { - r = next_color(&r_up, r, 20); - g = next_color(&g_up, g, 10); - b = next_color(&b_up, b, 5); - - for (iter = modeset_list; iter; iter = iter->next) { - for (j = 0; j < iter->height; ++j) { - for (k = 0; k < iter->width; ++k) { - off = iter->stride * j + k * 4; - *(uint32_t*)&iter->map[off] = - (r << 16) | (g << 8) | b; - } - } - } - - usleep(100000); - } -} - -/* - * modeset_cleanup(fd): This cleans up all the devices we created during + * psplash_drm_destroy(drm): This cleans up all the devices we created during * modeset_prepare(). It resets the CRTCs to their saved states and deallocates * all memory. * It should be pretty obvious how all of this works. */ -static void modeset_cleanup(int fd) +void psplash_drm_destroy(PSplashDRM *drm) { struct modeset_dev *iter; struct drm_mode_destroy_dumb dreq; + if (!drm) + return; + while (modeset_list) { /* remove from global list */ iter = modeset_list; modeset_list = iter->next; /* restore saved CRTC configuration */ - drmModeSetCrtc(fd, + drmModeSetCrtc(drm->fd, iter->saved_crtc->crtc_id, iter->saved_crtc->buffer_id, iter->saved_crtc->x, @@ -694,16 +630,19 @@ static void modeset_cleanup(int fd) munmap(iter->map, iter->size); /* delete framebuffer */ - drmModeRmFB(fd, iter->fb); + drmModeRmFB(drm->fd, iter->fb); /* delete dumb buffer */ memset(&dreq, 0, sizeof(dreq)); dreq.handle = iter->handle; - drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + drmIoctl(drm->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); /* free allocated memory */ free(iter); } + + close(drm->fd); + free(drm); } /* diff --git a/psplash-drm.h b/psplash-drm.h new file mode 100644 index 0000000..e987fd6 --- /dev/null +++ b/psplash-drm.h @@ -0,0 +1,17 @@ +#ifndef _HAVE_PSPLASH_DRM_H +#define _HAVE_PSPLASH_DRM_H + +#include "psplash-draw.h" + +typedef struct PSplashDRM +{ + PSplashCanvas canvas; + int fd; +} +PSplashDRM; + +void psplash_drm_destroy(PSplashDRM *drm); + +PSplashDRM* psplash_drm_new(int angle, int dev_id); + +#endif diff --git a/psplash.c b/psplash.c index 036dfb1..ebf8d7a 100644 --- a/psplash.c +++ b/psplash.c @@ -12,6 +12,9 @@ #include "psplash.h" #include "psplash-fb.h" +#ifdef ENABLE_DRM +#include "psplash-drm.h" +#endif #include "psplash-config.h" #include "psplash-colors.h" #include "psplash-poky-img.h" @@ -224,8 +227,11 @@ int main (int argc, char** argv) { char *rundir; - int pipe_fd, i = 0, angle = 0, fbdev_id = 0, ret = 0; - PSplashFB *fb; + int pipe_fd, i = 0, angle = 0, dev_id = 0, use_drm = 0, ret = 0; + PSplashFB *fb = NULL; +#ifdef ENABLE_DRM + PSplashDRM *drm = NULL; +#endif PSplashCanvas *canvas; bool disable_console_switch = FALSE; @@ -247,16 +253,24 @@ main (int argc, char** argv) continue; } - if (!strcmp(argv[i],"-f") || !strcmp(argv[i],"--fbdev")) + if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--fbdev") || + !strcmp(argv[i], "-d") || !strcmp(argv[i], "--dev")) { if (++i >= argc) goto fail; - fbdev_id = atoi(argv[i]); + dev_id = atoi(argv[i]); continue; } +#ifdef ENABLE_DRM + if (!strcmp(argv[i], "--drm")) { + use_drm = 1; + continue; + } +#endif + fail: fprintf(stderr, - "Usage: %s [-n|--no-console-switch][-a|--angle <0|90|180|270>][-f|--fbdev <0..9>]\n", + "Usage: %s [-n|--no-console-switch][-a|--angle <0|90|180|270>][-f|--fbdev|-d|--dev <0..9>][--drm]\n", argv[0]); exit(-1); } @@ -291,12 +305,21 @@ main (int argc, char** argv) if (!disable_console_switch) psplash_console_switch (); - if ((fb = psplash_fb_new(angle,fbdev_id)) == NULL) - { - ret = -1; - goto fb_fail; + if (use_drm) { +#ifdef ENABLE_DRM + if ((drm = psplash_drm_new(angle, dev_id)) == NULL) { + ret = -1; + goto error; + } + canvas = &drm->canvas; +#endif + } else { + if ((fb = psplash_fb_new(angle, dev_id)) == NULL) { + ret = -1; + goto error; } - canvas = &fb->canvas; + canvas = &fb->canvas; + } #ifdef HAVE_SYSTEMD sd_notify(0, "READY=1"); @@ -349,9 +372,14 @@ main (int argc, char** argv) psplash_main(canvas, pipe_fd, 0); - psplash_fb_destroy (fb); + if (fb) + psplash_fb_destroy(fb); +#ifdef ENABLE_DRM + if (drm) + psplash_drm_destroy(drm); +#endif - fb_fail: + error: unlink(PSPLASH_FIFO); if (!disable_console_switch) -- 2.25.1