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