From patchwork Wed Apr 9 11:40:27 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 3954931 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 4DC8B9F38C for ; Wed, 9 Apr 2014 11:41:39 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id BD9792018A for ; Wed, 9 Apr 2014 11:41:37 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 1699B2054D for ; Wed, 9 Apr 2014 11:41:36 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 55FB46EB16; Wed, 9 Apr 2014 04:41:34 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-bk0-f53.google.com (mail-bk0-f53.google.com [209.85.214.53]) by gabe.freedesktop.org (Postfix) with ESMTP id 6196F6EB1D for ; Wed, 9 Apr 2014 04:41:32 -0700 (PDT) Received: by mail-bk0-f53.google.com with SMTP id r7so2190777bkg.40 for ; Wed, 09 Apr 2014 04:41:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; bh=bSwWuzaaYX2l5V7S+zV5C9727aKh4gxplLMZ3QuPp0E=; b=eS0dTE5eEgjSM0hfHjwZjXmZKVQj1E9srmIay0UYfBGK6+6UPA06dwMJZPpbzJ0E7j POL2jdv3/3PWeNLxiW6zVBEU03F6yJJogxUSQ9GKcCDGvQi8DkAWx81FwWl8WwjM/RoC cq9lBVNgbrRhcHtzYMSqODp2mcZcnv5OFLtoc9p5ZMoJm/KfEQlNjUf8Dx4CVCDYv2le nhXl9dgYWnJF23pXp8tl5JeiNGziOhi50cTVm4QnuZx+Y6zLqoMjfJYBno8JlpCo8xjw 8PUjTktaSAPUIfTtGE/oYZzV5Z4vFyCeGoIY0ozORQQVtSGTHJzJse2u+38pkS1LLkkE b8tw== X-Received: by 10.204.197.202 with SMTP id el10mr8647389bkb.24.1397043691514; Wed, 09 Apr 2014 04:41:31 -0700 (PDT) Received: from localhost (port-93174.pppoe.wtnet.de. [84.46.76.115]) by mx.google.com with ESMTPSA id t9sm1843001bkh.16.2014.04.09.04.41.30 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 09 Apr 2014 04:41:30 -0700 (PDT) From: Thierry Reding To: dri-devel@lists.freedesktop.org Subject: [PATCH v2 libdrm 4/7] tegra: Add channel, job, pushbuf and fence APIs Date: Wed, 9 Apr 2014 13:40:27 +0200 Message-Id: <1397043630-13972-5-git-send-email-thierry.reding@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1397043630-13972-1-git-send-email-thierry.reding@gmail.com> References: <1397043630-13972-1-git-send-email-thierry.reding@gmail.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Thierry Reding 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 --- Changes in v2: - automatically allocate buffer objects as required by pushbuffers - pushbuffers can now have more than one associated buffer object - add drm_tegra_pushbuf_prepare() function tegra/Makefile.am | 4 ++ tegra/channel.c | 127 +++++++++++++++++++++++++++++++++ tegra/fence.c | 71 +++++++++++++++++++ tegra/job.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++ tegra/private.h | 72 +++++++++++++++++++ tegra/pushbuf.c | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tegra/tegra.c | 2 + tegra/tegra.h | 52 ++++++++++++++ 8 files changed, 713 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..3ab1d578f8e5 --- /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 +#include + +#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_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 000000000000..f58725ca8472 --- /dev/null +++ b/tegra/fence.c @@ -0,0 +1,71 @@ +/* + * 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 +#include + +#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; + + 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..8df643c9a9bb --- /dev/null +++ b/tegra/job.c @@ -0,0 +1,180 @@ +/* + * 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 +#include +#include + +#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; +} + +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; +} + +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->cmdbufs); + 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; + + /* + * 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(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_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(cmdbufs); + free(fence); + return -errno; + } + + if (fence) { + fence->syncpt = job->syncpt; + fence->value = args.fence; + fence->drm = drm; + *fencep = fence; + } + + free(syncpts); + free(cmdbufs); + + return 0; +} diff --git a/tegra/private.h b/tegra/private.h index 9b6bc9395d23..fc74fb56b58d 100644 --- a/tegra/private.h +++ b/tegra/private.h @@ -26,13 +26,31 @@ #define __DRM_TEGRA_PRIVATE_H__ 1 #include +#include #include #include +#include #include +#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_GR3D = 0x60, +}; + struct drm_tegra { bool close; int fd; @@ -40,6 +58,7 @@ struct drm_tegra { struct drm_tegra_bo { struct drm_tegra *drm; + drmMMListHead list; uint32_t handle; uint32_t offset; uint32_t flags; @@ -48,4 +67,57 @@ 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; + drmMMListHead list; + drmMMListHead bos; + + struct drm_tegra_bo *bo; + uint32_t *start; + uint32_t *end; +}; + +static inline struct drm_tegra_pushbuf_private * +pushbuf_priv(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 000000000000..178d5cd57541 --- /dev/null +++ b/tegra/pushbuf.c @@ -0,0 +1,205 @@ +/* + * 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 +#include +#include + +#include "private.h" + +#define HOST1X_OPCODE_NONINCR(offset, count) \ + ((0x2 << 28) | (((offset) & 0xfff) << 16) | ((count) & 0xffff)) + +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; +} + +static inline unsigned long +drm_tegra_pushbuf_get_offset(struct drm_tegra_pushbuf *pushbuf) +{ + struct drm_tegra_pushbuf_private *priv = pushbuf_priv(pushbuf); + + return (unsigned long)pushbuf->ptr - (unsigned long)priv->start; +} + +drm_public +int drm_tegra_pushbuf_new(struct drm_tegra_pushbuf **pushbufp, + struct drm_tegra_job *job) +{ + struct drm_tegra_pushbuf_private *pushbuf; + void *ptr; + int err; + + 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; +} + +drm_public +int drm_tegra_pushbuf_free(struct drm_tegra_pushbuf *pushbuf) +{ + struct drm_tegra_pushbuf_private *priv = pushbuf_priv(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_put(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 + */ +drm_public +int drm_tegra_pushbuf_prepare(struct drm_tegra_pushbuf *pushbuf, + unsigned int words) +{ + struct drm_tegra_pushbuf_private *priv = pushbuf_priv(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_put(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_put(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; +} + +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; + + *pushbuf->ptr++ = 0xdeadbeef; + + 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.c b/tegra/tegra.c index bf6d035453d5..1f7b9e7345b6 100644 --- a/tegra/tegra.c +++ b/tegra/tegra.c @@ -56,6 +56,7 @@ static void drm_tegra_bo_free(struct drm_tegra_bo *bo) drmIoctl(drm->fd, DRM_IOCTL_GEM_CLOSE, &args); + DRMLISTDEL(&bo->list); free(bo); } @@ -127,6 +128,7 @@ int drm_tegra_bo_new(struct drm_tegra_bo **bop, struct drm_tegra *drm, if (!bo) return -ENOMEM; + DRMINITLISTHEAD(&bo->list); atomic_set(&bo->ref, 1); bo->flags = flags; bo->size = size; diff --git a/tegra/tegra.h b/tegra/tegra.h index 0731cb3bd4dc..4cf3b49a3e3e 100644 --- a/tegra/tegra.h +++ b/tegra/tegra.h @@ -28,6 +28,13 @@ #include #include +#include + +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); +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); +int drm_tegra_fence_wait(struct drm_tegra_fence *fence); +void drm_tegra_fence_free(struct drm_tegra_fence *fence); + #endif /* __DRM_TEGRA_H__ */