[PATCH 65/71] drm: dumb framebuffer support
"Enrico Weigelt, metux IT consult" <[email protected]>
| Newsgroups | gmane.comp.lib.cairo |
|---|---|
| Message-ID | <[email protected]> |
Seems to be already functional, but yet quite rudimentary. Signed-off-by: Enrico Weigelt, metux IT consult <[email protected]> --- 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 -- cairo mailing list [email protected] https://lists.cairographics.org/mailman/listinfo/cairo