1/* 2 * Copyright (C) 2014-2015 Etnaviv Project 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 * 23 * Authors: 24 * Christian Gmeiner <christian.gmeiner@gmail.com> 25 */ 26 27#ifndef ETNAVIV_PRIV_H_ 28#define ETNAVIV_PRIV_H_ 29 30#include <stdlib.h> 31#include <errno.h> 32#include <string.h> 33#include <unistd.h> 34#include <errno.h> 35#include <fcntl.h> 36#include <sys/ioctl.h> 37#include <stdio.h> 38#include <assert.h> 39 40#include <xf86drm.h> 41 42#include "util/list.h" 43#include "util/macros.h" 44#include "util/simple_mtx.h" 45#include "util/timespec.h" 46#include "util/u_atomic.h" 47#include "util/u_debug.h" 48#include "util/vma.h" 49 50#include "etnaviv_drmif.h" 51#include "drm-uapi/etnaviv_drm.h" 52 53extern simple_mtx_t etna_drm_table_lock; 54 55struct etna_bo_bucket { 56 uint32_t size; 57 struct list_head list; 58}; 59 60struct etna_bo_cache { 61 struct etna_bo_bucket cache_bucket[14 * 4]; 62 unsigned num_buckets; 63 time_t time; 64}; 65 66struct etna_device { 67 int fd; 68 uint32_t drm_version; 69 int refcnt; 70 71 /* tables to keep track of bo's, to avoid "evil-twin" etna_bo objects: 72 * 73 * handle_table: maps handle to etna_bo 74 * name_table: maps flink name to etna_bo 75 * 76 * We end up needing two tables, because DRM_IOCTL_GEM_OPEN always 77 * returns a new handle. So we need to figure out if the bo is already 78 * open in the process first, before calling gem-open. 79 */ 80 void *handle_table, *name_table; 81 82 struct etna_bo_cache bo_cache; 83 84 int use_softpin; 85 struct util_vma_heap address_space; 86 87 int closefd; /* call close(fd) upon destruction */ 88}; 89 90void etna_bo_cache_init(struct etna_bo_cache *cache); 91void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t time); 92struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache, 93 uint32_t *size, uint32_t flags); 94int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo *bo); 95 96/* for where @etna_drm_table_lock is already held: */ 97void etna_device_del_locked(struct etna_device *dev); 98 99/* a GEM buffer object allocated from the DRM device */ 100struct etna_bo { 101 struct etna_device *dev; 102 void *map; /* userspace mmap'ing (if there is one) */ 103 uint32_t size; 104 uint32_t handle; 105 uint32_t flags; 106 uint32_t name; /* flink global handle (DRI2 name) */ 107 uint64_t offset; /* offset to mmap() */ 108 uint32_t va; /* GPU virtual address */ 109 int refcnt; 110 111 /* 112 * To avoid excess hashtable lookups, cache the stream this bo was 113 * last emitted on (since that will probably also be the next ring 114 * it is emitted on). 115 */ 116 struct etna_cmd_stream *current_stream; 117 uint32_t idx; 118 119 int reuse; 120 struct list_head list; /* bucket-list entry */ 121 time_t free_time; /* time when added to bucket-list */ 122}; 123 124struct etna_gpu { 125 struct etna_device *dev; 126 uint32_t core; 127 uint32_t model; 128 uint32_t revision; 129}; 130 131struct etna_pipe { 132 enum etna_pipe_id id; 133 struct etna_gpu *gpu; 134}; 135 136struct etna_cmd_stream_priv { 137 struct etna_cmd_stream base; 138 struct etna_pipe *pipe; 139 140 uint32_t last_timestamp; 141 142 /* submit ioctl related tables: */ 143 struct { 144 /* bo's table: */ 145 struct drm_etnaviv_gem_submit_bo *bos; 146 uint32_t nr_bos, max_bos; 147 148 /* reloc's table: */ 149 struct drm_etnaviv_gem_submit_reloc *relocs; 150 uint32_t nr_relocs, max_relocs; 151 152 /* perf's table: */ 153 struct drm_etnaviv_gem_submit_pmr *pmrs; 154 uint32_t nr_pmrs, max_pmrs; 155 } submit; 156 157 /* should have matching entries in submit.bos: */ 158 struct etna_bo **bos; 159 uint32_t nr_bos, max_bos; 160 161 /* notify callback if buffer reset happened */ 162 void (*force_flush)(struct etna_cmd_stream *stream, void *priv); 163 void *force_flush_priv; 164 165 void *bo_table; 166}; 167 168struct etna_perfmon { 169 struct list_head domains; 170 struct etna_pipe *pipe; 171}; 172 173struct etna_perfmon_domain 174{ 175 struct list_head head; 176 struct list_head signals; 177 uint8_t id; 178 char name[64]; 179}; 180 181struct etna_perfmon_signal 182{ 183 struct list_head head; 184 struct etna_perfmon_domain *domain; 185 uint8_t signal; 186 char name[64]; 187}; 188 189#define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1)) 190 191#define enable_debug 0 /* TODO make dynamic */ 192 193#define INFO_MSG(fmt, ...) \ 194 do { debug_printf("[I] "fmt " (%s:%d)\n", \ 195 ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0) 196#define DEBUG_MSG(fmt, ...) \ 197 do if (enable_debug) { debug_printf("[D] "fmt " (%s:%d)\n", \ 198 ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0) 199#define WARN_MSG(fmt, ...) \ 200 do { debug_printf("[W] "fmt " (%s:%d)\n", \ 201 ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0) 202#define ERROR_MSG(fmt, ...) \ 203 do { debug_printf("[E] " fmt " (%s:%d)\n", \ 204 ##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0) 205 206#define VOID2U64(x) ((uint64_t)(unsigned long)(x)) 207 208static inline void get_abs_timeout(struct drm_etnaviv_timespec *tv, uint64_t ns) 209{ 210 struct timespec t; 211 clock_gettime(CLOCK_MONOTONIC, &t); 212 tv->tv_sec = t.tv_sec + ns / NSEC_PER_SEC; 213 tv->tv_nsec = t.tv_nsec + ns % NSEC_PER_SEC; 214 if (tv->tv_nsec >= NSEC_PER_SEC) { 215 tv->tv_nsec -= NSEC_PER_SEC; 216 tv->tv_sec++; 217 } 218} 219 220#if HAVE_VALGRIND 221# include <memcheck.h> 222 223/* 224 * For tracking the backing memory (if valgrind enabled, we force a mmap 225 * for the purposes of tracking) 226 */ 227static inline void VG_BO_ALLOC(struct etna_bo *bo) 228{ 229 if (bo && RUNNING_ON_VALGRIND) { 230 VALGRIND_MALLOCLIKE_BLOCK(etna_bo_map(bo), bo->size, 0, 1); 231 } 232} 233 234static inline void VG_BO_FREE(struct etna_bo *bo) 235{ 236 VALGRIND_FREELIKE_BLOCK(bo->map, 0); 237} 238 239/* 240 * For tracking bo structs that are in the buffer-cache, so that valgrind 241 * doesn't attribute ownership to the first one to allocate the recycled 242 * bo. 243 * 244 * Note that the list_head in etna_bo is used to track the buffers in cache 245 * so disable error reporting on the range while they are in cache so 246 * valgrind doesn't squawk about list traversal. 247 * 248 */ 249static inline void VG_BO_RELEASE(struct etna_bo *bo) 250{ 251 if (RUNNING_ON_VALGRIND) { 252 VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(bo, sizeof(*bo)); 253 VALGRIND_MAKE_MEM_NOACCESS(bo, sizeof(*bo)); 254 VALGRIND_FREELIKE_BLOCK(bo->map, 0); 255 } 256} 257static inline void VG_BO_OBTAIN(struct etna_bo *bo) 258{ 259 if (RUNNING_ON_VALGRIND) { 260 VALGRIND_MAKE_MEM_DEFINED(bo, sizeof(*bo)); 261 VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(bo, sizeof(*bo)); 262 VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, 1); 263 } 264} 265#else 266static inline void VG_BO_ALLOC(struct etna_bo *bo) {} 267static inline void VG_BO_FREE(struct etna_bo *bo) {} 268static inline void VG_BO_RELEASE(struct etna_bo *bo) {} 269static inline void VG_BO_OBTAIN(struct etna_bo *bo) {} 270#endif 271 272#endif /* ETNAVIV_PRIV_H_ */ 273