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