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/u_memory.h"
9 #include "util/u_inlines.h"
10 #include "util/u_hash_table.h"
11 #include "util/u_pointer.h"
12 #include "os/os_thread.h"
13
14 #include "nouveau_drm_public.h"
15
16 #include "nouveau/nouveau_winsys.h"
17 #include "nouveau/nouveau_screen.h"
18
19 #include <nvif/class.h>
20 #include <nvif/cl0080.h>
21
22 static struct hash_table *fd_tab = NULL;
23
24 static mtx_t nouveau_screen_mutex = _MTX_INITIALIZER_NP;
25
nouveau_drm_screen_unref(struct nouveau_screen * screen)26 bool nouveau_drm_screen_unref(struct nouveau_screen *screen)
27 {
28 int ret;
29 if (screen->refcount == -1)
30 return true;
31
32 mtx_lock(&nouveau_screen_mutex);
33 ret = --screen->refcount;
34 assert(ret >= 0);
35 if (ret == 0)
36 _mesa_hash_table_remove_key(fd_tab, intptr_to_pointer(screen->drm->fd));
37 mtx_unlock(&nouveau_screen_mutex);
38 return ret == 0;
39 }
40
41 PUBLIC struct pipe_screen *
nouveau_drm_screen_create(int fd)42 nouveau_drm_screen_create(int fd)
43 {
44 struct nouveau_drm *drm = NULL;
45 struct nouveau_device *dev = NULL;
46 struct nouveau_screen *(*init)(struct nouveau_device *);
47 struct nouveau_screen *screen = NULL;
48 int ret, dupfd;
49
50 mtx_lock(&nouveau_screen_mutex);
51 if (!fd_tab) {
52 fd_tab = util_hash_table_create_fd_keys();
53 if (!fd_tab) {
54 mtx_unlock(&nouveau_screen_mutex);
55 return NULL;
56 }
57 }
58
59 screen = util_hash_table_get(fd_tab, intptr_to_pointer(fd));
60 if (screen) {
61 screen->refcount++;
62 mtx_unlock(&nouveau_screen_mutex);
63 return &screen->base;
64 }
65
66 /* Since the screen re-use is based on the device node and not the fd,
67 * create a copy of the fd to be owned by the device. Otherwise a
68 * scenario could occur where two screens are created, and the first
69 * one is shut down, along with the fd being closed. The second
70 * (identical) screen would now have a reference to the closed fd. We
71 * avoid this by duplicating the original fd. Note that
72 * nouveau_device_wrap does not close the fd in case of a device
73 * creation error.
74 */
75 dupfd = os_dupfd_cloexec(fd);
76
77 ret = nouveau_drm_new(dupfd, &drm);
78 if (ret)
79 goto err;
80
81 ret = nouveau_device_new(&drm->client, NV_DEVICE,
82 &(struct nv_device_v0) {
83 .device = ~0ULL,
84 }, sizeof(struct nv_device_v0), &dev);
85 if (ret)
86 goto err;
87
88 switch (dev->chipset & ~0xf) {
89 case 0x30:
90 case 0x40:
91 case 0x60:
92 init = nv30_screen_create;
93 break;
94 case 0x50:
95 case 0x80:
96 case 0x90:
97 case 0xa0:
98 init = nv50_screen_create;
99 break;
100 case 0xc0:
101 case 0xd0:
102 case 0xe0:
103 case 0xf0:
104 case 0x100:
105 case 0x110:
106 case 0x120:
107 case 0x130:
108 case 0x140:
109 case 0x160:
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 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 mtx_unlock(&nouveau_screen_mutex);
140 return NULL;
141 }
142