1 #include <sys/stat.h> 2 #include <unistd.h> 3 #include <fcntl.h> 4 #include "pipe/p_context.h" 5 #include "pipe/p_state.h" 6 #include "util/format/u_format.h" 7 #include "util/os_file.h" 8 #include "util/u_memory.h" 9 #include "util/u_inlines.h" 10 #include "util/u_hash_table.h" 11 #include "util/u_pointer.h" 12 #include "os/os_thread.h" 13 14 #include "nouveau_drm_public.h" 15 16 #include "nouveau/nouveau_winsys.h" 17 #include "nouveau/nouveau_screen.h" 18 19 #include <nvif/class.h> 20 #include <nvif/cl0080.h> 21 22 static struct hash_table *fd_tab = NULL; 23 24 static mtx_t nouveau_screen_mutex = _MTX_INITIALIZER_NP; 25 nouveau_drm_screen_unref(struct nouveau_screen * screen)26 bool nouveau_drm_screen_unref(struct nouveau_screen *screen) 27 { 28 int ret; 29 if (screen->refcount == -1) 30 return true; 31 32 mtx_lock(&nouveau_screen_mutex); 33 ret = --screen->refcount; 34 assert(ret >= 0); 35 if (ret == 0) 36 _mesa_hash_table_remove_key(fd_tab, intptr_to_pointer(screen->drm->fd)); 37 mtx_unlock(&nouveau_screen_mutex); 38 return ret == 0; 39 } 40 41 PUBLIC struct pipe_screen * nouveau_drm_screen_create(int fd)42 nouveau_drm_screen_create(int fd) 43 { 44 struct nouveau_drm *drm = NULL; 45 struct nouveau_device *dev = NULL; 46 struct nouveau_screen *(*init)(struct nouveau_device *); 47 struct nouveau_screen *screen = NULL; 48 int ret, dupfd; 49 50 mtx_lock(&nouveau_screen_mutex); 51 if (!fd_tab) { 52 fd_tab = util_hash_table_create_fd_keys(); 53 if (!fd_tab) { 54 mtx_unlock(&nouveau_screen_mutex); 55 return NULL; 56 } 57 } 58 59 screen = util_hash_table_get(fd_tab, intptr_to_pointer(fd)); 60 if (screen) { 61 screen->refcount++; 62 mtx_unlock(&nouveau_screen_mutex); 63 return &screen->base; 64 } 65 66 /* Since the screen re-use is based on the device node and not the fd, 67 * create a copy of the fd to be owned by the device. Otherwise a 68 * scenario could occur where two screens are created, and the first 69 * one is shut down, along with the fd being closed. The second 70 * (identical) screen would now have a reference to the closed fd. We 71 * avoid this by duplicating the original fd. Note that 72 * nouveau_device_wrap does not close the fd in case of a device 73 * creation error. 74 */ 75 dupfd = os_dupfd_cloexec(fd); 76 77 ret = nouveau_drm_new(dupfd, &drm); 78 if (ret) 79 goto err; 80 81 ret = nouveau_device_new(&drm->client, NV_DEVICE, 82 &(struct nv_device_v0) { 83 .device = ~0ULL, 84 }, sizeof(struct nv_device_v0), &dev); 85 if (ret) 86 goto err; 87 88 switch (dev->chipset & ~0xf) { 89 case 0x30: 90 case 0x40: 91 case 0x60: 92 init = nv30_screen_create; 93 break; 94 case 0x50: 95 case 0x80: 96 case 0x90: 97 case 0xa0: 98 init = nv50_screen_create; 99 break; 100 case 0xc0: 101 case 0xd0: 102 case 0xe0: 103 case 0xf0: 104 case 0x100: 105 case 0x110: 106 case 0x120: 107 case 0x130: 108 case 0x140: 109 case 0x160: 110 init = nvc0_screen_create; 111 break; 112 default: 113 debug_printf("%s: unknown chipset nv%02x\n", __func__, 114 dev->chipset); 115 goto err; 116 } 117 118 screen = init(dev); 119 if (!screen || !screen->base.context_create) 120 goto err; 121 122 /* Use dupfd in hash table, to avoid errors if the original fd gets 123 * closed by its owner. The hash key needs to live at least as long as 124 * the screen. 125 */ 126 _mesa_hash_table_insert(fd_tab, intptr_to_pointer(dupfd), screen); 127 screen->refcount = 1; 128 mtx_unlock(&nouveau_screen_mutex); 129 return &screen->base; 130 131 err: 132 if (screen) { 133 screen->base.destroy(&screen->base); 134 } else { 135 nouveau_device_del(&dev); 136 nouveau_drm_del(&drm); 137 close(dupfd); 138 } 139 mtx_unlock(&nouveau_screen_mutex); 140 return NULL; 141 } 142