libdrm: add etnaviv drm support

Add the libdrm_etnaviv helper library to encapsulate etnaviv-specific
interfaces to the DRM.

Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Tested-by: Rob Herring <robh@kernel.org>
main
The etnaviv authors 2016-09-06 18:15:53 +02:00 committed by Christian Gmeiner
parent 2d00869599
commit 95e2cc6a80
17 changed files with 1890 additions and 0 deletions

1
.gitignore vendored
View File

@ -57,6 +57,7 @@ libdrm_exynos.pc
libdrm_freedreno.pc libdrm_freedreno.pc
libdrm_amdgpu.pc libdrm_amdgpu.pc
libdrm_vc4.pc libdrm_vc4.pc
libdrm_etnaviv.pc
libkms.pc libkms.pc
libtool libtool
ltmain.sh ltmain.sh

View File

@ -36,6 +36,7 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \
--enable-freedreno \ --enable-freedreno \
--enable-freedreno-kgsl\ --enable-freedreno-kgsl\
--enable-tegra-experimental-api \ --enable-tegra-experimental-api \
--enable-etnaviv-experimental-api \
--enable-install-test-programs \ --enable-install-test-programs \
--enable-cairo-tests \ --enable-cairo-tests \
--enable-manpages \ --enable-manpages \
@ -84,6 +85,10 @@ if HAVE_VC4
VC4_SUBDIR = vc4 VC4_SUBDIR = vc4
endif endif
if HAVE_ETNAVIV
ETNAVIV_SUBDIR = etnaviv
endif
if BUILD_MANPAGES if BUILD_MANPAGES
if HAVE_MANPAGES_STYLESHEET if HAVE_MANPAGES_STYLESHEET
MAN_SUBDIR = man MAN_SUBDIR = man
@ -102,6 +107,7 @@ SUBDIRS = \
$(FREEDRENO_SUBDIR) \ $(FREEDRENO_SUBDIR) \
$(TEGRA_SUBDIR) \ $(TEGRA_SUBDIR) \
$(VC4_SUBDIR) \ $(VC4_SUBDIR) \
$(ETNAVIV_SUBDIR) \
tests \ tests \
$(MAN_SUBDIR) $(MAN_SUBDIR)

View File

@ -132,6 +132,11 @@ AC_ARG_ENABLE(vc4,
[Enable support for vc4's API (default: auto, enabled on arm)]), [Enable support for vc4's API (default: auto, enabled on arm)]),
[VC4=$enableval], [VC4=auto]) [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, AC_ARG_ENABLE(install-test-programs,
AS_HELP_STRING([--enable-install-test-programs], AS_HELP_STRING([--enable-install-test-programs],
[Install test programs (default: no)]), [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) LIBDRM_ATOMICS_NOT_FOUND_MSG($TEGRA, tegra, NVIDIA Tegra, tegra-experimental-api)
TEGRA=no TEGRA=no
LIBDRM_ATOMICS_NOT_FOUND_MSG($ETNAVIV, etnaviv, Vivante, etnaviv-experimental-api)
ETNAVIV=no
else else
if test "x$INTEL" = xauto; then if test "x$INTEL" = xauto; then
case $host_cpu in case $host_cpu in
@ -413,6 +421,11 @@ if test "x$VC4" = xyes; then
AC_DEFINE(HAVE_VC4, 1, [Have VC4 support]) AC_DEFINE(HAVE_VC4, 1, [Have VC4 support])
fi 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]) AM_CONDITIONAL(HAVE_INSTALL_TESTS, [test "x$INSTALL_TESTS" = xyes])
if test "x$INSTALL_TESTS" = xyes; then if test "x$INSTALL_TESTS" = xyes; then
AC_DEFINE(HAVE_INSTALL_TESTS, 1, [Install test programs]) AC_DEFINE(HAVE_INSTALL_TESTS, 1, [Install test programs])
@ -524,6 +537,8 @@ AC_CONFIG_FILES([
tegra/libdrm_tegra.pc tegra/libdrm_tegra.pc
vc4/Makefile vc4/Makefile
vc4/libdrm_vc4.pc vc4/libdrm_vc4.pc
etnaviv/Makefile
etnaviv/libdrm_etnaviv.pc
tests/Makefile tests/Makefile
tests/modeprint/Makefile tests/modeprint/Makefile
tests/modetest/Makefile tests/modetest/Makefile
@ -555,4 +570,5 @@ echo " EXYNOS API $EXYNOS"
echo " Freedreno API $FREEDRENO (kgsl: $FREEDRENO_KGSL)" echo " Freedreno API $FREEDRENO (kgsl: $FREEDRENO_KGSL)"
echo " Tegra API $TEGRA" echo " Tegra API $TEGRA"
echo " VC4 API $VC4" echo " VC4 API $VC4"
echo " Etnaviv API $ETNAVIV"
echo "" echo ""

18
etnaviv/Android.mk Normal file
View File

@ -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)

26
etnaviv/Makefile.am Normal file
View File

@ -0,0 +1,26 @@
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 1: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)

12
etnaviv/Makefile.sources Normal file
View File

@ -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

45
etnaviv/etnaviv-symbol-check Executable file
View File

@ -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_ETNAVIV_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"

347
etnaviv/etnaviv_bo.c Normal file
View File

@ -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@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));
}

196
etnaviv/etnaviv_bo_cache.c Normal file
View File

@ -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@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;
}

View File

@ -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@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);
}

96
etnaviv/etnaviv_device.c Normal file
View File

@ -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@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);
}

233
etnaviv/etnaviv_drm.h Normal file
View File

@ -0,0 +1,233 @@
/*
* 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_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)
#if defined(__cplusplus)
}
#endif
#endif /* __ETNAVIV_DRM_H__ */

188
etnaviv/etnaviv_drmif.h Normal file
View File

@ -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@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_ */

175
etnaviv/etnaviv_gpu.c Normal file
View File

@ -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@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;
}

78
etnaviv/etnaviv_pipe.c Normal file
View File

@ -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@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;
}

199
etnaviv/etnaviv_priv.h Normal file
View File

@ -0,0 +1,199 @@
/*
* 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"
#define VIV_FEATURES_WORD_COUNT 7
struct etna_specs {
uint32_t model;
uint32_t revision;
uint32_t features[VIV_FEATURES_WORD_COUNT];
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_ */

View File

@ -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