xref: /aosp_15_r20/external/mesa3d/src/nouveau/winsys/nouveau_context.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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