diff options
author | Thierry Reding <treding@nvidia.com> | 2017-09-06 09:36:04 +0200 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2017-09-06 09:36:04 +0200 |
commit | 73a2e697d37d8e9f81b94b878729acf3c16c79d6 (patch) | |
tree | 125061d4bb8500c45c42ebb8b30395472d7bb485 | |
parent | f19a3b1c7d799f0f1937443e395bb5d605d5ad49 (diff) | |
parent | f8f1d215115b7691ba0bd56f788309c5f44491c2 (diff) |
Merge branch 'staging/tegra' into staging/masterstaging/master
-rw-r--r-- | tegra/Android.mk | 42 | ||||
-rw-r--r-- | tegra/Makefile.am | 8 | ||||
-rw-r--r-- | tegra/Makefile.sources | 10 | ||||
-rw-r--r-- | tegra/channel.c | 131 | ||||
-rw-r--r-- | tegra/fence.c | 65 | ||||
-rw-r--r-- | tegra/job.c | 175 | ||||
-rw-r--r-- | tegra/private.h | 91 | ||||
-rw-r--r-- | tegra/pushbuf.c | 204 | ||||
-rw-r--r-- | tegra/tegra.c | 344 | ||||
-rw-r--r-- | tegra/tegra.h | 69 | ||||
-rw-r--r-- | tests/tegra/.gitignore | 1 | ||||
-rw-r--r-- | tests/tegra/Makefile.am | 28 | ||||
-rw-r--r-- | tests/tegra/drm-test-tegra.c | 154 | ||||
-rw-r--r-- | tests/tegra/drm-test-tegra.h | 60 | ||||
-rw-r--r-- | tests/tegra/drm-test.c | 247 | ||||
-rw-r--r-- | tests/tegra/drm-test.h | 72 | ||||
-rw-r--r-- | tests/tegra/gr2d-fill.c | 155 | ||||
-rw-r--r-- | tests/tegra/openclose.c | 52 | ||||
-rw-r--r-- | tests/tegra/vic-syncpt.c | 109 |
19 files changed, 1798 insertions, 219 deletions
diff --git a/tegra/Android.mk b/tegra/Android.mk new file mode 100644 index 00000000..2d6881a3 --- /dev/null +++ b/tegra/Android.mk @@ -0,0 +1,42 @@ +# +# Copyright © 2015 NVIDIA Corporation +# +# 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 +# THE AUTHORS OR COPYRIGHT HOLDERS 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. +# + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +# Import variables LIBDRM_TEGRA_FILES, LIBDRM_TEGRA_H_FILES +include $(LOCAL_PATH)/Makefile.sources + +LOCAL_MODULE := libdrm_tegra +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(LIBDRM_TEGRA_FILES) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) + +LOCAL_CFLAGS := \ + -DHAVE_LIBDRM_ATOMIC_PRIMITIVES=1 + +LOCAL_SHARED_LIBRARIES := \ + libdrm + +include $(BUILD_SHARED_LIBRARY) diff --git a/tegra/Makefile.am b/tegra/Makefile.am index fb40be55..a51e2259 100644 --- a/tegra/Makefile.am +++ b/tegra/Makefile.am @@ -1,3 +1,5 @@ +include Makefile.sources + AM_CPPFLAGS = \ -I$(top_srcdir) \ -I$(top_srcdir)/include/drm @@ -11,12 +13,10 @@ 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 = \ - private.h \ - tegra.c +libdrm_tegra_la_SOURCES = $(LIBDRM_TEGRA_FILES) libdrm_tegraincludedir = ${includedir}/libdrm -libdrm_tegrainclude_HEADERS = tegra.h +libdrm_tegrainclude_HEADERS = $(LIBDRM_TEGRA_H_FILES) pkgconfigdir = @pkgconfigdir@ pkgconfig_DATA = libdrm_tegra.pc diff --git a/tegra/Makefile.sources b/tegra/Makefile.sources new file mode 100644 index 00000000..52a21c8b --- /dev/null +++ b/tegra/Makefile.sources @@ -0,0 +1,10 @@ +LIBDRM_TEGRA_FILES := \ + channel.c \ + fence.c \ + job.c \ + private.h \ + pushbuf.c \ + tegra.c + +LIBDRM_TEGRA_H_FILES := \ + tegra.h diff --git a/tegra/channel.c b/tegra/channel.c new file mode 100644 index 00000000..982d008c --- /dev/null +++ b/tegra/channel.c @@ -0,0 +1,131 @@ +/* + * Copyright © 2012, 2013 Thierry Reding + * Copyright © 2013 Erik Faye-Lund + * Copyright © 2014 NVIDIA Corporation + * + * 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 <string.h> + +#include <sys/ioctl.h> + +#include "private.h" + +static int drm_tegra_channel_setup(struct drm_tegra_channel *channel) +{ + struct drm_tegra *drm = channel->drm; + struct drm_tegra_get_syncpt args; + int err; + + memset(&args, 0, sizeof(args)); + args.context = channel->context; + args.index = 0; + + err = ioctl(drm->fd, DRM_IOCTL_TEGRA_GET_SYNCPT, &args); + if (err < 0) + return -errno; + + channel->syncpt = args.id; + + return 0; +} + +int drm_tegra_channel_open(struct drm_tegra_channel **channelp, + struct drm_tegra *drm, + enum drm_tegra_class client) +{ + struct drm_tegra_open_channel args; + struct drm_tegra_channel *channel; + enum host1x_class class; + int err; + + switch (client) { + case DRM_TEGRA_GR2D: + class = HOST1X_CLASS_GR2D; + break; + + case DRM_TEGRA_GR3D: + class = HOST1X_CLASS_GR3D; + break; + + case DRM_TEGRA_VIC: + class = HOST1X_CLASS_VIC; + break; + + default: + return -EINVAL; + } + + channel = calloc(1, sizeof(*channel)); + if (!channel) + return -ENOMEM; + + channel->drm = drm; + + memset(&args, 0, sizeof(args)); + args.client = class; + + err = ioctl(drm->fd, DRM_IOCTL_TEGRA_OPEN_CHANNEL, &args); + if (err < 0) { + free(channel); + return -errno; + } + + channel->context = args.context; + channel->class = class; + + err = drm_tegra_channel_setup(channel); + if (err < 0) { + free(channel); + return err; + } + + *channelp = channel; + + return 0; +} + +int drm_tegra_channel_close(struct drm_tegra_channel *channel) +{ + struct drm_tegra_close_channel args; + struct drm_tegra *drm; + int err; + + if (!channel) + return -EINVAL; + + drm = channel->drm; + + memset(&args, 0, sizeof(args)); + args.context = channel->context; + + err = ioctl(drm->fd, DRM_IOCTL_TEGRA_CLOSE_CHANNEL, &args); + if (err < 0) + return -errno; + + free(channel); + + return 0; +} diff --git a/tegra/fence.c b/tegra/fence.c new file mode 100644 index 00000000..210415f7 --- /dev/null +++ b/tegra/fence.c @@ -0,0 +1,65 @@ +/* + * Copyright © 2012, 2013 Thierry Reding + * Copyright © 2013 Erik Faye-Lund + * Copyright © 2014 NVIDIA Corporation + * + * 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 <string.h> + +#include <sys/ioctl.h> + +#include "private.h" + +int drm_tegra_fence_wait_timeout(struct drm_tegra_fence *fence, + unsigned long timeout) +{ + struct drm_tegra_syncpt_wait args; + int err; + + memset(&args, 0, sizeof(args)); + args.id = fence->syncpt; + args.thresh = fence->value; + args.timeout = timeout; + + while (true) { + err = ioctl(fence->drm->fd, DRM_IOCTL_TEGRA_SYNCPT_WAIT, &args); + if (err < 0) { + if (errno == EINTR) + continue; + + return -errno; + } + + break; + } + + return 0; +} + +void drm_tegra_fence_free(struct drm_tegra_fence *fence) +{ + free(fence); +} diff --git a/tegra/job.c b/tegra/job.c new file mode 100644 index 00000000..671e9a85 --- /dev/null +++ b/tegra/job.c @@ -0,0 +1,175 @@ +/* + * Copyright © 2012, 2013 Thierry Reding + * Copyright © 2013 Erik Faye-Lund + * Copyright © 2014 NVIDIA Corporation + * + * 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 <stdlib.h> +#include <string.h> + +#include <sys/ioctl.h> + +#include "private.h" + +drm_private +int drm_tegra_job_add_reloc(struct drm_tegra_job *job, + const struct drm_tegra_reloc *reloc) +{ + struct drm_tegra_reloc *relocs; + size_t size; + + size = (job->num_relocs + 1) * sizeof(*reloc); + + relocs = realloc(job->relocs, size); + if (!reloc) + return -ENOMEM; + + job->relocs = relocs; + + job->relocs[job->num_relocs++] = *reloc; + + return 0; +} + +drm_private +int drm_tegra_job_add_cmdbuf(struct drm_tegra_job *job, + const struct drm_tegra_cmdbuf *cmdbuf) +{ + struct drm_tegra_cmdbuf *cmdbufs; + size_t size; + + size = (job->num_cmdbufs + 1) * sizeof(*cmdbuf); + + cmdbufs = realloc(job->cmdbufs, size); + if (!cmdbufs) + return -ENOMEM; + + cmdbufs[job->num_cmdbufs++] = *cmdbuf; + job->cmdbufs = cmdbufs; + + return 0; +} + +int drm_tegra_job_new(struct drm_tegra_job **jobp, + struct drm_tegra_channel *channel) +{ + struct drm_tegra_job *job; + + job = calloc(1, sizeof(*job)); + if (!job) + return -ENOMEM; + + DRMINITLISTHEAD(&job->pushbufs); + job->channel = channel; + + *jobp = job; + + return 0; +} + +int drm_tegra_job_free(struct drm_tegra_job *job) +{ + struct drm_tegra_pushbuf_private *pushbuf; + + if (!job) + return -EINVAL; + + DRMLISTFOREACHENTRY(pushbuf, &job->pushbufs, list) + drm_tegra_pushbuf_free(&pushbuf->base); + + free(job->cmdbufs); + free(job->relocs); + free(job); + + return 0; +} + +int drm_tegra_job_submit(struct drm_tegra_job *job, + struct drm_tegra_fence **fencep) +{ + struct drm_tegra *drm = job->channel->drm; + struct drm_tegra_fence *fence = NULL; + struct drm_tegra_syncpt *syncpts; + struct drm_tegra_submit args; + int err; + + /* + * Make sure the current command stream buffer is queued for + * submission. + */ + err = drm_tegra_pushbuf_queue(job->pushbuf); + if (err < 0) + return err; + + job->pushbuf = NULL; + + if (fencep) { + fence = calloc(1, sizeof(*fence)); + if (!fence) + return -ENOMEM; + } + + syncpts = calloc(1, sizeof(*syncpts)); + if (!syncpts) { + free(fence); + return -ENOMEM; + } + + syncpts[0].id = job->syncpt; + syncpts[0].incrs = job->increments; + + memset(&args, 0, sizeof(args)); + args.context = job->channel->context; + args.num_syncpts = 1; + args.num_cmdbufs = job->num_cmdbufs; + args.num_relocs = job->num_relocs; + args.num_waitchks = 0; + args.waitchk_mask = 0; + args.timeout = 1000; + + args.syncpts = (uintptr_t)syncpts; + args.cmdbufs = (uintptr_t)job->cmdbufs; + args.relocs = (uintptr_t)job->relocs; + args.waitchks = 0; + + err = ioctl(drm->fd, DRM_IOCTL_TEGRA_SUBMIT, &args); + if (err < 0) { + free(syncpts); + free(fence); + return -errno; + } + + if (fence) { + fence->syncpt = job->syncpt; + fence->value = args.fence; + fence->drm = drm; + *fencep = fence; + } + + free(syncpts); + + return 0; +} diff --git a/tegra/private.h b/tegra/private.h index bb6c1a51..02e1b7bf 100644 --- a/tegra/private.h +++ b/tegra/private.h @@ -26,26 +26,99 @@ #define __DRM_TEGRA_PRIVATE_H__ 1 #include <stdbool.h> +#include <stddef.h> #include <stdint.h> +#include <libdrm_lists.h> #include <libdrm_macros.h> #include <xf86atomic.h> +#include "tegra_drm.h" #include "tegra.h" +#define container_of(ptr, type, member) ({ \ + const typeof(((type *)0)->member) *__mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); \ + }) + +#define align(offset, align) \ + (((offset) + (align) - 1) & ~((align) - 1)) + +enum host1x_class { + HOST1X_CLASS_HOST1X = 0x01, + HOST1X_CLASS_GR2D = 0x51, + HOST1X_CLASS_GR2D_SB = 0x52, + HOST1X_CLASS_VIC = 0x5d, + HOST1X_CLASS_GR3D = 0x60, +}; + struct drm_tegra { - bool close; - int fd; + bool close; + int fd; }; struct drm_tegra_bo { - struct drm_tegra *drm; - uint32_t handle; - uint32_t offset; - uint32_t flags; - uint32_t size; - atomic_t ref; - void *map; + struct drm_tegra *drm; + drmMMListHead list; + uint32_t handle; + uint32_t offset; + uint32_t flags; + uint32_t size; + atomic_t ref; + void *map; +}; + +struct drm_tegra_channel { + struct drm_tegra *drm; + enum host1x_class class; + uint64_t context; + uint32_t syncpt; +}; + +struct drm_tegra_fence { + struct drm_tegra *drm; + uint32_t syncpt; + uint32_t value; +}; + +struct drm_tegra_pushbuf_private { + struct drm_tegra_pushbuf base; + struct drm_tegra_job *job; + drmMMListHead list; + drmMMListHead bos; + + struct drm_tegra_bo *bo; + uint32_t *start; + uint32_t *end; +}; + +static inline struct drm_tegra_pushbuf_private * +drm_tegra_pushbuf(struct drm_tegra_pushbuf *pb) +{ + return container_of(pb, struct drm_tegra_pushbuf_private, base); +} + +int drm_tegra_pushbuf_queue(struct drm_tegra_pushbuf_private *pushbuf); + +struct drm_tegra_job { + struct drm_tegra_channel *channel; + + unsigned int increments; + uint32_t syncpt; + + struct drm_tegra_reloc *relocs; + unsigned int num_relocs; + + struct drm_tegra_cmdbuf *cmdbufs; + unsigned int num_cmdbufs; + + struct drm_tegra_pushbuf_private *pushbuf; + drmMMListHead pushbufs; }; +int drm_tegra_job_add_reloc(struct drm_tegra_job *job, + const struct drm_tegra_reloc *reloc); +int drm_tegra_job_add_cmdbuf(struct drm_tegra_job *job, + const struct drm_tegra_cmdbuf *cmdbuf); + #endif /* __DRM_TEGRA_PRIVATE_H__ */ diff --git a/tegra/pushbuf.c b/tegra/pushbuf.c new file mode 100644 index 00000000..e578a1f1 --- /dev/null +++ b/tegra/pushbuf.c @@ -0,0 +1,204 @@ +/* + * Copyright © 2012, 2013 Thierry Reding + * Copyright © 2013 Erik Faye-Lund + * Copyright © 2014 NVIDIA Corporation + * + * 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 <stdlib.h> +#include <string.h> + +#include "private.h" + +#define HOST1X_OPCODE_NONINCR(offset, count) \ + ((0x2 << 28) | (((offset) & 0xfff) << 16) | ((count) & 0xffff)) + +static inline unsigned long +drm_tegra_pushbuf_get_offset(struct drm_tegra_pushbuf *pushbuf) +{ + struct drm_tegra_pushbuf_private *priv = drm_tegra_pushbuf(pushbuf); + + return (unsigned long)pushbuf->ptr - (unsigned long)priv->start; +} + +drm_private +int drm_tegra_pushbuf_queue(struct drm_tegra_pushbuf_private *pushbuf) +{ + struct drm_tegra_cmdbuf cmdbuf; + int err; + + if (!pushbuf || !pushbuf->bo) + return 0; + + /* unmap buffer object since it won't be accessed anymore */ + drm_tegra_bo_unmap(pushbuf->bo); + + /* add buffer object as command buffers for this job */ + memset(&cmdbuf, 0, sizeof(cmdbuf)); + cmdbuf.words = pushbuf->base.ptr - pushbuf->start; + cmdbuf.handle = pushbuf->bo->handle; + cmdbuf.offset = 0; + + err = drm_tegra_job_add_cmdbuf(pushbuf->job, &cmdbuf); + if (err < 0) + return err; + + return 0; +} + +int drm_tegra_pushbuf_new(struct drm_tegra_pushbuf **pushbufp, + struct drm_tegra_job *job) +{ + struct drm_tegra_pushbuf_private *pushbuf; + + pushbuf = calloc(1, sizeof(*pushbuf)); + if (!pushbuf) + return -ENOMEM; + + DRMINITLISTHEAD(&pushbuf->list); + DRMINITLISTHEAD(&pushbuf->bos); + pushbuf->job = job; + + *pushbufp = &pushbuf->base; + + DRMLISTADD(&pushbuf->list, &job->pushbufs); + job->pushbuf = pushbuf; + + return 0; +} + +int drm_tegra_pushbuf_free(struct drm_tegra_pushbuf *pushbuf) +{ + struct drm_tegra_pushbuf_private *priv = drm_tegra_pushbuf(pushbuf); + struct drm_tegra_bo *bo, *tmp; + + if (!pushbuf) + return -EINVAL; + + drm_tegra_bo_unmap(priv->bo); + + DRMLISTFOREACHENTRYSAFE(bo, tmp, &priv->bos, list) + drm_tegra_bo_unref(priv->bo); + + DRMLISTDEL(&priv->list); + free(priv); + + return 0; +} + +/** + * drm_tegra_pushbuf_prepare() - prepare push buffer for a series of pushes + * @pushbuf: push buffer + * @words: maximum number of words in series of pushes to follow + */ +int drm_tegra_pushbuf_prepare(struct drm_tegra_pushbuf *pushbuf, + unsigned int words) +{ + struct drm_tegra_pushbuf_private *priv = drm_tegra_pushbuf(pushbuf); + struct drm_tegra_channel *channel = priv->job->channel; + struct drm_tegra_bo *bo; + void *ptr; + int err; + + if (priv->bo && (pushbuf->ptr + words < priv->end)) + return 0; + + /* + * Align to full pages, since buffer object allocations are page + * granular anyway. + */ + words = align(words, 1024); + + err = drm_tegra_bo_new(&bo, channel->drm, 0, words * sizeof(uint32_t)); + if (err < 0) + return err; + + err = drm_tegra_bo_map(bo, &ptr); + if (err < 0) { + drm_tegra_bo_unref(bo); + return err; + } + + /* queue current command stream buffer for submission */ + err = drm_tegra_pushbuf_queue(priv); + if (err < 0) { + drm_tegra_bo_unmap(bo); + drm_tegra_bo_unref(bo); + return err; + } + + DRMLISTADD(&bo->list, &priv->bos); + + priv->start = priv->base.ptr = ptr; + priv->end = priv->start + bo->size; + priv->bo = bo; + + return 0; +} + +int drm_tegra_pushbuf_relocate(struct drm_tegra_pushbuf *pushbuf, + struct drm_tegra_bo *target, + unsigned long offset, + unsigned long shift) +{ + struct drm_tegra_pushbuf_private *priv = drm_tegra_pushbuf(pushbuf); + struct drm_tegra_reloc reloc; + int err; + + memset(&reloc, 0, sizeof(reloc)); + reloc.cmdbuf.handle = priv->bo->handle; + reloc.cmdbuf.offset = drm_tegra_pushbuf_get_offset(pushbuf); + reloc.target.handle = target->handle; + reloc.target.offset = offset; + reloc.shift = shift; + + err = drm_tegra_job_add_reloc(priv->job, &reloc); + if (err < 0) + return err; + + *pushbuf->ptr++ = 0xdeadbeef; + + return 0; +} + +int drm_tegra_pushbuf_sync(struct drm_tegra_pushbuf *pushbuf, + enum drm_tegra_syncpt_cond cond) +{ + struct drm_tegra_pushbuf_private *priv = drm_tegra_pushbuf(pushbuf); + int err; + + if (cond >= DRM_TEGRA_SYNCPT_COND_MAX) + return -EINVAL; + + err = drm_tegra_pushbuf_prepare(pushbuf, 2); + if (err < 0) + return err; + + *pushbuf->ptr++ = HOST1X_OPCODE_NONINCR(0x0, 0x1); + *pushbuf->ptr++ = cond << 8 | priv->job->syncpt; + priv->job->increments++; + + return 0; +} diff --git a/tegra/tegra.c b/tegra/tegra.c index f7dc89ad..c3fbc9af 100644 --- a/tegra/tegra.c +++ b/tegra/tegra.c @@ -41,292 +41,294 @@ static void drm_tegra_bo_free(struct drm_tegra_bo *bo) { - struct drm_tegra *drm = bo->drm; - struct drm_gem_close args; + struct drm_tegra *drm = bo->drm; + struct drm_gem_close args; - if (bo->map) - munmap(bo->map, bo->size); + if (bo->map) + munmap(bo->map, bo->size); - memset(&args, 0, sizeof(args)); - args.handle = bo->handle; + memset(&args, 0, sizeof(args)); + args.handle = bo->handle; - drmIoctl(drm->fd, DRM_IOCTL_GEM_CLOSE, &args); + drmIoctl(drm->fd, DRM_IOCTL_GEM_CLOSE, &args); - free(bo); + DRMLISTDEL(&bo->list); + free(bo); } static int drm_tegra_wrap(struct drm_tegra **drmp, int fd, bool close) { - struct drm_tegra *drm; + struct drm_tegra *drm; - if (fd < 0 || !drmp) - return -EINVAL; + if (fd < 0 || !drmp) + return -EINVAL; - drm = calloc(1, sizeof(*drm)); - if (!drm) - return -ENOMEM; + drm = calloc(1, sizeof(*drm)); + if (!drm) + return -ENOMEM; - drm->close = close; - drm->fd = fd; + drm->close = close; + drm->fd = fd; - *drmp = drm; + *drmp = drm; - return 0; + return 0; } int drm_tegra_new(struct drm_tegra **drmp, int fd) { - bool supported = false; - drmVersionPtr version; + bool supported = false; + drmVersionPtr version; - version = drmGetVersion(fd); - if (!version) - return -ENOMEM; + version = drmGetVersion(fd); + if (!version) + return -ENOMEM; - if (!strncmp(version->name, "tegra", version->name_len)) - supported = true; + if (!strncmp(version->name, "tegra", version->name_len)) + supported = true; - drmFreeVersion(version); + drmFreeVersion(version); - if (!supported) - return -ENOTSUP; + if (!supported) + return -ENOTSUP; - return drm_tegra_wrap(drmp, fd, false); + return drm_tegra_wrap(drmp, fd, false); } void drm_tegra_close(struct drm_tegra *drm) { - if (!drm) - return; + if (!drm) + return; - if (drm->close) - close(drm->fd); + if (drm->close) + close(drm->fd); - free(drm); + free(drm); } int drm_tegra_bo_new(struct drm_tegra_bo **bop, struct drm_tegra *drm, - uint32_t flags, uint32_t size) + uint32_t flags, uint32_t size) { - struct drm_tegra_gem_create args; - struct drm_tegra_bo *bo; - int err; + struct drm_tegra_gem_create args; + struct drm_tegra_bo *bo; + int err; - if (!drm || size == 0 || !bop) - return -EINVAL; + if (!drm || size == 0 || !bop) + return -EINVAL; - bo = calloc(1, sizeof(*bo)); - if (!bo) - return -ENOMEM; + bo = calloc(1, sizeof(*bo)); + if (!bo) + return -ENOMEM; - atomic_set(&bo->ref, 1); - bo->flags = flags; - bo->size = size; - bo->drm = drm; + DRMINITLISTHEAD(&bo->list); + atomic_set(&bo->ref, 1); + bo->flags = flags; + bo->size = size; + bo->drm = drm; - memset(&args, 0, sizeof(args)); - args.flags = flags; - args.size = size; + memset(&args, 0, sizeof(args)); + args.flags = flags; + args.size = size; - err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_CREATE, &args, - sizeof(args)); - if (err < 0) { - err = -errno; - free(bo); - return err; - } + err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_CREATE, &args, + sizeof(args)); + if (err < 0) { + err = -errno; + free(bo); + return err; + } - bo->handle = args.handle; + bo->handle = args.handle; - *bop = bo; + *bop = bo; - return 0; + return 0; } int drm_tegra_bo_wrap(struct drm_tegra_bo **bop, struct drm_tegra *drm, - uint32_t handle, uint32_t flags, uint32_t size) + uint32_t handle, uint32_t flags, uint32_t size) { - struct drm_tegra_bo *bo; + struct drm_tegra_bo *bo; - if (!drm || !bop) - return -EINVAL; + if (!drm || !bop) + return -EINVAL; - bo = calloc(1, sizeof(*bo)); - if (!bo) - return -ENOMEM; + bo = calloc(1, sizeof(*bo)); + if (!bo) + return -ENOMEM; - atomic_set(&bo->ref, 1); - bo->handle = handle; - bo->flags = flags; - bo->size = size; - bo->drm = drm; + atomic_set(&bo->ref, 1); + bo->handle = handle; + bo->flags = flags; + bo->size = size; + bo->drm = drm; - *bop = bo; + *bop = bo; - return 0; + return 0; } struct drm_tegra_bo *drm_tegra_bo_ref(struct drm_tegra_bo *bo) { - if (bo) - atomic_inc(&bo->ref); + if (bo) + atomic_inc(&bo->ref); - return bo; + return bo; } void drm_tegra_bo_unref(struct drm_tegra_bo *bo) { - if (bo && atomic_dec_and_test(&bo->ref)) - drm_tegra_bo_free(bo); + if (bo && atomic_dec_and_test(&bo->ref)) + drm_tegra_bo_free(bo); } int drm_tegra_bo_get_handle(struct drm_tegra_bo *bo, uint32_t *handle) { - if (!bo || !handle) - return -EINVAL; + if (!bo || !handle) + return -EINVAL; - *handle = bo->handle; + *handle = bo->handle; - return 0; + return 0; } int drm_tegra_bo_map(struct drm_tegra_bo *bo, void **ptr) { - struct drm_tegra *drm = bo->drm; + struct drm_tegra *drm = bo->drm; - if (!bo->map) { - struct drm_tegra_gem_mmap args; - int err; + if (!bo->map) { + struct drm_tegra_gem_mmap args; + int err; - memset(&args, 0, sizeof(args)); - args.handle = bo->handle; + memset(&args, 0, sizeof(args)); + args.handle = bo->handle; - err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_MMAP, &args, - sizeof(args)); - if (err < 0) - return -errno; + err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_MMAP, &args, + sizeof(args)); + if (err < 0) + return -errno; - bo->offset = args.offset; + bo->offset = args.offset; - bo->map = mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, - drm->fd, bo->offset); - if (bo->map == MAP_FAILED) { - bo->map = NULL; - return -errno; - } - } + bo->map = mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, + drm->fd, bo->offset); + if (bo->map == MAP_FAILED) { + bo->map = NULL; + return -errno; + } + } - if (ptr) - *ptr = bo->map; + if (ptr) + *ptr = bo->map; - return 0; + return 0; } int drm_tegra_bo_unmap(struct drm_tegra_bo *bo) { - if (!bo) - return -EINVAL; + if (!bo) + return -EINVAL; - if (!bo->map) - return 0; + if (!bo->map) + return 0; - if (munmap(bo->map, bo->size)) - return -errno; + if (munmap(bo->map, bo->size)) + return -errno; - bo->map = NULL; + bo->map = NULL; - return 0; + return 0; } int drm_tegra_bo_get_flags(struct drm_tegra_bo *bo, uint32_t *flags) { - struct drm_tegra_gem_get_flags args; - struct drm_tegra *drm = bo->drm; - int err; + struct drm_tegra_gem_get_flags args; + struct drm_tegra *drm = bo->drm; + int err; - if (!bo) - return -EINVAL; + if (!bo) + return -EINVAL; - memset(&args, 0, sizeof(args)); - args.handle = bo->handle; + memset(&args, 0, sizeof(args)); + args.handle = bo->handle; - err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_GET_FLAGS, &args, - sizeof(args)); - if (err < 0) - return -errno; + err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_GET_FLAGS, &args, + sizeof(args)); + if (err < 0) + return -errno; - if (flags) - *flags = args.flags; + if (flags) + *flags = args.flags; - return 0; + return 0; } int drm_tegra_bo_set_flags(struct drm_tegra_bo *bo, uint32_t flags) { - struct drm_tegra_gem_get_flags args; - struct drm_tegra *drm = bo->drm; - int err; + struct drm_tegra_gem_get_flags args; + struct drm_tegra *drm = bo->drm; + int err; - if (!bo) - return -EINVAL; + if (!bo) + return -EINVAL; - memset(&args, 0, sizeof(args)); - args.handle = bo->handle; - args.flags = flags; + memset(&args, 0, sizeof(args)); + args.handle = bo->handle; + args.flags = flags; - err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_SET_FLAGS, &args, - sizeof(args)); - if (err < 0) - return -errno; + err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_SET_FLAGS, &args, + sizeof(args)); + if (err < 0) + return -errno; - return 0; + return 0; } int drm_tegra_bo_get_tiling(struct drm_tegra_bo *bo, - struct drm_tegra_bo_tiling *tiling) + struct drm_tegra_bo_tiling *tiling) { - struct drm_tegra_gem_get_tiling args; - struct drm_tegra *drm = bo->drm; - int err; + struct drm_tegra_gem_get_tiling args; + struct drm_tegra *drm = bo->drm; + int err; - if (!bo) - return -EINVAL; + if (!bo) + return -EINVAL; - memset(&args, 0, sizeof(args)); - args.handle = bo->handle; + memset(&args, 0, sizeof(args)); + args.handle = bo->handle; - err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_GET_TILING, &args, - sizeof(args)); - if (err < 0) - return -errno; + err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_GET_TILING, &args, + sizeof(args)); + if (err < 0) + return -errno; - if (tiling) { - tiling->mode = args.mode; - tiling->value = args.value; - } + if (tiling) { + tiling->mode = args.mode; + tiling->value = args.value; + } - return 0; + return 0; } int drm_tegra_bo_set_tiling(struct drm_tegra_bo *bo, - const struct drm_tegra_bo_tiling *tiling) + const struct drm_tegra_bo_tiling *tiling) { - struct drm_tegra_gem_set_tiling args; - struct drm_tegra *drm = bo->drm; - int err; + struct drm_tegra_gem_set_tiling args; + struct drm_tegra *drm = bo->drm; + int err; - if (!bo) - return -EINVAL; + if (!bo) + return -EINVAL; - memset(&args, 0, sizeof(args)); - args.handle = bo->handle; - args.mode = tiling->mode; - args.value = tiling->value; + memset(&args, 0, sizeof(args)); + args.handle = bo->handle; + args.mode = tiling->mode; + args.value = tiling->value; - err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_SET_TILING, &args, - sizeof(args)); - if (err < 0) - return -errno; + err = drmCommandWriteRead(drm->fd, DRM_TEGRA_GEM_SET_TILING, &args, + sizeof(args)); + if (err < 0) + return -errno; - return 0; + return 0; } diff --git a/tegra/tegra.h b/tegra/tegra.h index 31b0995a..20dd1bc9 100644 --- a/tegra/tegra.h +++ b/tegra/tegra.h @@ -28,6 +28,14 @@ #include <stdint.h> #include <stdlib.h> +#include <tegra_drm.h> + +enum drm_tegra_class { + DRM_TEGRA_GR2D, + DRM_TEGRA_GR3D, + DRM_TEGRA_VIC, +}; + struct drm_tegra_bo; struct drm_tegra; @@ -35,9 +43,9 @@ int drm_tegra_new(struct drm_tegra **drmp, int fd); void drm_tegra_close(struct drm_tegra *drm); int drm_tegra_bo_new(struct drm_tegra_bo **bop, struct drm_tegra *drm, - uint32_t flags, uint32_t size); + uint32_t flags, uint32_t size); int drm_tegra_bo_wrap(struct drm_tegra_bo **bop, struct drm_tegra *drm, - uint32_t handle, uint32_t flags, uint32_t size); + uint32_t handle, uint32_t flags, uint32_t size); struct drm_tegra_bo *drm_tegra_bo_ref(struct drm_tegra_bo *bo); void drm_tegra_bo_unref(struct drm_tegra_bo *bo); int drm_tegra_bo_get_handle(struct drm_tegra_bo *bo, uint32_t *handle); @@ -48,13 +56,62 @@ int drm_tegra_bo_get_flags(struct drm_tegra_bo *bo, uint32_t *flags); int drm_tegra_bo_set_flags(struct drm_tegra_bo *bo, uint32_t flags); struct drm_tegra_bo_tiling { - uint32_t mode; - uint32_t value; + uint32_t mode; + uint32_t value; }; int drm_tegra_bo_get_tiling(struct drm_tegra_bo *bo, - struct drm_tegra_bo_tiling *tiling); + struct drm_tegra_bo_tiling *tiling); int drm_tegra_bo_set_tiling(struct drm_tegra_bo *bo, - const struct drm_tegra_bo_tiling *tiling); + const struct drm_tegra_bo_tiling *tiling); + +struct drm_tegra_channel; +struct drm_tegra_job; + +struct drm_tegra_pushbuf { + uint32_t *ptr; +}; + +struct drm_tegra_fence; + +enum drm_tegra_syncpt_cond { + DRM_TEGRA_SYNCPT_COND_IMMEDIATE, + DRM_TEGRA_SYNCPT_COND_OP_DONE, + DRM_TEGRA_SYNCPT_COND_RD_DONE, + DRM_TEGRA_SYNCPT_COND_WR_SAFE, + DRM_TEGRA_SYNCPT_COND_MAX, +}; + +int drm_tegra_channel_open(struct drm_tegra_channel **channelp, + struct drm_tegra *drm, + enum drm_tegra_class client); +int drm_tegra_channel_close(struct drm_tegra_channel *channel); + +int drm_tegra_job_new(struct drm_tegra_job **jobp, + struct drm_tegra_channel *channel); +int drm_tegra_job_free(struct drm_tegra_job *job); +int drm_tegra_job_submit(struct drm_tegra_job *job, + struct drm_tegra_fence **fencep); + +int drm_tegra_pushbuf_new(struct drm_tegra_pushbuf **pushbufp, + struct drm_tegra_job *job); +int drm_tegra_pushbuf_free(struct drm_tegra_pushbuf *pushbuf); +int drm_tegra_pushbuf_prepare(struct drm_tegra_pushbuf *pushbuf, + unsigned int words); +int drm_tegra_pushbuf_relocate(struct drm_tegra_pushbuf *pushbuf, + struct drm_tegra_bo *target, + unsigned long offset, + unsigned long shift); +int drm_tegra_pushbuf_sync(struct drm_tegra_pushbuf *pushbuf, + enum drm_tegra_syncpt_cond cond); + +int drm_tegra_fence_wait_timeout(struct drm_tegra_fence *fence, + unsigned long timeout); +void drm_tegra_fence_free(struct drm_tegra_fence *fence); + +static inline int drm_tegra_fence_wait(struct drm_tegra_fence *fence) +{ + return drm_tegra_fence_wait_timeout(fence, -1); +} #endif /* __DRM_TEGRA_H__ */ diff --git a/tests/tegra/.gitignore b/tests/tegra/.gitignore index 5c5216c5..5e0b5c60 100644 --- a/tests/tegra/.gitignore +++ b/tests/tegra/.gitignore @@ -1 +1,2 @@ +gr2d-fill openclose diff --git a/tests/tegra/Makefile.am b/tests/tegra/Makefile.am index 8e625c8f..3fc92b79 100644 --- a/tests/tegra/Makefile.am +++ b/tests/tegra/Makefile.am @@ -5,9 +5,31 @@ AM_CPPFLAGS = \ AM_CFLAGS = $(WARN_CFLAGS) +noinst_LTLIBRARIES = libdrm-test.la +libdrm_test_la_SOURCES = \ + drm-test.c \ + drm-test.h \ + drm-test-tegra.c \ + drm-test-tegra.h + +libdrm_test_la_LIBADD = \ + ../../libdrm.la + LDADD = \ ../../tegra/libdrm_tegra.la \ - ../../libdrm.la + ../../libdrm.la \ + libdrm-test.la + +TESTS = \ + openclose \ + gr2d-fill \ + vic-syncpt -noinst_PROGRAMS = \ - openclose +if HAVE_INSTALL_TESTS +testdir = $(libexecdir)/libdrm/tests/tegra +test_PROGRAMS = \ + $(TESTS) +else +noinst_PROGRAMS = $(TESTS) +check_PROGRAMS = $(TESTS) +endif diff --git a/tests/tegra/drm-test-tegra.c b/tests/tegra/drm-test-tegra.c new file mode 100644 index 00000000..21d8b47f --- /dev/null +++ b/tests/tegra/drm-test-tegra.c @@ -0,0 +1,154 @@ +/* + * Copyright © 2014 NVIDIA Corporation + * + * 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 <stdio.h> + +#include "drm-test-tegra.h" +#include "tegra.h" + +int drm_tegra_gr2d_open(struct drm_tegra_gr2d **gr2dp, struct drm_tegra *drm) +{ + struct drm_tegra_gr2d *gr2d; + int err; + + gr2d = calloc(1, sizeof(*gr2d)); + if (!gr2d) + return -ENOMEM; + + gr2d->drm = drm; + + err = drm_tegra_channel_open(&gr2d->channel, drm, DRM_TEGRA_GR2D); + if (err < 0) { + free(gr2d); + return err; + } + + *gr2dp = gr2d; + + return 0; +} + +int drm_tegra_gr2d_close(struct drm_tegra_gr2d *gr2d) +{ + if (!gr2d) + return -EINVAL; + + drm_tegra_channel_close(gr2d->channel); + free(gr2d); + + return 0; +} + +int drm_tegra_gr2d_fill(struct drm_tegra_pushbuf *pushbuf, + struct drm_framebuffer *fb, unsigned int x, + unsigned int y, unsigned int width, + unsigned int height, uint32_t color) +{ + struct drm_tegra_bo *bo = fb->data; + int err; + + err = drm_tegra_pushbuf_prepare(pushbuf, 32); + if (err < 0) + return err; + + *pushbuf->ptr++ = HOST1X_OPCODE_SETCL(0, HOST1X_CLASS_GR2D, 0); + + *pushbuf->ptr++ = HOST1X_OPCODE_MASK(0x9, 0x9); + *pushbuf->ptr++ = 0x0000003a; + *pushbuf->ptr++ = 0x00000000; + + *pushbuf->ptr++ = HOST1X_OPCODE_MASK(0x1e, 0x7); + *pushbuf->ptr++ = 0x00000000; + *pushbuf->ptr++ = (2 << 16) | (1 << 6) | (1 << 2); + *pushbuf->ptr++ = 0x000000cc; + + *pushbuf->ptr++ = HOST1X_OPCODE_MASK(0x2b, 0x9); + + /* relocate destination buffer */ + err = drm_tegra_pushbuf_relocate(pushbuf, bo, 0, 0); + if (err < 0) { + fprintf(stderr, "failed to relocate buffer object: %d\n", err); + return err; + } + + *pushbuf->ptr++ = fb->pitch; + + *pushbuf->ptr++ = HOST1X_OPCODE_NONINCR(0x35, 1); + *pushbuf->ptr++ = color; + + *pushbuf->ptr++ = HOST1X_OPCODE_NONINCR(0x46, 1); + *pushbuf->ptr++ = 0x00000000; + + *pushbuf->ptr++ = HOST1X_OPCODE_MASK(0x38, 0x5); + *pushbuf->ptr++ = height << 16 | width; + *pushbuf->ptr++ = y << 16 | x; + + return 0; +} + +int drm_tegra_gr2d_fill_simple(struct drm_tegra_gr2d *gr2d, + struct drm_framebuffer *fb, unsigned int x, + unsigned int y, unsigned int width, + unsigned int height, uint32_t color) +{ + struct drm_tegra_pushbuf *pushbuf; + struct drm_tegra_fence *fence; + struct drm_tegra_job *job; + int err; + + err = drm_tegra_job_new(&job, gr2d->channel); + if (err < 0) + return err; + + err = drm_tegra_pushbuf_new(&pushbuf, job); + if (err < 0) + return err; + + err = drm_tegra_gr2d_fill(pushbuf, fb, x, y, width, height, color); + if (err < 0) { + fprintf(stderr, "failed to compose 2D fill: %d\n", err); + return err; + } + + err = drm_tegra_job_submit(job, &fence); + if (err < 0) { + fprintf(stderr, "failed to submit job: %d\n", err); + return err; + } + + err = drm_tegra_fence_wait(fence); + if (err < 0) { + fprintf(stderr, "failed to wait for fence: %d\n", err); + return err; + } + + drm_tegra_fence_free(fence); + drm_tegra_pushbuf_free(pushbuf); + drm_tegra_job_free(job); + + return 0; +} diff --git a/tests/tegra/drm-test-tegra.h b/tests/tegra/drm-test-tegra.h new file mode 100644 index 00000000..501e3b6b --- /dev/null +++ b/tests/tegra/drm-test-tegra.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2014 NVIDIA Corporation + * + * 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 TEGRA_DRM_TEST_TEGRA_H +#define TEGRA_DRM_TEST_TEGRA_H + +#include "drm-test.h" +#include "tegra.h" + +#define HOST1X_OPCODE_SETCL(offset, classid, mask) \ + ((0x0 << 28) | (((offset) & 0xfff) << 16) | (((classid) & 0x3ff) << 6) | ((mask) & 0x3f)) +#define HOST1X_OPCODE_INCR(offset, count) \ + ((0x1 << 28) | (((offset) & 0xfff) << 16) | ((count) & 0xffff)) +#define HOST1X_OPCODE_NONINCR(offset, count) \ + ((0x2 << 28) | (((offset) & 0xfff) << 16) | ((count) & 0xffff)) +#define HOST1X_OPCODE_MASK(offset, mask) \ + ((0x3 << 28) | (((offset) & 0xfff) << 16) | ((mask) & 0xffff)) +#define HOST1X_OPCODE_IMM(offset, data) \ + ((0x4 << 28) | (((offset) & 0xfff) << 16) | ((data) & 0xffff)) +#define HOST1X_OPCODE_EXTEND(subop, value) \ + ((0xe << 28) | (((subop) & 0xf) << 24) | ((value) & 0xffffff)) + +#define HOST1X_CLASS_GR2D 0x51 + +struct drm_tegra_gr2d { + struct drm_tegra *drm; + struct drm_tegra_channel *channel; +}; + +int drm_tegra_gr2d_open(struct drm_tegra_gr2d **gr2dp, struct drm_tegra *drm); +int drm_tegra_gr2d_close(struct drm_tegra_gr2d *gr2d); +int drm_tegra_gr2d_fill_simple(struct drm_tegra_gr2d *gr2d, + struct drm_framebuffer *fb, unsigned int x, + unsigned int y, unsigned int width, + unsigned int height, uint32_t color); +int drm_tegra_gr2d_fill(struct drm_tegra_pushbuf *pushbuf, + struct drm_framebuffer *fb, unsigned int x, + unsigned int y, unsigned int width, + unsigned int height, uint32_t color); + +#endif diff --git a/tests/tegra/drm-test.c b/tests/tegra/drm-test.c new file mode 100644 index 00000000..4ebc8ee4 --- /dev/null +++ b/tests/tegra/drm-test.c @@ -0,0 +1,247 @@ +/* + * Copyright © 2014 NVIDIA Corporation + * + * 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 <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/ioctl.h> + +#include "xf86drm.h" +#include "xf86drmMode.h" +#include "drm_fourcc.h" + +#include "drm-test.h" + +static int drm_screen_probe_connector(struct drm_screen *screen, + drmModeConnectorPtr connector) +{ + drmModeEncoderPtr encoder; + drmModeCrtcPtr crtc; + drmModeFBPtr fb; + + encoder = drmModeGetEncoder(screen->fd, connector->encoder_id); + if (!encoder) + return -ENODEV; + + crtc = drmModeGetCrtc(screen->fd, encoder->crtc_id); + if (!crtc) { + drmModeFreeEncoder(encoder); + return -ENODEV; + } + + screen->old_fb = crtc->buffer_id; + + fb = drmModeGetFB(screen->fd, crtc->buffer_id); + if (!fb) { + /* TODO: create new framebuffer */ + drmModeFreeEncoder(encoder); + drmModeFreeCrtc(crtc); + return -ENOSYS; + } + + screen->connector = connector->connector_id; + screen->old_fb = crtc->buffer_id; + screen->crtc = encoder->crtc_id; + /* TODO: check crtc->mode_valid */ + screen->mode = crtc->mode; + + screen->width = fb->width; + screen->height = fb->height; + screen->pitch = fb->pitch; + screen->depth = fb->depth; + screen->bpp = fb->bpp; + + drmModeFreeEncoder(encoder); + drmModeFreeCrtc(crtc); + drmModeFreeFB(fb); + + return 0; +} + +int drm_screen_open(struct drm_screen **screenp, int fd) +{ + drmModeConnectorPtr connector; + struct drm_screen *screen; + bool found = false; + drmModeResPtr res; + int err, i; + + if (!screenp || fd < 0) + return -EINVAL; + + screen = calloc(1, sizeof(*screen)); + if (!screen) + return -ENOMEM; + + screen->format = DRM_FORMAT_XRGB8888; + screen->fd = fd; + + res = drmModeGetResources(fd); + if (!res) { + free(screen); + return -ENOMEM; + } + + for (i = 0; i < res->count_connectors; i++) { + connector = drmModeGetConnector(fd, res->connectors[i]); + if (!connector) + continue; + + if (connector->connection != DRM_MODE_CONNECTED) { + drmModeFreeConnector(connector); + continue; + } + + err = drm_screen_probe_connector(screen, connector); + if (err < 0) { + drmModeFreeConnector(connector); + continue; + } + + drmModeFreeConnector(connector); + found = true; + break; + } + + drmModeFreeResources(res); + + if (!found) { + free(screen); + return -ENODEV; + } + + *screenp = screen; + + return 0; +} + +int drm_screen_close(struct drm_screen *screen) +{ + int err; + + err = drmModeSetCrtc(screen->fd, screen->crtc, screen->old_fb, 0, 0, + &screen->connector, 1, &screen->mode); + if (err < 0) { + fprintf(stderr, "drmModeSetCrtc() failed: %m\n"); + return -errno; + } + + free(screen); + + return 0; +} + +int drm_framebuffer_new(struct drm_framebuffer **fbp, + struct drm_screen *screen, uint32_t handle, + unsigned int width, unsigned int height, + unsigned int pitch, uint32_t format, + void *data) +{ + struct drm_framebuffer *fb; + uint32_t handles[4]; + uint32_t pitches[4]; + uint32_t offsets[4]; + int err; + + fb = calloc(1, sizeof(*fb)); + if (!fb) + return -ENOMEM; + + fb->fd = screen->fd; + fb->width = width; + fb->height = height; + fb->pitch = pitch; + fb->format = format; + fb->data = data; + + handles[0] = handle; + pitches[0] = pitch; + offsets[0] = 0; + + err = drmModeAddFB2(screen->fd, width, height, format, handles, + pitches, offsets, &fb->handle, 0); + if (err < 0) + return -errno; + + *fbp = fb; + + return 0; +} + +int drm_framebuffer_free(struct drm_framebuffer *fb) +{ + int err; + + err = drmModeRmFB(fb->fd, fb->handle); + if (err < 0) + return -errno; + + free(fb); + + return 0; +} + +int drm_screen_set_framebuffer(struct drm_screen *screen, + struct drm_framebuffer *fb) +{ + int err; + + err = drmModeSetCrtc(screen->fd, screen->crtc, fb->handle, 0, 0, + &screen->connector, 1, &screen->mode); + if (err < 0) + return -errno; + + return 0; +} + +int drm_open(const char *path) +{ + int fd, err; + + fd = open(path, O_RDWR); + if (fd < 0) + return -errno; + + err = drmSetMaster(fd); + if (err < 0) { + close(fd); + return -errno; + } + + return fd; +} + +void drm_close(int fd) +{ + drmDropMaster(fd); + close(fd); +} diff --git a/tests/tegra/drm-test.h b/tests/tegra/drm-test.h new file mode 100644 index 00000000..f11aed42 --- /dev/null +++ b/tests/tegra/drm-test.h @@ -0,0 +1,72 @@ +/* + * Copyright © 2014 NVIDIA Corporation + * + * 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 TEGRA_DRM_TEST_H +#define TEGRA_DRM_TEST_H + +#include <stdint.h> +#include <stdlib.h> + +#include "xf86drmMode.h" + +struct drm_screen { + int fd; + + unsigned int width; + unsigned int height; + unsigned int pitch; + unsigned int depth; + unsigned int bpp; + + drmModeModeInfo mode; + uint32_t connector; + uint32_t old_fb; + uint32_t format; + uint32_t crtc; +}; + +struct drm_framebuffer { + unsigned int width; + unsigned int height; + unsigned int pitch; + uint32_t format; + uint32_t handle; + void *data; + int fd; +}; + +int drm_screen_open(struct drm_screen **screenp, int fd); +int drm_screen_close(struct drm_screen *screen); +int drm_screen_set_framebuffer(struct drm_screen *screen, + struct drm_framebuffer *fb); + +int drm_framebuffer_new(struct drm_framebuffer **fbp, + struct drm_screen *screen, uint32_t handle, + unsigned int width, unsigned int height, + unsigned int pitch, uint32_t format, + void *data); +int drm_framebuffer_free(struct drm_framebuffer *fb); + +int drm_open(const char *path); +void drm_close(int fd); + +#endif diff --git a/tests/tegra/gr2d-fill.c b/tests/tegra/gr2d-fill.c new file mode 100644 index 00000000..990cd470 --- /dev/null +++ b/tests/tegra/gr2d-fill.c @@ -0,0 +1,155 @@ +/* + * Copyright © 2014 NVIDIA Corporation + * + * 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 <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/ioctl.h> + +#include "xf86drm.h" +#include "xf86drmMode.h" +#include "drm_fourcc.h" + +#include "drm-test-tegra.h" +#include "tegra.h" + +static const char default_device[] = "/dev/dri/card0"; + +int main(int argc, char *argv[]) +{ + uint32_t format = DRM_FORMAT_XRGB8888; + struct drm_tegra_gr2d *gr2d; + struct drm_framebuffer *fb; + struct drm_screen *screen; + unsigned int pitch, size; + struct drm_tegra_bo *bo; + struct drm_tegra *drm; + const char *device; + uint32_t handle; + int fd, err; + void *ptr; + + if (argc < 2) + device = default_device; + else + device = argv[1]; + + fd = drm_open(device); + if (fd < 0) { + fprintf(stderr, "failed to open DRM device %s: %s\n", device, + strerror(errno)); + return 1; + } + + err = drm_screen_open(&screen, fd); + if (err < 0) { + fprintf(stderr, "failed to open screen: %s\n", strerror(-err)); + return 1; + } + + err = drm_tegra_new(&drm, fd); + if (err < 0) { + fprintf(stderr, "failed to create Tegra DRM context: %s\n", + strerror(-err)); + return 1; + } + + err = drm_tegra_gr2d_open(&gr2d, drm); + if (err < 0) { + fprintf(stderr, "failed to open gr2d channel: %s\n", + strerror(-err)); + return 1; + } + + pitch = screen->width * screen->bpp / 8; + size = pitch * screen->height; + + err = drm_tegra_bo_new(&bo, drm, 0, size); + if (err < 0) { + fprintf(stderr, "failed to create buffer object: %s\n", + strerror(-err)); + return 1; + } + + err = drm_tegra_bo_get_handle(bo, &handle); + if (err < 0) { + fprintf(stderr, "failed to get handle to buffer object: %s\n", + strerror(-err)); + return 1; + } + + err = drm_tegra_bo_map(bo, &ptr); + if (err < 0) { + fprintf(stderr, "failed to map buffer object: %s\n", + strerror(-err)); + return 1; + } + + memset(ptr, 0xff, size); + + err = drm_framebuffer_new(&fb, screen, handle, screen->width, + screen->height, pitch, format, bo); + if (err < 0) { + fprintf(stderr, "failed to create framebuffer: %s\n", + strerror(-err)); + return 1; + } + + err = drm_screen_set_framebuffer(screen, fb); + if (err < 0) { + fprintf(stderr, "failed to display framebuffer: %s\n", + strerror(-err)); + return 1; + } + + sleep(1); + + err = drm_tegra_gr2d_fill_simple(gr2d, fb, fb->width / 4, + fb->height / 4, fb->width / 2, + fb->height / 2, 0x00000000); + if (err < 0) { + fprintf(stderr, "failed to fill rectangle: %s\n", + strerror(-err)); + return 1; + } + + sleep(1); + + drm_framebuffer_free(fb); + drm_tegra_bo_unref(bo); + drm_tegra_gr2d_close(gr2d); + drm_tegra_close(drm); + drm_screen_close(screen); + drm_close(fd); + + return 0; +} diff --git a/tests/tegra/openclose.c b/tests/tegra/openclose.c index 881d8aa4..08da41a6 100644 --- a/tests/tegra/openclose.c +++ b/tests/tegra/openclose.c @@ -35,37 +35,37 @@ static const char default_device[] = "/dev/dri/card0"; int main(int argc, char *argv[]) { - struct drm_tegra *tegra; - drmVersionPtr version; - const char *device; - int err, fd; + struct drm_tegra *tegra; + drmVersionPtr version; + const char *device; + int err, fd; - if (argc < 2) - device = default_device; - else - device = argv[1]; + if (argc < 2) + device = default_device; + else + device = argv[1]; - fd = open(device, O_RDWR); - if (fd < 0) - return 1; + fd = open(device, O_RDWR); + if (fd < 0) + return 1; - version = drmGetVersion(fd); - if (version) { - printf("Version: %d.%d.%d\n", version->version_major, - version->version_minor, version->version_patchlevel); - printf(" Name: %s\n", version->name); - printf(" Date: %s\n", version->date); - printf(" Description: %s\n", version->desc); + version = drmGetVersion(fd); + if (version) { + printf("Version: %d.%d.%d\n", version->version_major, + version->version_minor, version->version_patchlevel); + printf(" Name: %s\n", version->name); + printf(" Date: %s\n", version->date); + printf(" Description: %s\n", version->desc); - drmFreeVersion(version); - } + drmFreeVersion(version); + } - err = drm_tegra_new(&tegra, fd); - if (err < 0) - return 1; + err = drm_tegra_new(&tegra, fd); + if (err < 0) + return 1; - drm_tegra_close(tegra); - close(fd); + drm_tegra_close(tegra); + close(fd); - return 0; + return 0; } diff --git a/tests/tegra/vic-syncpt.c b/tests/tegra/vic-syncpt.c new file mode 100644 index 00000000..91a6baef --- /dev/null +++ b/tests/tegra/vic-syncpt.c @@ -0,0 +1,109 @@ +/* + * Copyright © 2014 NVIDIA Corporation + * + * 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 <stdio.h> +#include <string.h> + +#include "drm-test-tegra.h" +#include "tegra.h" + +static const char default_device[] = "/dev/dri/card0"; + +int main(int argc, char *argv[]) +{ + struct drm_tegra_channel *channel; + struct drm_tegra_pushbuf *pushbuf; + struct drm_tegra_fence *fence; + struct drm_tegra_job *job; + struct drm_tegra *drm; + const char *device; + int fd, err; + + if (argc < 2) + device = default_device; + else + device = argv[1]; + + fd = drm_open(device); + if (fd < 0) { + fprintf(stderr, "failed to open DRM device %s: %s\n", device, + strerror(errno)); + return 1; + } + + err = drm_tegra_new(&drm, fd); + if (err < 0) { + fprintf(stderr, "failed to create Tegra DRM context: %s\n", + strerror(-err)); + return 1; + } + + err = drm_tegra_channel_open(&channel, drm, DRM_TEGRA_VIC); + if (err < 0) { + fprintf(stderr, "failed to open channel to VIC: %s\n", + strerror(-err)); + return 1; + } + + err = drm_tegra_job_new(&job, channel); + if (err < 0) + return 1; + + err = drm_tegra_pushbuf_new(&pushbuf, job); + if (err < 0) + return 1; + + err = drm_tegra_pushbuf_prepare(pushbuf, 4); + if (err < 0) + return 1; + + *pushbuf->ptr++ = HOST1X_OPCODE_SETCL(0, HOST1X_CLASS_GR2D, 0); + + err = drm_tegra_pushbuf_sync(pushbuf, DRM_TEGRA_SYNCPT_COND_OP_DONE); + if (err < 0) + return 1; + + err = drm_tegra_job_submit(job, &fence); + if (err < 0) { + return 1; + } + + err = drm_tegra_fence_wait(fence); + if (err < 0) { + return 1; + } + + drm_tegra_fence_free(fence); + drm_tegra_pushbuf_free(pushbuf); + drm_tegra_job_free(job); + + drm_tegra_channel_close(channel); + drm_tegra_close(drm); + drm_close(fd); + + return 0; +} |