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