1 #include "nouveau_context.h"
2
3 #include "nouveau_device.h"
4
5 #include "drm-uapi/nouveau_drm.h"
6 #include "nvif/ioctl.h"
7
8 #include <errno.h>
9 #include <xf86drm.h>
10
11 static void
nouveau_ws_subchan_dealloc(int fd,struct nouveau_ws_object * obj)12 nouveau_ws_subchan_dealloc(int fd, struct nouveau_ws_object *obj)
13 {
14 struct {
15 struct nvif_ioctl_v0 ioctl;
16 struct nvif_ioctl_del del;
17 } args = {
18 .ioctl = {
19 .object = (uintptr_t)obj,
20 .owner = NVIF_IOCTL_V0_OWNER_ANY,
21 .route = 0x00,
22 .type = NVIF_IOCTL_V0_DEL,
23 .version = 0,
24 },
25 };
26
27 /* TODO returns -ENOENT for unknown reasons */
28 drmCommandWrite(fd, DRM_NOUVEAU_NVIF, &args, sizeof(args));
29 }
30
31 #define NOUVEAU_WS_CONTEXT_MAX_CLASSES 16
32
33 static int
nouveau_ws_context_query_classes(int fd,int channel,uint32_t classes[NOUVEAU_WS_CONTEXT_MAX_CLASSES])34 nouveau_ws_context_query_classes(int fd, int channel, uint32_t classes[NOUVEAU_WS_CONTEXT_MAX_CLASSES])
35 {
36 struct {
37 struct nvif_ioctl_v0 ioctl;
38 struct nvif_ioctl_sclass_v0 sclass;
39 struct nvif_ioctl_sclass_oclass_v0 list[NOUVEAU_WS_CONTEXT_MAX_CLASSES];
40 } args = {
41 .ioctl = {
42 .route = 0xff,
43 .token = channel,
44 .type = NVIF_IOCTL_V0_SCLASS,
45 .version = 0,
46 },
47 .sclass = {
48 .count = NOUVEAU_WS_CONTEXT_MAX_CLASSES,
49 .version = 0,
50 },
51 };
52
53 int ret = drmCommandWriteRead(fd, DRM_NOUVEAU_NVIF, &args, sizeof(args));
54 if (ret)
55 return ret;
56
57 assert(args.sclass.count <= NOUVEAU_WS_CONTEXT_MAX_CLASSES);
58 for (unsigned i = 0; i < NOUVEAU_WS_CONTEXT_MAX_CLASSES; i++)
59 classes[i] = args.list[i].oclass;
60
61 return 0;
62 }
63
64 static uint32_t
nouveau_ws_context_find_class(uint32_t classes[NOUVEAU_WS_CONTEXT_MAX_CLASSES],uint8_t type)65 nouveau_ws_context_find_class(uint32_t classes[NOUVEAU_WS_CONTEXT_MAX_CLASSES], uint8_t type)
66 {
67 uint32_t ret = 0;
68
69 /* find the highest matching one */
70 for (unsigned i = 0; i < NOUVEAU_WS_CONTEXT_MAX_CLASSES; i++) {
71 uint32_t val = classes[i];
72 if ((val & 0xff) == type)
73 ret = MAX2(ret, val);
74 }
75
76 return ret;
77 }
78
79 static int
nouveau_ws_subchan_alloc(int fd,int channel,uint32_t handle,uint16_t oclass,struct nouveau_ws_object * obj)80 nouveau_ws_subchan_alloc(int fd, int channel, uint32_t handle, uint16_t oclass, struct nouveau_ws_object *obj)
81 {
82 struct {
83 struct nvif_ioctl_v0 ioctl;
84 struct nvif_ioctl_new_v0 new;
85 } args = {
86 .ioctl = {
87 .route = 0xff,
88 .token = channel,
89 .type = NVIF_IOCTL_V0_NEW,
90 .version = 0,
91 },
92 .new = {
93 .handle = handle,
94 .object = (uintptr_t)obj,
95 .oclass = oclass,
96 .route = NVIF_IOCTL_V0_ROUTE_NVIF,
97 .token = (uintptr_t)obj,
98 .version = 0,
99 },
100 };
101
102 if (!oclass) {
103 assert(!"called with invalid oclass");
104 return -EINVAL;
105 }
106
107 obj->cls = oclass;
108
109 return drmCommandWrite(fd, DRM_NOUVEAU_NVIF, &args, sizeof(args));
110 }
111
112 static void
nouveau_ws_channel_dealloc(int fd,int channel)113 nouveau_ws_channel_dealloc(int fd, int channel)
114 {
115 struct drm_nouveau_channel_free req = {
116 .channel = channel,
117 };
118
119 int ret = drmCommandWrite(fd, DRM_NOUVEAU_CHANNEL_FREE, &req, sizeof(req));
120 assert(!ret);
121 }
122
123 int
nouveau_ws_context_create(struct nouveau_ws_device * dev,enum nouveau_ws_engines engines,struct nouveau_ws_context ** out)124 nouveau_ws_context_create(struct nouveau_ws_device *dev,
125 enum nouveau_ws_engines engines,
126 struct nouveau_ws_context **out)
127 {
128 struct drm_nouveau_channel_alloc req = { };
129 uint32_t classes[NOUVEAU_WS_CONTEXT_MAX_CLASSES];
130 uint32_t base;
131
132 *out = CALLOC_STRUCT(nouveau_ws_context);
133 if (!*out)
134 return -ENOMEM;
135
136 int ret = drmCommandWriteRead(dev->fd, DRM_NOUVEAU_CHANNEL_ALLOC, &req, sizeof(req));
137 if (ret)
138 goto fail_alloc;
139
140 ret = nouveau_ws_context_query_classes(dev->fd, req.channel, classes);
141 if (ret)
142 goto fail_subchan;
143
144 base = (uint32_t)(0xbeef + req.channel) << 16;
145
146 if (engines & NOUVEAU_WS_ENGINE_COPY) {
147 uint32_t obj_class = nouveau_ws_context_find_class(classes, 0xb5);
148 ret = nouveau_ws_subchan_alloc(dev->fd, req.channel, 0,
149 obj_class, &(*out)->copy);
150 if (ret)
151 goto fail_subchan;
152 }
153
154 if (engines & NOUVEAU_WS_ENGINE_2D) {
155 uint32_t obj_class = nouveau_ws_context_find_class(classes, 0x2d);
156 ret = nouveau_ws_subchan_alloc(dev->fd, req.channel, base | 0x902d,
157 obj_class, &(*out)->eng2d);
158 if (ret)
159 goto fail_subchan;
160 }
161
162 if (engines & NOUVEAU_WS_ENGINE_3D) {
163 uint32_t obj_class = nouveau_ws_context_find_class(classes, 0x97);
164 ret = nouveau_ws_subchan_alloc(dev->fd, req.channel, base | 0x003d,
165 obj_class, &(*out)->eng3d);
166 if (ret)
167 goto fail_subchan;
168 }
169
170 if (engines & NOUVEAU_WS_ENGINE_M2MF) {
171 uint32_t obj_class = nouveau_ws_context_find_class(classes, 0x40);
172 if (!obj_class)
173 obj_class = nouveau_ws_context_find_class(classes, 0x39);
174 ret = nouveau_ws_subchan_alloc(dev->fd, req.channel, base | 0x323f,
175 obj_class, &(*out)->m2mf);
176 if (ret)
177 goto fail_subchan;
178 }
179
180 if (engines & NOUVEAU_WS_ENGINE_COMPUTE) {
181 uint32_t obj_class = nouveau_ws_context_find_class(classes, 0xc0);
182 ret = nouveau_ws_subchan_alloc(dev->fd, req.channel, base | 0x00c0,
183 obj_class, &(*out)->compute);
184 if (ret)
185 goto fail_subchan;
186 }
187
188 (*out)->channel = req.channel;
189 (*out)->dev = dev;
190 return 0;
191
192 fail_subchan:
193 nouveau_ws_subchan_dealloc(dev->fd, &(*out)->compute);
194 nouveau_ws_subchan_dealloc(dev->fd, &(*out)->eng3d);
195 nouveau_ws_subchan_dealloc(dev->fd, &(*out)->copy);
196 nouveau_ws_subchan_dealloc(dev->fd, &(*out)->m2mf);
197 nouveau_ws_subchan_dealloc(dev->fd, &(*out)->eng2d);
198 nouveau_ws_channel_dealloc(dev->fd, req.channel);
199 fail_alloc:
200 FREE(*out);
201 return ret;
202 }
203
204 void
nouveau_ws_context_destroy(struct nouveau_ws_context * context)205 nouveau_ws_context_destroy(struct nouveau_ws_context *context)
206 {
207 nouveau_ws_subchan_dealloc(context->dev->fd, &context->compute);
208 nouveau_ws_subchan_dealloc(context->dev->fd, &context->eng3d);
209 nouveau_ws_subchan_dealloc(context->dev->fd, &context->copy);
210 nouveau_ws_subchan_dealloc(context->dev->fd, &context->m2mf);
211 nouveau_ws_subchan_dealloc(context->dev->fd, &context->eng2d);
212 nouveau_ws_channel_dealloc(context->dev->fd, context->channel);
213 FREE(context);
214 }
215
216 bool
nouveau_ws_context_killed(struct nouveau_ws_context * context)217 nouveau_ws_context_killed(struct nouveau_ws_context *context)
218 {
219 /* we are using the normal pushbuf submission ioctl as this is how nouveau implemented this on
220 * the kernel side.
221 * And as long as we submit nothing (e.g. nr_push is 0) it's more or less a noop on the kernel
222 * side.
223 */
224 struct drm_nouveau_gem_pushbuf req = {
225 .channel = context->channel,
226 };
227 int ret = drmCommandWriteRead(context->dev->fd, DRM_NOUVEAU_GEM_PUSHBUF, &req, sizeof(req));
228 /* nouveau returns ENODEV once the channel was killed */
229 return ret == -ENODEV;
230 }
231