[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