From: Ray Strode Date: Sun, 22 Aug 2010 21:13:45 +0000 (-0400) Subject: [drm] Add preliminary support for libkms X-Git-Tag: 0.8.4~142 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6e2df779ed02ae339342cde8196bdddf1bc4a742;p=thirdparty%2Fplymouth.git [drm] Add preliminary support for libkms This commit adds most of the pieces in place to use libkms, a library by Jakob Bornecrantz, that abstracts the drm drivers behind a common api. Right now, we only fallback to libkms if the existing backends won't work for the configured hardware. In theory, this will give us pretty boot in virtual machines, since libkms has support for the vmwgfx drm driver. Aside from vmwgfx, libkms also supports intel and nouveau right now. When it supports radeon, too, I'll probably switch to using libkms by default instead of as a fallback. Eventually, I'd like to drop all the non-libkms backend bits and the whole driver vtable abstraction thing from plymouth completely. This commit is just a copy-and-paste of one of the existing drm backend files, with changes made to accomodate the libkms api. I haven't actually tested it, yet, so it will probably need changes after I get a chance to do that. --- diff --git a/configure.ac b/configure.ac index f7c2c939..b5eddf5d 100644 --- a/configure.ac +++ b/configure.ac @@ -99,6 +99,16 @@ CFLAGS="$OLD_CFLAGS" AC_SUBST(DRM_CFLAGS) AC_SUBST(DRM_LIBS) +AC_ARG_ENABLE(libkms, AS_HELP_STRING([--enable-libkms],[enable building with libkms support]),enable_libkms=$enableval,enable_libkms=yes) +AM_CONDITIONAL(ENABLE_LIBKMS, [test "$enable_libkms" = yes]) + +if test x$enable_libkms = xyes; then + PKG_CHECK_MODULES(LIBKMS, [libkms]) + AC_SUBST(LIBKMS_CFLAGS) + AC_SUBST(LIBKMS_LIBS) + AC_DEFINE(PLY_ENABLE_LIBKMS, 1, [Enable support for libkms abstraction over drm drivers]) +fi + AC_ARG_ENABLE(tracing, AS_HELP_STRING([--enable-tracing],[enable verbose tracing code]),enable_tracing=$enableval,enable_tracing=yes) if test x$enable_tracing = xyes; then diff --git a/src/plugins/renderers/drm/Makefile.am b/src/plugins/renderers/drm/Makefile.am index ce7c04f8..7d2ef600 100644 --- a/src/plugins/renderers/drm/Makefile.am +++ b/src/plugins/renderers/drm/Makefile.am @@ -24,4 +24,11 @@ drm_la_SOURCES = $(srcdir)/plugin.c \ $(srcdir)/ply-renderer-nouveau-driver.h \ $(srcdir)/ply-renderer-nouveau-driver.c +if ENABLE_LIBKMS +drm_la_LIBADD += $(LIBKMS_LIBS) +drm_la_CFLAGS += $(LIBKMS_CFLAGS) +drm_la_SOURCES += $(srcdir)/ply-renderer-libkms-driver.h \ + $(srcdir)/ply-renderer-libkms-driver.c +endif + MAINTAINERCLEANFILES = Makefile.in diff --git a/src/plugins/renderers/drm/plugin.c b/src/plugins/renderers/drm/plugin.c index 308d0a00..a53cf0ee 100644 --- a/src/plugins/renderers/drm/plugin.c +++ b/src/plugins/renderers/drm/plugin.c @@ -63,6 +63,10 @@ #include "ply-renderer-radeon-driver.h" #include "ply-renderer-nouveau-driver.h" +#ifdef PLY_ENABLE_LIBKMS +#include "ply-renderer-libkms-driver.h" +#endif + #define BYTES_PER_PIXEL (4) struct _ply_renderer_head @@ -513,12 +517,16 @@ load_driver (ply_renderer_backend_t *backend) backend->driver_interface = ply_renderer_nouveau_driver_get_interface (); backend->driver_supports_mapping_console = false; } - free (driver_name); if (backend->driver_interface == NULL) { +#ifdef PLY_ENABLE_LIBKMS + backend->driver_interface = ply_renderer_libkms_driver_get_interface (); + backend->driver_supports_mapping_console = false; +#else close (device_fd); return false; +#endif } backend->driver = backend->driver_interface->create_driver (device_fd); diff --git a/src/plugins/renderers/drm/ply-renderer-libkms-driver.c b/src/plugins/renderers/drm/ply-renderer-libkms-driver.c new file mode 100644 index 00000000..18c7ccfd --- /dev/null +++ b/src/plugins/renderers/drm/ply-renderer-libkms-driver.c @@ -0,0 +1,430 @@ +/* ply-renderer-libkms-driver.c - interface to libkms abstraction over drm drivers + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: Ray Strode + */ +#include "config.h" + +#include "ply-renderer-libkms-driver.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ply-array.h" +#include "ply-hashtable.h" +#include "ply-logger.h" +#include "ply-renderer-driver.h" + +typedef struct _ply_renderer_buffer ply_renderer_buffer_t; + +struct _ply_renderer_buffer +{ + struct kms_bo *object; + uint32_t id; + unsigned long width; + unsigned long height; + unsigned long row_stride; + + void *map_address; + + uint32_t added_fb : 1; +}; + +struct _ply_renderer_driver +{ + int device_fd; + struct kms_driver *driver; + + ply_hashtable_t *buffers; +}; + +static ply_renderer_driver_t * +create_driver (int device_fd) +{ + ply_renderer_driver_t *driver; + int result; + + driver = calloc (1, sizeof (ply_renderer_driver_t)); + driver->device_fd = device_fd; + + result = kms_create (driver->device_fd, &driver->driver); + if (result != 0) + { + ply_trace ("kms buffer driver could not be initialized: %d", result); + free (driver); + return NULL; + } + + driver->buffers = ply_hashtable_new (ply_hashtable_direct_hash, + ply_hashtable_direct_compare); + + return driver; +} + +static void +destroy_driver (ply_renderer_driver_t *driver) +{ + ply_hashtable_free (driver->buffers); + + ply_trace ("uninitializing kms buffer driver"); + kms_destroy (&driver->driver); + free (driver); +} + +static ply_renderer_buffer_t * +ply_renderer_buffer_new (ply_renderer_driver_t *driver, + struct kms_bo *buffer_object, + uint32_t id, + unsigned long width, + unsigned long height, + unsigned long row_stride) +{ + ply_renderer_buffer_t *buffer; + + buffer = calloc (1, sizeof (ply_renderer_buffer_t)); + buffer->object = buffer_object; + buffer->id = id; + buffer->width = width; + buffer->height = height; + buffer->row_stride = row_stride; + + ply_trace ("returning %lux%lu buffer with stride %lu", + width, height, row_stride); + + return buffer; +} + +static ply_renderer_buffer_t * +get_buffer_from_id (ply_renderer_driver_t *driver, + uint32_t id) +{ + static ply_renderer_buffer_t *buffer; + + buffer = ply_hashtable_lookup (driver->buffers, (void *) (uintptr_t) id); + + return buffer; +} + +static struct kms_bo * +create_kms_bo_from_handle (ply_renderer_driver_t *driver, + uint32_t handle) +{ + struct drm_gem_flink flink_request; + struct kms_bo *buffer_object; + ply_array_t *attributes; + int result; + + /* FIXME: This can't be the right way to do this. + * + * 1) It requires skirting around the API and using ioctls + * 2) It requires taking a local handle, turning it into a + * a global handle ("name"), just so we can use an api that + * will open the global name and grab the local handle from it. + */ + + memset (&flink_request, 0, sizeof (struct drm_gem_flink)); + flink_request.handle = handle; + + if (ioctl (driver->device_fd, DRM_IOCTL_GEM_FLINK, &flink_request) < 0) + { + ply_trace ("Could not export global name for handle %u", handle); + return NULL; + } + + attributes = ply_array_new (PLY_ARRAY_ELEMENT_TYPE_UINT32); + ply_array_add_uint32_element (attributes, KMS_HANDLE); + ply_array_add_uint32_element (attributes, flink_request.name); + ply_array_add_uint32_element (attributes, KMS_TERMINATE_PROP_LIST); + result = kms_bo_create (driver->driver, + (const unsigned *) + ply_array_get_uint32_elements (attributes), + &buffer_object); + ply_array_free (attributes); + + if (result != 0) + { + ply_trace ("could not create buffer object from global name %u: %d", + flink_request.name, result); + return NULL; + } + + return buffer_object; +} + +static ply_renderer_buffer_t * +ply_renderer_buffer_new_from_id (ply_renderer_driver_t *driver, + uint32_t buffer_id) +{ + ply_renderer_buffer_t *buffer; + drmModeFB *fb; + struct kms_bo *buffer_object; + + fb = drmModeGetFB (driver->device_fd, buffer_id); + + if (fb == NULL) + { + ply_trace ("could not get FB with buffer id %u", buffer_id); + return NULL; + } + + buffer_object = create_kms_bo_from_handle (driver, fb->handle); + + if (buffer_object == NULL) + { + ply_trace ("could not create buffer object from handle %lu", + (unsigned long) fb->handle); + drmModeFreeFB (fb); + return NULL; + } + + buffer = ply_renderer_buffer_new (driver, buffer_object, buffer_id, + fb->width, fb->height, fb->pitch); + drmModeFreeFB (fb); + + return buffer; +} + +static bool +fetch_buffer (ply_renderer_driver_t *driver, + uint32_t buffer_id, + unsigned long *width, + unsigned long *height, + unsigned long *row_stride) +{ + ply_renderer_buffer_t *buffer; + + buffer = get_buffer_from_id (driver, buffer_id); + + if (buffer == NULL) + { + ply_trace ("could not fetch buffer %u, creating one", buffer_id); + buffer = ply_renderer_buffer_new_from_id (driver, buffer_id); + + if (buffer == NULL) + { + ply_trace ("could not create buffer either %u", buffer_id); + return false; + } + + ply_hashtable_insert (driver->buffers, + (void *) (uintptr_t) buffer_id, + buffer); + } + + if (width != NULL) + *width = buffer->width; + + if (height != NULL) + *height = buffer->height; + + if (row_stride != NULL) + *row_stride = buffer->row_stride; + + ply_trace ("fetched %lux%lu buffer with stride %lu", + buffer->width, buffer->height, buffer->row_stride); + return true; +} + +static uint32_t +create_buffer (ply_renderer_driver_t *driver, + unsigned long width, + unsigned long height, + unsigned long *row_stride) +{ + struct kms_bo *buffer_object; + ply_renderer_buffer_t *buffer; + uint32_t buffer_id; + int result; + unsigned int handle; + ply_array_t *attributes; + + *row_stride = ply_round_to_multiple (width * 4, 256); + + attributes = ply_array_new (PLY_ARRAY_ELEMENT_TYPE_UINT32); + ply_array_add_uint32_element (attributes, KMS_BO_TYPE); + ply_array_add_uint32_element (attributes, KMS_BO_TYPE_SCANOUT_X8R8G8B8); + ply_array_add_uint32_element (attributes, KMS_WIDTH); + ply_array_add_uint32_element (attributes, (uint32_t) width); + ply_array_add_uint32_element (attributes, KMS_HEIGHT); + ply_array_add_uint32_element (attributes, (uint32_t) height); + ply_array_add_uint32_element (attributes, KMS_PITCH); + ply_array_add_uint32_element (attributes, (uint32_t) *row_stride); + ply_array_add_uint32_element (attributes, KMS_TERMINATE_PROP_LIST); + result = kms_bo_create (driver->driver, + (const unsigned *) + ply_array_get_uint32_elements (attributes), + &buffer_object); + ply_array_free (attributes); + + if (result != 0) + { + ply_trace ("Could not allocate GEM object for frame buffer: %d", result); + return 0; + } + + result = kms_bo_get_prop (buffer_object, KMS_HANDLE, &handle); + + if (result != 0) + { + ply_trace ("Could not retrieve handle from GEM object: %d", result); + + kms_bo_destroy (&buffer_object); + return 0; + } + + if (drmModeAddFB (driver->device_fd, width, height, + 24, 32, *row_stride, handle, + &buffer_id) != 0) + { + ply_trace ("Could not set up GEM object as frame buffer: %m"); + kms_bo_destroy (&buffer_object); + return 0; + } + + buffer = ply_renderer_buffer_new (driver, + buffer_object, buffer_id, + width, height, *row_stride); + buffer->added_fb = true; + ply_hashtable_insert (driver->buffers, + (void *) (uintptr_t) buffer_id, + buffer); + + return buffer_id; +} + +static bool +map_buffer (ply_renderer_driver_t *driver, + uint32_t buffer_id) +{ + ply_renderer_buffer_t *buffer; + int result; + + buffer = get_buffer_from_id (driver, buffer_id); + + assert (buffer != NULL); + + result = kms_bo_map (buffer->object, &buffer->map_address); + + if (result != 0) + { + ply_trace ("could not map buffer %u: %d", buffer_id, result); + buffer->map_address = MAP_FAILED; + return false; + } + + return true; +} + +static void +unmap_buffer (ply_renderer_driver_t *driver, + uint32_t buffer_id) +{ + ply_renderer_buffer_t *buffer; + + buffer = get_buffer_from_id (driver, buffer_id); + + assert (buffer != NULL); + + kms_bo_unmap (buffer->object); + buffer->map_address = MAP_FAILED; +} + +static char * +begin_flush (ply_renderer_driver_t *driver, + uint32_t buffer_id) +{ + ply_renderer_buffer_t *buffer; + + buffer = get_buffer_from_id (driver, buffer_id); + + assert (buffer != NULL); + + return (char *) buffer->map_address; +} + +static void +end_flush (ply_renderer_driver_t *driver, + uint32_t buffer_id) +{ + ply_renderer_buffer_t *buffer; + + buffer = get_buffer_from_id (driver, buffer_id); + + assert (buffer != NULL); +} + +static void +destroy_buffer (ply_renderer_driver_t *driver, + uint32_t buffer_id) +{ + ply_renderer_buffer_t *buffer; + + buffer = get_buffer_from_id (driver, buffer_id); + + assert (buffer != NULL); + + if (buffer->added_fb) + drmModeRmFB (driver->device_fd, buffer->id); + + kms_bo_destroy (&buffer->object); + + ply_hashtable_remove (driver->buffers, + (void *) (uintptr_t) buffer_id); + free (buffer); +} + +ply_renderer_driver_interface_t * +ply_renderer_libkms_driver_get_interface (void) +{ + static ply_renderer_driver_interface_t driver_interface = + { + .create_driver = create_driver, + .destroy_driver = destroy_driver, + .create_buffer = create_buffer, + .fetch_buffer = fetch_buffer, + .map_buffer = map_buffer, + .unmap_buffer = unmap_buffer, + .begin_flush = begin_flush, + .end_flush = end_flush, + .destroy_buffer = destroy_buffer, + }; + + return &driver_interface; +} + +/* vim: set ts=4 sw=4 et ai ci cino={.5s,^-2,+.5s,t0,g0,e-2,n-2,p2s,(0,=.5s,:.5s */ diff --git a/src/plugins/renderers/drm/ply-renderer-libkms-driver.h b/src/plugins/renderers/drm/ply-renderer-libkms-driver.h new file mode 100644 index 00000000..b419a942 --- /dev/null +++ b/src/plugins/renderers/drm/ply-renderer-libkms-driver.h @@ -0,0 +1,32 @@ +/* ply-renderer-kms-driver.h + * + * Copyright (C) 2009 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written By: Ray Strode + */ +#ifndef PLY_RENDERER_LIBKMS_DRIVER_H +#define PLY_RENDERER_LIBKMS_DRIVER_H + +#include "ply-renderer-driver.h" + +#ifndef PLY_HIDE_FUNCTION_DECLARATIONS +ply_renderer_driver_interface_t *ply_renderer_libkms_driver_get_interface (void); +#endif + +#endif /* PLY_RENDERER_LIBKMS_DRIVER_H */ +/* vim: set ts=4 sw=4 et ai ci cino={.5s,^-2,+.5s,t0,g0,e-2,n-2,p2s,(0,=.5s,:.5s */