From: The etnaviv authors <dri-devel@lists.freedesktop.org> Add the libdrm_etnaviv helper library to encapsulate etnaviv-specific interfaces to the DRM.
Signed-off-by: Christian Gmeiner <christian.gmeiner at gmail.com> Signed-off-by: Lucas Stach <l.stach at pengutronix.de> --- .gitignore | 1 + Makefile.am | 6 + configure.ac | 16 ++ etnaviv/Android.mk | 18 +++ etnaviv/Makefile.am | 27 ++++ etnaviv/Makefile.sources | 12 ++ etnaviv/etnaviv-symbol-check | 45 ++++++ etnaviv/etnaviv_bo.c | 347 +++++++++++++++++++++++++++++++++++++++++++ etnaviv/etnaviv_bo_cache.c | 196 ++++++++++++++++++++++++ etnaviv/etnaviv_cmd_stream.c | 243 ++++++++++++++++++++++++++++++ etnaviv/etnaviv_device.c | 96 ++++++++++++ etnaviv/etnaviv_drm.h | 225 ++++++++++++++++++++++++++++ etnaviv/etnaviv_drmif.h | 188 +++++++++++++++++++++++ etnaviv/etnaviv_gpu.c | 175 ++++++++++++++++++++++ etnaviv/etnaviv_pipe.c | 78 ++++++++++ etnaviv/etnaviv_priv.h | 200 +++++++++++++++++++++++++ etnaviv/libdrm_etnaviv.pc.in | 11 ++ 17 files changed, 1884 insertions(+) create mode 100644 etnaviv/Android.mk create mode 100644 etnaviv/Makefile.am create mode 100644 etnaviv/Makefile.sources create mode 100755 etnaviv/etnaviv-symbol-check create mode 100644 etnaviv/etnaviv_bo.c create mode 100644 etnaviv/etnaviv_bo_cache.c create mode 100644 etnaviv/etnaviv_cmd_stream.c create mode 100644 etnaviv/etnaviv_device.c create mode 100644 etnaviv/etnaviv_drm.h create mode 100644 etnaviv/etnaviv_drmif.h create mode 100644 etnaviv/etnaviv_gpu.c create mode 100644 etnaviv/etnaviv_pipe.c create mode 100644 etnaviv/etnaviv_priv.h create mode 100644 etnaviv/libdrm_etnaviv.pc.in diff --git a/.gitignore b/.gitignore index a44566f..3226b3a 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ libdrm_exynos.pc libdrm_freedreno.pc libdrm_amdgpu.pc libdrm_vc4.pc +libdrm_etnaviv.pc libkms.pc libtool ltmain.sh diff --git a/Makefile.am b/Makefile.am index 2ceb352..630edc4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -36,6 +36,7 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \ --enable-freedreno \ --enable-freedreno-kgsl\ --enable-tegra-experimental-api \ + --enable-etnaviv-experimental-api \ --enable-install-test-programs \ --enable-cairo-tests \ --enable-manpages \ @@ -84,6 +85,10 @@ if HAVE_VC4 VC4_SUBDIR = vc4 endif +if HAVE_ETNAVIV +ETNAVIV_SUBDIR = etnaviv +endif + if BUILD_MANPAGES if HAVE_MANPAGES_STYLESHEET MAN_SUBDIR = man @@ -102,6 +107,7 @@ SUBDIRS = \ $(FREEDRENO_SUBDIR) \ $(TEGRA_SUBDIR) \ $(VC4_SUBDIR) \ + $(ETNAVIV_SUBDIR) \ tests \ $(MAN_SUBDIR) diff --git a/configure.ac b/configure.ac index e3048c7..64f3e6c 100644 --- a/configure.ac +++ b/configure.ac @@ -132,6 +132,11 @@ AC_ARG_ENABLE(vc4, [Enable support for vc4's API (default: auto, enabled on arm)]), [VC4=$enableval], [VC4=auto]) +AC_ARG_ENABLE(etnaviv-experimental-api, + AS_HELP_STRING([--enable-etnaviv-experimental-api], + [Enable support for etnaviv's experimental API (default: disabled)]), + [ETNAVIV=$enableval], [ETNAVIV=no]) + AC_ARG_ENABLE(install-test-programs, AS_HELP_STRING([--enable-install-test-programs], [Install test programs (default: no)]), @@ -274,6 +279,9 @@ if test "x$drm_cv_atomic_primitives" = "xnone"; then LIBDRM_ATOMICS_NOT_FOUND_MSG($TEGRA, tegra, NVIDIA Tegra, tegra-experimental-api) TEGRA=no + + LIBDRM_ATOMICS_NOT_FOUND_MSG($ETNAVIV, etnaviv, Vivante, etnaviv-experimental-api) + ETNAVIV=no else if test "x$INTEL" = xauto; then case $host_cpu in @@ -413,6 +421,11 @@ if test "x$VC4" = xyes; then AC_DEFINE(HAVE_VC4, 1, [Have VC4 support]) fi +AM_CONDITIONAL(HAVE_ETNAVIV, [test "x$ETNAVIV" = xyes]) +if test "x$ETNAVIV" = xyes; then + AC_DEFINE(HAVE_ETNAVIV, 1, [Have etnaviv support]) +fi + AM_CONDITIONAL(HAVE_INSTALL_TESTS, [test "x$INSTALL_TESTS" = xyes]) if test "x$INSTALL_TESTS" = xyes; then AC_DEFINE(HAVE_INSTALL_TESTS, 1, [Install test programs]) @@ -524,6 +537,8 @@ AC_CONFIG_FILES([ tegra/libdrm_tegra.pc vc4/Makefile vc4/libdrm_vc4.pc + etnaviv/Makefile + etnaviv/libdrm_etnaviv.pc tests/Makefile tests/modeprint/Makefile tests/modetest/Makefile @@ -555,4 +570,5 @@ echo " EXYNOS API $EXYNOS" echo " Freedreno API $FREEDRENO (kgsl: $FREEDRENO_KGSL)" echo " Tegra API $TEGRA" echo " VC4 API $VC4" +echo " Etnaviv API $ETNAVIV" echo "" diff --git a/etnaviv/Android.mk b/etnaviv/Android.mk new file mode 100644 index 0000000..a3a2295 --- /dev/null +++ b/etnaviv/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +# Import variables LIBDRM_ETNAVIV_FILES, LIBDRM_ETNAVIV_H_FILES +include $(LOCAL_PATH)/Makefile.sources + +LOCAL_MODULE := libdrm_etnaviv +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := libdrm + +LOCAL_SRC_FILES := $(patsubst %.h, , $(LIBDRM_ETNAVIV_FILES)) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) + +LOCAL_CFLAGS := \ + -DHAVE_LIBDRM_ATOMIC_PRIMITIVES=1 + +include $(BUILD_SHARED_LIBRARY) diff --git a/etnaviv/Makefile.am b/etnaviv/Makefile.am new file mode 100644 index 0000000..1c40e56 --- /dev/null +++ b/etnaviv/Makefile.am @@ -0,0 +1,27 @@ +AUTOMAKE_OPTIONS=subdir-objects +include Makefile.sources + +AM_CFLAGS = \ + $(WARN_CFLAGS) \ + -I$(top_srcdir) \ + $(PTHREADSTUBS_CFLAGS) \ + -I$(top_srcdir)/include/drm + +libdrm_etnaviv_ladir = $(libdir) +libdrm_etnaviv_la_LTLIBRARIES = libdrm_etnaviv.la +libdrm_etnaviv_la_LDFLAGS = -version-number 0:0:0 -no-undefined +libdrm_etnaviv_la_LIBADD = \ + ../libdrm.la \ + @PTHREADSTUBS_LIBS@ \ + @CLOCK_LIB@ + +libdrm_etnaviv_la_SOURCES = $(LIBDRM_ETNAVIV_FILES) + +libdrm_etnavivincludedir = ${includedir}/libdrm +libdrm_etnavivinclude_HEADERS = $(LIBDRM_ETNAVIV_H_FILES) + +pkgconfigdir = @pkgconfigdir@ +pkgconfig_DATA = libdrm_etnaviv.pc + +TESTS = etnaviv-symbol-check +EXTRA_DIST = $(TESTS) diff --git a/etnaviv/Makefile.sources b/etnaviv/Makefile.sources new file mode 100644 index 0000000..5258056 --- /dev/null +++ b/etnaviv/Makefile.sources @@ -0,0 +1,12 @@ +LIBDRM_ETNAVIV_FILES := \ + etnaviv_device.c \ + etnaviv_gpu.c \ + etnaviv_bo.c \ + etnaviv_bo_cache.c \ + etnaviv_pipe.c \ + etnaviv_cmd_stream.c \ + etnaviv_drm.h \ + etnaviv_priv.h + +LIBDRM_ETNAVIV_H_FILES := \ + etnaviv_drmif.h diff --git a/etnaviv/etnaviv-symbol-check b/etnaviv/etnaviv-symbol-check new file mode 100755 index 0000000..7795023 --- /dev/null +++ b/etnaviv/etnaviv-symbol-check @@ -0,0 +1,45 @@ +#!/bin/bash + +# The following symbols (past the first five) are taken from the public headers. +# A list of the latter should be available Makefile.sources/LIBDRM_FREEDRENO_H_FILES + +FUNCS=$(nm -D --format=bsd --defined-only ${1-.libs/libdrm_etnaviv.so} | awk '{print $3}'| while read func; do +( grep -q "^$func$" || echo $func ) <<EOF +__bss_start +_edata +_end +_fini +_init +etna_device_new +etna_device_ref +etna_device_del +etna_gpu_new +etna_gpu_del +etna_gpu_get_param +etna_pipe_new +etna_pipe_del +etna_pipe_wait +etna_bo_new +etna_bo_from_handle +etna_bo_from_name +etna_bo_from_dmabuf +etna_bo_ref +etna_bo_del +etna_bo_get_name +etna_bo_handle +etna_bo_dmabuf +etna_bo_size +etna_bo_map +etna_bo_cpu_prep +etna_bo_cpu_fini +etna_cmd_stream_new +etna_cmd_stream_del +etna_cmd_stream_timestamp +etna_cmd_stream_flush +etna_cmd_stream_finish +etna_cmd_stream_reloc +EOF +done) + +test ! -n "$FUNCS" || echo $FUNCS +test ! -n "$FUNCS" diff --git a/etnaviv/etnaviv_bo.c b/etnaviv/etnaviv_bo.c new file mode 100644 index 0000000..833f8bd --- /dev/null +++ b/etnaviv/etnaviv_bo.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2014 Etnaviv Project + * + * 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. + * + * Authors: + * Christian Gmeiner <christian.gmeiner at gmail.com> + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "etnaviv_priv.h" +#include "etnaviv_drmif.h" + +drm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER; +drm_private void bo_del(struct etna_bo *bo); + +/* set buffer name, and add to table, call w/ table_lock held: */ +static void set_name(struct etna_bo *bo, uint32_t name) +{ + bo->name = name; + /* add ourself into the name table: */ + drmHashInsert(bo->dev->name_table, name, bo); +} + +/* Called under table_lock */ +drm_private void bo_del(struct etna_bo *bo) +{ + if (bo->map) + drm_munmap(bo->map, bo->size); + + if (bo->name) + drmHashDelete(bo->dev->name_table, bo->name); + + if (bo->handle) { + struct drm_gem_close req = { + .handle = bo->handle, + }; + + drmHashDelete(bo->dev->handle_table, bo->handle); + drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req); + } + + free(bo); +} + +/* lookup a buffer from it's handle, call w/ table_lock held: */ +static struct etna_bo *lookup_bo(void *tbl, uint32_t handle) +{ + struct etna_bo *bo = NULL; + + if (!drmHashLookup(tbl, handle, (void **)&bo)) { + /* found, incr refcnt and return: */ + bo = etna_bo_ref(bo); + + /* don't break the bucket if this bo was found in one */ + list_delinit(&bo->list); + } + + return bo; +} + +/* allocate a new buffer object, call w/ table_lock held */ +static struct etna_bo *bo_from_handle(struct etna_device *dev, + uint32_t size, uint32_t handle, uint32_t flags) +{ + struct etna_bo *bo = calloc(sizeof(*bo), 1); + + if (!bo) { + struct drm_gem_close req = { + .handle = handle, + }; + + drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req); + + return NULL; + } + + bo->dev = etna_device_ref(dev); + bo->size = size; + bo->handle = handle; + bo->flags = flags; + atomic_set(&bo->refcnt, 1); + list_inithead(&bo->list); + /* add ourselves to the handle table: */ + drmHashInsert(dev->handle_table, handle, bo); + + return bo; +} + +/* allocate a new (un-tiled) buffer object */ +struct etna_bo *etna_bo_new(struct etna_device *dev, uint32_t size, + uint32_t flags) +{ + struct etna_bo *bo; + int ret; + struct drm_etnaviv_gem_new req = { + .flags = flags, + }; + + bo = etna_bo_cache_alloc(&dev->bo_cache, &size, flags); + if (bo) + return bo; + + req.size = size; + ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GEM_NEW, + &req, sizeof(req)); + if (ret) + return NULL; + + pthread_mutex_lock(&table_lock); + bo = bo_from_handle(dev, size, req.handle, flags); + bo->reuse = 1; + pthread_mutex_unlock(&table_lock); + + return bo; +} + +struct etna_bo *etna_bo_ref(struct etna_bo *bo) +{ + atomic_inc(&bo->refcnt); + + return bo; +} + +/* get buffer info */ +static int get_buffer_info(struct etna_bo *bo) +{ + int ret; + struct drm_etnaviv_gem_info req = { + .handle = bo->handle, + }; + + ret = drmCommandWriteRead(bo->dev->fd, DRM_ETNAVIV_GEM_INFO, + &req, sizeof(req)); + if (ret) { + return ret; + } + + /* really all we need for now is mmap offset */ + bo->offset = req.offset; + + return 0; +} + +/* import a buffer object from DRI2 name */ +struct etna_bo *etna_bo_from_name(struct etna_device *dev, uint32_t name) +{ + struct etna_bo *bo; + struct drm_gem_open req = { + .name = name, + }; + + pthread_mutex_lock(&table_lock); + + /* check name table first, to see if bo is already open: */ + bo = lookup_bo(dev->name_table, req.handle); + if (bo) + goto out_unlock; + + if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) { + ERROR_MSG("gem-open failed: %s", strerror(errno)); + goto out_unlock; + } + + bo = lookup_bo(dev->handle_table, req.handle); + if (bo) + goto out_unlock; + + bo = bo_from_handle(dev, req.size, req.handle, 0); + if (bo) + set_name(bo, name); + +out_unlock: + pthread_mutex_unlock(&table_lock); + + return bo; +} + +/* import a buffer from dmabuf fd, does not take ownership of the + * fd so caller should close() the fd when it is otherwise done + * with it (even if it is still using the 'struct etna_bo *') + */ +struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd) +{ + struct etna_bo *bo; + int ret, size; + uint32_t handle; + + pthread_mutex_lock(&table_lock); + + ret = drmPrimeFDToHandle(dev->fd, fd, &handle); + if (ret) { + return NULL; + } + + bo = lookup_bo(dev->handle_table, handle); + if (bo) + goto out_unlock; + + /* lseek() to get bo size */ + size = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_CUR); + + bo = bo_from_handle(dev, size, handle, 0); + +out_unlock: + pthread_mutex_unlock(&table_lock); + + return bo; +} + +/* destroy a buffer object */ +void etna_bo_del(struct etna_bo *bo) +{ + struct etna_device *dev = bo->dev; + + if (!bo) + return; + + if (!atomic_dec_and_test(&bo->refcnt)) + return; + + pthread_mutex_lock(&table_lock); + + if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0)) + goto out; + + bo_del(bo); + etna_device_del_locked(dev); +out: + pthread_mutex_unlock(&table_lock); +} + +/* get the global flink/DRI2 buffer name */ +int etna_bo_get_name(struct etna_bo *bo, uint32_t *name) +{ + if (!bo->name) { + struct drm_gem_flink req = { + .handle = bo->handle, + }; + int ret; + + ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req); + if (ret) { + return ret; + } + + pthread_mutex_lock(&table_lock); + set_name(bo, req.name); + pthread_mutex_unlock(&table_lock); + bo->reuse = 0; + } + + *name = bo->name; + + return 0; +} + +uint32_t etna_bo_handle(struct etna_bo *bo) +{ + return bo->handle; +} + +/* caller owns the dmabuf fd that is returned and is responsible + * to close() it when done + */ +int etna_bo_dmabuf(struct etna_bo *bo) +{ + int ret, prime_fd; + + ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC, + &prime_fd); + if (ret) { + ERROR_MSG("failed to get dmabuf fd: %d", ret); + return ret; + } + + bo->reuse = 0; + + return prime_fd; +} + +uint32_t etna_bo_size(struct etna_bo *bo) +{ + return bo->size; +} + +void *etna_bo_map(struct etna_bo *bo) +{ + if (!bo->map) { + if (!bo->offset) { + get_buffer_info(bo); + } + + bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, + MAP_SHARED, bo->dev->fd, bo->offset); + if (bo->map == MAP_FAILED) { + ERROR_MSG("mmap failed: %s", strerror(errno)); + bo->map = NULL; + } + } + + return bo->map; +} + +int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op) +{ + struct drm_etnaviv_gem_cpu_prep req = { + .handle = bo->handle, + .op = op, + }; + + get_abs_timeout(&req.timeout, 5000); + + return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP, + &req, sizeof(req)); +} + +void etna_bo_cpu_fini(struct etna_bo *bo) +{ + struct drm_etnaviv_gem_cpu_fini req = { + .handle = bo->handle, + }; + + drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI, + &req, sizeof(req)); +} diff --git a/etnaviv/etnaviv_bo_cache.c b/etnaviv/etnaviv_bo_cache.c new file mode 100644 index 0000000..8924651 --- /dev/null +++ b/etnaviv/etnaviv_bo_cache.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2016 Etnaviv Project + * + * 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. + * + * Authors: + * Christian Gmeiner <christian.gmeiner at gmail.com> + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "etnaviv_priv.h" +#include "etnaviv_drmif.h" + +drm_private void bo_del(struct etna_bo *bo); +drm_private extern pthread_mutex_t table_lock; + +static void add_bucket(struct etna_bo_cache *cache, int size) +{ + unsigned i = cache->num_buckets; + + assert(i < ARRAY_SIZE(cache->cache_bucket)); + + list_inithead(&cache->cache_bucket[i].list); + cache->cache_bucket[i].size = size; + cache->num_buckets++; +} + +drm_private void etna_bo_cache_init(struct etna_bo_cache *cache) +{ + unsigned long size, cache_max_size = 64 * 1024 * 1024; + + /* OK, so power of two buckets was too wasteful of memory. + * Give 3 other sizes between each power of two, to hopefully + * cover things accurately enough. (The alternative is + * probably to just go for exact matching of sizes, and assume + * that for things like composited window resize the tiled + * width/height alignment and rounding of sizes to pages will + * get us useful cache hit rates anyway) + */ + add_bucket(cache, 4096); + add_bucket(cache, 4096 * 2); + add_bucket(cache, 4096 * 3); + + /* Initialize the linked lists for BO reuse cache. */ + for (size = 4 * 4096; size <= cache_max_size; size *= 2) { + add_bucket(cache, size); + add_bucket(cache, size + size * 1 / 4); + add_bucket(cache, size + size * 2 / 4); + add_bucket(cache, size + size * 3 / 4); + } +} + +/* Frees older cached buffers. Called under table_lock */ +drm_private void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t time) +{ + unsigned i; + + if (cache->time == time) + return; + + for (i = 0; i < cache->num_buckets; i++) { + struct etna_bo_bucket *bucket = &cache->cache_bucket[i]; + struct etna_bo *bo; + + while (!LIST_IS_EMPTY(&bucket->list)) { + bo = LIST_ENTRY(struct etna_bo, bucket->list.next, list); + + /* keep things in cache for at least 1 second: */ + if (time && ((time - bo->free_time) <= 1)) + break; + + list_del(&bo->list); + bo_del(bo); + } + } + + cache->time = time; +} + +static struct etna_bo_bucket *get_bucket(struct etna_bo_cache *cache, uint32_t size) +{ + unsigned i; + + /* hmm, this is what intel does, but I suppose we could calculate our + * way to the correct bucket size rather than looping.. + */ + for (i = 0; i < cache->num_buckets; i++) { + struct etna_bo_bucket *bucket = &cache->cache_bucket[i]; + if (bucket->size >= size) { + return bucket; + } + } + + return NULL; +} + +static int is_idle(struct etna_bo *bo) +{ + return etna_bo_cpu_prep(bo, + DRM_ETNA_PREP_READ | + DRM_ETNA_PREP_WRITE | + DRM_ETNA_PREP_NOSYNC) == 0; +} + +static struct etna_bo *find_in_bucket(struct etna_bo_bucket *bucket, uint32_t flags) +{ + struct etna_bo *bo = NULL; + + pthread_mutex_lock(&table_lock); + while (!LIST_IS_EMPTY(&bucket->list)) { + bo = LIST_ENTRY(struct etna_bo, bucket->list.next, list); + + if (bo->flags == flags && is_idle(bo)) { + list_del(&bo->list); + break; + } + + bo = NULL; + break; + } + pthread_mutex_unlock(&table_lock); + + return bo; +} + +/* allocate a new (un-tiled) buffer object + * + * NOTE: size is potentially rounded up to bucket size + */ +drm_private struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache, uint32_t *size, + uint32_t flags) +{ + struct etna_bo *bo; + struct etna_bo_bucket *bucket; + + *size = ALIGN(*size, 4096); + bucket = get_bucket(cache, *size); + + /* see if we can be green and recycle: */ + if (bucket) { + *size = bucket->size; + bo = find_in_bucket(bucket, flags); + if (bo) { + atomic_set(&bo->refcnt, 1); + etna_device_ref(bo->dev); + return bo; + } + } + + return NULL; +} + +drm_private int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo *bo) +{ + struct etna_bo_bucket *bucket = get_bucket(cache, bo->size); + + /* see if we can be green and recycle: */ + if (bucket) { + struct timespec time; + + clock_gettime(CLOCK_MONOTONIC, &time); + + bo->free_time = time.tv_sec; + list_addtail(&bo->list, &bucket->list); + etna_bo_cache_cleanup(cache, time.tv_sec); + + /* bo's in the bucket cache don't have a ref and + * don't hold a ref to the dev: + */ + etna_device_del_locked(bo->dev); + + return 0; + } + + return -1; +} diff --git a/etnaviv/etnaviv_cmd_stream.c b/etnaviv/etnaviv_cmd_stream.c new file mode 100644 index 0000000..9ce3f36 --- /dev/null +++ b/etnaviv/etnaviv_cmd_stream.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2014-2015 Etnaviv Project + * + * 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. + * + * Authors: + * Christian Gmeiner <christian.gmeiner at gmail.com> + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <assert.h> + +#include "etnaviv_drmif.h" +#include "etnaviv_priv.h" + +static pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER; + +static void *grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz) +{ + if ((nr + 1) > *max) { + if ((*max * 2) < (nr + 1)) + *max = nr + 5; + else + *max = *max * 2; + ptr = realloc(ptr, *max * sz); + } + + return ptr; +} + +#define APPEND(x, name) ({ \ + (x)->name = grow((x)->name, (x)->nr_ ## name, &(x)->max_ ## name, sizeof((x)->name[0])); \ + (x)->nr_ ## name ++; \ +}) + +static inline struct etna_cmd_stream_priv * +etna_cmd_stream_priv(struct etna_cmd_stream *stream) +{ + return (struct etna_cmd_stream_priv *)stream; +} + +struct etna_cmd_stream *etna_cmd_stream_new(struct etna_pipe *pipe, uint32_t size, + void (*reset_notify)(struct etna_cmd_stream *stream, void *priv), + void *priv) +{ + struct etna_cmd_stream_priv *stream = NULL; + + if (size == 0) { + ERROR_MSG("invalid size of 0"); + goto fail; + } + + stream = calloc(1, sizeof(*stream)); + if (!stream) { + ERROR_MSG("allocation failed"); + goto fail; + } + + /* allocate even number of 32-bit words */ + size = ALIGN(size, 2); + + stream->base.buffer = malloc(size * sizeof(uint32_t)); + if (!stream->base.buffer) { + ERROR_MSG("allocation failed"); + goto fail; + } + + stream->base.size = size; + stream->pipe = pipe; + stream->reset_notify = reset_notify; + stream->reset_notify_priv = priv; + + return &stream->base; + +fail: + if (stream) + etna_cmd_stream_del(&stream->base); + + return NULL; +} + +void etna_cmd_stream_del(struct etna_cmd_stream *stream) +{ + struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); + + free(stream->buffer); + free(priv->submit.relocs); + free(priv); +} + +static void reset_buffer(struct etna_cmd_stream *stream) +{ + struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); + + stream->offset = 0; + priv->submit.nr_bos = 0; + priv->submit.nr_relocs = 0; + priv->nr_bos = 0; + + if (priv->reset_notify) + priv->reset_notify(stream, priv->reset_notify_priv); +} + +uint32_t etna_cmd_stream_timestamp(struct etna_cmd_stream *stream) +{ + return etna_cmd_stream_priv(stream)->last_timestamp; +} + +static uint32_t append_bo(struct etna_cmd_stream *stream, struct etna_bo *bo) +{ + struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); + uint32_t idx; + + idx = APPEND(&priv->submit, bos); + idx = APPEND(priv, bos); + + priv->submit.bos[idx].flags = 0; + priv->submit.bos[idx].handle = bo->handle; + + priv->bos[idx] = etna_bo_ref(bo); + + return idx; +} + +/* add (if needed) bo, return idx: */ +static uint32_t bo2idx(struct etna_cmd_stream *stream, struct etna_bo *bo, + uint32_t flags) +{ + struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); + uint32_t idx; + + pthread_mutex_lock(&idx_lock); + + if (!bo->current_stream) { + idx = append_bo(stream, bo); + bo->current_stream = stream; + bo->idx = idx; + } else if (bo->current_stream == stream) { + idx = bo->idx; + } else { + /* slow-path: */ + for (idx = 0; idx < priv->nr_bos; idx++) + if (priv->bos[idx] == bo) + break; + if (idx == priv->nr_bos) { + /* not found */ + idx = append_bo(stream, bo); + } + } + pthread_mutex_unlock(&idx_lock); + + if (flags & ETNA_RELOC_READ) + priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_READ; + if (flags & ETNA_RELOC_WRITE) + priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_WRITE; + + return idx; +} + +static void flush(struct etna_cmd_stream *stream) +{ + struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); + int ret, id = priv->pipe->id; + struct etna_gpu *gpu = priv->pipe->gpu; + + struct drm_etnaviv_gem_submit req = { + .pipe = gpu->core, + .exec_state = id, + .bos = VOID2U64(priv->submit.bos), + .nr_bos = priv->submit.nr_bos, + .relocs = VOID2U64(priv->submit.relocs), + .nr_relocs = priv->submit.nr_relocs, + .stream = VOID2U64(stream->buffer), + .stream_size = stream->offset * 4, /* in bytes */ + }; + + ret = drmCommandWriteRead(gpu->dev->fd, DRM_ETNAVIV_GEM_SUBMIT, + &req, sizeof(req)); + + if (ret) + ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno)); + else + priv->last_timestamp = req.fence; + + for (uint32_t i = 0; i < priv->nr_bos; i++) { + struct etna_bo *bo = priv->bos[i]; + + bo->current_stream = NULL; + etna_bo_del(bo); + } +} + +void etna_cmd_stream_flush(struct etna_cmd_stream *stream) +{ + flush(stream); + reset_buffer(stream); +} + +void etna_cmd_stream_finish(struct etna_cmd_stream *stream) +{ + struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); + + flush(stream); + etna_pipe_wait(priv->pipe, priv->last_timestamp, 5000); + reset_buffer(stream); +} + +void etna_cmd_stream_reloc(struct etna_cmd_stream *stream, const struct etna_reloc *r) +{ + struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); + struct drm_etnaviv_gem_submit_reloc *reloc; + uint32_t idx = APPEND(&priv->submit, relocs); + uint32_t addr = 0; + + reloc = &priv->submit.relocs[idx]; + + reloc->reloc_idx = bo2idx(stream, r->bo, r->flags); + reloc->reloc_offset = r->offset; + reloc->submit_offset = stream->offset * 4; /* in bytes */ + reloc->flags = 0; + + etna_cmd_stream_emit(stream, addr); +} diff --git a/etnaviv/etnaviv_device.c b/etnaviv/etnaviv_device.c new file mode 100644 index 0000000..f954ca4 --- /dev/null +++ b/etnaviv/etnaviv_device.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2014 Etnaviv Project + * + * 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. + * + * Authors: + * Christian Gmeiner <christian.gmeiner at gmail.com> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <linux/stddef.h> +#include <linux/types.h> +#include <errno.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <unistd.h> +#include <pthread.h> + +#include <xf86drm.h> +#include <xf86atomic.h> + +#include "etnaviv_priv.h" +#include "etnaviv_drmif.h" + +static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER; + +struct etna_device *etna_device_new(int fd) +{ + struct etna_device *dev = calloc(sizeof(*dev), 1); + + if (!dev) + return NULL; + + atomic_set(&dev->refcnt, 1); + dev->fd = fd; + dev->handle_table = drmHashCreate(); + dev->name_table = drmHashCreate(); + etna_bo_cache_init(&dev->bo_cache); + + return dev; +} + +struct etna_device *etna_device_ref(struct etna_device *dev) +{ + atomic_inc(&dev->refcnt); + + return dev; +} + +static void etna_device_del_impl(struct etna_device *dev) +{ + etna_bo_cache_cleanup(&dev->bo_cache, 0); + drmHashDestroy(dev->handle_table); + drmHashDestroy(dev->name_table); + + free(dev); +} + +drm_private void etna_device_del_locked(struct etna_device *dev) +{ + if (!atomic_dec_and_test(&dev->refcnt)) + return; + + etna_device_del_impl(dev); +} + +void etna_device_del(struct etna_device *dev) +{ + if (!atomic_dec_and_test(&dev->refcnt)) + return; + + pthread_mutex_lock(&table_lock); + etna_device_del_impl(dev); + pthread_mutex_unlock(&table_lock); +} diff --git a/etnaviv/etnaviv_drm.h b/etnaviv/etnaviv_drm.h new file mode 100644 index 0000000..462241c --- /dev/null +++ b/etnaviv/etnaviv_drm.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2015 Etnaviv Project + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __ETNAVIV_DRM_H__ +#define __ETNAVIV_DRM_H__ + +#include "drm.h" + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints: + * 1) Do not use pointers, use __u64 instead for 32 bit / 64 bit + * user/kernel compatibility + * 2) Keep fields aligned to their size + * 3) Because of how drm_ioctl() works, we can add new fields at + * the end of an ioctl if some care is taken: drm_ioctl() will + * zero out the new fields at the tail of the ioctl, so a zero + * value should have a backwards compatible meaning. And for + * output params, userspace won't see the newly added output + * fields.. so that has to be somehow ok. + */ + +/* timeouts are specified in clock-monotonic absolute times (to simplify + * restarting interrupted ioctls). The following struct is logically the + * same as 'struct timespec' but 32/64b ABI safe. + */ +struct drm_etnaviv_timespec { + __s64 tv_sec; /* seconds */ + __s64 tv_nsec; /* nanoseconds */ +}; + +#define ETNAVIV_PARAM_GPU_MODEL 0x01 +#define ETNAVIV_PARAM_GPU_REVISION 0x02 +#define ETNAVIV_PARAM_GPU_FEATURES_0 0x03 +#define ETNAVIV_PARAM_GPU_FEATURES_1 0x04 +#define ETNAVIV_PARAM_GPU_FEATURES_2 0x05 +#define ETNAVIV_PARAM_GPU_FEATURES_3 0x06 +#define ETNAVIV_PARAM_GPU_FEATURES_4 0x07 +#define ETNAVIV_PARAM_GPU_FEATURES_5 0x08 +#define ETNAVIV_PARAM_GPU_FEATURES_6 0x09 + +#define ETNAVIV_PARAM_GPU_STREAM_COUNT 0x10 +#define ETNAVIV_PARAM_GPU_REGISTER_MAX 0x11 +#define ETNAVIV_PARAM_GPU_THREAD_COUNT 0x12 +#define ETNAVIV_PARAM_GPU_VERTEX_CACHE_SIZE 0x13 +#define ETNAVIV_PARAM_GPU_SHADER_CORE_COUNT 0x14 +#define ETNAVIV_PARAM_GPU_PIXEL_PIPES 0x15 +#define ETNAVIV_PARAM_GPU_VERTEX_OUTPUT_BUFFER_SIZE 0x16 +#define ETNAVIV_PARAM_GPU_BUFFER_SIZE 0x17 +#define ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT 0x18 +#define ETNAVIV_PARAM_GPU_NUM_CONSTANTS 0x19 +#define ETNAVIV_PARAM_GPU_NUM_VARYINGS 0x1a + +#define ETNA_MAX_PIPES 4 + +struct drm_etnaviv_param { + __u32 pipe; /* in */ + __u32 param; /* in, ETNAVIV_PARAM_x */ + __u64 value; /* out (get_param) or in (set_param) */ +}; + +/* + * GEM buffers: + */ + +#define ETNA_BO_CACHE_MASK 0x000f0000 +/* cache modes */ +#define ETNA_BO_CACHED 0x00010000 +#define ETNA_BO_WC 0x00020000 +#define ETNA_BO_UNCACHED 0x00040000 +/* map flags */ +#define ETNA_BO_FORCE_MMU 0x00100000 + +struct drm_etnaviv_gem_new { + __u64 size; /* in */ + __u32 flags; /* in, mask of ETNA_BO_x */ + __u32 handle; /* out */ +}; + +struct drm_etnaviv_gem_info { + __u32 handle; /* in */ + __u32 pad; + __u64 offset; /* out, offset to pass to mmap() */ +}; + +#define ETNA_PREP_READ 0x01 +#define ETNA_PREP_WRITE 0x02 +#define ETNA_PREP_NOSYNC 0x04 + +struct drm_etnaviv_gem_cpu_prep { + __u32 handle; /* in */ + __u32 op; /* in, mask of ETNA_PREP_x */ + struct drm_etnaviv_timespec timeout; /* in */ +}; + +struct drm_etnaviv_gem_cpu_fini { + __u32 handle; /* in */ + __u32 flags; /* in, placeholder for now, no defined values */ +}; + +/* + * Cmdstream Submission: + */ + +/* The value written into the cmdstream is logically: + * relocbuf->gpuaddr + reloc_offset + * + * NOTE that reloc's must be sorted by order of increasing submit_offset, + * otherwise EINVAL. + */ +struct drm_etnaviv_gem_submit_reloc { + __u32 submit_offset; /* in, offset from submit_bo */ + __u32 reloc_idx; /* in, index of reloc_bo buffer */ + __u64 reloc_offset; /* in, offset from start of reloc_bo */ + __u32 flags; /* in, placeholder for now, no defined values */ +}; + +/* Each buffer referenced elsewhere in the cmdstream submit (ie. the + * cmdstream buffer(s) themselves or reloc entries) has one (and only + * one) entry in the submit->bos[] table. + * + * As a optimization, the current buffer (gpu virtual address) can be + * passed back through the 'presumed' field. If on a subsequent reloc, + * userspace passes back a 'presumed' address that is still valid, + * then patching the cmdstream for this entry is skipped. This can + * avoid kernel needing to map/access the cmdstream bo in the common + * case. + */ +#define ETNA_SUBMIT_BO_READ 0x0001 +#define ETNA_SUBMIT_BO_WRITE 0x0002 +struct drm_etnaviv_gem_submit_bo { + __u32 flags; /* in, mask of ETNA_SUBMIT_BO_x */ + __u32 handle; /* in, GEM handle */ + __u64 presumed; /* in/out, presumed buffer address */ +}; + +/* Each cmdstream submit consists of a table of buffers involved, and + * one or more cmdstream buffers. This allows for conditional execution + * (context-restore), and IB buffers needed for per tile/bin draw cmds. + */ +#define ETNA_PIPE_3D 0x00 +#define ETNA_PIPE_2D 0x01 +#define ETNA_PIPE_VG 0x02 +struct drm_etnaviv_gem_submit { + __u32 fence; /* out */ + __u32 pipe; /* in */ + __u32 exec_state; /* in, initial execution state (ETNA_PIPE_x) */ + __u32 nr_bos; /* in, number of submit_bo's */ + __u32 nr_relocs; /* in, number of submit_reloc's */ + __u32 stream_size; /* in, cmdstream size */ + __u64 bos; /* in, ptr to array of submit_bo's */ + __u64 relocs; /* in, ptr to array of submit_reloc's */ + __u64 stream; /* in, ptr to cmdstream */ +}; + +/* The normal way to synchronize with the GPU is just to CPU_PREP on + * a buffer if you need to access it from the CPU (other cmdstream + * submission from same or other contexts, PAGE_FLIP ioctl, etc, all + * handle the required synchronization under the hood). This ioctl + * mainly just exists as a way to implement the gallium pipe_fence + * APIs without requiring a dummy bo to synchronize on. + */ +#define ETNA_WAIT_NONBLOCK 0x01 +struct drm_etnaviv_wait_fence { + __u32 pipe; /* in */ + __u32 fence; /* in */ + __u32 flags; /* in, mask of ETNA_WAIT_x */ + __u32 pad; + struct drm_etnaviv_timespec timeout; /* in */ +}; + +#define ETNA_USERPTR_READ 0x01 +#define ETNA_USERPTR_WRITE 0x02 +struct drm_etnaviv_gem_userptr { + __u64 user_ptr; /* in, page aligned user pointer */ + __u64 user_size; /* in, page aligned user size */ + __u32 flags; /* in, flags */ + __u32 handle; /* out, non-zero handle */ +}; + +struct drm_etnaviv_gem_wait { + __u32 pipe; /* in */ + __u32 handle; /* in, bo to be waited for */ + __u32 flags; /* in, mask of ETNA_WAIT_x */ + __u32 pad; + struct drm_etnaviv_timespec timeout; /* in */ +}; + +#define DRM_ETNAVIV_GET_PARAM 0x00 +/* placeholder: +#define DRM_ETNAVIV_SET_PARAM 0x01 + */ +#define DRM_ETNAVIV_GEM_NEW 0x02 +#define DRM_ETNAVIV_GEM_INFO 0x03 +#define DRM_ETNAVIV_GEM_CPU_PREP 0x04 +#define DRM_ETNAVIV_GEM_CPU_FINI 0x05 +#define DRM_ETNAVIV_GEM_SUBMIT 0x06 +#define DRM_ETNAVIV_WAIT_FENCE 0x07 +#define DRM_ETNAVIV_GEM_USERPTR 0x08 +#define DRM_ETNAVIV_GEM_WAIT 0x09 +#define DRM_ETNAVIV_NUM_IOCTLS 0x0a + +#define DRM_IOCTL_ETNAVIV_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GET_PARAM, struct drm_etnaviv_param) +#define DRM_IOCTL_ETNAVIV_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_NEW, struct drm_etnaviv_gem_new) +#define DRM_IOCTL_ETNAVIV_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_INFO, struct drm_etnaviv_gem_info) +#define DRM_IOCTL_ETNAVIV_GEM_CPU_PREP DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_PREP, struct drm_etnaviv_gem_cpu_prep) +#define DRM_IOCTL_ETNAVIV_GEM_CPU_FINI DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_FINI, struct drm_etnaviv_gem_cpu_fini) +#define DRM_IOCTL_ETNAVIV_GEM_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_SUBMIT, struct drm_etnaviv_gem_submit) +#define DRM_IOCTL_ETNAVIV_WAIT_FENCE DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_WAIT_FENCE, struct drm_etnaviv_wait_fence) +#define DRM_IOCTL_ETNAVIV_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_USERPTR, struct drm_etnaviv_gem_userptr) +#define DRM_IOCTL_ETNAVIV_GEM_WAIT DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_WAIT, struct drm_etnaviv_gem_wait) + +#endif /* __ETNAVIV_DRM_H__ */ diff --git a/etnaviv/etnaviv_drmif.h b/etnaviv/etnaviv_drmif.h new file mode 100644 index 0000000..979b16a --- /dev/null +++ b/etnaviv/etnaviv_drmif.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2014-2015 Etnaviv Project + * + * 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. + * + * Authors: + * Christian Gmeiner <christian.gmeiner at gmail.com> + */ + +#ifndef ETNAVIV_DRMIF_H_ +#define ETNAVIV_DRMIF_H_ + +#include <xf86drm.h> +#include <stdint.h> + +struct etna_bo; +struct etna_pipe; +struct etna_gpu; +struct etna_device; +struct etna_cmd_stream; + +enum etna_pipe_id { + ETNA_PIPE_3D = 0, + ETNA_PIPE_2D = 1, + ETNA_PIPE_VG = 2, + ETNA_PIPE_MAX +}; + +enum etna_param_id { + ETNA_GPU_MODEL = 0x1, + ETNA_GPU_REVISION = 0x2, + ETNA_GPU_FEATURES_0 = 0x3, + ETNA_GPU_FEATURES_1 = 0x4, + ETNA_GPU_FEATURES_2 = 0x5, + ETNA_GPU_FEATURES_3 = 0x6, + ETNA_GPU_FEATURES_4 = 0x7, + ETNA_GPU_FEATURES_5 = 0x8, + ETNA_GPU_FEATURES_6 = 0x9, + + ETNA_GPU_STREAM_COUNT = 0x10, + ETNA_GPU_REGISTER_MAX = 0x11, + ETNA_GPU_THREAD_COUNT = 0x12, + ETNA_GPU_VERTEX_CACHE_SIZE = 0x13, + ETNA_GPU_SHADER_CORE_COUNT = 0x14, + ETNA_GPU_PIXEL_PIPES = 0x15, + ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE = 0x16, + ETNA_GPU_BUFFER_SIZE = 0x17, + ETNA_GPU_INSTRUCTION_COUNT = 0x18, + ETNA_GPU_NUM_CONSTANTS = 0x19, + ETNA_GPU_NUM_VARYINGS = 0x1a +}; + +/* bo flags: */ +#define DRM_ETNA_GEM_CACHE_CACHED 0x00010000 +#define DRM_ETNA_GEM_CACHE_WC 0x00020000 +#define DRM_ETNA_GEM_CACHE_UNCACHED 0x00040000 +#define DRM_ETNA_GEM_CACHE_MASK 0x000f0000 +/* map flags */ +#define DRM_ETNA_GEM_FORCE_MMU 0x00100000 + +/* bo access flags: (keep aligned to ETNA_PREP_x) */ +#define DRM_ETNA_PREP_READ 0x01 +#define DRM_ETNA_PREP_WRITE 0x02 +#define DRM_ETNA_PREP_NOSYNC 0x04 + +/* device functions: + */ + +struct etna_device *etna_device_new(int fd); +struct etna_device *etna_device_ref(struct etna_device *dev); +void etna_device_del(struct etna_device *dev); + +/* gpu functions: + */ + +struct etna_gpu *etna_gpu_new(struct etna_device *dev, unsigned int core); +void etna_gpu_del(struct etna_gpu *gpu); +int etna_gpu_get_param(struct etna_gpu *gpu, enum etna_param_id param, + uint64_t *value); + + +/* pipe functions: + */ + +struct etna_pipe *etna_pipe_new(struct etna_gpu *gpu, enum etna_pipe_id id); +void etna_pipe_del(struct etna_pipe *pipe); +int etna_pipe_wait(struct etna_pipe *pipe, uint32_t timestamp, uint32_t ms); + + +/* buffer-object functions: + */ + +struct etna_bo *etna_bo_new(struct etna_device *dev, + uint32_t size, uint32_t flags); +struct etna_bo *etna_bo_from_handle(struct etna_device *dev, + uint32_t handle, uint32_t size); +struct etna_bo *etna_bo_from_name(struct etna_device *dev, uint32_t name); +struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd); +struct etna_bo *etna_bo_ref(struct etna_bo *bo); +void etna_bo_del(struct etna_bo *bo); +int etna_bo_get_name(struct etna_bo *bo, uint32_t *name); +uint32_t etna_bo_handle(struct etna_bo *bo); +int etna_bo_dmabuf(struct etna_bo *bo); +uint32_t etna_bo_size(struct etna_bo *bo); +void * etna_bo_map(struct etna_bo *bo); +int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op); +void etna_bo_cpu_fini(struct etna_bo *bo); + + +/* cmd stream functions: + */ + +struct etna_cmd_stream { + uint32_t *buffer; + uint32_t offset; /* in 32-bit words */ + uint32_t size; /* in 32-bit words */ +}; + +struct etna_cmd_stream *etna_cmd_stream_new(struct etna_pipe *pipe, uint32_t size, + void (*reset_notify)(struct etna_cmd_stream *stream, void *priv), + void *priv); +void etna_cmd_stream_del(struct etna_cmd_stream *stream); +uint32_t etna_cmd_stream_timestamp(struct etna_cmd_stream *stream); +void etna_cmd_stream_flush(struct etna_cmd_stream *stream); +void etna_cmd_stream_finish(struct etna_cmd_stream *stream); + +static inline uint32_t etna_cmd_stream_avail(struct etna_cmd_stream *stream) +{ + static const uint32_t END_CLEARANCE = 2; /* LINK op code */ + + return stream->size - stream->offset - END_CLEARANCE; +} + +static inline void etna_cmd_stream_reserve(struct etna_cmd_stream *stream, size_t n) +{ + if (etna_cmd_stream_avail(stream) < n) + etna_cmd_stream_flush(stream); +} + +static inline void etna_cmd_stream_emit(struct etna_cmd_stream *stream, uint32_t data) +{ + stream->buffer[stream->offset++] = data; +} + +static inline uint32_t etna_cmd_stream_get(struct etna_cmd_stream *stream, uint32_t offset) +{ + return stream->buffer[offset]; +} + +static inline void etna_cmd_stream_set(struct etna_cmd_stream *stream, uint32_t offset, + uint32_t data) +{ + stream->buffer[offset] = data; +} + +static inline uint32_t etna_cmd_stream_offset(struct etna_cmd_stream *stream) +{ + return stream->offset; +} + +struct etna_reloc { + struct etna_bo *bo; +#define ETNA_RELOC_READ 0x0001 +#define ETNA_RELOC_WRITE 0x0002 + uint32_t flags; + uint32_t offset; +}; + +void etna_cmd_stream_reloc(struct etna_cmd_stream *stream, const struct etna_reloc *r); + +#endif /* ETNAVIV_DRMIF_H_ */ diff --git a/etnaviv/etnaviv_gpu.c b/etnaviv/etnaviv_gpu.c new file mode 100644 index 0000000..35dec6c --- /dev/null +++ b/etnaviv/etnaviv_gpu.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2015 Etnaviv Project + * + * 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. + * + * Authors: + * Christian Gmeiner <christian.gmeiner at gmail.com> + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "etnaviv_priv.h" +#include "etnaviv_drmif.h" + +static uint64_t get_param(struct etna_device *dev, uint32_t core, uint32_t param) +{ + struct drm_etnaviv_param req = { + .pipe = core, + .param = param, + }; + int ret; + + ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GET_PARAM, &req, sizeof(req)); + if (ret) { + ERROR_MSG("get-param (%x) failed! %d (%s)", param, ret, strerror(errno)); + return 0; + } + + return req.value; +} + +struct etna_gpu *etna_gpu_new(struct etna_device *dev, unsigned int core) +{ + struct etna_gpu *gpu; + + gpu = calloc(1, sizeof(*gpu)); + if (!gpu) { + ERROR_MSG("allocation failed"); + goto fail; + } + + gpu->dev = dev; + gpu->core = core; + + /* get specs from kernel space */ + gpu->specs.model = get_param(dev, core, ETNAVIV_PARAM_GPU_MODEL); + gpu->specs.revision = get_param(dev, core, ETNAVIV_PARAM_GPU_REVISION); + gpu->specs.features[0] = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_0); + gpu->specs.features[1] = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_1); + gpu->specs.features[2] = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_2); + gpu->specs.features[3] = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_3); + gpu->specs.features[4] = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_4); + gpu->specs.features[5] = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_5); + gpu->specs.features[6] = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_6); + gpu->specs.stream_count = get_param(dev, core, ETNA_GPU_STREAM_COUNT); + gpu->specs.register_max = get_param(dev, core, ETNA_GPU_REGISTER_MAX); + gpu->specs.thread_count = get_param(dev, core, ETNA_GPU_THREAD_COUNT); + gpu->specs.vertex_cache_size = get_param(dev, core, ETNA_GPU_VERTEX_CACHE_SIZE); + gpu->specs.shader_core_count = get_param(dev, core, ETNA_GPU_SHADER_CORE_COUNT); + gpu->specs.pixel_pipes = get_param(dev, core, ETNA_GPU_PIXEL_PIPES); + gpu->specs.vertex_output_buffer_size = get_param(dev, core, ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE); + gpu->specs.buffer_size = get_param(dev, core, ETNA_GPU_BUFFER_SIZE); + gpu->specs.instruction_count = get_param(dev, core, ETNA_GPU_INSTRUCTION_COUNT); + gpu->specs.num_constants = get_param(dev, core, ETNA_GPU_NUM_CONSTANTS); + gpu->specs.num_varyings = get_param(dev, core, ETNA_GPU_NUM_VARYINGS); + + if (!gpu->specs.model) + goto fail; + + INFO_MSG(" GPU model: 0x%x (rev %x)", gpu->specs.model, gpu->specs.revision); + + return gpu; +fail: + if (gpu) + etna_gpu_del(gpu); + + return NULL; +} + +void etna_gpu_del(struct etna_gpu *gpu) +{ + free(gpu); +} + +int etna_gpu_get_param(struct etna_gpu *gpu, enum etna_param_id param, + uint64_t *value) +{ + switch(param) { + case ETNA_GPU_MODEL: + *value = gpu->specs.model; + return 0; + case ETNA_GPU_REVISION: + *value = gpu->specs.revision; + return 0; + case ETNA_GPU_FEATURES_0: + *value = gpu->specs.features[0]; + return 0; + case ETNA_GPU_FEATURES_1: + *value = gpu->specs.features[1]; + return 0; + case ETNA_GPU_FEATURES_2: + *value = gpu->specs.features[2]; + return 0; + case ETNA_GPU_FEATURES_3: + *value = gpu->specs.features[3]; + return 0; + case ETNA_GPU_FEATURES_4: + *value = gpu->specs.features[4]; + return 0; + case ETNA_GPU_FEATURES_5: + *value = gpu->specs.features[5]; + return 0; + case ETNA_GPU_FEATURES_6: + *value = gpu->specs.features[6]; + return 0; + case ETNA_GPU_STREAM_COUNT: + *value = gpu->specs.stream_count; + return 0; + case ETNA_GPU_REGISTER_MAX: + *value = gpu->specs.register_max; + return 0; + case ETNA_GPU_THREAD_COUNT: + *value = gpu->specs.thread_count; + return 0; + case ETNA_GPU_VERTEX_CACHE_SIZE: + *value = gpu->specs.vertex_cache_size; + return 0; + case ETNA_GPU_SHADER_CORE_COUNT: + *value = gpu->specs.shader_core_count; + return 0; + case ETNA_GPU_PIXEL_PIPES: + *value = gpu->specs.pixel_pipes; + return 0; + case ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE: + *value = gpu->specs.vertex_output_buffer_size; + return 0; + case ETNA_GPU_BUFFER_SIZE: + *value = gpu->specs.buffer_size; + return 0; + case ETNA_GPU_INSTRUCTION_COUNT: + *value = gpu->specs.instruction_count; + return 0; + case ETNA_GPU_NUM_CONSTANTS: + *value = gpu->specs.num_constants; + return 0; + case ETNA_GPU_NUM_VARYINGS: + *value = gpu->specs.num_varyings; + return 0; + + default: + ERROR_MSG("invalid param id: %d", param); + return -1; + } + + return 0; +} diff --git a/etnaviv/etnaviv_pipe.c b/etnaviv/etnaviv_pipe.c new file mode 100644 index 0000000..402b71d --- /dev/null +++ b/etnaviv/etnaviv_pipe.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2014-2015 Etnaviv Project + * + * 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. + * + * Authors: + * Christian Gmeiner <christian.gmeiner at gmail.com> + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "etnaviv_priv.h" + +int etna_pipe_wait(struct etna_pipe *pipe, uint32_t timestamp, uint32_t ms) +{ + struct etna_device *dev = pipe->gpu->dev; + int ret; + + struct drm_etnaviv_wait_fence req = { + .pipe = pipe->gpu->core, + .fence = timestamp, + }; + + if (ms == 0) + req.flags |= ETNA_WAIT_NONBLOCK; + + get_abs_timeout(&req.timeout, ms); + + ret = drmCommandWrite(dev->fd, DRM_ETNAVIV_WAIT_FENCE, &req, sizeof(req)); + if (ret) { + ERROR_MSG("wait-fence failed! %d (%s)", ret, strerror(errno)); + return ret; + } + + return 0; +} + +void etna_pipe_del(struct etna_pipe *pipe) +{ + free(pipe); +} + +struct etna_pipe *etna_pipe_new(struct etna_gpu *gpu, enum etna_pipe_id id) +{ + struct etna_pipe *pipe; + + pipe = calloc(1, sizeof(*pipe)); + if (!pipe) { + ERROR_MSG("allocation failed"); + goto fail; + } + + pipe->id = id; + pipe->gpu = gpu; + + return pipe; +fail: + return NULL; +} diff --git a/etnaviv/etnaviv_priv.h b/etnaviv/etnaviv_priv.h new file mode 100644 index 0000000..42d9a45 --- /dev/null +++ b/etnaviv/etnaviv_priv.h @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2014-2015 Etnaviv Project + * + * 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. + * + * Authors: + * Christian Gmeiner <christian.gmeiner at gmail.com> + */ + +#ifndef ETNAVIV_PRIV_H_ +#define ETNAVIV_PRIV_H_ + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <pthread.h> +#include <stdio.h> +#include <assert.h> + +#include "libdrm_macros.h" +#include "xf86drm.h" +#include "xf86atomic.h" + +#include "util_double_list.h" + +#include "etnaviv_drmif.h" +//#include "drm.h" +#include "etnaviv_drm.h" + +#define VIV_FEATURES_WORD_COUNT 6 + +struct etna_specs { + uint32_t model; + uint32_t revision; + uint32_t features[VIV_FEATURES_WORD_COUNT + 1]; + uint32_t stream_count; + uint32_t register_max; + uint32_t thread_count; + uint32_t shader_core_count; + uint32_t vertex_cache_size; + uint32_t vertex_output_buffer_size; + uint32_t pixel_pipes; + uint32_t instruction_count; + uint32_t num_constants; + uint32_t num_varyings; + uint32_t buffer_size; +}; + +struct etna_bo_bucket { + uint32_t size; + struct list_head list; +}; + +struct etna_bo_cache { + struct etna_bo_bucket cache_bucket[14 * 4]; + unsigned num_buckets; + time_t time; +}; + +struct etna_device { + int fd; + atomic_t refcnt; + + /* tables to keep track of bo's, to avoid "evil-twin" etna_bo objects: + * + * handle_table: maps handle to etna_bo + * name_table: maps flink name to etna_bo + * + * We end up needing two tables, because DRM_IOCTL_GEM_OPEN always + * returns a new handle. So we need to figure out if the bo is already + * open in the process first, before calling gem-open. + */ + void *handle_table, *name_table; + + struct etna_bo_cache bo_cache; +}; + +drm_private void etna_bo_cache_init(struct etna_bo_cache *cache); +drm_private void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t time); +drm_private struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache, + uint32_t *size, uint32_t flags); +drm_private int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo *bo); + +/* for where @table_lock is already held: */ +drm_private void etna_device_del_locked(struct etna_device *dev); + +/* a GEM buffer object allocated from the DRM device */ +struct etna_bo { + struct etna_device *dev; + void *map; /* userspace mmap'ing (if there is one) */ + uint32_t size; + uint32_t handle; + uint32_t flags; + uint32_t name; /* flink global handle (DRI2 name) */ + uint64_t offset; /* offset to mmap() */ + atomic_t refcnt; + + /* in the common case, a bo won't be referenced by more than a single + * command stream. So to avoid looping over all the bo's in the + * reloc table to find the idx of a bo that might already be in the + * table, we cache the idx in the bo. But in order to detect the + * slow-path where bo is ref'd in multiple streams, we also must track + * the current_stream for which the idx is valid. See bo2idx(). + */ + struct etna_cmd_stream *current_stream; + uint32_t idx; + + int reuse; + struct list_head list; /* bucket-list entry */ + time_t free_time; /* time when added to bucket-list */ +}; + +struct etna_gpu { + struct etna_device *dev; + struct etna_specs specs; + uint32_t core; +}; + +struct etna_pipe { + enum etna_pipe_id id; + struct etna_gpu *gpu; +}; + +struct etna_cmd_stream_priv { + struct etna_cmd_stream base; + struct etna_pipe *pipe; + + uint32_t last_timestamp; + + /* submit ioctl related tables: */ + struct { + /* bo's table: */ + struct drm_etnaviv_gem_submit_bo *bos; + uint32_t nr_bos, max_bos; + + /* reloc's table: */ + struct drm_etnaviv_gem_submit_reloc *relocs; + uint32_t nr_relocs, max_relocs; + } submit; + + /* should have matching entries in submit.bos: */ + struct etna_bo **bos; + uint32_t nr_bos, max_bos; + + /* notify callback if buffer reset happend */ + void (*reset_notify)(struct etna_cmd_stream *stream, void *priv); + void *reset_notify_priv; +}; + +#define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1)) +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +#define enable_debug 1 /* TODO make dynamic */ + +#define INFO_MSG(fmt, ...) \ + do { drmMsg("[I] "fmt " (%s:%d)\n", \ + ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0) +#define DEBUG_MSG(fmt, ...) \ + do if (enable_debug) { drmMsg("[D] "fmt " (%s:%d)\n", \ + ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0) +#define WARN_MSG(fmt, ...) \ + do { drmMsg("[W] "fmt " (%s:%d)\n", \ + ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0) +#define ERROR_MSG(fmt, ...) \ + do { drmMsg("[E] " fmt " (%s:%d)\n", \ + ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0) + +#define VOID2U64(x) ((uint64_t)(unsigned long)(x)) + +static inline void get_abs_timeout(struct drm_etnaviv_timespec *tv, uint32_t ms) +{ + struct timespec t; + uint32_t s = ms / 1000; + clock_gettime(CLOCK_MONOTONIC, &t); + tv->tv_sec = t.tv_sec + s; + tv->tv_nsec = t.tv_nsec + ((ms - (s * 1000)) * 1000000); +} + +#endif /* ETNAVIV_PRIV_H_ */ diff --git a/etnaviv/libdrm_etnaviv.pc.in b/etnaviv/libdrm_etnaviv.pc.in new file mode 100644 index 0000000..13fed01 --- /dev/null +++ b/etnaviv/libdrm_etnaviv.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libdrm_etnaviv +Description: Userspace interface to etnaviv kernel DRM services +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -ldrm_etnaviv +Cflags: -I${includedir} -I${includedir}/libdrm +Requires.private: libdrm -- 2.7.4