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