From: Thierry Reding <treding@xxxxxxxxxx> These functions can be used to open channels to engines, manage job submissions, create push buffers to store command streams in and wait until jobs have been completed. Signed-off-by: Thierry Reding <treding@xxxxxxxxxx> --- tegra/Makefile.am | 4 ++ tegra/channel.c | 127 +++++++++++++++++++++++++++++++++++++++++ tegra/fence.c | 72 +++++++++++++++++++++++ tegra/job.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tegra/private.h | 59 +++++++++++++++++++ tegra/pushbuf.c | 137 ++++++++++++++++++++++++++++++++++++++++++++ tegra/tegra.h | 52 +++++++++++++++++ 7 files changed, 618 insertions(+) create mode 100644 tegra/channel.c create mode 100644 tegra/fence.c create mode 100644 tegra/job.c create mode 100644 tegra/pushbuf.c diff --git a/tegra/Makefile.am b/tegra/Makefile.am index 1b83145b120d..c73587e8661e 100644 --- a/tegra/Makefile.am +++ b/tegra/Makefile.am @@ -11,6 +11,10 @@ libdrm_tegra_la_LDFLAGS = -version-number 0:0:0 -no-undefined libdrm_tegra_la_LIBADD = ../libdrm.la @PTHREADSTUBS_LIBS@ libdrm_tegra_la_SOURCES = \ + channel.c \ + fence.c \ + job.c \ + pushbuf.c \ tegra.c libdrm_tegraincludedir = ${includedir}/libdrm diff --git a/tegra/channel.c b/tegra/channel.c new file mode 100644 index 000000000000..03cce30e98b9 --- /dev/null +++ b/tegra/channel.c @@ -0,0 +1,127 @@ +/* + * 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 "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; +} + +drm_public +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; + + 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; +} + +drm_public +int drm_tegra_channel_close(struct drm_tegra_channel *channel) +{ + struct drm_tegra_open_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 000000000000..6af60500c5f1 --- /dev/null +++ b/tegra/fence.c @@ -0,0 +1,72 @@ +/* + * 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 "private.h" + +drm_public +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; + + drmMsg("DRM_IOCTL_TEGRA_SYNCPT_WAIT: %d\n", -errno); + return -errno; + } + + break; + } + + return 0; +} + +drm_public +int drm_tegra_fence_wait(struct drm_tegra_fence *fence) +{ + return drm_tegra_fence_wait_timeout(fence, -1); +} + +drm_public +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 000000000000..506164cec95e --- /dev/null +++ b/tegra/job.c @@ -0,0 +1,167 @@ +/* + * 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" + +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_public +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; +} + +drm_public +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->relocs); + free(job); + + return 0; +} + +drm_public +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_pushbuf_private *pushbuf; + struct drm_tegra_fence *fence = NULL; + struct drm_tegra_cmdbuf *cmdbufs; + struct drm_tegra_syncpt *syncpts; + struct drm_tegra_submit args; + unsigned int i; + int err; + + if (fencep) { + fence = calloc(1, sizeof(*fence)); + if (!fence) + return -ENOMEM; + } + + cmdbufs = calloc(job->num_pushbufs, sizeof(*cmdbufs)); + if (!cmdbufs) { + free(fence); + return -ENOMEM; + } + + DRMLISTFOREACHENTRY(pushbuf, &job->pushbufs, list) { + struct drm_tegra_cmdbuf *cmdbuf = &cmdbufs[i]; + + cmdbuf->handle = pushbuf->bo->handle; + cmdbuf->offset = pushbuf->offset; + cmdbuf->words = pushbuf->base.ptr - pushbuf->start; + } + + syncpts = calloc(1, sizeof(*syncpts)); + if (!syncpts) { + free(cmdbufs); + 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_pushbufs; + 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)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(cmdbufs); + free(fence); + return -errno; + } + + if (fence) { + fence->syncpt = job->syncpt; + fence->value = args.fence; + fence->drm = drm; + } + + if (fencep) + *fencep = fence; + + free(syncpts); + free(cmdbufs); + + return 0; +} diff --git a/tegra/private.h b/tegra/private.h index ec69295c2cf8..3a72e9962f53 100644 --- a/tegra/private.h +++ b/tegra/private.h @@ -26,10 +26,13 @@ #define __DRM_TEGRA_PRIVATE_H__ 1 #include <stdbool.h> +#include <stddef.h> #include <stdint.h> +#include <libdrm_lists.h> #include <xf86atomic.h> +#include "tegra_drm.h" #include "tegra.h" #if defined(HAVE_VISIBILITY) @@ -40,6 +43,18 @@ # define drm_public #endif +#define container_of(ptr, type, member) ({ \ + const typeof(((type *)0)->member) *__mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); \ + }) + +enum host1x_class { + HOST1X_CLASS_HOST1X = 0x01, + HOST1X_CLASS_GR2D = 0x51, + HOST1X_CLASS_GR2D_SB = 0x52, + HOST1X_CLASS_GR3D = 0x60, +}; + struct drm_tegra { bool close; int fd; @@ -55,4 +70,48 @@ struct drm_tegra_bo { 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; + struct drm_tegra_bo *bo; + unsigned long offset; + drmMMListHead list; + uint32_t *start; +}; + +static inline struct drm_tegra_pushbuf_private * +pushbuf_priv(struct drm_tegra_pushbuf *pb) +{ + return container_of(pb, struct drm_tegra_pushbuf_private, base); +} + +struct drm_tegra_job { + struct drm_tegra_channel *channel; + + unsigned int increments; + uint32_t syncpt; + + struct drm_tegra_reloc *relocs; + unsigned int num_relocs; + + unsigned int num_pushbufs; + drmMMListHead pushbufs; +}; + +int drm_tegra_job_add_reloc(struct drm_tegra_job *job, + const struct drm_tegra_reloc *reloc); + #endif /* __DRM_TEGRA_PRIVATE_H__ */ diff --git a/tegra/pushbuf.c b/tegra/pushbuf.c new file mode 100644 index 000000000000..93f72fd40650 --- /dev/null +++ b/tegra/pushbuf.c @@ -0,0 +1,137 @@ +/* + * 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 = pushbuf_priv(pushbuf); + struct drm_tegra_bo *bo = priv->bo; + + return (unsigned long)pushbuf->ptr - (unsigned long)bo->map; +} + +drm_public +int drm_tegra_pushbuf_new(struct drm_tegra_pushbuf **pushbufp, + struct drm_tegra_job *job, + struct drm_tegra_bo *bo, + unsigned long offset) +{ + struct drm_tegra_pushbuf_private *pushbuf; + void *ptr; + int err; + + pushbuf = calloc(1, sizeof(*pushbuf)); + if (!pushbuf) + return -ENOMEM; + + pushbuf->bo = drm_tegra_bo_get(bo); + DRMINITLISTHEAD(&pushbuf->list); + pushbuf->job = job; + + err = drm_tegra_bo_map(bo, &ptr); + if (err < 0) { + drm_tegra_bo_put(bo); + free(pushbuf); + return err; + } + + pushbuf->start = pushbuf->base.ptr = ptr + offset; + pushbuf->offset = offset; + + DRMLISTADD(&pushbuf->list, &job->pushbufs); + job->num_pushbufs++; + + *pushbufp = &pushbuf->base; + + return 0; +} + +drm_public +int drm_tegra_pushbuf_free(struct drm_tegra_pushbuf *pushbuf) +{ + struct drm_tegra_pushbuf_private *priv = pushbuf_priv(pushbuf); + + if (!pushbuf) + return -EINVAL; + + drm_tegra_bo_unmap(priv->bo); + drm_tegra_bo_put(priv->bo); + DRMLISTDEL(&priv->list); + free(priv); + + return 0; +} + +drm_public +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 = pushbuf_priv(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; + + return 0; +} + +drm_public +int drm_tegra_pushbuf_sync(struct drm_tegra_pushbuf *pushbuf, + enum drm_tegra_syncpt_cond cond) +{ + struct drm_tegra_pushbuf_private *priv = pushbuf_priv(pushbuf); + + if (cond >= DRM_TEGRA_SYNCPT_COND_MAX) + return -EINVAL; + + *pushbuf->ptr++ = HOST1X_OPCODE_NONINCR(0x0, 0x1); + *pushbuf->ptr++ = cond << 8 | priv->job->syncpt; + priv->job->increments++; + + return 0; +} diff --git a/tegra/tegra.h b/tegra/tegra.h index 0731cb3bd4dc..ca0ade1bddad 100644 --- a/tegra/tegra.h +++ b/tegra/tegra.h @@ -28,6 +28,13 @@ #include <stdint.h> #include <stdlib.h> +#include <tegra_drm.h> + +enum drm_tegra_class { + DRM_TEGRA_GR2D, + DRM_TEGRA_GR3D, +}; + struct drm_tegra_bo; struct drm_tegra; @@ -44,4 +51,49 @@ int drm_tegra_bo_get_handle(struct drm_tegra_bo *bo, uint32_t *handle); int drm_tegra_bo_map(struct drm_tegra_bo *bo, void **ptr); int drm_tegra_bo_unmap(struct drm_tegra_bo *bo); +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, + struct drm_tegra_bo *bo, + unsigned long offset); +int drm_tegra_pushbuf_free(struct drm_tegra_pushbuf *pushbuf); +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); +int drm_tegra_fence_wait(struct drm_tegra_fence *fence); +void drm_tegra_fence_free(struct drm_tegra_fence *fence); + #endif /* __DRM_TEGRA_H__ */ -- 1.8.4.2 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel