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
22static struct hash_table *fd_tab = NULL;
23
24static mtx_t nouveau_screen_mutex = _MTX_INITIALIZER_NP;
25
26bool 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
41PUBLIC struct pipe_screen *
42nouveau_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
131err:
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