[cairo] [PATCH 65/71] drm: dumb framebuffer support

Enrico Weigelt, metux IT consult enrico.weigelt at gr13.net
Mon Apr 17 16:57:44 UTC 2017


Seems to be already functional, but yet quite rudimentary.

Signed-off-by: Enrico Weigelt, metux IT consult <enrico.weigelt at gr13.net>
---
 configure.ac                      |   6 +
 src/Makefile.sources              |   4 +
 src/drm/cairo-drm-basic-private.h | 194 ++++++++++++++++
 src/drm/cairo-drm-basic-surface.c | 205 +++++++++++++++++
 src/drm/cairo-drm-basic.c         | 468 ++++++++++++++++++++++++++++++++++++++
 src/drm/cairo-drm.c               |  16 +-
 6 files changed, 892 insertions(+), 1 deletion(-)
 create mode 100644 src/drm/cairo-drm-basic-private.h
 create mode 100644 src/drm/cairo-drm-basic-surface.c
 create mode 100644 src/drm/cairo-drm-basic.c

diff --git a/configure.ac b/configure.ac
index 5472efdcd..eb64220b7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -302,6 +302,12 @@ CAIRO_ENABLE_SURFACE_BACKEND(drm, DRM, no, [
   enable_cairo_surface_drm=1
 ])
 
+CAIRO_ENABLE_SURFACE_BACKEND(drm_basic, DRM_BASIC, no, [
+  if test "$enable_cairo_surface_drm" != "1"; then
+    AC_MSG_ERROR([drm-basic backend needs drm backend])
+  fi
+])
+
 CAIRO_ENABLE_SURFACE_BACKEND(drm_intel, DRM_INTEL, no, [
   if test "$enable_cairo_surface_drm" != "1"; then
     AC_MSG_ERROR([drm-intel backend needs drm backend])
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 693124314..5f3bd334f 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -455,6 +455,10 @@ cairo_drm_radeon_sources = \
 		    drm/cairo-drm-radeon.c \
 		    drm/cairo-drm-radeon-surface.c
 
+cairo_drm_basic_sources = \
+		    drm/cairo-drm-basic.c \
+		    drm/cairo-drm-basic-surface.c
+
 cairo_drm_headers = cairo-drm.h
 cairo_drm_private = drm/cairo-drm-private.h
 
diff --git a/src/drm/cairo-drm-basic-private.h b/src/drm/cairo-drm-basic-private.h
new file mode 100644
index 000000000..aac139594
--- /dev/null
+++ b/src/drm/cairo-drm-basic-private.h
@@ -0,0 +1,194 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2015 Enrico Weigelt, metux IT consult
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef CAIRO_DRM_BASIC_PRIVATE_H
+#define CAIRO_DRM_BASIC_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+#include "cairo-drm-private.h"
+#include "cairo-freelist-private.h"
+
+struct modeset_dev {
+    struct modeset_dev *next;
+
+    uint32_t stride;
+    uint32_t size;
+    uint32_t handle;
+
+    uint32_t map_offset;
+
+    drmModeModeInfo mode;
+    uint32_t fb;
+    uint32_t conn;
+    uint32_t crtc;
+    drmModeCrtc *saved_crtc;
+};
+
+typedef struct _cairo_drm_basic_bo {
+    cairo_drm_bo_t base;
+    struct modeset_dev *mode_dev;
+} cairo_drm_basic_bo_t;
+
+typedef struct _cairo_drm_basic_device {
+    cairo_drm_device_t base;
+
+    struct modeset_dev *mode_list;
+
+    uint32_t fb_id;
+} cairo_drm_basic_device_t;
+
+typedef struct _cairo_drm_basic_surface {
+    cairo_drm_surface_t base;
+} cairo_drm_basic_surface_t;
+
+static inline cairo_drm_basic_surface_t *
+_cairo_abstract_surface_cast_basic (cairo_surface_t *surface)
+{
+    return cairo_container_of (
+	_cairo_abstract_surface_cast_drm (surface),
+	cairo_drm_basic_surface_t,
+	base);
+}
+
+/* cast cairo_drm_device_t* to cairo_drm_basic_device_t* */
+static inline cairo_drm_basic_surface_t *
+_cairo_drm_surface_cast_basic (cairo_drm_surface_t *surface)
+{
+    return cairo_container_of (surface, cairo_drm_basic_surface_t, base);
+}
+
+/* cast cairo_device_t to cairo_drm_basic_device_t */
+static inline cairo_drm_basic_surface_t *
+_cairo_surface_cast_basic (cairo_surface_t *surface)
+{
+    return _cairo_drm_surface_cast_basic (
+	_cairo_surface_cast_drm (surface));
+}
+
+static inline cairo_drm_basic_device_t *
+_cairo_drm_device_cast_basic (cairo_drm_device_t *device)
+{
+    return cairo_container_of (device, cairo_drm_basic_device_t, base);
+}
+
+static inline const cairo_drm_basic_device_t *
+_cairo_drm_device_cast_basic_const (const cairo_drm_device_t *device)
+{
+    return cairo_container_of (device, const cairo_drm_basic_device_t, base);
+}
+
+static inline cairo_drm_basic_device_t *
+_cairo_device_cast_basic (cairo_device_t *device)
+{
+    return _cairo_drm_device_cast_basic (
+	_cairo_device_cast_drm (device));
+}
+
+static inline const cairo_drm_basic_device_t *
+_cairo_device_cast_basic_const (const cairo_device_t *device)
+{
+    return _cairo_drm_device_cast_basic_const (
+	_cairo_device_cast_drm_const (device));
+}
+
+static inline cairo_drm_basic_bo_t *
+_cairo_drm_bo_cast_basic (cairo_drm_bo_t *bo)
+{
+    return cairo_container_of (bo, cairo_drm_basic_bo_t, base);
+}
+
+static inline const cairo_drm_basic_bo_t *
+_cairo_drm_bo_cast_basic_const (const cairo_drm_bo_t *bo)
+{
+    return cairo_container_of (bo, const cairo_drm_basic_bo_t, base);
+}
+
+/* get basic device from basic surface */
+static inline cairo_drm_basic_device_t *
+_cairo_basic_surface_get_device (const cairo_drm_basic_surface_t *surface)
+{
+    return _cairo_device_cast_basic (surface->base.base.device);
+}
+
+/* get basic bo from basic surface */
+static inline cairo_drm_basic_bo_t *
+_cairo_basic_surface_get_bo (const cairo_drm_basic_surface_t *surface)
+{
+    return _cairo_drm_bo_cast_basic (surface->base.bo);
+}
+
+cairo_private cairo_status_t
+_cairo_drm_basic_device_init (cairo_drm_basic_device_t *device, int fd);
+
+cairo_private void
+_cairo_drm_basic_device_fini (cairo_drm_basic_device_t *device);
+
+cairo_private void
+_cairo_drm_basic_bo_read (const cairo_drm_basic_device_t *dev,
+	        cairo_drm_basic_bo_t *bo,
+	        unsigned long offset,
+		unsigned long size,
+		void *data);
+
+cairo_private void *
+_cairo_drm_basic_bo_map (const cairo_drm_device_t *dev, cairo_drm_bo_t *bo);
+
+cairo_private cairo_surface_t *
+_cairo_drm_basic_surface_create_for_name (cairo_drm_device_t *device,
+					  unsigned int name,
+					  cairo_format_t format,
+					  int width,
+					  int height,
+					  int stride);
+
+cairo_private void
+_cairo_drm_basic_device_destroy (void *data);
+
+cairo_private cairo_surface_t *
+_cairo_drm_basic_surface_create (cairo_drm_device_t *device,
+				 cairo_format_t format,
+				 int width,
+				 int height);
+
+cairo_private cairo_drm_bo_t *
+_cairo_drm_basic_bo_create (cairo_drm_basic_device_t *dev,
+			    cairo_format_t format,
+			    uint32_t width,
+			    uint32_t height);
+
+cairo_private cairo_surface_t *
+_cairo_drm_basic_bo_get_image (const cairo_drm_device_t *device,
+				cairo_drm_bo_t *bo,
+				const cairo_drm_surface_t *surface);
+
+cairo_private uint32_t
+_cairo_drm_surface_get_crtc_id (cairo_drm_surface_t *abstract_surface);
+
+#endif /* CAIRO_DRM_BASIC_PRIVATE_H */
diff --git a/src/drm/cairo-drm-basic-surface.c b/src/drm/cairo-drm-basic-surface.c
new file mode 100644
index 000000000..ea4ad91e1
--- /dev/null
+++ b/src/drm/cairo-drm-basic-surface.c
@@ -0,0 +1,205 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2015 Enrico Weigelt, metux IT consult
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include <inttypes.h>		/* workaround for broken radeon_drm.h */
+#include <stddef.h>
+#include <libdrm/drm.h>
+#include <libdrm/drm_mode.h>
+#include <xf86drmMode.h>
+#include <libdrm/radeon_drm.h>
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-basic-private.h"
+
+#include "cairo-default-context-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+
+#define _DEBUG(text, ...)       \
+    { fprintf(stderr, "[drm/basic] " text "\n", ##__VA_ARGS__); }
+
+#define MAX_SIZE 2048
+
+static cairo_surface_t *
+_cairo_drm_basic_surface_create_similar (void		*abstract_surface,
+			      cairo_content_t		 content,
+			      int			 width,
+			      int			 height)
+{
+    return cairo_image_surface_create (_cairo_format_from_content (content),
+				       width,
+				       height);
+}
+
+static const cairo_surface_backend_t _cairo_drm_basic_surface_backend = {
+    .type			= CAIRO_SURFACE_TYPE_DRM,
+    .create_context		= _cairo_default_context_create,
+    .create_similar		= _cairo_drm_basic_surface_create_similar,
+    .finish			= _cairo_drm_surface_finish,
+    .acquire_source_image	= _cairo_drm_surface_acquire_source_image,
+    .release_source_image	= _cairo_drm_surface_release_source_image,
+    .get_extents		= _cairo_drm_surface_get_extents,
+    .get_font_options		= _cairo_drm_surface_get_font_options,
+    .flush			= _cairo_drm_surface_flush,
+    .paint			= _cairo_drm_surface_paint,
+    .mask			= _cairo_drm_surface_mask,
+    .stroke			= _cairo_drm_surface_stroke,
+    .fill			= _cairo_drm_surface_fill,
+    .show_glyphs		= _cairo_drm_surface_glyphs,
+};
+
+static cairo_surface_t *
+_cairo_drm_basic_surface_create_for_bo (cairo_drm_device_t *device,
+					cairo_drm_bo_t *drm_bo,
+					cairo_format_t format)
+{
+    if (unlikely (drm_bo == NULL)) {
+	_DEBUG("_cairo_drm_basic_surface_create_for_bo() NULL bo");
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    switch (format) {
+	case CAIRO_FORMAT_ARGB32:
+	case CAIRO_FORMAT_RGB24:
+	    break;
+	case CAIRO_FORMAT_INVALID:
+	case CAIRO_FORMAT_A1:
+	case CAIRO_FORMAT_RGB16_565:
+	case CAIRO_FORMAT_RGB30:
+	case CAIRO_FORMAT_A8:
+	    _DEBUG("_cairo_drm_basic_surface_create_for_bo() unsupported color format: %d", format);
+	    return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+    }
+
+    cairo_drm_basic_bo_t *basic_bo = _cairo_drm_bo_cast_basic(drm_bo);
+    assert(basic_bo != NULL);
+
+    _DEBUG("_cairo_drm_basic_surface_create_for_bo() width=%d height=%d",
+	basic_bo->mode_dev->mode.hdisplay,
+	basic_bo->mode_dev->mode.vdisplay);
+
+    cairo_drm_basic_surface_t *surface = calloc (1, sizeof (cairo_drm_basic_surface_t));
+    if (unlikely (surface == NULL))
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    _cairo_surface_init (&surface->base.base,
+			 &_cairo_drm_basic_surface_backend,
+			 &device->base,
+			 _cairo_content_from_format (format),
+			 FALSE);
+
+    _cairo_drm_surface_init (&surface->base,
+			     format,
+			     basic_bo->mode_dev->mode.hdisplay,
+			     basic_bo->mode_dev->mode.vdisplay);
+
+    surface->base.bo = drm_bo;
+    surface->base.stride = basic_bo->mode_dev->stride;
+
+    return &surface->base.base;
+}
+
+cairo_surface_t *
+_cairo_drm_basic_surface_create (cairo_drm_device_t *device,
+				 cairo_format_t format,
+				 int width,
+				 int height)
+{
+    _DEBUG("_cairo_drm_basic_surface_create() %dx%d", width, height);
+
+    // the dimensions given here are just preferred - actual one may differ
+    cairo_drm_bo_t *drm_bo = _cairo_drm_basic_bo_create (
+	_cairo_drm_device_cast_basic (device),
+	format,
+	width,
+	height);
+
+    return _cairo_drm_basic_surface_create_for_bo(device, drm_bo, format);
+}
+
+cairo_surface_t *
+_cairo_drm_basic_surface_create_for_name (cairo_drm_device_t *device,
+					  unsigned int name,
+					  cairo_format_t format,
+					  int width,
+					  int height,
+					  int stride)
+{
+    _DEBUG("_cairo_drm_basic_surface_create_for_name()");
+
+    // FIXME: completely untested !
+
+    if (stride < cairo_format_stride_for_width (format, width))
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+
+    // FIXME: should call a basic-device specific bo ?
+    cairo_drm_bo_t *drm_bo = _cairo_drm_bo_create_for_name (device, name);
+
+    return _cairo_drm_basic_surface_create_for_bo(device, drm_bo, format);
+}
+
+void
+_cairo_drm_basic_device_destroy (void *data)
+{
+    cairo_drm_basic_device_t *device = _cairo_device_cast_basic (data);
+
+    _cairo_drm_device_fini (&device->base);
+
+    free (data);
+}
+
+cairo_drm_device_t *
+_cairo_drm_basic_device_create (int fd, dev_t dev, int vendor_id, int chip_id)
+{
+    cairo_drm_basic_device_t *device;
+    cairo_status_t status;
+
+    device = calloc (1, sizeof (cairo_drm_basic_device_t));
+    if (device == NULL)
+	return _cairo_drm_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = _cairo_drm_basic_device_init (device, fd);
+    if (unlikely (status)) {
+	_DEBUG("device init failed");
+	free (device);
+	return _cairo_drm_device_create_in_error (status);
+    }
+
+    return _cairo_drm_device_init (&device->base, fd, dev, vendor_id, chip_id, MAX_SIZE);
+}
+
+uint32_t
+_cairo_drm_surface_get_crtc_id (cairo_drm_surface_t *abstract_surface)
+{
+    cairo_drm_basic_surface_t *surface = _cairo_drm_surface_cast_basic (abstract_surface);
+    cairo_drm_basic_bo_t *bo = _cairo_drm_bo_cast_basic (surface->base.bo);
+    return bo->mode_dev->crtc;
+}
diff --git a/src/drm/cairo-drm-basic.c b/src/drm/cairo-drm-basic.c
new file mode 100644
index 000000000..dcc3d07a5
--- /dev/null
+++ b/src/drm/cairo-drm-basic.c
@@ -0,0 +1,468 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2015 Enrico Weigelt, metux IT consult
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include <inttypes.h>		// broken xf86drmMode.h
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+#include <libdrm/drm.h>
+#include <libdrm/drm_mode.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "cairoint.h"
+
+#include "cairo-drm-private.h"
+#include "cairo-drm-basic-private.h"
+
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+
+#define _DEBUG(text, ...)	\
+    { fprintf(stderr, "[drm/basic] " text "\n", ##__VA_ARGS__); }
+
+// FIXME: double-copy
+void
+_cairo_drm_basic_bo_read (const cairo_drm_basic_device_t *device,
+			  cairo_drm_basic_bo_t *bo,
+			  unsigned long offset,
+			  unsigned long size,
+			  void *data)
+{
+    _DEBUG("_cairo_drm_basic_bo_read()");
+
+    assert (device);
+    assert (bo);
+    assert (data);
+
+    uint8_t *ptr = device->base.bo.map (&(device->base), &(bo->base));
+
+    if (ptr == NULL) {
+	_DEBUG("_cairo_drm_basic_bo_read(): failed to map");
+	return;
+    }
+
+    memcpy (data, ptr + offset, size);
+    _cairo_drm_bo_unmap (&(bo->base));
+
+    VG (VALGRIND_MAKE_MEM_DEFINED (data, size));
+}
+
+void *
+_cairo_drm_basic_bo_map (const cairo_drm_device_t *drm_dev,
+			 cairo_drm_bo_t *drm_bo)
+{
+    _DEBUG("_cairo_drm_basic_bo_map()");
+
+    const cairo_drm_basic_device_t* device = _cairo_drm_device_cast_basic_const (drm_dev);
+    cairo_drm_basic_bo_t *bo = _cairo_drm_bo_cast_basic (drm_bo);
+
+    assert(drm_dev);
+    assert(drm_bo);
+    assert(device);
+    assert(bo);
+    assert(bo->mode_dev);
+
+    if (bo->base.mapped != NULL) {
+	_DEBUG("WARN: bo is already mapped");
+	return bo->base.mapped;
+    }
+
+    /* perform actual memory mapping */
+    bo->base.mapped = mmap(0, bo->mode_dev->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+	        device->base.fd, bo->mode_dev->map_offset);
+    if (bo->base.mapped  == MAP_FAILED) {
+	_DEBUG("cannot mmap dumb buffer (%d): %m", errno);
+	return NULL;
+    }
+
+    /* clear the framebuffer to 0 */
+    _DEBUG("bo_map() clearing framebuffer at: %ld => %d", (long)bo->base.mapped, bo->base.size);
+    memset(bo->base.mapped, 0x66, bo->base.size);
+
+    return bo->base.mapped;
+}
+
+static int modeset_find_crtc(cairo_drm_basic_device_t *device,
+			     drmModeRes *res,
+			     drmModeConnector *conn,
+			     struct modeset_dev *dev)
+{
+    drmModeEncoder *enc = NULL;
+
+    /* first try the currently conected encoder+crtc */
+    if (conn->encoder_id)
+	enc = drmModeGetEncoder(device->base.fd, conn->encoder_id);
+
+    if (enc) {
+	if (enc->crtc_id) {
+
+	    struct modeset_dev *iter;
+	    int have_crtc = 1;
+
+	    for (iter = device->mode_list; iter; iter = iter->next) {
+		if (iter->crtc == enc->crtc_id) {
+		    have_crtc = 0;
+		    break;
+		}
+	    }
+
+	    if (have_crtc) {
+		drmModeFreeEncoder(enc);
+		dev->crtc = enc->crtc_id;
+		return 1;
+	    }
+	}
+
+	drmModeFreeEncoder(enc);
+    }
+
+    /* If the connector is not currently bound to an encoder or if the
+     * encoder+crtc is already used by another connector (actually unlikely
+     * but lets be safe), iterate all other available encoders to find a
+     * matching CRTC. */
+    int i;
+    for (i = 0; i < conn->count_encoders; ++i) {
+	enc = drmModeGetEncoder(device->base.fd, conn->encoders[i]);
+	if (!enc) {
+	    _DEBUG("cannot retrieve encoder %u:%u (%d): %m",
+		i, conn->encoders[i], errno);
+	    continue;
+	}
+
+	/* iterate all global CRTCs */
+	int j;
+	for (j = 0; j < res->count_crtcs; ++j) {
+	    /* check whether this CRTC works with the encoder */
+	    if (!(enc->possible_crtcs & (1 << j)))
+		continue;
+
+	    /* check that no other device already uses this CRTC */
+	    struct modeset_dev *iter;
+	    int have_crtc = 1;
+	    for (iter = device->mode_list; iter; iter = iter->next) {
+		if (iter->crtc == res->crtcs[j]) {
+		    have_crtc = 0;
+		    break;
+		}
+	    }
+
+	    /* we have found a CRTC, so save it and return */
+	    if (have_crtc) {
+		drmModeFreeEncoder(enc);
+		dev->crtc = res->crtcs[j];
+		return 1;
+	    }
+	}
+
+	drmModeFreeEncoder(enc);
+    }
+
+    _DEBUG("cannot find suitable CRTC for connector %u", conn->connector_id);
+    return 0;
+}
+
+static int modeset_create_fb(cairo_drm_basic_device_t *device,
+			     struct modeset_dev *dev)
+{
+    struct drm_mode_map_dumb mreq = { 0 };
+    int ret;
+
+    /* create dumb buffer */
+    struct drm_mode_create_dumb creq = {
+	.width = dev->mode.hdisplay,
+	.height = dev->mode.vdisplay,
+	.bpp = 32
+    };
+
+    ret = drmIoctl(device->base.fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
+    if (ret < 0) {
+	_DEBUG("cannot create dumb buffer (%d): %m", errno);
+	return -errno;
+    }
+    dev->stride = creq.pitch;
+    dev->size = creq.size;
+    dev->handle = creq.handle;
+
+    /* create framebuffer object for the dumb-buffer */
+    ret = drmModeAddFB(device->base.fd,
+		       dev->mode.hdisplay,
+		       dev->mode.vdisplay,
+		       24,
+		       32,
+		       dev->stride,
+		       dev->handle,
+		       &dev->fb);
+    if (ret) {
+	_DEBUG("cannot create framebuffer: %s", strerror(errno));
+	ret = -errno;
+	goto err_destroy;
+    }
+
+    /* prepare buffer for memory mapping */
+    memset(&mreq, 0, sizeof(mreq));
+    mreq.handle = dev->handle;
+    ret = drmIoctl(device->base.fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
+    if (ret) {
+	_DEBUG("cannot map dumb buffer (%d): %m", errno);
+	ret = -errno;
+	goto err_fb;
+    }
+
+    dev->map_offset = mreq.offset;
+
+    return 0;
+
+err_fb:
+    drmModeRmFB(device->base.fd, dev->fb);
+
+err_destroy:
+    {
+	struct drm_mode_destroy_dumb dreq = { .handle = dev->handle };
+	drmIoctl(device->base.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
+	return ret;
+    }
+}
+
+static int modeset_setup_dev(cairo_drm_basic_device_t *device,
+			     drmModeRes *res,
+			     drmModeConnector *conn,
+			     struct modeset_dev *dev)
+{
+    int ret;
+
+    /* check if a monitor is connected */
+    if (conn->connection != DRM_MODE_CONNECTED) {
+	_DEBUG("ignoring unused connector %u", conn->connector_id);
+	return -ENOENT;
+    }
+
+    /* check if there is at least one valid mode */
+    if (conn->count_modes == 0) {
+	_DEBUG("no valid mode for connector %u", conn->connector_id);
+	return -EFAULT;
+    }
+
+    /* copy the mode information into our device structure */
+    dev->mode = conn->modes[0];
+
+    _DEBUG("mode for connector %u is %ux%u",
+	conn->connector_id, dev->mode.hdisplay, dev->mode.vdisplay);
+
+    /* find a crtc for this connector */
+    if (!modeset_find_crtc(device, res, conn, dev)) {
+	_DEBUG("no valid crtc for connector %u",
+	    conn->connector_id);
+	return -ENOENT;
+    }
+
+    /* create a framebuffer for this CRTC */
+    ret = modeset_create_fb(device, dev);
+    if (ret) {
+	_DEBUG("cannot create framebuffer for connector %u",
+	    conn->connector_id);
+	return ret;
+    }
+
+    return 0;
+}
+
+static int modeset_prepare(cairo_drm_basic_device_t *device)
+{
+    drmModeRes *res;
+    drmModeConnector *conn;
+    struct modeset_dev *dev;
+    int ret;
+
+    device->mode_list = NULL;
+
+    /* retrieve resources */
+    res = drmModeGetResources(device->base.fd);
+    if (!res) {
+	_DEBUG("cannot retrieve DRM resources (%d): %m", errno);
+	return -errno;
+    }
+
+    /* iterate all connectors */
+    int i;
+    for (i = 0; i < res->count_connectors; ++i) {
+	/* get information for each connector */
+	conn = drmModeGetConnector(device->base.fd, res->connectors[i]);
+	if (!conn) {
+	    _DEBUG("cannot retrieve DRM connector %u:%u (%d): %m",
+		i, res->connectors[i], errno);
+	    continue;
+	}
+
+	/* create a device structure */
+	dev = malloc(sizeof(*dev));
+	memset(dev, 0, sizeof(*dev));
+	dev->conn = conn->connector_id;
+
+	/* call helper function to prepare this connector */
+	ret = modeset_setup_dev(device, res, conn, dev);
+	if (ret) {
+	    if (ret != -ENOENT) {
+		errno = -ret;
+		_DEBUG("cannot setup device for connector %u:%u (%d): %m",
+		    i, res->connectors[i], errno);
+	    }
+	    free(dev);
+	    drmModeFreeConnector(conn);
+	    continue;
+	}
+
+	/* free connector data and link device into global list */
+	drmModeFreeConnector(conn);
+	dev->next = device->mode_list;
+	device->mode_list = dev;
+    }
+
+    /* free resources again */
+    drmModeFreeResources(res);
+    return 0;
+}
+
+cairo_drm_bo_t *
+_cairo_drm_basic_bo_create (cairo_drm_basic_device_t *device,
+			    cairo_format_t format,
+			    uint32_t width,
+			    uint32_t height)
+{
+    cairo_drm_basic_bo_t *bo = _cairo_drm_bo_cast_basic (_cairo_drm_bo_from_pool (&device->base));
+
+    assert(bo);
+
+    _DEBUG("_cairo_drm_basic_bo_create() format=%d width=%d height=%d", format, width, height);
+
+    // FIXME: just picking the first one
+    struct modeset_dev *mode_dev = bo->mode_dev = device->mode_list;
+
+    // FIXME: need to handle dimensions!
+    bo->base.handle = mode_dev->handle;
+    bo->base.size   = mode_dev->size;
+
+    _DEBUG("fb id: %u", mode_dev->fb);
+
+    CAIRO_REFERENCE_COUNT_INIT (&bo->base.ref_count, 1);
+    return &bo->base;
+}
+
+cairo_surface_t *
+_cairo_drm_basic_bo_get_image (const cairo_drm_device_t *drm_dev,
+			       cairo_drm_bo_t *drm_bo,
+			       const cairo_drm_surface_t *surface)
+{
+    cairo_image_surface_t *image;
+    uint8_t *dst;
+    int size, row;
+
+    _DEBUG("drm_basic_bo_get_image()");
+
+    const cairo_drm_basic_device_t *device = _cairo_drm_device_cast_basic_const (drm_dev);
+    cairo_drm_basic_bo_t *bo = _cairo_drm_bo_cast_basic (drm_bo);
+
+    image = _cairo_surface_cast_image (
+	cairo_image_surface_create (surface->format,
+				    surface->width,
+				    surface->height));
+    if (unlikely (image->base.status))
+	return &image->base;
+
+    if (image->stride == surface->stride) {
+	size = surface->stride * surface->height;
+	_cairo_drm_basic_bo_read (device, bo, 0, size, image->data);
+    } else {
+	int offset;
+
+	size = surface->width;
+	if (surface->format != CAIRO_FORMAT_A8)
+	    size *= 4;
+
+	offset = 0;
+	row = surface->height;
+	dst = image->data;
+	while (row--) {
+	    _cairo_drm_basic_bo_read (device, bo, offset, size, dst);
+	    offset += surface->stride;
+	    dst += image->stride;
+	}
+    }
+
+    return &image->base;
+}
+
+cairo_status_t
+_cairo_drm_basic_device_init (cairo_drm_basic_device_t *device,
+			      int fd)
+{
+    _cairo_freepool_init (&device->base.bo_pool, sizeof (cairo_drm_basic_bo_t));
+
+    device->base.bo.release			= _cairo_drm_bo_release;
+    device->base.bo.map				= _cairo_drm_basic_bo_map;
+    device->base.bo.get_image			= _cairo_drm_basic_bo_get_image;
+
+    device->base.surface.create			= _cairo_drm_basic_surface_create;
+    device->base.surface.create_for_name	= _cairo_drm_basic_surface_create_for_name;
+    device->base.surface.flink			= _cairo_drm_surface_flink;
+    device->base.surface.get_crtc_id		= _cairo_drm_surface_get_crtc_id;
+
+    device->base.device.destroy			= _cairo_drm_basic_device_destroy;
+
+    device->base.fd = fd;
+
+    /* prepare all connectors and CRTCs */
+    int ret;
+    if ((ret = modeset_prepare(device)))
+    {
+	_DEBUG("modeset_prepare() failed: %s", strerror(errno));
+	return CAIRO_STATUS_DEVICE_ERROR;
+    }
+
+    /* perform actual modesetting on each found connector+CRTC */
+    struct modeset_dev *iter;
+    for (iter = device->mode_list; iter; iter = iter->next) {
+	ret = drmModeSetCrtc(
+		device->base.fd,
+		iter->crtc,
+		iter->fb,
+		0,
+		0,
+		&iter->conn,
+		1,
+		&iter->mode);
+
+	if (ret)
+	    _DEBUG("cannot set CRTC for connector %u (%d): %m",
+		iter->conn, errno);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/drm/cairo-drm.c b/src/drm/cairo-drm.c
index 42bb04199..7f6161f7c 100644
--- a/src/drm/cairo-drm.c
+++ b/src/drm/cairo-drm.c
@@ -230,6 +230,16 @@ _do_drm_device_get (struct udev_device *device)
 	return NULL;
     }
 
+#if CAIRO_HAS_DRM_BASIC_SURFACE
+    if (getenv ("CAIRO_DRM_BASIC_FORCE"))
+    {
+	fprintf (stderr, "[cairo/drm] forcing basic drm interface\n");
+	return _cairo_drm_basic_device_create (fd, devid, -1 , -1);
+    }
+#else
+    fprintf (stderr, "[cairo/drm] no support for basic framebuffer\n");
+#endif
+
 #if CAIRO_HAS_GALLIUM_SURFACE
     /* do not probe native driver - just use gallium */
     if (!getenv ("CAIRO_GALLIUM_FORCE"))
@@ -247,7 +257,11 @@ _do_drm_device_get (struct udev_device *device)
 	return dev;
 #endif
 
-    /* FIXME: need an plain framebuffer fallback */
+    fprintf(stderr, "[cairo/drm] no driver native found - trying dumb framebuffer\n");
+
+    dev = _cairo_drm_basic_device_create(fd, devid, -1, -1);
+    if (dev != NULL)
+	return dev;
 
     close (fd);
 
-- 
2.11.0.rc0.7.gbe5a750



More information about the cairo mailing list