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