1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright (C) 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io>
3bf215546Sopenharmony_ci * Copyright 2019 Collabora, Ltd.
4bf215546Sopenharmony_ci *
5bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
6bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
7bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation
8bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
10bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
11bf215546Sopenharmony_ci *
12bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next
13bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
14bf215546Sopenharmony_ci * Software.
15bf215546Sopenharmony_ci *
16bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21bf215546Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22bf215546Sopenharmony_ci * SOFTWARE.
23bf215546Sopenharmony_ci */
24bf215546Sopenharmony_ci
25bf215546Sopenharmony_ci#include <inttypes.h>
26bf215546Sopenharmony_ci#include "agx_device.h"
27bf215546Sopenharmony_ci#include "agx_bo.h"
28bf215546Sopenharmony_ci#include "decode.h"
29bf215546Sopenharmony_ci
30bf215546Sopenharmony_ciunsigned AGX_FAKE_HANDLE = 0;
31bf215546Sopenharmony_ciuint64_t AGX_FAKE_LO = 0;
32bf215546Sopenharmony_ciuint64_t AGX_FAKE_HI = (1ull << 32);
33bf215546Sopenharmony_ci
34bf215546Sopenharmony_cistatic void
35bf215546Sopenharmony_ciagx_bo_free(struct agx_device *dev, struct agx_bo *bo)
36bf215546Sopenharmony_ci{
37bf215546Sopenharmony_ci#if __APPLE__
38bf215546Sopenharmony_ci   const uint64_t handle = bo->handle;
39bf215546Sopenharmony_ci
40bf215546Sopenharmony_ci   kern_return_t ret = IOConnectCallScalarMethod(dev->fd,
41bf215546Sopenharmony_ci                       AGX_SELECTOR_FREE_MEM,
42bf215546Sopenharmony_ci                       &handle, 1, NULL, NULL);
43bf215546Sopenharmony_ci
44bf215546Sopenharmony_ci   if (ret)
45bf215546Sopenharmony_ci      fprintf(stderr, "error freeing BO mem: %u\n", ret);
46bf215546Sopenharmony_ci#else
47bf215546Sopenharmony_ci   free(bo->ptr.cpu);
48bf215546Sopenharmony_ci#endif
49bf215546Sopenharmony_ci
50bf215546Sopenharmony_ci   /* Reset the handle */
51bf215546Sopenharmony_ci   memset(bo, 0, sizeof(*bo));
52bf215546Sopenharmony_ci}
53bf215546Sopenharmony_ci
54bf215546Sopenharmony_civoid
55bf215546Sopenharmony_ciagx_shmem_free(struct agx_device *dev, unsigned handle)
56bf215546Sopenharmony_ci{
57bf215546Sopenharmony_ci#if __APPLE__
58bf215546Sopenharmony_ci	const uint64_t input = handle;
59bf215546Sopenharmony_ci   kern_return_t ret = IOConnectCallScalarMethod(dev->fd,
60bf215546Sopenharmony_ci                       AGX_SELECTOR_FREE_SHMEM,
61bf215546Sopenharmony_ci                       &input, 1, NULL, NULL);
62bf215546Sopenharmony_ci
63bf215546Sopenharmony_ci   if (ret)
64bf215546Sopenharmony_ci      fprintf(stderr, "error freeing shmem: %u\n", ret);
65bf215546Sopenharmony_ci#else
66bf215546Sopenharmony_ci#endif
67bf215546Sopenharmony_ci}
68bf215546Sopenharmony_ci
69bf215546Sopenharmony_cistruct agx_bo
70bf215546Sopenharmony_ciagx_shmem_alloc(struct agx_device *dev, size_t size, bool cmdbuf)
71bf215546Sopenharmony_ci{
72bf215546Sopenharmony_ci   struct agx_bo bo;
73bf215546Sopenharmony_ci
74bf215546Sopenharmony_ci#if __APPLE__
75bf215546Sopenharmony_ci   struct agx_create_shmem_resp out = {};
76bf215546Sopenharmony_ci   size_t out_sz = sizeof(out);
77bf215546Sopenharmony_ci
78bf215546Sopenharmony_ci   uint64_t inputs[2] = {
79bf215546Sopenharmony_ci      size,
80bf215546Sopenharmony_ci      cmdbuf ? 1 : 0 // 2 - error reporting, 1 - no error reporting
81bf215546Sopenharmony_ci   };
82bf215546Sopenharmony_ci
83bf215546Sopenharmony_ci   kern_return_t ret = IOConnectCallMethod(dev->fd,
84bf215546Sopenharmony_ci                                           AGX_SELECTOR_CREATE_SHMEM, inputs, 2, NULL, 0, NULL,
85bf215546Sopenharmony_ci                                           NULL, &out, &out_sz);
86bf215546Sopenharmony_ci
87bf215546Sopenharmony_ci   assert(ret == 0);
88bf215546Sopenharmony_ci   assert(out_sz == sizeof(out));
89bf215546Sopenharmony_ci   assert(out.size == size);
90bf215546Sopenharmony_ci   assert(out.map != 0);
91bf215546Sopenharmony_ci
92bf215546Sopenharmony_ci   bo = (struct agx_bo) {
93bf215546Sopenharmony_ci      .type = cmdbuf ? AGX_ALLOC_CMDBUF : AGX_ALLOC_MEMMAP,
94bf215546Sopenharmony_ci      .handle = out.id,
95bf215546Sopenharmony_ci      .ptr.cpu = out.map,
96bf215546Sopenharmony_ci      .size = out.size,
97bf215546Sopenharmony_ci      .guid = 0, /* TODO? */
98bf215546Sopenharmony_ci   };
99bf215546Sopenharmony_ci#else
100bf215546Sopenharmony_ci   bo = (struct agx_bo) {
101bf215546Sopenharmony_ci      .type = cmdbuf ? AGX_ALLOC_CMDBUF : AGX_ALLOC_MEMMAP,
102bf215546Sopenharmony_ci      .handle = AGX_FAKE_HANDLE++,
103bf215546Sopenharmony_ci      .ptr.cpu = calloc(1, size),
104bf215546Sopenharmony_ci      .size = size,
105bf215546Sopenharmony_ci      .guid = 0, /* TODO? */
106bf215546Sopenharmony_ci   };
107bf215546Sopenharmony_ci#endif
108bf215546Sopenharmony_ci
109bf215546Sopenharmony_ci   if (dev->debug & AGX_DBG_TRACE)
110bf215546Sopenharmony_ci      agxdecode_track_alloc(&bo);
111bf215546Sopenharmony_ci
112bf215546Sopenharmony_ci   return bo;
113bf215546Sopenharmony_ci}
114bf215546Sopenharmony_ci
115bf215546Sopenharmony_cistatic struct agx_bo *
116bf215546Sopenharmony_ciagx_bo_alloc(struct agx_device *dev, size_t size,
117bf215546Sopenharmony_ci             uint32_t flags)
118bf215546Sopenharmony_ci{
119bf215546Sopenharmony_ci   struct agx_bo *bo;
120bf215546Sopenharmony_ci   unsigned handle = 0;
121bf215546Sopenharmony_ci
122bf215546Sopenharmony_ci#if __APPLE__
123bf215546Sopenharmony_ci   uint32_t mode = 0x430; // shared, ?
124bf215546Sopenharmony_ci
125bf215546Sopenharmony_ci   uint32_t args_in[24] = { 0 };
126bf215546Sopenharmony_ci   args_in[4] = 0x4000101; //0x1000101; // unk
127bf215546Sopenharmony_ci   args_in[5] = mode;
128bf215546Sopenharmony_ci   args_in[16] = size;
129bf215546Sopenharmony_ci   args_in[20] = flags;
130bf215546Sopenharmony_ci
131bf215546Sopenharmony_ci   uint64_t out[10] = { 0 };
132bf215546Sopenharmony_ci   size_t out_sz = sizeof(out);
133bf215546Sopenharmony_ci
134bf215546Sopenharmony_ci   kern_return_t ret = IOConnectCallMethod(dev->fd,
135bf215546Sopenharmony_ci                                           AGX_SELECTOR_ALLOCATE_MEM, NULL, 0, args_in,
136bf215546Sopenharmony_ci                                           sizeof(args_in), NULL, 0, out, &out_sz);
137bf215546Sopenharmony_ci
138bf215546Sopenharmony_ci   assert(ret == 0);
139bf215546Sopenharmony_ci   assert(out_sz == sizeof(out));
140bf215546Sopenharmony_ci   handle = (out[3] >> 32ull);
141bf215546Sopenharmony_ci#else
142bf215546Sopenharmony_ci   /* Faked software path until we have a DRM driver */
143bf215546Sopenharmony_ci   handle = (++AGX_FAKE_HANDLE);
144bf215546Sopenharmony_ci#endif
145bf215546Sopenharmony_ci
146bf215546Sopenharmony_ci   pthread_mutex_lock(&dev->bo_map_lock);
147bf215546Sopenharmony_ci   bo = agx_lookup_bo(dev, handle);
148bf215546Sopenharmony_ci   pthread_mutex_unlock(&dev->bo_map_lock);
149bf215546Sopenharmony_ci
150bf215546Sopenharmony_ci   /* Fresh handle */
151bf215546Sopenharmony_ci   assert(!memcmp(bo, &((struct agx_bo) {}), sizeof(*bo)));
152bf215546Sopenharmony_ci
153bf215546Sopenharmony_ci   bo->type = AGX_ALLOC_REGULAR;
154bf215546Sopenharmony_ci   bo->size = size;
155bf215546Sopenharmony_ci   bo->flags = flags;
156bf215546Sopenharmony_ci   bo->dev = dev;
157bf215546Sopenharmony_ci   bo->handle = handle;
158bf215546Sopenharmony_ci
159bf215546Sopenharmony_ci   ASSERTED bool lo = (flags & 0x08000000);
160bf215546Sopenharmony_ci
161bf215546Sopenharmony_ci#if __APPLE__
162bf215546Sopenharmony_ci   bo->ptr.gpu = out[0];
163bf215546Sopenharmony_ci   bo->ptr.cpu = (void *) out[1];
164bf215546Sopenharmony_ci   bo->guid = out[5];
165bf215546Sopenharmony_ci#else
166bf215546Sopenharmony_ci   if (lo) {
167bf215546Sopenharmony_ci      bo->ptr.gpu = AGX_FAKE_LO;
168bf215546Sopenharmony_ci      AGX_FAKE_LO += bo->size;
169bf215546Sopenharmony_ci   } else {
170bf215546Sopenharmony_ci      bo->ptr.gpu = AGX_FAKE_HI;
171bf215546Sopenharmony_ci      AGX_FAKE_HI += bo->size;
172bf215546Sopenharmony_ci   }
173bf215546Sopenharmony_ci
174bf215546Sopenharmony_ci   bo->ptr.gpu = (((uint64_t) bo->handle) << (lo ? 16 : 24));
175bf215546Sopenharmony_ci   bo->ptr.cpu = calloc(1, bo->size);
176bf215546Sopenharmony_ci#endif
177bf215546Sopenharmony_ci
178bf215546Sopenharmony_ci   assert(bo->ptr.gpu < (1ull << (lo ? 32 : 40)));
179bf215546Sopenharmony_ci
180bf215546Sopenharmony_ci   return bo;
181bf215546Sopenharmony_ci}
182bf215546Sopenharmony_ci
183bf215546Sopenharmony_civoid
184bf215546Sopenharmony_ciagx_bo_reference(struct agx_bo *bo)
185bf215546Sopenharmony_ci{
186bf215546Sopenharmony_ci   if (bo) {
187bf215546Sopenharmony_ci      ASSERTED int count = p_atomic_inc_return(&bo->refcnt);
188bf215546Sopenharmony_ci      assert(count != 1);
189bf215546Sopenharmony_ci   }
190bf215546Sopenharmony_ci}
191bf215546Sopenharmony_ci
192bf215546Sopenharmony_civoid
193bf215546Sopenharmony_ciagx_bo_unreference(struct agx_bo *bo)
194bf215546Sopenharmony_ci{
195bf215546Sopenharmony_ci   if (!bo)
196bf215546Sopenharmony_ci      return;
197bf215546Sopenharmony_ci
198bf215546Sopenharmony_ci   /* Don't return to cache if there are still references */
199bf215546Sopenharmony_ci   if (p_atomic_dec_return(&bo->refcnt))
200bf215546Sopenharmony_ci      return;
201bf215546Sopenharmony_ci
202bf215546Sopenharmony_ci   struct agx_device *dev = bo->dev;
203bf215546Sopenharmony_ci
204bf215546Sopenharmony_ci   pthread_mutex_lock(&dev->bo_map_lock);
205bf215546Sopenharmony_ci
206bf215546Sopenharmony_ci   /* Someone might have imported this BO while we were waiting for the
207bf215546Sopenharmony_ci    * lock, let's make sure it's still not referenced before freeing it.
208bf215546Sopenharmony_ci    */
209bf215546Sopenharmony_ci   if (p_atomic_read(&bo->refcnt) == 0) {
210bf215546Sopenharmony_ci      if (dev->debug & AGX_DBG_TRACE)
211bf215546Sopenharmony_ci         agxdecode_track_free(bo);
212bf215546Sopenharmony_ci
213bf215546Sopenharmony_ci      /* TODO: cache */
214bf215546Sopenharmony_ci      agx_bo_free(dev, bo);
215bf215546Sopenharmony_ci
216bf215546Sopenharmony_ci   }
217bf215546Sopenharmony_ci   pthread_mutex_unlock(&dev->bo_map_lock);
218bf215546Sopenharmony_ci}
219bf215546Sopenharmony_ci
220bf215546Sopenharmony_cistruct agx_bo *
221bf215546Sopenharmony_ciagx_bo_create(struct agx_device *dev, unsigned size, unsigned flags)
222bf215546Sopenharmony_ci{
223bf215546Sopenharmony_ci   struct agx_bo *bo;
224bf215546Sopenharmony_ci   assert(size > 0);
225bf215546Sopenharmony_ci
226bf215546Sopenharmony_ci   /* To maximize BO cache usage, don't allocate tiny BOs */
227bf215546Sopenharmony_ci   size = ALIGN_POT(size, 4096);
228bf215546Sopenharmony_ci
229bf215546Sopenharmony_ci   /* TODO: Cache fetch */
230bf215546Sopenharmony_ci   bo = agx_bo_alloc(dev, size, flags);
231bf215546Sopenharmony_ci
232bf215546Sopenharmony_ci   if (!bo) {
233bf215546Sopenharmony_ci      fprintf(stderr, "BO creation failed\n");
234bf215546Sopenharmony_ci      return NULL;
235bf215546Sopenharmony_ci   }
236bf215546Sopenharmony_ci
237bf215546Sopenharmony_ci   p_atomic_set(&bo->refcnt, 1);
238bf215546Sopenharmony_ci
239bf215546Sopenharmony_ci   if (dev->debug & AGX_DBG_TRACE)
240bf215546Sopenharmony_ci      agxdecode_track_alloc(bo);
241bf215546Sopenharmony_ci
242bf215546Sopenharmony_ci   return bo;
243bf215546Sopenharmony_ci}
244bf215546Sopenharmony_ci
245bf215546Sopenharmony_cistatic void
246bf215546Sopenharmony_ciagx_get_global_ids(struct agx_device *dev)
247bf215546Sopenharmony_ci{
248bf215546Sopenharmony_ci#if __APPLE__
249bf215546Sopenharmony_ci   uint64_t out[2] = {};
250bf215546Sopenharmony_ci   size_t out_sz = sizeof(out);
251bf215546Sopenharmony_ci
252bf215546Sopenharmony_ci   ASSERTED kern_return_t ret = IOConnectCallStructMethod(dev->fd,
253bf215546Sopenharmony_ci                       AGX_SELECTOR_GET_GLOBAL_IDS,
254bf215546Sopenharmony_ci                       NULL, 0, &out, &out_sz);
255bf215546Sopenharmony_ci
256bf215546Sopenharmony_ci   assert(ret == 0);
257bf215546Sopenharmony_ci   assert(out_sz == sizeof(out));
258bf215546Sopenharmony_ci   assert(out[1] > out[0]);
259bf215546Sopenharmony_ci
260bf215546Sopenharmony_ci   dev->next_global_id = out[0];
261bf215546Sopenharmony_ci   dev->last_global_id = out[1];
262bf215546Sopenharmony_ci#else
263bf215546Sopenharmony_ci   dev->next_global_id = 0;
264bf215546Sopenharmony_ci   dev->last_global_id = 0x1000000;
265bf215546Sopenharmony_ci#endif
266bf215546Sopenharmony_ci}
267bf215546Sopenharmony_ci
268bf215546Sopenharmony_ciuint64_t
269bf215546Sopenharmony_ciagx_get_global_id(struct agx_device *dev)
270bf215546Sopenharmony_ci{
271bf215546Sopenharmony_ci   if (unlikely(dev->next_global_id >= dev->last_global_id)) {
272bf215546Sopenharmony_ci      agx_get_global_ids(dev);
273bf215546Sopenharmony_ci   }
274bf215546Sopenharmony_ci
275bf215546Sopenharmony_ci   return dev->next_global_id++;
276bf215546Sopenharmony_ci}
277bf215546Sopenharmony_ci
278bf215546Sopenharmony_ci/* Tries to open an AGX device, returns true if successful */
279bf215546Sopenharmony_ci
280bf215546Sopenharmony_cibool
281bf215546Sopenharmony_ciagx_open_device(void *memctx, struct agx_device *dev)
282bf215546Sopenharmony_ci{
283bf215546Sopenharmony_ci#if __APPLE__
284bf215546Sopenharmony_ci   kern_return_t ret;
285bf215546Sopenharmony_ci
286bf215546Sopenharmony_ci   /* TODO: Support other models */
287bf215546Sopenharmony_ci   CFDictionaryRef matching = IOServiceNameMatching("AGXAcceleratorG13G_B0");
288bf215546Sopenharmony_ci
289bf215546Sopenharmony_ci   io_service_t service =
290bf215546Sopenharmony_ci      IOServiceGetMatchingService(kIOMasterPortDefault, matching);
291bf215546Sopenharmony_ci
292bf215546Sopenharmony_ci   if (!service)
293bf215546Sopenharmony_ci      return false;
294bf215546Sopenharmony_ci
295bf215546Sopenharmony_ci   ret = IOServiceOpen(service, mach_task_self(), AGX_SERVICE_TYPE, &dev->fd);
296bf215546Sopenharmony_ci
297bf215546Sopenharmony_ci   if (ret)
298bf215546Sopenharmony_ci      return false;
299bf215546Sopenharmony_ci
300bf215546Sopenharmony_ci   const char *api = "Equestria";
301bf215546Sopenharmony_ci   char in[16] = { 0 };
302bf215546Sopenharmony_ci   assert(strlen(api) < sizeof(in));
303bf215546Sopenharmony_ci   memcpy(in, api, strlen(api));
304bf215546Sopenharmony_ci
305bf215546Sopenharmony_ci   ret = IOConnectCallStructMethod(dev->fd, AGX_SELECTOR_SET_API, in,
306bf215546Sopenharmony_ci                                   sizeof(in), NULL, NULL);
307bf215546Sopenharmony_ci
308bf215546Sopenharmony_ci   /* Oddly, the return codes are flipped for SET_API */
309bf215546Sopenharmony_ci   if (ret != 1)
310bf215546Sopenharmony_ci      return false;
311bf215546Sopenharmony_ci#else
312bf215546Sopenharmony_ci   /* Only open a fake AGX device on other operating systems if forced */
313bf215546Sopenharmony_ci   if (!getenv("AGX_FAKE_DEVICE"))
314bf215546Sopenharmony_ci      return false;
315bf215546Sopenharmony_ci#endif
316bf215546Sopenharmony_ci
317bf215546Sopenharmony_ci   dev->memctx = memctx;
318bf215546Sopenharmony_ci   util_sparse_array_init(&dev->bo_map, sizeof(struct agx_bo), 512);
319bf215546Sopenharmony_ci
320bf215546Sopenharmony_ci   dev->queue = agx_create_command_queue(dev);
321bf215546Sopenharmony_ci   dev->cmdbuf = agx_shmem_alloc(dev, 0x4000, true); // length becomes kernelCommandDataSize
322bf215546Sopenharmony_ci   dev->memmap = agx_shmem_alloc(dev, 0x10000, false);
323bf215546Sopenharmony_ci   agx_get_global_ids(dev);
324bf215546Sopenharmony_ci
325bf215546Sopenharmony_ci   return true;
326bf215546Sopenharmony_ci}
327bf215546Sopenharmony_ci
328bf215546Sopenharmony_civoid
329bf215546Sopenharmony_ciagx_close_device(struct agx_device *dev)
330bf215546Sopenharmony_ci{
331bf215546Sopenharmony_ci   util_sparse_array_finish(&dev->bo_map);
332bf215546Sopenharmony_ci
333bf215546Sopenharmony_ci#if __APPLE__
334bf215546Sopenharmony_ci   kern_return_t ret = IOServiceClose(dev->fd);
335bf215546Sopenharmony_ci
336bf215546Sopenharmony_ci   if (ret)
337bf215546Sopenharmony_ci      fprintf(stderr, "Error from IOServiceClose: %u\n", ret);
338bf215546Sopenharmony_ci#endif
339bf215546Sopenharmony_ci}
340bf215546Sopenharmony_ci
341bf215546Sopenharmony_ci#if __APPLE__
342bf215546Sopenharmony_cistatic struct agx_notification_queue
343bf215546Sopenharmony_ciagx_create_notification_queue(mach_port_t connection)
344bf215546Sopenharmony_ci{
345bf215546Sopenharmony_ci   struct agx_create_notification_queue_resp resp;
346bf215546Sopenharmony_ci   size_t resp_size = sizeof(resp);
347bf215546Sopenharmony_ci   assert(resp_size == 0x10);
348bf215546Sopenharmony_ci
349bf215546Sopenharmony_ci   ASSERTED kern_return_t ret = IOConnectCallStructMethod(connection,
350bf215546Sopenharmony_ci                       AGX_SELECTOR_CREATE_NOTIFICATION_QUEUE,
351bf215546Sopenharmony_ci                       NULL, 0, &resp, &resp_size);
352bf215546Sopenharmony_ci
353bf215546Sopenharmony_ci   assert(resp_size == sizeof(resp));
354bf215546Sopenharmony_ci   assert(ret == 0);
355bf215546Sopenharmony_ci
356bf215546Sopenharmony_ci   mach_port_t notif_port = IODataQueueAllocateNotificationPort();
357bf215546Sopenharmony_ci   IOConnectSetNotificationPort(connection, 0, notif_port, resp.unk2);
358bf215546Sopenharmony_ci
359bf215546Sopenharmony_ci   return (struct agx_notification_queue) {
360bf215546Sopenharmony_ci      .port = notif_port,
361bf215546Sopenharmony_ci      .queue = resp.queue,
362bf215546Sopenharmony_ci      .id = resp.unk2
363bf215546Sopenharmony_ci   };
364bf215546Sopenharmony_ci}
365bf215546Sopenharmony_ci#endif
366bf215546Sopenharmony_ci
367bf215546Sopenharmony_cistruct agx_command_queue
368bf215546Sopenharmony_ciagx_create_command_queue(struct agx_device *dev)
369bf215546Sopenharmony_ci{
370bf215546Sopenharmony_ci#if __APPLE__
371bf215546Sopenharmony_ci   struct agx_command_queue queue = {};
372bf215546Sopenharmony_ci
373bf215546Sopenharmony_ci   {
374bf215546Sopenharmony_ci      uint8_t buffer[1024 + 8] = { 0 };
375bf215546Sopenharmony_ci      const char *path = "/tmp/a.out";
376bf215546Sopenharmony_ci      assert(strlen(path) < 1022);
377bf215546Sopenharmony_ci      memcpy(buffer + 0, path, strlen(path));
378bf215546Sopenharmony_ci
379bf215546Sopenharmony_ci      /* Copy to the end */
380bf215546Sopenharmony_ci      unsigned END_LEN = MIN2(strlen(path), 1024 - strlen(path));
381bf215546Sopenharmony_ci      unsigned SKIP = strlen(path) - END_LEN;
382bf215546Sopenharmony_ci      unsigned OFFS = 1024 - END_LEN;
383bf215546Sopenharmony_ci      memcpy(buffer + OFFS, path + SKIP, END_LEN);
384bf215546Sopenharmony_ci
385bf215546Sopenharmony_ci      buffer[1024] = 0x2;
386bf215546Sopenharmony_ci
387bf215546Sopenharmony_ci      struct agx_create_command_queue_resp out = {};
388bf215546Sopenharmony_ci      size_t out_sz = sizeof(out);
389bf215546Sopenharmony_ci
390bf215546Sopenharmony_ci      ASSERTED kern_return_t ret = IOConnectCallStructMethod(dev->fd,
391bf215546Sopenharmony_ci                          AGX_SELECTOR_CREATE_COMMAND_QUEUE,
392bf215546Sopenharmony_ci                          buffer, sizeof(buffer),
393bf215546Sopenharmony_ci                          &out, &out_sz);
394bf215546Sopenharmony_ci
395bf215546Sopenharmony_ci      assert(ret == 0);
396bf215546Sopenharmony_ci      assert(out_sz == sizeof(out));
397bf215546Sopenharmony_ci
398bf215546Sopenharmony_ci      queue.id = out.id;
399bf215546Sopenharmony_ci      assert(queue.id);
400bf215546Sopenharmony_ci   }
401bf215546Sopenharmony_ci
402bf215546Sopenharmony_ci   queue.notif = agx_create_notification_queue(dev->fd);
403bf215546Sopenharmony_ci
404bf215546Sopenharmony_ci   {
405bf215546Sopenharmony_ci      uint64_t scalars[2] = {
406bf215546Sopenharmony_ci         queue.id,
407bf215546Sopenharmony_ci         queue.notif.id
408bf215546Sopenharmony_ci      };
409bf215546Sopenharmony_ci
410bf215546Sopenharmony_ci      ASSERTED kern_return_t ret = IOConnectCallScalarMethod(dev->fd,
411bf215546Sopenharmony_ci                          0x1D,
412bf215546Sopenharmony_ci                          scalars, 2, NULL, NULL);
413bf215546Sopenharmony_ci
414bf215546Sopenharmony_ci      assert(ret == 0);
415bf215546Sopenharmony_ci   }
416bf215546Sopenharmony_ci
417bf215546Sopenharmony_ci   {
418bf215546Sopenharmony_ci      uint64_t scalars[2] = {
419bf215546Sopenharmony_ci         queue.id,
420bf215546Sopenharmony_ci         0x1ffffffffull
421bf215546Sopenharmony_ci      };
422bf215546Sopenharmony_ci
423bf215546Sopenharmony_ci      ASSERTED kern_return_t ret = IOConnectCallScalarMethod(dev->fd,
424bf215546Sopenharmony_ci                          0x31,
425bf215546Sopenharmony_ci                          scalars, 2, NULL, NULL);
426bf215546Sopenharmony_ci
427bf215546Sopenharmony_ci      assert(ret == 0);
428bf215546Sopenharmony_ci   }
429bf215546Sopenharmony_ci
430bf215546Sopenharmony_ci   return queue;
431bf215546Sopenharmony_ci#else
432bf215546Sopenharmony_ci   return (struct agx_command_queue) {
433bf215546Sopenharmony_ci      0
434bf215546Sopenharmony_ci   };
435bf215546Sopenharmony_ci#endif
436bf215546Sopenharmony_ci}
437bf215546Sopenharmony_ci
438bf215546Sopenharmony_civoid
439bf215546Sopenharmony_ciagx_submit_cmdbuf(struct agx_device *dev, unsigned cmdbuf, unsigned mappings, uint64_t scalar)
440bf215546Sopenharmony_ci{
441bf215546Sopenharmony_ci#if __APPLE__
442bf215546Sopenharmony_ci   struct agx_submit_cmdbuf_req req = {
443bf215546Sopenharmony_ci      .count = 1,
444bf215546Sopenharmony_ci      .command_buffer_shmem_id = cmdbuf,
445bf215546Sopenharmony_ci      .segment_list_shmem_id = mappings,
446bf215546Sopenharmony_ci      .notify_1 = 0xABCD,
447bf215546Sopenharmony_ci      .notify_2 = 0x1234,
448bf215546Sopenharmony_ci   };
449bf215546Sopenharmony_ci
450bf215546Sopenharmony_ci   ASSERTED kern_return_t ret = IOConnectCallMethod(dev->fd,
451bf215546Sopenharmony_ci                                           AGX_SELECTOR_SUBMIT_COMMAND_BUFFERS,
452bf215546Sopenharmony_ci                                           &scalar, 1,
453bf215546Sopenharmony_ci                                           &req, sizeof(req),
454bf215546Sopenharmony_ci                                           NULL, 0, NULL, 0);
455bf215546Sopenharmony_ci   assert(ret == 0);
456bf215546Sopenharmony_ci   return;
457bf215546Sopenharmony_ci#endif
458bf215546Sopenharmony_ci}
459bf215546Sopenharmony_ci
460bf215546Sopenharmony_ci/*
461bf215546Sopenharmony_ci * Wait for a frame to finish rendering.
462bf215546Sopenharmony_ci *
463bf215546Sopenharmony_ci * The macOS kernel indicates that rendering has finished using a notification
464bf215546Sopenharmony_ci * queue. The kernel will send two messages on the notification queue. The
465bf215546Sopenharmony_ci * second message indicates that rendering has completed. This simple routine
466bf215546Sopenharmony_ci * waits for both messages. It's important that IODataQueueDequeue is used in a
467bf215546Sopenharmony_ci * loop to flush the entire queue before calling
468bf215546Sopenharmony_ci * IODataQueueWaitForAvailableData. Otherwise, we can race and get stuck in
469bf215546Sopenharmony_ci * WaitForAvailabaleData.
470bf215546Sopenharmony_ci */
471bf215546Sopenharmony_civoid
472bf215546Sopenharmony_ciagx_wait_queue(struct agx_command_queue queue)
473bf215546Sopenharmony_ci{
474bf215546Sopenharmony_ci#if __APPLE__
475bf215546Sopenharmony_ci   uint64_t data[4];
476bf215546Sopenharmony_ci   unsigned sz = sizeof(data);
477bf215546Sopenharmony_ci   unsigned message_id = 0;
478bf215546Sopenharmony_ci   uint64_t magic_numbers[2] = { 0xABCD, 0x1234 };
479bf215546Sopenharmony_ci
480bf215546Sopenharmony_ci   while (message_id < 2) {
481bf215546Sopenharmony_ci      IOReturn ret = IODataQueueWaitForAvailableData(queue.notif.queue, queue.notif.port);
482bf215546Sopenharmony_ci
483bf215546Sopenharmony_ci      if (ret) {
484bf215546Sopenharmony_ci         fprintf(stderr, "Error waiting for available data\n");
485bf215546Sopenharmony_ci         return;
486bf215546Sopenharmony_ci      }
487bf215546Sopenharmony_ci
488bf215546Sopenharmony_ci      while (IODataQueueDequeue(queue.notif.queue, data, &sz) == kIOReturnSuccess) {
489bf215546Sopenharmony_ci         assert(sz == sizeof(data));
490bf215546Sopenharmony_ci         assert(data[0] == magic_numbers[message_id]);
491bf215546Sopenharmony_ci         message_id++;
492bf215546Sopenharmony_ci      }
493bf215546Sopenharmony_ci   }
494bf215546Sopenharmony_ci#endif
495bf215546Sopenharmony_ci}
496