diff options
Diffstat (limited to 'etnaviv')
-rw-r--r-- | etnaviv/Android.mk | 14 | ||||
-rw-r--r-- | etnaviv/Makefile.sources | 13 | ||||
-rw-r--r-- | etnaviv/etnaviv-symbols.txt | 36 | ||||
-rw-r--r-- | etnaviv/etnaviv_bo.c | 340 | ||||
-rw-r--r-- | etnaviv/etnaviv_bo_cache.c | 204 | ||||
-rw-r--r-- | etnaviv/etnaviv_cmd_stream.c | 279 | ||||
-rw-r--r-- | etnaviv/etnaviv_device.c | 115 | ||||
-rw-r--r-- | etnaviv/etnaviv_drm.h | 300 | ||||
-rw-r--r-- | etnaviv/etnaviv_drmif.h | 214 | ||||
-rw-r--r-- | etnaviv/etnaviv_gpu.c | 155 | ||||
-rw-r--r-- | etnaviv/etnaviv_perfmon.c | 185 | ||||
-rw-r--r-- | etnaviv/etnaviv_pipe.c | 79 | ||||
-rw-r--r-- | etnaviv/etnaviv_priv.h | 208 | ||||
-rw-r--r-- | etnaviv/libdrm_etnaviv.pc.in | 11 | ||||
-rw-r--r-- | etnaviv/meson.build | 66 |
15 files changed, 2219 insertions, 0 deletions
diff --git a/etnaviv/Android.mk b/etnaviv/Android.mk new file mode 100644 index 0000000..390f9a9 --- /dev/null +++ b/etnaviv/Android.mk @@ -0,0 +1,14 @@ +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_SHARED_LIBRARIES := libdrm + +LOCAL_SRC_FILES := $(LIBDRM_ETNAVIV_FILES) + +include $(LIBDRM_COMMON_MK) +include $(BUILD_SHARED_LIBRARY) diff --git a/etnaviv/Makefile.sources b/etnaviv/Makefile.sources new file mode 100644 index 0000000..0eb7378 --- /dev/null +++ b/etnaviv/Makefile.sources @@ -0,0 +1,13 @@ +LIBDRM_ETNAVIV_FILES := \ + etnaviv_device.c \ + etnaviv_gpu.c \ + etnaviv_bo.c \ + etnaviv_bo_cache.c \ + etnaviv_perfmon.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-symbols.txt b/etnaviv/etnaviv-symbols.txt new file mode 100644 index 0000000..f48cece --- /dev/null +++ b/etnaviv/etnaviv-symbols.txt @@ -0,0 +1,36 @@ +etna_device_new +etna_device_new_dup +etna_device_ref +etna_device_del +etna_device_fd +etna_gpu_new +etna_gpu_del +etna_gpu_get_param +etna_pipe_new +etna_pipe_del +etna_pipe_wait +etna_pipe_wait_ns +etna_bo_new +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_flush2 +etna_cmd_stream_finish +etna_cmd_stream_perf +etna_cmd_stream_reloc +etna_perfmon_create +etna_perfmon_del +etna_perfmon_get_dom_by_name +etna_perfmon_get_sig_by_name diff --git a/etnaviv/etnaviv_bo.c b/etnaviv/etnaviv_bo.c new file mode 100644 index 0000000..27123e6 --- /dev/null +++ b/etnaviv/etnaviv_bo.c @@ -0,0 +1,340 @@ +/* + * 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@gmail.com> + */ + +#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) { + drmHashDelete(bo->dev->handle_table, bo->handle); + drmCloseBufferHandle(bo->dev->fd, bo->handle); + } + + 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) { + drmCloseBufferHandle(dev->fd, handle); + 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 */ +drm_public 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; +} + +drm_public 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 */ +drm_public 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, name); + 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 *') + */ +drm_public struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd) +{ + struct etna_bo *bo; + int ret, size; + uint32_t handle; + + /* take the lock before calling drmPrimeFDToHandle to avoid + * racing against etna_bo_del, which might invalidate the + * returned handle. + */ + pthread_mutex_lock(&table_lock); + + ret = drmPrimeFDToHandle(dev->fd, fd, &handle); + if (ret) { + pthread_mutex_unlock(&table_lock); + 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 */ +drm_public 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 */ +drm_public 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; +} + +drm_public 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 + */ +drm_public 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; +} + +drm_public uint32_t etna_bo_size(struct etna_bo *bo) +{ + return bo->size; +} + +drm_public 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; +} + +drm_public 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, 5000000000); + + return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP, + &req, sizeof(req)); +} + +drm_public 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..c81de26 --- /dev/null +++ b/etnaviv/etnaviv_bo_cache.c @@ -0,0 +1,204 @@ +/* + * 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@gmail.com> + */ + +#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, *tmp; + + pthread_mutex_lock(&table_lock); + + if (LIST_IS_EMPTY(&bucket->list)) + goto out_unlock; + + LIST_FOR_EACH_ENTRY_SAFE(bo, tmp, &bucket->list, list) { + /* skip BOs with different flags */ + if (bo->flags != flags) + continue; + + /* check if the first BO with matching flags is idle */ + if (is_idle(bo)) { + list_delinit(&bo->list); + goto out_unlock; + } + + /* If the oldest BO is still busy, don't try younger ones */ + break; + } + + /* There was no matching buffer found */ + bo = NULL; + +out_unlock: + 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..261777b --- /dev/null +++ b/etnaviv/etnaviv_cmd_stream.c @@ -0,0 +1,279 @@ +/* + * 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@gmail.com> + */ + +#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; +} + +drm_public 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; +} + +drm_public 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->submit.pmrs); + 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->submit.nr_pmrs = 0; + priv->nr_bos = 0; + + if (priv->reset_notify) + priv->reset_notify(stream, priv->reset_notify_priv); +} + +drm_public 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 == 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); + } + bo->current_stream = stream; + bo->idx = idx; + } + 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, int in_fence_fd, + int *out_fence_fd) +{ + 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, + .pmrs = VOID2U64(priv->submit.pmrs), + .nr_pmrs = priv->submit.nr_pmrs, + .stream = VOID2U64(stream->buffer), + .stream_size = stream->offset * 4, /* in bytes */ + }; + + if (in_fence_fd != -1) { + req.flags |= ETNA_SUBMIT_FENCE_FD_IN | ETNA_SUBMIT_NO_IMPLICIT; + req.fence_fd = in_fence_fd; + } + + if (out_fence_fd) + req.flags |= ETNA_SUBMIT_FENCE_FD_OUT; + + 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); + } + + if (out_fence_fd) + *out_fence_fd = req.fence_fd; +} + +drm_public void etna_cmd_stream_flush(struct etna_cmd_stream *stream) +{ + flush(stream, -1, NULL); + reset_buffer(stream); +} + +drm_public void etna_cmd_stream_flush2(struct etna_cmd_stream *stream, + int in_fence_fd, + int *out_fence_fd) +{ + flush(stream, in_fence_fd, out_fence_fd); + reset_buffer(stream); +} + +drm_public void etna_cmd_stream_finish(struct etna_cmd_stream *stream) +{ + struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); + + flush(stream, -1, NULL); + etna_pipe_wait(priv->pipe, priv->last_timestamp, 5000); + reset_buffer(stream); +} + +drm_public 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); +} + +drm_public void etna_cmd_stream_perf(struct etna_cmd_stream *stream, const struct etna_perf *p) +{ + struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream); + struct drm_etnaviv_gem_submit_pmr *pmr; + uint32_t idx = APPEND(&priv->submit, pmrs); + + pmr = &priv->submit.pmrs[idx]; + + pmr->flags = p->flags; + pmr->sequence = p->sequence; + pmr->read_offset = p->offset; + pmr->read_idx = bo2idx(stream, p->bo, ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE); + pmr->domain = p->signal->domain->id; + pmr->signal = p->signal->signal; +} diff --git a/etnaviv/etnaviv_device.c b/etnaviv/etnaviv_device.c new file mode 100644 index 0000000..699df25 --- /dev/null +++ b/etnaviv/etnaviv_device.c @@ -0,0 +1,115 @@ +/* + * 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@gmail.com> + */ + +#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; + +drm_public 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; +} + +/* like etna_device_new() but creates it's own private dup() of the fd + * which is close()d when the device is finalized. */ +drm_public struct etna_device *etna_device_new_dup(int fd) +{ + int dup_fd = dup(fd); + struct etna_device *dev = etna_device_new(dup_fd); + + if (dev) + dev->closefd = 1; + else + close(dup_fd); + + return dev; +} + +drm_public 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); + + if (dev->closefd) + close(dev->fd); + + 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); +} + +drm_public 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); +} + +drm_public int etna_device_fd(struct etna_device *dev) +{ + return dev->fd; +} diff --git a/etnaviv/etnaviv_drm.h b/etnaviv/etnaviv_drm.h new file mode 100644 index 0000000..af024d9 --- /dev/null +++ b/etnaviv/etnaviv_drm.h @@ -0,0 +1,300 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * 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" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* 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_FEATURES_7 0x0a +#define ETNAVIV_PARAM_GPU_FEATURES_8 0x0b +#define ETNAVIV_PARAM_GPU_FEATURES_9 0x0c +#define ETNAVIV_PARAM_GPU_FEATURES_10 0x0d +#define ETNAVIV_PARAM_GPU_FEATURES_11 0x0e +#define ETNAVIV_PARAM_GPU_FEATURES_12 0x0f + +#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 ETNAVIV_PARAM_SOFTPIN_START_ADDR 0x1b +#define ETNAVIV_PARAM_GPU_PRODUCT_ID 0x1c +#define ETNAVIV_PARAM_GPU_CUSTOMER_ID 0x1d +#define ETNAVIV_PARAM_GPU_ECO_ID 0x1e + +#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. + * If the submit is a softpin submit (ETNA_SUBMIT_SOFTPIN) the 'presumed' + * field is interpreted as the fixed location to map the bo into the gpu + * virtual address space. If the kernel is unable to map the buffer at + * this location the submit will fail. This means userspace is responsible + * for the whole gpu virtual address management. + */ +#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 */ +}; + +/* performance monitor request (pmr) */ +#define ETNA_PM_PROCESS_PRE 0x0001 +#define ETNA_PM_PROCESS_POST 0x0002 +struct drm_etnaviv_gem_submit_pmr { + __u32 flags; /* in, when to process request (ETNA_PM_PROCESS_x) */ + __u8 domain; /* in, pm domain */ + __u8 pad; + __u16 signal; /* in, pm signal */ + __u32 sequence; /* in, sequence number */ + __u32 read_offset; /* in, offset from read_bo */ + __u32 read_idx; /* in, index of read_bo buffer */ +}; + +/* 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_SUBMIT_NO_IMPLICIT 0x0001 +#define ETNA_SUBMIT_FENCE_FD_IN 0x0002 +#define ETNA_SUBMIT_FENCE_FD_OUT 0x0004 +#define ETNA_SUBMIT_SOFTPIN 0x0008 +#define ETNA_SUBMIT_FLAGS (ETNA_SUBMIT_NO_IMPLICIT | \ + ETNA_SUBMIT_FENCE_FD_IN | \ + ETNA_SUBMIT_FENCE_FD_OUT| \ + ETNA_SUBMIT_SOFTPIN) +#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 */ + __u32 flags; /* in, mask of ETNA_SUBMIT_x */ + __s32 fence_fd; /* in/out, fence fd (see ETNA_SUBMIT_FENCE_FD_x) */ + __u64 pmrs; /* in, ptr to array of submit_pmr's */ + __u32 nr_pmrs; /* in, number of submit_pmr's */ + __u32 pad; +}; + +/* 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 */ +}; + +/* + * Performance Monitor (PM): + */ + +struct drm_etnaviv_pm_domain { + __u32 pipe; /* in */ + __u8 iter; /* in/out, select pm domain at index iter */ + __u8 id; /* out, id of domain */ + __u16 nr_signals; /* out, how many signals does this domain provide */ + char name[64]; /* out, name of domain */ +}; + +struct drm_etnaviv_pm_signal { + __u32 pipe; /* in */ + __u8 domain; /* in, pm domain index */ + __u8 pad; + __u16 iter; /* in/out, select pm source at index iter */ + __u16 id; /* out, id of signal */ + char name[64]; /* out, name of domain */ +}; + +#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_PM_QUERY_DOM 0x0a +#define DRM_ETNAVIV_PM_QUERY_SIG 0x0b +#define DRM_ETNAVIV_NUM_IOCTLS 0x0c + +#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) +#define DRM_IOCTL_ETNAVIV_PM_QUERY_DOM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_PM_QUERY_DOM, struct drm_etnaviv_pm_domain) +#define DRM_IOCTL_ETNAVIV_PM_QUERY_SIG DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_PM_QUERY_SIG, struct drm_etnaviv_pm_signal) + +#if defined(__cplusplus) +} +#endif + +#endif /* __ETNAVIV_DRM_H__ */ diff --git a/etnaviv/etnaviv_drmif.h b/etnaviv/etnaviv_drmif.h new file mode 100644 index 0000000..80aedc1 --- /dev/null +++ b/etnaviv/etnaviv_drmif.h @@ -0,0 +1,214 @@ +/* + * 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@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; +struct etna_perfmon; +struct etna_perfmon_domain; +struct etna_perfmon_signal; + +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_new_dup(int fd); +struct etna_device *etna_device_ref(struct etna_device *dev); +void etna_device_del(struct etna_device *dev); +int etna_device_fd(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); +int etna_pipe_wait_ns(struct etna_pipe *pipe, uint32_t timestamp, uint64_t ns); + + +/* 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_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_flush2(struct etna_cmd_stream *stream, int in_fence_fd, + int *out_fence_fd); +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); + +/* performance monitoring functions: + */ + +struct etna_perfmon *etna_perfmon_create(struct etna_pipe *pipe); +void etna_perfmon_del(struct etna_perfmon *perfmon); +struct etna_perfmon_domain *etna_perfmon_get_dom_by_name(struct etna_perfmon *pm, const char *name); +struct etna_perfmon_signal *etna_perfmon_get_sig_by_name(struct etna_perfmon_domain *dom, const char *name); + +struct etna_perf { +#define ETNA_PM_PROCESS_PRE 0x0001 +#define ETNA_PM_PROCESS_POST 0x0002 + uint32_t flags; + uint32_t sequence; + struct etna_perfmon_signal *signal; + struct etna_bo *bo; + uint32_t offset; +}; + +void etna_cmd_stream_perf(struct etna_cmd_stream *stream, const struct etna_perf *p); + +#endif /* ETNAVIV_DRMIF_H_ */ diff --git a/etnaviv/etnaviv_gpu.c b/etnaviv/etnaviv_gpu.c new file mode 100644 index 0000000..dc4c126 --- /dev/null +++ b/etnaviv/etnaviv_gpu.c @@ -0,0 +1,155 @@ +/* + * 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@gmail.com> + */ + +#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; +} + +drm_public 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; + + gpu->model = get_param(dev, core, ETNAVIV_PARAM_GPU_MODEL); + gpu->revision = get_param(dev, core, ETNAVIV_PARAM_GPU_REVISION); + + if (!gpu->model) + goto fail; + + INFO_MSG(" GPU model: 0x%x (rev %x)", gpu->model, gpu->revision); + + return gpu; +fail: + if (gpu) + etna_gpu_del(gpu); + + return NULL; +} + +drm_public void etna_gpu_del(struct etna_gpu *gpu) +{ + free(gpu); +} + +drm_public int etna_gpu_get_param(struct etna_gpu *gpu, enum etna_param_id param, + uint64_t *value) +{ + struct etna_device *dev = gpu->dev; + unsigned int core = gpu->core; + + switch(param) { + case ETNA_GPU_MODEL: + *value = gpu->model; + return 0; + case ETNA_GPU_REVISION: + *value = gpu->revision; + return 0; + case ETNA_GPU_FEATURES_0: + *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_0); + return 0; + case ETNA_GPU_FEATURES_1: + *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_1); + return 0; + case ETNA_GPU_FEATURES_2: + *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_2); + return 0; + case ETNA_GPU_FEATURES_3: + *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_3); + return 0; + case ETNA_GPU_FEATURES_4: + *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_4); + return 0; + case ETNA_GPU_FEATURES_5: + *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_5); + return 0; + case ETNA_GPU_FEATURES_6: + *value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_6); + return 0; + case ETNA_GPU_STREAM_COUNT: + *value = get_param(dev, core, ETNA_GPU_STREAM_COUNT); + return 0; + case ETNA_GPU_REGISTER_MAX: + *value = get_param(dev, core, ETNA_GPU_REGISTER_MAX); + return 0; + case ETNA_GPU_THREAD_COUNT: + *value = get_param(dev, core, ETNA_GPU_THREAD_COUNT); + return 0; + case ETNA_GPU_VERTEX_CACHE_SIZE: + *value = get_param(dev, core, ETNA_GPU_VERTEX_CACHE_SIZE); + return 0; + case ETNA_GPU_SHADER_CORE_COUNT: + *value = get_param(dev, core, ETNA_GPU_SHADER_CORE_COUNT); + return 0; + case ETNA_GPU_PIXEL_PIPES: + *value = get_param(dev, core, ETNA_GPU_PIXEL_PIPES); + return 0; + case ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE: + *value = get_param(dev, core, ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE); + return 0; + case ETNA_GPU_BUFFER_SIZE: + *value = get_param(dev, core, ETNA_GPU_BUFFER_SIZE); + return 0; + case ETNA_GPU_INSTRUCTION_COUNT: + *value = get_param(dev, core, ETNA_GPU_INSTRUCTION_COUNT); + return 0; + case ETNA_GPU_NUM_CONSTANTS: + *value = get_param(dev, core, ETNA_GPU_NUM_CONSTANTS); + return 0; + case ETNA_GPU_NUM_VARYINGS: + *value = get_param(dev, core, ETNA_GPU_NUM_VARYINGS); + return 0; + + default: + ERROR_MSG("invalid param id: %d", param); + return -1; + } + + return 0; +} diff --git a/etnaviv/etnaviv_perfmon.c b/etnaviv/etnaviv_perfmon.c new file mode 100644 index 0000000..f6576b8 --- /dev/null +++ b/etnaviv/etnaviv_perfmon.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2017 Etnaviv Project + * Copyright (C) 2017 Zodiac Inflight Innovations + * + * 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@gmail.com> + */ + +#include "etnaviv_priv.h" + +static int etna_perfmon_query_signals(struct etna_perfmon *pm, struct etna_perfmon_domain *dom) +{ + struct etna_device *dev = pm->pipe->gpu->dev; + struct drm_etnaviv_pm_signal req = { + .pipe = pm->pipe->id, + .domain = dom->id + }; + + do { + struct etna_perfmon_signal *sig; + int ret; + + ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_PM_QUERY_SIG, &req, sizeof(req)); + if (ret) + break; + + sig = calloc(1, sizeof(*sig)); + if (!sig) + return -ENOMEM; + + INFO_MSG("perfmon signal:"); + INFO_MSG("id = %d", req.id); + INFO_MSG("name = %s", req.name); + + sig->domain = dom; + sig->signal = req.id; + strncpy(sig->name, req.name, sizeof(sig->name)); + list_addtail(&sig->head, &dom->signals); + } while (req.iter != 0xffff); + + return 0; +} + +static int etna_perfmon_query_domains(struct etna_perfmon *pm) +{ + struct etna_device *dev = pm->pipe->gpu->dev; + struct drm_etnaviv_pm_domain req = { + .pipe = pm->pipe->id + }; + + do { + struct etna_perfmon_domain *dom; + int ret; + + ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_PM_QUERY_DOM, &req, sizeof(req)); + if (ret) + break; + + dom = calloc(1, sizeof(*dom)); + if (!dom) + return -ENOMEM; + + list_inithead(&dom->signals); + dom->id = req.id; + strncpy(dom->name, req.name, sizeof(dom->name)); + list_addtail(&dom->head, &pm->domains); + + INFO_MSG("perfmon domain:"); + INFO_MSG("id = %d", req.id); + INFO_MSG("name = %s", req.name); + INFO_MSG("nr_signals = %d", req.nr_signals); + + /* Query all available signals for this domain. */ + if (req.nr_signals > 0) { + ret = etna_perfmon_query_signals(pm, dom); + if (ret) + return ret; + } + } while (req.iter != 0xff); + + return 0; +} + +static void etna_perfmon_free_signals(struct etna_perfmon_domain *dom) +{ + struct etna_perfmon_signal *sig, *next; + + LIST_FOR_EACH_ENTRY_SAFE(sig, next, &dom->signals, head) { + list_del(&sig->head); + free(sig); + } +} + +static void etna_perfmon_free_domains(struct etna_perfmon *pm) +{ + struct etna_perfmon_domain *dom, *next; + + LIST_FOR_EACH_ENTRY_SAFE(dom, next, &pm->domains, head) { + etna_perfmon_free_signals(dom); + list_del(&dom->head); + free(dom); + } +} + +drm_public struct etna_perfmon *etna_perfmon_create(struct etna_pipe *pipe) +{ + struct etna_perfmon *pm; + int ret; + + pm = calloc(1, sizeof(*pm)); + if (!pm) { + ERROR_MSG("allocation failed"); + return NULL; + } + + list_inithead(&pm->domains); + pm->pipe = pipe; + + /* query all available domains and sources for this device */ + ret = etna_perfmon_query_domains(pm); + if (ret) + goto fail; + + return pm; + +fail: + etna_perfmon_del(pm); + return NULL; +} + +drm_public void etna_perfmon_del(struct etna_perfmon *pm) +{ + if (!pm) + return; + + etna_perfmon_free_domains(pm); + free(pm); +} + +drm_public struct etna_perfmon_domain *etna_perfmon_get_dom_by_name(struct etna_perfmon *pm, const char *name) +{ + struct etna_perfmon_domain *dom; + + if (pm) { + LIST_FOR_EACH_ENTRY(dom, &pm->domains, head) { + if (!strcmp(dom->name, name)) + return dom; + } + } + + return NULL; +} + +drm_public struct etna_perfmon_signal *etna_perfmon_get_sig_by_name(struct etna_perfmon_domain *dom, const char *name) +{ + struct etna_perfmon_signal *signal; + + if (dom) { + LIST_FOR_EACH_ENTRY(signal, &dom->signals, head) { + if (!strcmp(signal->name, name)) + return signal; + } + } + + return NULL; +} diff --git a/etnaviv/etnaviv_pipe.c b/etnaviv/etnaviv_pipe.c new file mode 100644 index 0000000..4120a36 --- /dev/null +++ b/etnaviv/etnaviv_pipe.c @@ -0,0 +1,79 @@ +/* + * 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@gmail.com> + */ + +#include "etnaviv_priv.h" + +drm_public int etna_pipe_wait(struct etna_pipe *pipe, uint32_t timestamp, uint32_t ms) +{ + return etna_pipe_wait_ns(pipe, timestamp, ms * 1000000); +} + +drm_public int etna_pipe_wait_ns(struct etna_pipe *pipe, uint32_t timestamp, uint64_t ns) +{ + struct etna_device *dev = pipe->gpu->dev; + int ret; + + struct drm_etnaviv_wait_fence req = { + .pipe = pipe->gpu->core, + .fence = timestamp, + }; + + if (ns == 0) + req.flags |= ETNA_WAIT_NONBLOCK; + + get_abs_timeout(&req.timeout, ns); + + 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; +} + +drm_public void etna_pipe_del(struct etna_pipe *pipe) +{ + free(pipe); +} + +drm_public 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..eef7f49 --- /dev/null +++ b/etnaviv/etnaviv_priv.h @@ -0,0 +1,208 @@ +/* + * 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@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 "etnaviv_drm.h" + +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; + + int closefd; /* call close(fd) upon destruction */ +}; + +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; + uint32_t core; + uint32_t model; + uint32_t revision; +}; + +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; + + /* perf's table: */ + struct drm_etnaviv_gem_submit_pmr *pmrs; + uint32_t nr_pmrs, max_pmrs; + } submit; + + /* should have matching entries in submit.bos: */ + struct etna_bo **bos; + uint32_t nr_bos, max_bos; + + /* notify callback if buffer reset happened */ + void (*reset_notify)(struct etna_cmd_stream *stream, void *priv); + void *reset_notify_priv; +}; + +struct etna_perfmon { + struct list_head domains; + struct etna_pipe *pipe; +}; + +struct etna_perfmon_domain +{ + struct list_head head; + struct list_head signals; + uint8_t id; + char name[64]; +}; + +struct etna_perfmon_signal +{ + struct list_head head; + struct etna_perfmon_domain *domain; + uint8_t signal; + char name[64]; +}; + +#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, uint64_t ns) +{ + struct timespec t; + uint32_t s = ns / 1000000000; + clock_gettime(CLOCK_MONOTONIC, &t); + tv->tv_sec = t.tv_sec + s; + tv->tv_nsec = t.tv_nsec + ns - (s * 1000000000); +} + +#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 diff --git a/etnaviv/meson.build b/etnaviv/meson.build new file mode 100644 index 0000000..9ccf869 --- /dev/null +++ b/etnaviv/meson.build @@ -0,0 +1,66 @@ +# Copyright © 2017-2018 Intel 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 +# 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. + + +libdrm_etnaviv = library( + 'drm_etnaviv', + [ + files( + 'etnaviv_device.c', 'etnaviv_gpu.c', 'etnaviv_bo.c', 'etnaviv_bo_cache.c', + 'etnaviv_perfmon.c', 'etnaviv_pipe.c', 'etnaviv_cmd_stream.c', + ), + config_file + ], + include_directories : [inc_root, inc_drm], + link_with : libdrm, + c_args : libdrm_c_args, + gnu_symbol_visibility : 'hidden', + dependencies : [dep_threads, dep_rt, dep_atomic_ops], + version : '1.0.0', + install : true, +) + +install_headers('etnaviv_drmif.h', subdir : 'libdrm') + +pkg.generate( + libdrm_etnaviv, + name : 'libdrm_etnaviv', + subdirs : ['.', 'libdrm'], + description : 'Userspace interface to Tegra kernel DRM services', +) + +ext_libdrm_etnaviv = declare_dependency( + link_with : [libdrm, libdrm_etnaviv], + include_directories : [inc_drm, include_directories('.')], +) + +if meson.version().version_compare('>= 0.54.0') + meson.override_dependency('libdrm_etnaviv', ext_libdrm_etnaviv) +endif + +test( + 'etnaviv-symbols-check', + symbols_check, + args : [ + '--lib', libdrm_etnaviv, + '--symbols-file', files('etnaviv-symbols.txt'), + '--nm', prog_nm.path(), + ], +) |