• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
pvr_winsys_helper_display_buffer_create(int master_fd,uint64_t size,uint32_t * const handle_out)37 int 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 
pvr_winsys_helper_display_buffer_destroy(int master_fd,uint32_t handle)57 int 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  */
pvr_winsys_helper_winsys_heap_init(struct pvr_winsys * const ws,pvr_dev_addr_t base_address,uint64_t size,pvr_dev_addr_t reserved_address,uint64_t reserved_size,uint32_t log2_page_size,const struct pvr_winsys_static_data_offsets * const static_data_offsets,struct pvr_winsys_heap * const heap)69 VkResult 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 
pvr_winsys_helper_winsys_heap_finish(struct pvr_winsys_heap * const heap)127 bool 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 
pvr_winsys_helper_heap_alloc(struct pvr_winsys_heap * const heap,uint64_t size,uint64_t alignment,struct pvr_winsys_vma * const vma_out)138 bool 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 
pvr_winsys_helper_heap_free(struct pvr_winsys_vma * const vma)177 void 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  */
195 static VkResult
pvr_buffer_create_and_map(struct pvr_winsys * const ws,heap_alloc_reserved_func heap_alloc_reserved,struct pvr_winsys_heap * heap,pvr_dev_addr_t dev_addr,uint64_t size,uint64_t alignment,struct pvr_winsys_vma ** const vma_out)196 pvr_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 
244 err_pvr_winsys_heap_free:
245    ws->ops->heap_free(vma);
246 
247 err_pvr_winsys_buffer_destroy:
248    ws->ops->buffer_destroy(bo);
249 
250    return result;
251 }
252 
pvr_buffer_destroy_and_unmap(struct pvr_winsys_vma * vma)253 static 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 
pvr_winsys_helper_allocate_static_memory(struct pvr_winsys * const ws,heap_alloc_reserved_func heap_alloc_reserved,struct pvr_winsys_heap * const general_heap,struct pvr_winsys_heap * const pds_heap,struct pvr_winsys_heap * const usc_heap,struct pvr_winsys_vma ** const general_vma_out,struct pvr_winsys_vma ** const pds_vma_out,struct pvr_winsys_vma ** const usc_vma_out)264 VkResult 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 
315 err_pvr_buffer_destroy_and_unmap_pds:
316    pvr_buffer_destroy_and_unmap(pds_vma);
317 
318 err_pvr_buffer_destroy_and_unmap_general:
319    pvr_buffer_destroy_and_unmap(general_vma);
320 
321    return result;
322 }
323 
pvr_winsys_helper_free_static_memory(struct pvr_winsys_vma * const general_vma,struct pvr_winsys_vma * const pds_vma,struct pvr_winsys_vma * const usc_vma)324 void 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 
pvr_setup_static_vdm_sync(uint8_t * const pds_ptr,uint64_t pds_sync_offset_in_bytes,uint8_t * const usc_ptr,uint64_t usc_sync_offset_in_bytes)334 static 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 
362 static void
pvr_setup_static_pixel_event_program(uint8_t * const pds_ptr,uint64_t pds_eot_offset_in_bytes)363 pvr_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 
374 VkResult
pvr_winsys_helper_fill_static_memory(struct pvr_winsys * const ws,struct pvr_winsys_vma * const general_vma,struct pvr_winsys_vma * const pds_vma,struct pvr_winsys_vma * const usc_vma)375 pvr_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 
415 err_pvr_srv_winsys_buffer_unmap_pds:
416    ws->ops->buffer_unmap(pds_vma->bo);
417 
418 err_pvr_srv_winsys_buffer_unmap_general:
419    ws->ops->buffer_unmap(general_vma->bo);
420 
421    return result;
422 }
423