1/* 2 * Copyright (c) 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, sub license, 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 12 * next paragraph) shall be included in all copies or substantial portions 13 * of the 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 NON-INFRINGEMENT. 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 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 * 23 * Authors: 24 * Christian Gmeiner <christian.gmeiner@gmail.com> 25 */ 26 27#include <sys/stat.h> 28 29#include "util/os_file.h" 30#include "util/u_hash_table.h" 31#include "util/u_pointer.h" 32 33#include "etnaviv/etnaviv_screen.h" 34#include "etnaviv/hw/common.xml.h" 35#include "etnaviv_drm_public.h" 36 37#include <stdio.h> 38 39static uint32_t hash_file_description(const void *key) 40{ 41 int fd = pointer_to_intptr(key); 42 struct stat stat; 43 44 // File descriptions can't be hashed, but it should be safe to assume 45 // that the same file description will always refer to he same file 46 if(fstat(fd, &stat) == -1) 47 return ~0; // Make sure fstat failing won't result in a random hash 48 49 return stat.st_dev ^ stat.st_ino ^ stat.st_rdev; 50} 51 52 53static bool equal_file_description(const void *key1, const void *key2) 54{ 55 int ret; 56 int fd1 = pointer_to_intptr(key1); 57 int fd2 = pointer_to_intptr(key2); 58 struct stat stat1, stat2; 59 60 // If the file descriptors are the same, the file description will be too 61 // This will also catch sentinels, such as -1 62 if (fd1 == fd2) 63 return true; 64 65 ret = os_same_file_description(fd1, fd2); 66 if (ret >= 0) 67 return (ret == 0); 68 69 { 70 static bool has_warned; 71 if (!has_warned) 72 fprintf(stderr, "os_same_file_description couldn't determine if " 73 "two DRM fds reference the same file description. (%s)\n" 74 "Let's just assume that file descriptors for the same file probably" 75 "share the file description instead. This may cause problems when" 76 "that isn't the case.\n", strerror(errno)); 77 has_warned = true; 78 } 79 80 // Let's at least check that it's the same file, different files can't 81 // have the same file descriptions 82 fstat(fd1, &stat1); 83 fstat(fd2, &stat2); 84 85 return stat1.st_dev == stat2.st_dev && 86 stat1.st_ino == stat2.st_ino && 87 stat1.st_rdev == stat2.st_rdev; 88} 89 90 91static struct hash_table * 92hash_table_create_file_description_keys(void) 93{ 94 return _mesa_hash_table_create(NULL, hash_file_description, equal_file_description); 95} 96 97static struct pipe_screen * 98screen_create(int gpu_fd, struct renderonly *ro) 99{ 100 struct etna_device *dev; 101 struct etna_gpu *gpu; 102 uint64_t val; 103 int i; 104 105 dev = etna_device_new_dup(gpu_fd); 106 if (!dev) { 107 fprintf(stderr, "Error creating device\n"); 108 return NULL; 109 } 110 111 for (i = 0;; i++) { 112 gpu = etna_gpu_new(dev, i); 113 if (!gpu) { 114 fprintf(stderr, "Error creating gpu\n"); 115 return NULL; 116 } 117 118 /* Look for a 3D capable GPU */ 119 int ret = etna_gpu_get_param(gpu, ETNA_GPU_FEATURES_0, &val); 120 if (ret == 0 && (val & chipFeatures_PIPE_3D)) 121 break; 122 123 etna_gpu_del(gpu); 124 } 125 126 return etna_screen_create(dev, gpu, ro); 127} 128 129static struct hash_table *fd_tab = NULL; 130 131static mtx_t etna_screen_mutex = _MTX_INITIALIZER_NP; 132 133static void 134etna_drm_screen_destroy(struct pipe_screen *pscreen) 135{ 136 struct etna_screen *screen = etna_screen(pscreen); 137 boolean destroy; 138 139 mtx_lock(&etna_screen_mutex); 140 destroy = --screen->refcnt == 0; 141 if (destroy) { 142 int fd = etna_device_fd(screen->dev); 143 _mesa_hash_table_remove_key(fd_tab, intptr_to_pointer(fd)); 144 145 if (!fd_tab->entries) { 146 _mesa_hash_table_destroy(fd_tab, NULL); 147 fd_tab = NULL; 148 } 149 } 150 mtx_unlock(&etna_screen_mutex); 151 152 if (destroy) { 153 pscreen->destroy = screen->winsys_priv; 154 pscreen->destroy(pscreen); 155 } 156} 157 158static struct pipe_screen * 159etna_lookup_or_create_screen(int gpu_fd, struct renderonly *ro) 160{ 161 struct pipe_screen *pscreen = NULL; 162 163 mtx_lock(&etna_screen_mutex); 164 if (!fd_tab) { 165 fd_tab = hash_table_create_file_description_keys(); 166 if (!fd_tab) 167 goto unlock; 168 } 169 170 pscreen = util_hash_table_get(fd_tab, intptr_to_pointer(gpu_fd)); 171 if (pscreen) { 172 etna_screen(pscreen)->refcnt++; 173 } else { 174 pscreen = screen_create(gpu_fd, ro); 175 if (pscreen) { 176 int fd = etna_device_fd(etna_screen(pscreen)->dev); 177 _mesa_hash_table_insert(fd_tab, intptr_to_pointer(fd), pscreen); 178 179 /* Bit of a hack, to avoid circular linkage dependency, 180 * ie. pipe driver having to call in to winsys, we 181 * override the pipe drivers screen->destroy() */ 182 etna_screen(pscreen)->winsys_priv = pscreen->destroy; 183 pscreen->destroy = etna_drm_screen_destroy; 184 } 185 } 186 187unlock: 188 mtx_unlock(&etna_screen_mutex); 189 return pscreen; 190} 191 192struct pipe_screen * 193etna_drm_screen_create_renderonly(struct renderonly *ro) 194{ 195 return etna_lookup_or_create_screen(ro->gpu_fd, ro); 196} 197 198struct pipe_screen * 199etna_drm_screen_create(int fd) 200{ 201 return etna_lookup_or_create_screen(fd, NULL); 202} 203