Hi,
I mentioned in a reply to Terje's patch series for 2D acceleration that I had prototyped some libdrm support a few weeks back. I've spent a bit of time cleaning it up and decided to post it for early review.
There's really not much interesting code here. A basic API is provided along with two IOCTLs that can be used to create Tegra-specific GEM, as opposed to dumb buffer objects. Given the various comments on Terje's proposed IOCTLs I wanted to make sure that these will be safe. I've seen that other chips use 64-bit fields for the size and offset of buffer objects and I wonder if those are really necessary.
Linux kernel patches for the IOCTLs are also in the works and I hope to get around to posting them this week. Obviously there will be some overlap between this and what Terje posted in his series, but it should be easy to synchronize.
Thierry
Thierry Reding (1): libdrm: Add NVIDIA Tegra support
Makefile.am | 6 +- configure.ac | 15 ++- include/drm/Makefile.am | 1 + include/drm/tegra_drm.h | 48 ++++++++++ tegra/Makefile.am | 17 ++++ tegra/libdrm_tegra.pc.in | 11 +++ tegra/tegra.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++ tegra/tegra.h | 51 +++++++++++ tests/modetest/modetest.c | 2 +- tests/vbltest/vbltest.c | 2 +- 10 files changed, 376 insertions(+), 4 deletions(-) create mode 100644 include/drm/tegra_drm.h create mode 100644 tegra/Makefile.am create mode 100644 tegra/libdrm_tegra.pc.in create mode 100644 tegra/tegra.c create mode 100644 tegra/tegra.h
Add the libdrm_tegra helper library to encapsulate Tegra-specific interfaces to the DRM.
Furthermore, Tegra is added to the list of supported chips in the modetest and vbltest programs.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de --- Makefile.am | 6 +- configure.ac | 15 ++- include/drm/Makefile.am | 1 + include/drm/tegra_drm.h | 48 ++++++++++ tegra/Makefile.am | 17 ++++ tegra/libdrm_tegra.pc.in | 11 +++ tegra/tegra.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++ tegra/tegra.h | 51 +++++++++++ tests/modetest/modetest.c | 2 +- tests/vbltest/vbltest.c | 2 +- 10 files changed, 376 insertions(+), 4 deletions(-) create mode 100644 include/drm/tegra_drm.h create mode 100644 tegra/Makefile.am create mode 100644 tegra/libdrm_tegra.pc.in create mode 100644 tegra/tegra.c create mode 100644 tegra/tegra.h
diff --git a/Makefile.am b/Makefile.am index 8ecd9d9..e90ae43 100644 --- a/Makefile.am +++ b/Makefile.am @@ -49,7 +49,11 @@ if HAVE_EXYNOS EXYNOS_SUBDIR = exynos endif
-SUBDIRS = . $(LIBKMS_SUBDIR) $(INTEL_SUBDIR) $(NOUVEAU_SUBDIR) $(RADEON_SUBDIR) $(OMAP_SUBDIR) $(EXYNOS_SUBDIR) tests include man +if HAVE_TEGRA +TEGRA_SUBDIR = tegra +endif + +SUBDIRS = . $(LIBKMS_SUBDIR) $(INTEL_SUBDIR) $(NOUVEAU_SUBDIR) $(RADEON_SUBDIR) $(OMAP_SUBDIR) $(EXYNOS_SUBDIR) $(TEGRA_SUBDIR) tests include man
libdrm_la_LTLIBRARIES = libdrm.la libdrm_ladir = $(libdir) diff --git a/configure.ac b/configure.ac index 0c19929..72d6dfa 100644 --- a/configure.ac +++ b/configure.ac @@ -114,6 +114,11 @@ AC_ARG_ENABLE(exynos-experimental-api, [Enable support for EXYNOS's experimental API (default: disabled)]), [EXYNOS=$enableval], [EXYNOS=no])
+AC_ARG_ENABLE(tegra-experimental-api, + AS_HELP_STRING([--enable-tegra-experimental-api], + [Enable support for Tegra's experimental API (default: disabled)]), + [TEGRA=$enableval], [TEGRA=no]) + dnl =========================================================================== dnl check compiler flags AC_DEFUN([LIBDRM_CC_TRY_FLAG], [ @@ -222,6 +227,11 @@ if test "x$EXYNOS" = xyes; then AC_DEFINE(HAVE_EXYNOS, 1, [Have EXYNOS support]) fi
+AM_CONDITIONAL(HAVE_TEGRA, [test "x$TEGRA" = xyes]) +if test "x$TEGRA" = xyes; then + AC_DEFINE(HAVE_TEGRA, 1, [Have Tegra support]) +fi + AC_ARG_ENABLE([cairo-tests], [AS_HELP_STRING([--enable-cairo-tests], [Enable support for Cairo rendering in tests (default: auto)])], @@ -247,7 +257,7 @@ if test "x$HAVE_LIBUDEV" = xyes; then fi AM_CONDITIONAL(HAVE_LIBUDEV, [test "x$HAVE_LIBUDEV" = xyes])
-if test "x$INTEL" != "xno" -o "x$RADEON" != "xno" -o "x$NOUVEAU" != "xno" -o "x$OMAP" != "xno"; then +if test "x$INTEL" != "xno" -o "x$RADEON" != "xno" -o "x$NOUVEAU" != "xno" -o "x$OMAP" != "xno" -o "x$TEGRA" != "xno"; then # Check for atomic intrinsics AC_CACHE_CHECK([for native atomic primitives], drm_cv_atomic_primitives, [ @@ -358,6 +368,8 @@ AC_CONFIG_FILES([ omap/libdrm_omap.pc exynos/Makefile exynos/libdrm_exynos.pc + tegra/Makefile + tegra/libdrm_tegra.pc tests/Makefile tests/modeprint/Makefile tests/modetest/Makefile @@ -380,4 +392,5 @@ echo " Radeon API $RADEON" echo " Nouveau API $NOUVEAU" echo " OMAP API $OMAP" echo " EXYNOS API $EXYNOS" +echo " Tegra API $TEGRA" echo "" diff --git a/include/drm/Makefile.am b/include/drm/Makefile.am index 2923ab4..3e33ed0 100644 --- a/include/drm/Makefile.am +++ b/include/drm/Makefile.am @@ -35,6 +35,7 @@ klibdrminclude_HEADERS = \ radeon_drm.h \ savage_drm.h \ sis_drm.h \ + tegra_drm.h \ via_drm.h \ mach64_drm.h
diff --git a/include/drm/tegra_drm.h b/include/drm/tegra_drm.h new file mode 100644 index 0000000..eaec602 --- /dev/null +++ b/include/drm/tegra_drm.h @@ -0,0 +1,48 @@ +/* + * Copyright © 2012 Thierry Reding + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __TEGRA_DRM_H__ +#define __TEGRA_DRM_H__ + +struct drm_tegra_gem_create { + uint32_t handle; + uint32_t offset; + uint32_t flags; + uint32_t size; +}; + +struct drm_tegra_gem_info { + uint32_t handle; + uint32_t offset; + uint32_t flags; + uint32_t size; +}; + +#define DRM_TEGRA_GEM_CREATE 0x40 +#define DRM_TEGRA_GEM_INFO 0x41 + +#define DRM_IOCTL_TEGRA_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_CREATE, struct drm_tegra_gem_create) +#define DRM_IOCTL_TEGRA_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_INFO, struct drm_tegra_gem_info) + +#endif /* __TEGRA_DRM_H__ */ diff --git a/tegra/Makefile.am b/tegra/Makefile.am new file mode 100644 index 0000000..65cd628 --- /dev/null +++ b/tegra/Makefile.am @@ -0,0 +1,17 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include/drm + +libdrm_tegra_ladir = $(libdir) +libdrm_tegra_la_LTLIBRARIES = libdrm_tegra.la +libdrm_tegra_la_LDFLAGS = -version-number 0:0:0 -no-undefined +libdrm_tegra_la_LIBADD = ../libdrm.la @PTHREADSTUBS_LIBS@ + +libdrm_tegra_la_SOURCES = \ + tegra.c + +libdrm_tegraincludedir = ${includedir}/libdrm +libdrm_tegrainclude_HEADERS = tegra.h + +pkgconfigdir = @pkgconfigdir@ +pkgconfig_DATA = libdrm_tegra.pc diff --git a/tegra/libdrm_tegra.pc.in b/tegra/libdrm_tegra.pc.in new file mode 100644 index 0000000..f83c74c --- /dev/null +++ b/tegra/libdrm_tegra.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libdrm_tegra +Description: Userspace interface to Tegra kernel DRM services +Version: 2.4.40 +Libs: -L${libdir} -ldrm_tegra +Cflags: -I${includedir} -I${includedir}/libdrm +Requires.private: libdrm diff --git a/tegra/tegra.c b/tegra/tegra.c new file mode 100644 index 0000000..d92eca5 --- /dev/null +++ b/tegra/tegra.c @@ -0,0 +1,227 @@ +/* + * Copyright © 2012 Thierry Reding + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <errno.h> +#include <fcntl.h> +#include <string.h> + +#include <sys/mman.h> + +#include <libdrm_lists.h> +#include <xf86atomic.h> +#include <xf86drm.h> + +#include <tegra_drm.h> + +#include "tegra.h" + +struct drm_tegra { + drmMMListHead bo_list; + int fd; +}; +struct tegra_bo { + struct drm_tegra_bo base; + drmMMListHead list; + atomic_t ref; +}; + +static inline struct tegra_bo *tegra_bo(struct drm_tegra_bo *bo) +{ + return (struct tegra_bo *)bo; +} + +static void drm_tegra_bo_free(struct drm_tegra_bo *bo) +{ + struct tegra_bo *priv = tegra_bo(bo); + struct drm_gem_close args; + + DRMLISTDEL(&priv->list); + + if (bo->map) + munmap(bo->map, bo->size); + + memset(&args, 0, sizeof(args)); + args.handle = bo->handle; + + drmIoctl(bo->device->fd, DRM_IOCTL_GEM_CLOSE, &args); + + free(bo); +} + +int drm_tegra_open(const char *path, struct drm_tegra **devicep) +{ + struct drm_tegra *device; + int err; + + if (!path || !devicep) + return -EINVAL; + + device = calloc(1, sizeof(*device)); + if (!device) + return -ENOMEM; + + DRMINITLISTHEAD(&device->bo_list); + + device->fd = open(path, O_RDWR); + if (device->fd < 0) { + err = -errno; + free(device); + return err; + } + + *devicep = device; + + return 0; +} + +void drm_tegra_close(struct drm_tegra *device) +{ + if (device) { + close(device->fd); + free(device); + } +} + +int drm_tegra_bo_new(struct drm_tegra *device, uint32_t flags, uint32_t size, + struct drm_tegra_bo **bop) +{ + struct drm_tegra_gem_create args; + struct drm_tegra_bo *bo; + struct tegra_bo *priv; + int err; + + if (!device || size == 0 || !bop) + return -EINVAL; + + bo = calloc(1, sizeof(*bo)); + if (!bo) + return -ENOMEM; + + priv = tegra_bo(bo); + + DRMLISTINITHEAD(&priv->list); + atomic_set(&priv->ref, 1); + bo->device = device; + bo->flags = flags; + bo->size = size; + + memset(&args, 0, sizeof(args)); + args.flags = flags; + args.size = size; + + err = drmCommandWriteRead(device->fd, DRM_TEGRA_GEM_CREATE, &args, + sizeof(args)); + if (err < 0) { + err = -errno; + free(bo); + return err; + } + + DRMLISTADD(&priv->list, &device->bo_list); + bo->handle = args.handle; + bo->offset = args.offset; + + *bop = bo; + + return 0; +} + +int drm_tegra_bo_open(struct drm_tegra *device, uint32_t handle, + struct drm_tegra_bo **bop) +{ + struct drm_tegra_gem_info args; + struct drm_tegra_bo *bo; + struct tegra_bo *priv; + int err; + + if (!device || !bop) + return -EINVAL; + + bo = calloc(1, sizeof(*bo)); + if (!bo) + return -ENOMEM; + + priv = tegra_bo(bo); + + DRMLISTINITHEAD(&priv->list); + atomic_set(&priv->ref, 1); + bo->device = device; + + memset(&args, 0, sizeof(args)); + args.handle = handle; + + err = drmCommandWriteRead(device->fd, DRM_TEGRA_GEM_INFO, &args, + sizeof(args)); + if (err < 0) { + err = -errno; + free(bo); + return err; + } + + DRMLISTADD(&priv->list, &device->bo_list); + bo->handle = args.handle; + bo->offset = args.offset; + bo->flags = args.flags; + bo->size = args.size; + + *bop = bo; + + return 0; +} + +struct drm_tegra_bo *drm_tegra_bo_get(struct drm_tegra_bo *bo) +{ + if (bo) { + struct tegra_bo *priv = tegra_bo(bo); + atomic_inc(&priv->ref); + } + + return bo; +} + +void drm_tegra_bo_put(struct drm_tegra_bo *bo) +{ + if (bo) { + struct tegra_bo *priv = tegra_bo(bo); + + if (atomic_dec_and_test(&priv->ref)) + drm_tegra_bo_free(bo); + } +} + +int drm_tegra_bo_map(struct drm_tegra_bo *bo) +{ + if (!bo->map) { + bo->map = mmap(0, bo->size, PROT_READ | PROT_WRITE, + MAP_SHARED, bo->device->fd, bo->offset); + if (bo->map == MAP_FAILED) { + bo->map = NULL; + return -errno; + } + } + + return 0; +} diff --git a/tegra/tegra.h b/tegra/tegra.h new file mode 100644 index 0000000..619090c --- /dev/null +++ b/tegra/tegra.h @@ -0,0 +1,51 @@ +/* + * Copyright © 2012 Thierry Reding + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __DRM_TEGRA_H__ +#define __DRM_TEGRA_H__ 1 + +#include <stdint.h> +#include <stdlib.h> + +struct drm_tegra; + +struct drm_tegra_bo { + struct drm_tegra *device; + uint32_t handle; + uint32_t offset; + uint32_t flags; + uint32_t size; + void *map; +}; + +int drm_tegra_open(const char *path, struct drm_tegra **devicep); +void drm_tegra_close(struct drm_tegra *device); + +int drm_tegra_bo_new(struct drm_tegra *device, uint32_t flags, uint32_t size, + struct drm_tegra_bo **bop); +int drm_tegra_bo_open(struct drm_tegra *device, uint32_t handle, + struct drm_tegra_bo **bop); +int drm_tegra_bo_map(struct drm_tegra_bo *bo); +struct drm_tegra_bo *drm_tegra_bo_get(struct drm_tegra_bo *bo); +void drm_tegra_bo_put(struct drm_tegra_bo *bo); + +#endif /* __DRM_TEGRA_H__ */ diff --git a/tests/modetest/modetest.c b/tests/modetest/modetest.c index c91bb9d..913ede8 100644 --- a/tests/modetest/modetest.c +++ b/tests/modetest/modetest.c @@ -939,7 +939,7 @@ int main(int argc, char **argv) int c; int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0; int test_vsync = 0; - char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos" }; + char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos", "tegra" }; unsigned int i; int count = 0, plane_count = 0; struct connector con_args[2]; diff --git a/tests/vbltest/vbltest.c b/tests/vbltest/vbltest.c index 4fccd59..22a5339 100644 --- a/tests/vbltest/vbltest.c +++ b/tests/vbltest/vbltest.c @@ -103,7 +103,7 @@ static void usage(char *name) int main(int argc, char **argv) { int i, c, fd, ret; - char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "exynos" }; + char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "exynos", "tegra" }; drmVBlank vbl; drmEventContext evctx; struct vbl_info handler_info;
On Tue, Dec 4, 2012 at 9:13 AM, Thierry Reding thierry.reding@avionic-design.de wrote:
btw, x11 can rapidly create/destroy temporary pixmaps.. which don't necessarily need userspace access. You might prefer to avoid creating mmap offset on GEM bo creation, and instead do this only if userspace needs to mmap the bo
BR, -R
On Tue, Dec 04, 2012 at 09:28:25AM -0600, Rob Clark wrote:
Ah, so that's the reason for the MMAP IOCTL provided by other DRMs. Yes, that might be better in the long run. For now I don't think it makes much of a difference since the GEM objects are backed by CMA, and therefore the offset will be created along with the GEM anyway.
With Terje's upcoming rework that handles all the allocations in host1x this should become more important, especially when the mapping is done through the Tegra30's IOMMU.
I'll rework that. Thanks for the quick reply.
Thierry
On 12/04/2012 05:13 PM, Thierry Reding wrote:
I think you shouldn't ask the path from the application (=DDX) here, but use drmOpen() that automatically finds the correct device for you.
I'd also prefer letting the application open and close the device and modify drm_tegra_open() to take the fd as a parameter. That way the DDX could easily access also all generic libdrm functions.
- Arto
dri-devel@lists.freedesktop.org