mesa/src/imagination/vulkan/winsys/pvr_winsys_helper.c

423 lines
14 KiB
C

/*
* Copyright © 2022 Imagination Technologies Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <vulkan/vulkan.h>
#include <xf86drm.h>
#include "pvr_private.h"
#include "pvr_types.h"
#include "pvr_winsys.h"
#include "pvr_winsys_helper.h"
#include "util/u_atomic.h"
#include "vk_log.h"
int pvr_winsys_helper_display_buffer_create(int master_fd,
uint64_t size,
uint32_t *const handle_out)
{
struct drm_mode_create_dumb args = {
.width = size,
.height = 1,
.bpp = 8,
};
int ret;
ret = drmIoctl(master_fd, DRM_IOCTL_MODE_CREATE_DUMB, &args);
if (ret)
return ret;
*handle_out = args.handle;
return 0;
}
int pvr_winsys_helper_display_buffer_destroy(int master_fd, uint32_t handle)
{
struct drm_mode_destroy_dumb args = {
.handle = handle,
};
return drmIoctl(master_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &args);
}
/* reserved_size can be 0 when no reserved area is needed. reserved_address must
* be 0 if reserved_size is 0.
*/
VkResult 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)
{
const bool reserved_area_bottom_of_heap = reserved_address.addr ==
base_address.addr;
const pvr_dev_addr_t vma_heap_begin_addr =
reserved_area_bottom_of_heap
? PVR_DEV_ADDR_OFFSET(base_address, reserved_size)
: base_address;
const uint64_t vma_heap_size = size - reserved_size;
assert(base_address.addr);
assert(reserved_size <= size);
/* As per the reserved_base powervr-km uapi documentation the reserved
* region can only be at the beginning of the heap or at the end.
* reserved_address is 0 if there is no reserved region.
* pvrsrv-km doesn't explicitly provide this info and it's assumed that it's
* always at the beginning.
*/
assert(reserved_area_bottom_of_heap ||
reserved_address.addr + reserved_size == base_address.addr + size ||
(!reserved_address.addr && !reserved_size));
heap->ws = ws;
heap->base_addr = base_address;
heap->reserved_addr = reserved_address;
heap->size = size;
heap->reserved_size = reserved_size;
heap->page_size = 1 << log2_page_size;
heap->log2_page_size = log2_page_size;
util_vma_heap_init(&heap->vma_heap, vma_heap_begin_addr.addr, vma_heap_size);
heap->vma_heap.alloc_high = false;
/* It's expected that the heap destroy function to be the last thing that's
* called, so we start the ref_count at 0.
*/
p_atomic_set(&heap->ref_count, 0);
if (pthread_mutex_init(&heap->lock, NULL))
return vk_error(NULL, VK_ERROR_INITIALIZATION_FAILED);
heap->static_data_offsets = *static_data_offsets;
return VK_SUCCESS;
}
bool pvr_winsys_helper_winsys_heap_finish(struct pvr_winsys_heap *const heap)
{
if (p_atomic_read(&heap->ref_count) != 0)
return false;
pthread_mutex_destroy(&heap->lock);
util_vma_heap_finish(&heap->vma_heap);
return true;
}
bool pvr_winsys_helper_heap_alloc(struct pvr_winsys_heap *const heap,
uint64_t size,
uint64_t alignment,
struct pvr_winsys_vma *const vma_out)
{
struct pvr_winsys_vma vma = {
.heap = heap,
};
assert(util_is_power_of_two_nonzero(alignment));
/* pvr_srv_winsys_buffer_create() page aligns the size. We must do the same
* here to ensure enough heap space is allocated to be able to map the
* buffer to the GPU.
* We have to do this for the powervr kernel mode driver as well, as it
* returns a page aligned size when allocating buffers.
*/
alignment = MAX2(alignment, heap->page_size);
size = ALIGN_POT(size, alignment);
vma.size = size;
pthread_mutex_lock(&heap->lock);
vma.dev_addr =
PVR_DEV_ADDR(util_vma_heap_alloc(&heap->vma_heap, size, heap->page_size));
pthread_mutex_unlock(&heap->lock);
if (!vma.dev_addr.addr) {
vk_error(NULL, VK_ERROR_OUT_OF_DEVICE_MEMORY);
return false;
}
p_atomic_inc(&heap->ref_count);
*vma_out = vma;
return true;
}
void pvr_winsys_helper_heap_free(struct pvr_winsys_vma *const vma)
{
struct pvr_winsys_heap *const heap = vma->heap;
/* A vma with an existing device mapping should not be freed. */
assert(!vma->bo);
pthread_mutex_lock(&heap->lock);
util_vma_heap_free(&heap->vma_heap, vma->dev_addr.addr, vma->size);
pthread_mutex_unlock(&heap->lock);
p_atomic_dec(&heap->ref_count);
}
/* Note: the function assumes the heap allocation in the reserved memory area
* can be freed with the regular heap allocation free function. The free
* function gets called on mapping failure.
*/
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)
{
struct pvr_winsys_vma *vma;
struct pvr_winsys_bo *bo;
pvr_dev_addr_t addr;
VkResult result;
/* Address should not be NULL, this function is used to allocate and map
* reserved addresses and is only supposed to be used internally.
*/
assert(dev_addr.addr);
result = ws->ops->buffer_create(ws,
size,
alignment,
PVR_WINSYS_BO_TYPE_GPU,
PVR_WINSYS_BO_FLAG_CPU_ACCESS,
&bo);
if (result != VK_SUCCESS)
return result;
vma = heap_alloc_reserved(heap, dev_addr, size, alignment);
if (!vma) {
result = VK_ERROR_OUT_OF_DEVICE_MEMORY;
goto err_pvr_winsys_buffer_destroy;
}
addr = ws->ops->vma_map(vma, bo, 0, size);
if (!addr.addr) {
result = VK_ERROR_MEMORY_MAP_FAILED;
goto err_pvr_winsys_heap_free;
}
/* Note this won't destroy bo as its being used by VMA, once vma is
* unmapped, bo will be destroyed automatically.
*/
ws->ops->buffer_destroy(bo);
*vma_out = vma;
return VK_SUCCESS;
err_pvr_winsys_heap_free:
ws->ops->heap_free(vma);
err_pvr_winsys_buffer_destroy:
ws->ops->buffer_destroy(bo);
return result;
}
static void inline pvr_buffer_destroy_and_unmap(struct pvr_winsys_vma *vma)
{
const struct pvr_winsys *const ws = vma->heap->ws;
/* Buffer object associated with the vma will be automatically destroyed
* once vma is unmapped.
*/
ws->ops->vma_unmap(vma);
ws->ops->heap_free(vma);
}
VkResult 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)
{
struct pvr_winsys_vma *general_vma;
struct pvr_winsys_vma *pds_vma;
struct pvr_winsys_vma *usc_vma;
VkResult result;
result = pvr_buffer_create_and_map(ws,
heap_alloc_reserved,
general_heap,
general_heap->reserved_addr,
general_heap->reserved_size,
general_heap->page_size,
&general_vma);
if (result != VK_SUCCESS)
return result;
result = pvr_buffer_create_and_map(ws,
heap_alloc_reserved,
pds_heap,
pds_heap->reserved_addr,
pds_heap->reserved_size,
pds_heap->page_size,
&pds_vma);
if (result != VK_SUCCESS)
goto err_pvr_buffer_destroy_and_unmap_general;
result = pvr_buffer_create_and_map(ws,
heap_alloc_reserved,
usc_heap,
usc_heap->reserved_addr,
pds_heap->reserved_size,
usc_heap->page_size,
&usc_vma);
if (result != VK_SUCCESS)
goto err_pvr_buffer_destroy_and_unmap_pds;
*general_vma_out = general_vma;
*pds_vma_out = pds_vma;
*usc_vma_out = usc_vma;
return VK_SUCCESS;
err_pvr_buffer_destroy_and_unmap_pds:
pvr_buffer_destroy_and_unmap(pds_vma);
err_pvr_buffer_destroy_and_unmap_general:
pvr_buffer_destroy_and_unmap(general_vma);
return result;
}
void 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)
{
pvr_buffer_destroy_and_unmap(usc_vma);
pvr_buffer_destroy_and_unmap(pds_vma);
pvr_buffer_destroy_and_unmap(general_vma);
}
static void 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)
{
/* TODO: this needs to be auto-generated */
const uint8_t state_update[] = { 0x44, 0xA0, 0x80, 0x05,
0x00, 0x00, 0x00, 0xFF };
struct pvr_pds_kickusc_program ppp_state_update_program = { 0 };
memcpy(usc_ptr + usc_sync_offset_in_bytes,
state_update,
sizeof(state_update));
pvr_pds_setup_doutu(&ppp_state_update_program.usc_task_control,
usc_sync_offset_in_bytes,
0,
PVRX(PDSINST_DOUTU_SAMPLE_RATE_INSTANCE),
false);
pvr_pds_kick_usc(&ppp_state_update_program,
(uint32_t *)&pds_ptr[pds_sync_offset_in_bytes],
0,
false,
PDS_GENERATE_CODEDATA_SEGMENTS);
}
static void
pvr_setup_static_pixel_event_program(uint8_t *const pds_ptr,
uint64_t pds_eot_offset_in_bytes)
{
struct pvr_pds_event_program pixel_event_program = { 0 };
pvr_pds_generate_pixel_event(&pixel_event_program,
(uint32_t *)&pds_ptr[pds_eot_offset_in_bytes],
PDS_GENERATE_CODE_SEGMENT,
NULL);
}
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)
{
uint8_t *general_ptr, *pds_ptr, *usc_ptr;
VkResult result;
general_ptr = ws->ops->buffer_map(general_vma->bo);
if (!general_ptr)
return VK_ERROR_MEMORY_MAP_FAILED;
pds_ptr = ws->ops->buffer_map(pds_vma->bo);
if (!pds_ptr) {
result = VK_ERROR_MEMORY_MAP_FAILED;
goto err_pvr_srv_winsys_buffer_unmap_general;
}
usc_ptr = ws->ops->buffer_map(usc_vma->bo);
if (!usc_ptr) {
result = VK_ERROR_MEMORY_MAP_FAILED;
goto err_pvr_srv_winsys_buffer_unmap_pds;
}
pvr_setup_static_vdm_sync(pds_ptr,
pds_vma->heap->static_data_offsets.vdm_sync,
usc_ptr,
usc_vma->heap->static_data_offsets.vdm_sync);
pvr_setup_static_pixel_event_program(pds_ptr,
pds_vma->heap->static_data_offsets.eot);
/* TODO: Complete control block copying work. */
ws->ops->buffer_unmap(usc_vma->bo);
ws->ops->buffer_unmap(pds_vma->bo);
ws->ops->buffer_unmap(general_vma->bo);
return VK_SUCCESS;
err_pvr_srv_winsys_buffer_unmap_pds:
ws->ops->buffer_unmap(pds_vma->bo);
err_pvr_srv_winsys_buffer_unmap_general:
ws->ops->buffer_unmap(general_vma->bo);
return result;
}