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