17ec681f3Smrg/*
27ec681f3Smrg * Copyright (C) 2014-2015 Etnaviv Project
37ec681f3Smrg *
47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg * to deal in the Software without restriction, including without limitation
77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg *
117ec681f3Smrg * The above copyright notice and this permission notice (including the next
127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg * Software.
147ec681f3Smrg *
157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
207ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
217ec681f3Smrg * SOFTWARE.
227ec681f3Smrg *
237ec681f3Smrg * Authors:
247ec681f3Smrg *    Christian Gmeiner <christian.gmeiner@gmail.com>
257ec681f3Smrg */
267ec681f3Smrg
277ec681f3Smrg#ifndef ETNAVIV_PRIV_H_
287ec681f3Smrg#define ETNAVIV_PRIV_H_
297ec681f3Smrg
307ec681f3Smrg#include <stdlib.h>
317ec681f3Smrg#include <errno.h>
327ec681f3Smrg#include <string.h>
337ec681f3Smrg#include <unistd.h>
347ec681f3Smrg#include <errno.h>
357ec681f3Smrg#include <fcntl.h>
367ec681f3Smrg#include <sys/ioctl.h>
377ec681f3Smrg#include <stdio.h>
387ec681f3Smrg#include <assert.h>
397ec681f3Smrg
407ec681f3Smrg#include <xf86drm.h>
417ec681f3Smrg
427ec681f3Smrg#include "util/list.h"
437ec681f3Smrg#include "util/macros.h"
447ec681f3Smrg#include "util/simple_mtx.h"
457ec681f3Smrg#include "util/timespec.h"
467ec681f3Smrg#include "util/u_atomic.h"
477ec681f3Smrg#include "util/u_debug.h"
487ec681f3Smrg#include "util/vma.h"
497ec681f3Smrg
507ec681f3Smrg#include "etnaviv_drmif.h"
517ec681f3Smrg#include "drm-uapi/etnaviv_drm.h"
527ec681f3Smrg
537ec681f3Smrgextern simple_mtx_t etna_drm_table_lock;
547ec681f3Smrg
557ec681f3Smrgstruct etna_bo_bucket {
567ec681f3Smrg	uint32_t size;
577ec681f3Smrg	struct list_head list;
587ec681f3Smrg};
597ec681f3Smrg
607ec681f3Smrgstruct etna_bo_cache {
617ec681f3Smrg	struct etna_bo_bucket cache_bucket[14 * 4];
627ec681f3Smrg	unsigned num_buckets;
637ec681f3Smrg	time_t time;
647ec681f3Smrg};
657ec681f3Smrg
667ec681f3Smrgstruct etna_device {
677ec681f3Smrg	int fd;
687ec681f3Smrg	uint32_t drm_version;
697ec681f3Smrg	int refcnt;
707ec681f3Smrg
717ec681f3Smrg	/* tables to keep track of bo's, to avoid "evil-twin" etna_bo objects:
727ec681f3Smrg	 *
737ec681f3Smrg	 *   handle_table: maps handle to etna_bo
747ec681f3Smrg	 *   name_table: maps flink name to etna_bo
757ec681f3Smrg	 *
767ec681f3Smrg	 * We end up needing two tables, because DRM_IOCTL_GEM_OPEN always
777ec681f3Smrg	 * returns a new handle.  So we need to figure out if the bo is already
787ec681f3Smrg	 * open in the process first, before calling gem-open.
797ec681f3Smrg	 */
807ec681f3Smrg	void *handle_table, *name_table;
817ec681f3Smrg
827ec681f3Smrg	struct etna_bo_cache bo_cache;
837ec681f3Smrg
847ec681f3Smrg	int use_softpin;
857ec681f3Smrg	struct util_vma_heap address_space;
867ec681f3Smrg
877ec681f3Smrg	int closefd;        /* call close(fd) upon destruction */
887ec681f3Smrg};
897ec681f3Smrg
907ec681f3Smrgvoid etna_bo_cache_init(struct etna_bo_cache *cache);
917ec681f3Smrgvoid etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t time);
927ec681f3Smrgstruct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache,
937ec681f3Smrg		uint32_t *size, uint32_t flags);
947ec681f3Smrgint etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo *bo);
957ec681f3Smrg
967ec681f3Smrg/* for where @etna_drm_table_lock is already held: */
977ec681f3Smrgvoid etna_device_del_locked(struct etna_device *dev);
987ec681f3Smrg
997ec681f3Smrg/* a GEM buffer object allocated from the DRM device */
1007ec681f3Smrgstruct etna_bo {
1017ec681f3Smrg	struct etna_device      *dev;
1027ec681f3Smrg	void            *map;           /* userspace mmap'ing (if there is one) */
1037ec681f3Smrg	uint32_t        size;
1047ec681f3Smrg	uint32_t        handle;
1057ec681f3Smrg	uint32_t        flags;
1067ec681f3Smrg	uint32_t        name;           /* flink global handle (DRI2 name) */
1077ec681f3Smrg	uint64_t        offset;         /* offset to mmap() */
1087ec681f3Smrg	uint32_t        va;             /* GPU virtual address */
1097ec681f3Smrg	int		refcnt;
1107ec681f3Smrg
1117ec681f3Smrg	/*
1127ec681f3Smrg	 * To avoid excess hashtable lookups, cache the stream this bo was
1137ec681f3Smrg	 * last emitted on (since that will probably also be the next ring
1147ec681f3Smrg	 * it is emitted on).
1157ec681f3Smrg	 */
1167ec681f3Smrg	struct etna_cmd_stream *current_stream;
1177ec681f3Smrg	uint32_t idx;
1187ec681f3Smrg
1197ec681f3Smrg	int reuse;
1207ec681f3Smrg	struct list_head list;   /* bucket-list entry */
1217ec681f3Smrg	time_t free_time;        /* time when added to bucket-list */
1227ec681f3Smrg};
1237ec681f3Smrg
1247ec681f3Smrgstruct etna_gpu {
1257ec681f3Smrg	struct etna_device *dev;
1267ec681f3Smrg	uint32_t core;
1277ec681f3Smrg	uint32_t model;
1287ec681f3Smrg	uint32_t revision;
1297ec681f3Smrg};
1307ec681f3Smrg
1317ec681f3Smrgstruct etna_pipe {
1327ec681f3Smrg	enum etna_pipe_id id;
1337ec681f3Smrg	struct etna_gpu *gpu;
1347ec681f3Smrg};
1357ec681f3Smrg
1367ec681f3Smrgstruct etna_cmd_stream_priv {
1377ec681f3Smrg	struct etna_cmd_stream base;
1387ec681f3Smrg	struct etna_pipe *pipe;
1397ec681f3Smrg
1407ec681f3Smrg	uint32_t last_timestamp;
1417ec681f3Smrg
1427ec681f3Smrg	/* submit ioctl related tables: */
1437ec681f3Smrg	struct {
1447ec681f3Smrg		/* bo's table: */
1457ec681f3Smrg		struct drm_etnaviv_gem_submit_bo *bos;
1467ec681f3Smrg		uint32_t nr_bos, max_bos;
1477ec681f3Smrg
1487ec681f3Smrg		/* reloc's table: */
1497ec681f3Smrg		struct drm_etnaviv_gem_submit_reloc *relocs;
1507ec681f3Smrg		uint32_t nr_relocs, max_relocs;
1517ec681f3Smrg
1527ec681f3Smrg		/* perf's table: */
1537ec681f3Smrg		struct drm_etnaviv_gem_submit_pmr *pmrs;
1547ec681f3Smrg		uint32_t nr_pmrs, max_pmrs;
1557ec681f3Smrg	} submit;
1567ec681f3Smrg
1577ec681f3Smrg	/* should have matching entries in submit.bos: */
1587ec681f3Smrg	struct etna_bo **bos;
1597ec681f3Smrg	uint32_t nr_bos, max_bos;
1607ec681f3Smrg
1617ec681f3Smrg	/* notify callback if buffer reset happened */
1627ec681f3Smrg	void (*force_flush)(struct etna_cmd_stream *stream, void *priv);
1637ec681f3Smrg	void *force_flush_priv;
1647ec681f3Smrg
1657ec681f3Smrg	void *bo_table;
1667ec681f3Smrg};
1677ec681f3Smrg
1687ec681f3Smrgstruct etna_perfmon {
1697ec681f3Smrg	struct list_head domains;
1707ec681f3Smrg	struct etna_pipe *pipe;
1717ec681f3Smrg};
1727ec681f3Smrg
1737ec681f3Smrgstruct etna_perfmon_domain
1747ec681f3Smrg{
1757ec681f3Smrg	struct list_head head;
1767ec681f3Smrg	struct list_head signals;
1777ec681f3Smrg	uint8_t id;
1787ec681f3Smrg	char name[64];
1797ec681f3Smrg};
1807ec681f3Smrg
1817ec681f3Smrgstruct etna_perfmon_signal
1827ec681f3Smrg{
1837ec681f3Smrg	struct list_head head;
1847ec681f3Smrg	struct etna_perfmon_domain *domain;
1857ec681f3Smrg	uint8_t signal;
1867ec681f3Smrg	char name[64];
1877ec681f3Smrg};
1887ec681f3Smrg
1897ec681f3Smrg#define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1))
1907ec681f3Smrg
1917ec681f3Smrg#define enable_debug 0  /* TODO make dynamic */
1927ec681f3Smrg
1937ec681f3Smrg#define INFO_MSG(fmt, ...) \
1947ec681f3Smrg		do { debug_printf("[I] "fmt " (%s:%d)\n", \
1957ec681f3Smrg				##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
1967ec681f3Smrg#define DEBUG_MSG(fmt, ...) \
1977ec681f3Smrg		do if (enable_debug) { debug_printf("[D] "fmt " (%s:%d)\n", \
1987ec681f3Smrg				##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
1997ec681f3Smrg#define WARN_MSG(fmt, ...) \
2007ec681f3Smrg		do { debug_printf("[W] "fmt " (%s:%d)\n", \
2017ec681f3Smrg				##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
2027ec681f3Smrg#define ERROR_MSG(fmt, ...) \
2037ec681f3Smrg		do { debug_printf("[E] " fmt " (%s:%d)\n", \
2047ec681f3Smrg				##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
2057ec681f3Smrg
2067ec681f3Smrg#define VOID2U64(x) ((uint64_t)(unsigned long)(x))
2077ec681f3Smrg
2087ec681f3Smrgstatic inline void get_abs_timeout(struct drm_etnaviv_timespec *tv, uint64_t ns)
2097ec681f3Smrg{
2107ec681f3Smrg	struct timespec t;
2117ec681f3Smrg	clock_gettime(CLOCK_MONOTONIC, &t);
2127ec681f3Smrg	tv->tv_sec = t.tv_sec + ns / NSEC_PER_SEC;
2137ec681f3Smrg	tv->tv_nsec = t.tv_nsec + ns % NSEC_PER_SEC;
2147ec681f3Smrg	if (tv->tv_nsec >= NSEC_PER_SEC) {
2157ec681f3Smrg		tv->tv_nsec -= NSEC_PER_SEC;
2167ec681f3Smrg		tv->tv_sec++;
2177ec681f3Smrg	}
2187ec681f3Smrg}
2197ec681f3Smrg
2207ec681f3Smrg#if HAVE_VALGRIND
2217ec681f3Smrg#  include <memcheck.h>
2227ec681f3Smrg
2237ec681f3Smrg/*
2247ec681f3Smrg * For tracking the backing memory (if valgrind enabled, we force a mmap
2257ec681f3Smrg * for the purposes of tracking)
2267ec681f3Smrg */
2277ec681f3Smrgstatic inline void VG_BO_ALLOC(struct etna_bo *bo)
2287ec681f3Smrg{
2297ec681f3Smrg	if (bo && RUNNING_ON_VALGRIND) {
2307ec681f3Smrg		VALGRIND_MALLOCLIKE_BLOCK(etna_bo_map(bo), bo->size, 0, 1);
2317ec681f3Smrg	}
2327ec681f3Smrg}
2337ec681f3Smrg
2347ec681f3Smrgstatic inline void VG_BO_FREE(struct etna_bo *bo)
2357ec681f3Smrg{
2367ec681f3Smrg	VALGRIND_FREELIKE_BLOCK(bo->map, 0);
2377ec681f3Smrg}
2387ec681f3Smrg
2397ec681f3Smrg/*
2407ec681f3Smrg * For tracking bo structs that are in the buffer-cache, so that valgrind
2417ec681f3Smrg * doesn't attribute ownership to the first one to allocate the recycled
2427ec681f3Smrg * bo.
2437ec681f3Smrg *
2447ec681f3Smrg * Note that the list_head in etna_bo is used to track the buffers in cache
2457ec681f3Smrg * so disable error reporting on the range while they are in cache so
2467ec681f3Smrg * valgrind doesn't squawk about list traversal.
2477ec681f3Smrg *
2487ec681f3Smrg */
2497ec681f3Smrgstatic inline void VG_BO_RELEASE(struct etna_bo *bo)
2507ec681f3Smrg{
2517ec681f3Smrg	if (RUNNING_ON_VALGRIND) {
2527ec681f3Smrg		VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(bo, sizeof(*bo));
2537ec681f3Smrg		VALGRIND_MAKE_MEM_NOACCESS(bo, sizeof(*bo));
2547ec681f3Smrg		VALGRIND_FREELIKE_BLOCK(bo->map, 0);
2557ec681f3Smrg	}
2567ec681f3Smrg}
2577ec681f3Smrgstatic inline void VG_BO_OBTAIN(struct etna_bo *bo)
2587ec681f3Smrg{
2597ec681f3Smrg	if (RUNNING_ON_VALGRIND) {
2607ec681f3Smrg		VALGRIND_MAKE_MEM_DEFINED(bo, sizeof(*bo));
2617ec681f3Smrg		VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(bo, sizeof(*bo));
2627ec681f3Smrg		VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, 1);
2637ec681f3Smrg	}
2647ec681f3Smrg}
2657ec681f3Smrg#else
2667ec681f3Smrgstatic inline void VG_BO_ALLOC(struct etna_bo *bo)   {}
2677ec681f3Smrgstatic inline void VG_BO_FREE(struct etna_bo *bo)    {}
2687ec681f3Smrgstatic inline void VG_BO_RELEASE(struct etna_bo *bo) {}
2697ec681f3Smrgstatic inline void VG_BO_OBTAIN(struct etna_bo *bo)  {}
2707ec681f3Smrg#endif
2717ec681f3Smrg
2727ec681f3Smrg#endif /* ETNAVIV_PRIV_H_ */
273