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
parent
2d00869599
commit
95e2cc6a80
|
@ -57,6 +57,7 @@ libdrm_exynos.pc
|
|||
libdrm_freedreno.pc
|
||||
libdrm_amdgpu.pc
|
||||
libdrm_vc4.pc
|
||||
libdrm_etnaviv.pc
|
||||
libkms.pc
|
||||
libtool
|
||||
ltmain.sh
|
||||
|
|
|
@ -36,6 +36,7 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \
|
|||
--enable-freedreno \
|
||||
--enable-freedreno-kgsl\
|
||||
--enable-tegra-experimental-api \
|
||||
--enable-etnaviv-experimental-api \
|
||||
--enable-install-test-programs \
|
||||
--enable-cairo-tests \
|
||||
--enable-manpages \
|
||||
|
@ -84,6 +85,10 @@ if HAVE_VC4
|
|||
VC4_SUBDIR = vc4
|
||||
endif
|
||||
|
||||
if HAVE_ETNAVIV
|
||||
ETNAVIV_SUBDIR = etnaviv
|
||||
endif
|
||||
|
||||
if BUILD_MANPAGES
|
||||
if HAVE_MANPAGES_STYLESHEET
|
||||
MAN_SUBDIR = man
|
||||
|
@ -102,6 +107,7 @@ SUBDIRS = \
|
|||
$(FREEDRENO_SUBDIR) \
|
||||
$(TEGRA_SUBDIR) \
|
||||
$(VC4_SUBDIR) \
|
||||
$(ETNAVIV_SUBDIR) \
|
||||
tests \
|
||||
$(MAN_SUBDIR)
|
||||
|
||||
|
|
16
configure.ac
16
configure.ac
|
@ -132,6 +132,11 @@ AC_ARG_ENABLE(vc4,
|
|||
[Enable support for vc4's API (default: auto, enabled on arm)]),
|
||||
[VC4=$enableval], [VC4=auto])
|
||||
|
||||
AC_ARG_ENABLE(etnaviv-experimental-api,
|
||||
AS_HELP_STRING([--enable-etnaviv-experimental-api],
|
||||
[Enable support for etnaviv's experimental API (default: disabled)]),
|
||||
[ETNAVIV=$enableval], [ETNAVIV=no])
|
||||
|
||||
AC_ARG_ENABLE(install-test-programs,
|
||||
AS_HELP_STRING([--enable-install-test-programs],
|
||||
[Install test programs (default: no)]),
|
||||
|
@ -274,6 +279,9 @@ if test "x$drm_cv_atomic_primitives" = "xnone"; then
|
|||
|
||||
LIBDRM_ATOMICS_NOT_FOUND_MSG($TEGRA, tegra, NVIDIA Tegra, tegra-experimental-api)
|
||||
TEGRA=no
|
||||
|
||||
LIBDRM_ATOMICS_NOT_FOUND_MSG($ETNAVIV, etnaviv, Vivante, etnaviv-experimental-api)
|
||||
ETNAVIV=no
|
||||
else
|
||||
if test "x$INTEL" = xauto; then
|
||||
case $host_cpu in
|
||||
|
@ -413,6 +421,11 @@ if test "x$VC4" = xyes; then
|
|||
AC_DEFINE(HAVE_VC4, 1, [Have VC4 support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_ETNAVIV, [test "x$ETNAVIV" = xyes])
|
||||
if test "x$ETNAVIV" = xyes; then
|
||||
AC_DEFINE(HAVE_ETNAVIV, 1, [Have etnaviv support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_INSTALL_TESTS, [test "x$INSTALL_TESTS" = xyes])
|
||||
if test "x$INSTALL_TESTS" = xyes; then
|
||||
AC_DEFINE(HAVE_INSTALL_TESTS, 1, [Install test programs])
|
||||
|
@ -524,6 +537,8 @@ AC_CONFIG_FILES([
|
|||
tegra/libdrm_tegra.pc
|
||||
vc4/Makefile
|
||||
vc4/libdrm_vc4.pc
|
||||
etnaviv/Makefile
|
||||
etnaviv/libdrm_etnaviv.pc
|
||||
tests/Makefile
|
||||
tests/modeprint/Makefile
|
||||
tests/modetest/Makefile
|
||||
|
@ -555,4 +570,5 @@ echo " EXYNOS API $EXYNOS"
|
|||
echo " Freedreno API $FREEDRENO (kgsl: $FREEDRENO_KGSL)"
|
||||
echo " Tegra API $TEGRA"
|
||||
echo " VC4 API $VC4"
|
||||
echo " Etnaviv API $ETNAVIV"
|
||||
echo ""
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
|
@ -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"
|
|
@ -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));
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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__ */
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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_ */
|
|
@ -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
|
Loading…
Reference in New Issue