#include #include #include #include "pipe/p_context.h" #include "pipe/p_state.h" #include "util/format/u_format.h" #include "util/os_file.h" #include "util/u_memory.h" #include "util/u_inlines.h" #include "util/u_hash_table.h" #include "util/u_pointer.h" #include "os/os_thread.h" #include "nouveau_drm_public.h" #include "nouveau/nouveau_winsys.h" #include "nouveau/nouveau_screen.h" #include #include static struct hash_table *fd_tab = NULL; static mtx_t nouveau_screen_mutex = _MTX_INITIALIZER_NP; bool nouveau_drm_screen_unref(struct nouveau_screen *screen) { int ret; if (screen->refcount == -1) return true; mtx_lock(&nouveau_screen_mutex); ret = --screen->refcount; assert(ret >= 0); if (ret == 0) _mesa_hash_table_remove_key(fd_tab, intptr_to_pointer(screen->drm->fd)); mtx_unlock(&nouveau_screen_mutex); return ret == 0; } PUBLIC struct pipe_screen * nouveau_drm_screen_create(int fd) { struct nouveau_drm *drm = NULL; struct nouveau_device *dev = NULL; struct nouveau_screen *(*init)(struct nouveau_device *); struct nouveau_screen *screen = NULL; int ret, dupfd; mtx_lock(&nouveau_screen_mutex); if (!fd_tab) { fd_tab = util_hash_table_create_fd_keys(); if (!fd_tab) { mtx_unlock(&nouveau_screen_mutex); return NULL; } } screen = util_hash_table_get(fd_tab, intptr_to_pointer(fd)); if (screen) { screen->refcount++; mtx_unlock(&nouveau_screen_mutex); return &screen->base; } /* Since the screen re-use is based on the device node and not the fd, * create a copy of the fd to be owned by the device. Otherwise a * scenario could occur where two screens are created, and the first * one is shut down, along with the fd being closed. The second * (identical) screen would now have a reference to the closed fd. We * avoid this by duplicating the original fd. Note that * nouveau_device_wrap does not close the fd in case of a device * creation error. */ dupfd = os_dupfd_cloexec(fd); ret = nouveau_drm_new(dupfd, &drm); if (ret) goto err; ret = nouveau_device_new(&drm->client, NV_DEVICE, &(struct nv_device_v0) { .device = ~0ULL, }, sizeof(struct nv_device_v0), &dev); if (ret) goto err; switch (dev->chipset & ~0xf) { case 0x30: case 0x40: case 0x60: init = nv30_screen_create; break; case 0x50: case 0x80: case 0x90: case 0xa0: init = nv50_screen_create; break; case 0xc0: case 0xd0: case 0xe0: case 0xf0: case 0x100: case 0x110: case 0x120: case 0x130: case 0x140: case 0x160: init = nvc0_screen_create; break; default: debug_printf("%s: unknown chipset nv%02x\n", __func__, dev->chipset); goto err; } screen = init(dev); if (!screen || !screen->base.context_create) goto err; /* Use dupfd in hash table, to avoid errors if the original fd gets * closed by its owner. The hash key needs to live at least as long as * the screen. */ _mesa_hash_table_insert(fd_tab, intptr_to_pointer(dupfd), screen); screen->refcount = 1; mtx_unlock(&nouveau_screen_mutex); return &screen->base; err: if (screen) { screen->base.destroy(&screen->base); } else { nouveau_device_del(&dev); nouveau_drm_del(&drm); close(dupfd); } mtx_unlock(&nouveau_screen_mutex); return NULL; }