+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
#include "pipe/p_context.h"
#include "pipe/p_state.h"
#include "util/u_format.h"
#include "util/u_memory.h"
#include "util/u_inlines.h"
+#include "util/u_hash_table.h"
+#include "os/os_thread.h"
-#include "nouveau_drm_winsys.h"
#include "nouveau_drm_public.h"
-#include "nouveau_drmif.h"
-#include "nouveau_channel.h"
-#include "nouveau_bo.h"
-
#include "nouveau/nouveau_winsys.h"
#include "nouveau/nouveau_screen.h"
-static void
-nouveau_drm_destroy_winsys(struct pipe_winsys *s)
+#include <nvif/class.h>
+#include <nvif/cl0080.h>
+
+static struct util_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)
+ util_hash_table_remove(fd_tab, intptr_to_pointer(screen->drm->fd));
+ pipe_mutex_unlock(nouveau_screen_mutex);
+ return ret == 0;
+}
+
+static unsigned hash_fd(void *key)
+{
+ int fd = pointer_to_intptr(key);
+ struct stat stat;
+ fstat(fd, &stat);
+
+ return stat.st_dev ^ stat.st_ino ^ stat.st_rdev;
+}
+
+static int compare_fd(void *key1, void *key2)
{
- struct nouveau_winsys *nv_winsys = nouveau_winsys(s);
- struct nouveau_screen *nv_screen= nouveau_screen(nv_winsys->pscreen);
- if (nv_screen)
- nouveau_device_close(&nv_screen->device);
- FREE(nv_winsys);
+ int fd1 = pointer_to_intptr(key1);
+ int fd2 = pointer_to_intptr(key2);
+ struct stat stat1, stat2;
+ fstat(fd1, &stat1);
+ fstat(fd2, &stat2);
+
+ return stat1.st_dev != stat2.st_dev ||
+ stat1.st_ino != stat2.st_ino ||
+ stat1.st_rdev != stat2.st_rdev;
}
-struct pipe_screen *
+PUBLIC struct pipe_screen *
nouveau_drm_screen_create(int fd)
{
- struct nouveau_winsys *nvws;
- struct pipe_winsys *ws;
+ struct nouveau_drm *drm = NULL;
struct nouveau_device *dev = NULL;
- struct pipe_screen *(*init)(struct pipe_winsys *,
- struct nouveau_device *);
- int ret;
+ 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(hash_fd, compare_fd);
+ if (!fd_tab) {
+ pipe_mutex_unlock(nouveau_screen_mutex);
+ return NULL;
+ }
+ }
+
+ screen = util_hash_table_get(fd_tab, intptr_to_pointer(fd));
+ if (screen) {
+ screen->refcount++;
+ pipe_mutex_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 = fcntl(fd, F_DUPFD_CLOEXEC, 3);
- ret = nouveau_device_open_existing(&dev, 0, fd, 0);
+ ret = nouveau_drm_new(dupfd, &drm);
if (ret)
- return NULL;
+ goto err;
- switch (dev->chipset & 0xf0) {
+ 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 = nvfx_screen_create;
+ init = nv30_screen_create;
break;
case 0x50:
case 0x80:
case 0xa0:
init = nv50_screen_create;
break;
+ case 0xc0:
+ case 0xd0:
+ case 0xe0:
+ case 0xf0:
+ case 0x100:
+ case 0x110:
+ case 0x120:
+ case 0x130:
+ init = nvc0_screen_create;
+ break;
default:
debug_printf("%s: unknown chipset nv%02x\n", __func__,
dev->chipset);
- return NULL;
+ goto err;
}
- nvws = CALLOC_STRUCT(nouveau_winsys);
- if (!nvws) {
- nouveau_device_close(&dev);
- return NULL;
- }
- ws = &nvws->base;
- ws->destroy = nouveau_drm_destroy_winsys;
+ screen = init(dev);
+ if (!screen || !screen->base.context_create)
+ goto err;
- nvws->pscreen = init(ws, dev);
- if (!nvws->pscreen) {
- ws->destroy(ws);
- return NULL;
- }
+ /* 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.
+ */
+ util_hash_table_set(fd_tab, intptr_to_pointer(dupfd), screen);
+ screen->refcount = 1;
+ pipe_mutex_unlock(nouveau_screen_mutex);
+ return &screen->base;
- return nvws->pscreen;
+err:
+ if (screen) {
+ screen->base.destroy(&screen->base);
+ } else {
+ nouveau_device_del(&dev);
+ nouveau_drm_del(&drm);
+ close(dupfd);
+ }
+ pipe_mutex_unlock(nouveau_screen_mutex);
+ return NULL;
}