1/*
2 * Copyright © 2022 Imagination Technologies Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include <assert.h>
25#include <stdbool.h>
26#include <stdint.h>
27#include <vulkan/vulkan.h>
28#include <xf86drm.h>
29
30#include "pvr_private.h"
31#include "pvr_types.h"
32#include "pvr_winsys.h"
33#include "pvr_winsys_helper.h"
34#include "util/u_atomic.h"
35#include "vk_log.h"
36
37int pvr_winsys_helper_display_buffer_create(int master_fd,
38                                            uint64_t size,
39                                            uint32_t *const handle_out)
40{
41   struct drm_mode_create_dumb args = {
42      .width = size,
43      .height = 1,
44      .bpp = 8,
45   };
46   int ret;
47
48   ret = drmIoctl(master_fd, DRM_IOCTL_MODE_CREATE_DUMB, &args);
49   if (ret)
50      return ret;
51
52   *handle_out = args.handle;
53
54   return 0;
55}
56
57int pvr_winsys_helper_display_buffer_destroy(int master_fd, uint32_t handle)
58{
59   struct drm_mode_destroy_dumb args = {
60      .handle = handle,
61   };
62
63   return drmIoctl(master_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &args);
64}
65
66/* reserved_size can be 0 when no reserved area is needed. reserved_address must
67 * be 0 if reserved_size is 0.
68 */
69VkResult pvr_winsys_helper_winsys_heap_init(
70   struct pvr_winsys *const ws,
71   pvr_dev_addr_t base_address,
72   uint64_t size,
73   pvr_dev_addr_t reserved_address,
74   uint64_t reserved_size,
75   uint32_t log2_page_size,
76   const struct pvr_winsys_static_data_offsets *const static_data_offsets,
77   struct pvr_winsys_heap *const heap)
78{
79   const bool reserved_area_bottom_of_heap = reserved_address.addr ==
80                                             base_address.addr;
81   const pvr_dev_addr_t vma_heap_begin_addr =
82      reserved_area_bottom_of_heap
83         ? PVR_DEV_ADDR_OFFSET(base_address, reserved_size)
84         : base_address;
85   const uint64_t vma_heap_size = size - reserved_size;
86
87   assert(base_address.addr);
88   assert(reserved_size <= size);
89
90   /* As per the reserved_base powervr-km uapi documentation the reserved
91    * region can only be at the beginning of the heap or at the end.
92    * reserved_address is 0 if there is no reserved region.
93    * pvrsrv-km doesn't explicitly provide this info and it's assumed that it's
94    * always at the beginning.
95    */
96   assert(reserved_area_bottom_of_heap ||
97          reserved_address.addr + reserved_size == base_address.addr + size ||
98          (!reserved_address.addr && !reserved_size));
99
100   heap->ws = ws;
101   heap->base_addr = base_address;
102   heap->reserved_addr = reserved_address;
103
104   heap->size = size;
105   heap->reserved_size = reserved_size;
106
107   heap->page_size = 1 << log2_page_size;
108   heap->log2_page_size = log2_page_size;
109
110   util_vma_heap_init(&heap->vma_heap, vma_heap_begin_addr.addr, vma_heap_size);
111
112   heap->vma_heap.alloc_high = false;
113
114   /* It's expected that the heap destroy function to be the last thing that's
115    * called, so we start the ref_count at 0.
116    */
117   p_atomic_set(&heap->ref_count, 0);
118
119   if (pthread_mutex_init(&heap->lock, NULL))
120      return vk_error(NULL, VK_ERROR_INITIALIZATION_FAILED);
121
122   heap->static_data_offsets = *static_data_offsets;
123
124   return VK_SUCCESS;
125}
126
127bool pvr_winsys_helper_winsys_heap_finish(struct pvr_winsys_heap *const heap)
128{
129   if (p_atomic_read(&heap->ref_count) != 0)
130      return false;
131
132   pthread_mutex_destroy(&heap->lock);
133   util_vma_heap_finish(&heap->vma_heap);
134
135   return true;
136}
137
138bool pvr_winsys_helper_heap_alloc(struct pvr_winsys_heap *const heap,
139                                  uint64_t size,
140                                  uint64_t alignment,
141                                  struct pvr_winsys_vma *const vma_out)
142{
143   struct pvr_winsys_vma vma = {
144      .heap = heap,
145   };
146
147   assert(util_is_power_of_two_nonzero(alignment));
148
149   /* pvr_srv_winsys_buffer_create() page aligns the size. We must do the same
150    * here to ensure enough heap space is allocated to be able to map the
151    * buffer to the GPU.
152    * We have to do this for the powervr kernel mode driver as well, as it
153    * returns a page aligned size when allocating buffers.
154    */
155   alignment = MAX2(alignment, heap->page_size);
156
157   size = ALIGN_POT(size, alignment);
158   vma.size = size;
159
160   pthread_mutex_lock(&heap->lock);
161   vma.dev_addr =
162      PVR_DEV_ADDR(util_vma_heap_alloc(&heap->vma_heap, size, heap->page_size));
163   pthread_mutex_unlock(&heap->lock);
164
165   if (!vma.dev_addr.addr) {
166      vk_error(NULL, VK_ERROR_OUT_OF_DEVICE_MEMORY);
167      return false;
168   }
169
170   p_atomic_inc(&heap->ref_count);
171
172   *vma_out = vma;
173
174   return true;
175}
176
177void pvr_winsys_helper_heap_free(struct pvr_winsys_vma *const vma)
178{
179   struct pvr_winsys_heap *const heap = vma->heap;
180
181   /* A vma with an existing device mapping should not be freed. */
182   assert(!vma->bo);
183
184   pthread_mutex_lock(&heap->lock);
185   util_vma_heap_free(&heap->vma_heap, vma->dev_addr.addr, vma->size);
186   pthread_mutex_unlock(&heap->lock);
187
188   p_atomic_dec(&heap->ref_count);
189}
190
191/* Note: the function assumes the heap allocation in the reserved memory area
192 * can be freed with the regular heap allocation free function. The free
193 * function gets called on mapping failure.
194 */
195static VkResult
196pvr_buffer_create_and_map(struct pvr_winsys *const ws,
197                          heap_alloc_reserved_func heap_alloc_reserved,
198                          struct pvr_winsys_heap *heap,
199                          pvr_dev_addr_t dev_addr,
200                          uint64_t size,
201                          uint64_t alignment,
202                          struct pvr_winsys_vma **const vma_out)
203{
204   struct pvr_winsys_vma *vma;
205   struct pvr_winsys_bo *bo;
206   pvr_dev_addr_t addr;
207   VkResult result;
208
209   /* Address should not be NULL, this function is used to allocate and map
210    * reserved addresses and is only supposed to be used internally.
211    */
212   assert(dev_addr.addr);
213
214   result = ws->ops->buffer_create(ws,
215                                   size,
216                                   alignment,
217                                   PVR_WINSYS_BO_TYPE_GPU,
218                                   PVR_WINSYS_BO_FLAG_CPU_ACCESS,
219                                   &bo);
220   if (result != VK_SUCCESS)
221      return result;
222
223   vma = heap_alloc_reserved(heap, dev_addr, size, alignment);
224   if (!vma) {
225      result = VK_ERROR_OUT_OF_DEVICE_MEMORY;
226      goto err_pvr_winsys_buffer_destroy;
227   }
228
229   addr = ws->ops->vma_map(vma, bo, 0, size);
230   if (!addr.addr) {
231      result = VK_ERROR_MEMORY_MAP_FAILED;
232      goto err_pvr_winsys_heap_free;
233   }
234
235   /* Note this won't destroy bo as its being used by VMA, once vma is
236    * unmapped, bo will be destroyed automatically.
237    */
238   ws->ops->buffer_destroy(bo);
239
240   *vma_out = vma;
241
242   return VK_SUCCESS;
243
244err_pvr_winsys_heap_free:
245   ws->ops->heap_free(vma);
246
247err_pvr_winsys_buffer_destroy:
248   ws->ops->buffer_destroy(bo);
249
250   return result;
251}
252
253static void inline pvr_buffer_destroy_and_unmap(struct pvr_winsys_vma *vma)
254{
255   const struct pvr_winsys *const ws = vma->heap->ws;
256
257   /* Buffer object associated with the vma will be automatically destroyed
258    * once vma is unmapped.
259    */
260   ws->ops->vma_unmap(vma);
261   ws->ops->heap_free(vma);
262}
263
264VkResult pvr_winsys_helper_allocate_static_memory(
265   struct pvr_winsys *const ws,
266   heap_alloc_reserved_func heap_alloc_reserved,
267   struct pvr_winsys_heap *const general_heap,
268   struct pvr_winsys_heap *const pds_heap,
269   struct pvr_winsys_heap *const usc_heap,
270   struct pvr_winsys_vma **const general_vma_out,
271   struct pvr_winsys_vma **const pds_vma_out,
272   struct pvr_winsys_vma **const usc_vma_out)
273{
274   struct pvr_winsys_vma *general_vma;
275   struct pvr_winsys_vma *pds_vma;
276   struct pvr_winsys_vma *usc_vma;
277   VkResult result;
278
279   result = pvr_buffer_create_and_map(ws,
280                                      heap_alloc_reserved,
281                                      general_heap,
282                                      general_heap->reserved_addr,
283                                      general_heap->reserved_size,
284                                      general_heap->page_size,
285                                      &general_vma);
286   if (result != VK_SUCCESS)
287      return result;
288
289   result = pvr_buffer_create_and_map(ws,
290                                      heap_alloc_reserved,
291                                      pds_heap,
292                                      pds_heap->reserved_addr,
293                                      pds_heap->reserved_size,
294                                      pds_heap->page_size,
295                                      &pds_vma);
296   if (result != VK_SUCCESS)
297      goto err_pvr_buffer_destroy_and_unmap_general;
298
299   result = pvr_buffer_create_and_map(ws,
300                                      heap_alloc_reserved,
301                                      usc_heap,
302                                      usc_heap->reserved_addr,
303                                      pds_heap->reserved_size,
304                                      usc_heap->page_size,
305                                      &usc_vma);
306   if (result != VK_SUCCESS)
307      goto err_pvr_buffer_destroy_and_unmap_pds;
308
309   *general_vma_out = general_vma;
310   *pds_vma_out = pds_vma;
311   *usc_vma_out = usc_vma;
312
313   return VK_SUCCESS;
314
315err_pvr_buffer_destroy_and_unmap_pds:
316   pvr_buffer_destroy_and_unmap(pds_vma);
317
318err_pvr_buffer_destroy_and_unmap_general:
319   pvr_buffer_destroy_and_unmap(general_vma);
320
321   return result;
322}
323
324void pvr_winsys_helper_free_static_memory(
325   struct pvr_winsys_vma *const general_vma,
326   struct pvr_winsys_vma *const pds_vma,
327   struct pvr_winsys_vma *const usc_vma)
328{
329   pvr_buffer_destroy_and_unmap(usc_vma);
330   pvr_buffer_destroy_and_unmap(pds_vma);
331   pvr_buffer_destroy_and_unmap(general_vma);
332}
333
334static void pvr_setup_static_vdm_sync(uint8_t *const pds_ptr,
335                                      uint64_t pds_sync_offset_in_bytes,
336                                      uint8_t *const usc_ptr,
337                                      uint64_t usc_sync_offset_in_bytes)
338{
339   /* TODO: this needs to be auto-generated */
340   const uint8_t state_update[] = { 0x44, 0xA0, 0x80, 0x05,
341                                    0x00, 0x00, 0x00, 0xFF };
342
343   struct pvr_pds_kickusc_program ppp_state_update_program = { 0 };
344
345   memcpy(usc_ptr + usc_sync_offset_in_bytes,
346          state_update,
347          sizeof(state_update));
348
349   pvr_pds_setup_doutu(&ppp_state_update_program.usc_task_control,
350                       usc_sync_offset_in_bytes,
351                       0,
352                       PVRX(PDSINST_DOUTU_SAMPLE_RATE_INSTANCE),
353                       false);
354
355   pvr_pds_kick_usc(&ppp_state_update_program,
356                    (uint32_t *)&pds_ptr[pds_sync_offset_in_bytes],
357                    0,
358                    false,
359                    PDS_GENERATE_CODEDATA_SEGMENTS);
360}
361
362static void
363pvr_setup_static_pixel_event_program(uint8_t *const pds_ptr,
364                                     uint64_t pds_eot_offset_in_bytes)
365{
366   struct pvr_pds_event_program pixel_event_program = { 0 };
367
368   pvr_pds_generate_pixel_event(&pixel_event_program,
369                                (uint32_t *)&pds_ptr[pds_eot_offset_in_bytes],
370                                PDS_GENERATE_CODE_SEGMENT,
371                                NULL);
372}
373
374VkResult
375pvr_winsys_helper_fill_static_memory(struct pvr_winsys *const ws,
376                                     struct pvr_winsys_vma *const general_vma,
377                                     struct pvr_winsys_vma *const pds_vma,
378                                     struct pvr_winsys_vma *const usc_vma)
379{
380   uint8_t *general_ptr, *pds_ptr, *usc_ptr;
381   VkResult result;
382
383   general_ptr = ws->ops->buffer_map(general_vma->bo);
384   if (!general_ptr)
385      return VK_ERROR_MEMORY_MAP_FAILED;
386
387   pds_ptr = ws->ops->buffer_map(pds_vma->bo);
388   if (!pds_ptr) {
389      result = VK_ERROR_MEMORY_MAP_FAILED;
390      goto err_pvr_srv_winsys_buffer_unmap_general;
391   }
392
393   usc_ptr = ws->ops->buffer_map(usc_vma->bo);
394   if (!usc_ptr) {
395      result = VK_ERROR_MEMORY_MAP_FAILED;
396      goto err_pvr_srv_winsys_buffer_unmap_pds;
397   }
398
399   pvr_setup_static_vdm_sync(pds_ptr,
400                             pds_vma->heap->static_data_offsets.vdm_sync,
401                             usc_ptr,
402                             usc_vma->heap->static_data_offsets.vdm_sync);
403
404   pvr_setup_static_pixel_event_program(pds_ptr,
405                                        pds_vma->heap->static_data_offsets.eot);
406
407   /* TODO: Complete control block copying work. */
408
409   ws->ops->buffer_unmap(usc_vma->bo);
410   ws->ops->buffer_unmap(pds_vma->bo);
411   ws->ops->buffer_unmap(general_vma->bo);
412
413   return VK_SUCCESS;
414
415err_pvr_srv_winsys_buffer_unmap_pds:
416   ws->ops->buffer_unmap(pds_vma->bo);
417
418err_pvr_srv_winsys_buffer_unmap_general:
419   ws->ops->buffer_unmap(general_vma->bo);
420
421   return result;
422}
423