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, &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 case 0x140:
107 case 0x160:
108 case 0x170:
109 case 0x190:
110 init = nvc0_screen_create;
111 break;
112 default:
113 debug_printf("%s: unknown chipset nv%02x\n", __func__,
114 dev->chipset);
115 goto err;
116 }
117
118 screen = init(dev);
119 if (!screen || !screen->base.context_create)
120 goto err;
121
122 /* Use dupfd in hash table, to avoid errors if the original fd gets
123 * closed by its owner. The hash key needs to live at least as long as
124 * the screen.
125 */
126 _mesa_hash_table_insert(fd_tab, intptr_to_pointer(dupfd), screen);
127 screen->refcount = 1;
128 simple_mtx_unlock(&nouveau_screen_mutex);
129 return &screen->base;
130
131 err:
132 if (screen) {
133 screen->base.destroy(&screen->base);
134 } else {
135 nouveau_device_del(&dev);
136 nouveau_drm_del(&drm);
137 close(dupfd);
138 }
139 simple_mtx_unlock(&nouveau_screen_mutex);
140 return NULL;
141 }
142