[psplash,RFC,11/14] Implement drm backend

Message ID 20220425075954.10427-12-vasyl.vavrychuk@opensynergy.com
State New
Headers show
Series Implement DRM backend | expand

Commit Message

Vasyl Vavrychuk April 25, 2022, 7:59 a.m. UTC
Limitation is that splash screen is drawn only on the first connector.

Signed-off-by: Vasyl Vavrychuk <vasyl.vavrychuk@opensynergy.com>
---
 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

Patch

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 <errno.h>
 #include <fcntl.h>
-#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -39,6 +37,7 @@ 
 #include <unistd.h>
 #include <xf86drm.h>
 #include <xf86drmMode.h>
+#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)