500 lines
14 KiB
C
500 lines
14 KiB
C
/****************************************************************************
|
|
*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2014 - 2020 Vivante Corporation
|
|
*
|
|
* 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 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 "vg_lite_platform.h"
|
|
#include "vg_lite_kernel.h"
|
|
#include "vg_lite_hal.h"
|
|
#include "vg_lite_hw.h"
|
|
#include "vg_lite_os.h"
|
|
|
|
#if !_BAREMETAL
|
|
#include "rtthread.h"
|
|
#else
|
|
#include "xil_cache.h"
|
|
#endif
|
|
|
|
#if _BAREMETAL
|
|
/* The followings should be configured by FPGA. */
|
|
static uint32_t registerMemBase = 0x43c80000;
|
|
#else
|
|
static uint32_t registerMemBase = 0x40240000;
|
|
#endif
|
|
|
|
#define HEAP_NODE_USED 0xABBAF00D
|
|
|
|
volatile void* contiguousMem = NULL;
|
|
uint32_t gpuMemBase = 0;
|
|
|
|
/* Default heap size is 16MB. */
|
|
static int heap_size = MAX_CONTIGUOUS_SIZE;
|
|
|
|
void vg_lite_init_mem(uint32_t register_mem_base,
|
|
uint32_t gpu_mem_base,
|
|
volatile void * contiguous_mem_base,
|
|
uint32_t contiguous_mem_size)
|
|
{
|
|
registerMemBase = register_mem_base;
|
|
gpuMemBase = gpu_mem_base;
|
|
contiguousMem = contiguous_mem_base;
|
|
heap_size = contiguous_mem_size;
|
|
}
|
|
|
|
/* Implementation of list. ****************************************/
|
|
typedef struct list_head {
|
|
struct list_head *next;
|
|
struct list_head *prev;
|
|
}list_head_t;
|
|
|
|
#define INIT_LIST_HEAD(entry) \
|
|
(entry)->next = (entry);\
|
|
(entry)->prev = (entry);
|
|
|
|
/* Add the list item in front of "head". */
|
|
static inline void add_list(list_head_t *to_add, list_head_t *head)
|
|
{
|
|
/* Link the new item. */
|
|
to_add->next = head;
|
|
to_add->prev = head->prev;
|
|
|
|
/* Modify the neighbor. */
|
|
head->prev = to_add;
|
|
if (to_add->prev != NULL) {
|
|
to_add->prev->next = to_add;
|
|
}
|
|
}
|
|
|
|
/* Remove an entry out of the list. */
|
|
static inline void delete_list(list_head_t *entry)
|
|
{
|
|
if (entry->prev != NULL) {
|
|
entry->prev->next = entry->next;
|
|
}
|
|
if (entry->next != NULL) {
|
|
entry->next->prev = entry->prev;
|
|
}
|
|
}
|
|
|
|
/* End of list implementation. ***********/
|
|
static inline void _memset(void *mem, unsigned char value, int size)
|
|
{
|
|
int i;
|
|
for (i = 0; i < size; i++) {
|
|
((unsigned char*)mem)[i] = value;
|
|
}
|
|
}
|
|
|
|
typedef struct heap_node {
|
|
list_head_t list; /* TODO: Linux specific, needs to rewrite. */
|
|
uint32_t offset;
|
|
unsigned long size;
|
|
uint32_t status;
|
|
}heap_node_t;
|
|
|
|
struct memory_heap {
|
|
uint32_t free;
|
|
list_head_t list;
|
|
};
|
|
|
|
struct mapped_memory {
|
|
void * logical;
|
|
uint32_t physical;
|
|
int page_count;
|
|
struct page ** pages;
|
|
};
|
|
|
|
struct vg_lite_device {
|
|
/* void * gpu; */
|
|
uint32_t gpu; /* Always use physical for register access in RTOS. */
|
|
/* struct page * pages; */
|
|
volatile void * contiguous;
|
|
unsigned int order;
|
|
unsigned int heap_size;
|
|
void * virtual;
|
|
uint32_t physical;
|
|
uint32_t size;
|
|
struct memory_heap heap;
|
|
int irq_enabled;
|
|
|
|
#if defined(VG_DRIVER_SINGLE_THREAD)
|
|
volatile uint32_t int_flags;
|
|
#if _BAREMETAL
|
|
/* wait_queue_head_t int_queue; */
|
|
xSemaphoreHandle int_queue;
|
|
#else
|
|
/* wait_queue_head_t int_queue; */
|
|
rt_sem_t int_queue;
|
|
#endif
|
|
#endif /* VG_DRIVER_SINGLE_THREAD */
|
|
|
|
void * device;
|
|
int registered;
|
|
int major;
|
|
struct class * class;
|
|
int created;
|
|
};
|
|
|
|
struct client_data {
|
|
struct vg_lite_device * device;
|
|
struct vm_area_struct * vm;
|
|
void * contiguous_mapped;
|
|
};
|
|
|
|
static struct vg_lite_device Device, * device;
|
|
|
|
void vg_lite_hal_delay(uint32_t ms)
|
|
{
|
|
vg_lite_os_sleep(ms);
|
|
}
|
|
|
|
void vg_lite_hal_barrier(void)
|
|
{
|
|
/*Memory barrier. */
|
|
#if _BAREMETAL
|
|
Xil_DCacheFlush();
|
|
#else
|
|
__asm("DSB");
|
|
#endif
|
|
}
|
|
|
|
static int vg_lite_init(void);
|
|
vg_lite_error_t vg_lite_hal_initialize(void)
|
|
{
|
|
int32_t error = VG_LITE_SUCCESS;
|
|
/* TODO: Turn on the power. */
|
|
vg_lite_init();
|
|
/* TODO: Turn on the clock. */
|
|
error = vg_lite_os_initialize();
|
|
|
|
return (vg_lite_error_t)error;
|
|
}
|
|
|
|
void vg_lite_hal_deinitialize(void)
|
|
{
|
|
/* TODO: Remove clock. */
|
|
vg_lite_os_deinitialize();
|
|
/* TODO: Remove power. */
|
|
}
|
|
|
|
static int split_node(heap_node_t * node, unsigned long size)
|
|
{
|
|
/* TODO: the original is linux specific list based, needs rewrite.
|
|
*/
|
|
heap_node_t * split;
|
|
|
|
/*
|
|
* If the newly allocated object fits exactly the size of the free
|
|
* node, there is no need to split.
|
|
*/
|
|
if (node->size - size == 0)
|
|
return 0;
|
|
|
|
/* Allocate a new node. */
|
|
split = (heap_node_t *)vg_lite_os_malloc(sizeof(heap_node_t));
|
|
|
|
if (split == NULL)
|
|
return -1;
|
|
|
|
/* Fill in the data of this node of the remaning size. */
|
|
split->offset = node->offset + size;
|
|
split->size = node->size - size;
|
|
split->status = 0;
|
|
|
|
/* Add the new node behind the current node. */
|
|
add_list(&split->list, &node->list);
|
|
|
|
/* Adjust the size of the current node. */
|
|
node->size = size;
|
|
return 0;
|
|
}
|
|
|
|
vg_lite_error_t vg_lite_hal_allocate_contiguous(unsigned long size, void ** logical, uint32_t * physical,void ** node)
|
|
{
|
|
unsigned long aligned_size;
|
|
heap_node_t * pos;
|
|
|
|
/* Align the size to 64 bytes. */
|
|
aligned_size = (size + 63) & ~63;
|
|
|
|
/* Check if there is enough free memory available. */
|
|
if (aligned_size > device->heap.free) {
|
|
return VG_LITE_OUT_OF_MEMORY;
|
|
}
|
|
|
|
/* Walk the heap backwards. */
|
|
for (pos = (heap_node_t*)device->heap.list.prev;
|
|
&pos->list != &device->heap.list;
|
|
pos = (heap_node_t*) pos->list.prev) {
|
|
/* Check if the current node is free and is big enough. */
|
|
if (pos->status == 0 && pos->size >= aligned_size) {
|
|
/* See if we the current node is big enough to split. */
|
|
if (0 != split_node(pos, aligned_size))
|
|
{
|
|
return VG_LITE_OUT_OF_RESOURCES;
|
|
}
|
|
/* Mark the current node as used. */
|
|
pos->status = HEAP_NODE_USED;
|
|
|
|
/* Return the logical/physical address. */
|
|
/* *logical = (uint8_t *) private_data->contiguous_mapped + pos->offset; */
|
|
*logical = (uint8_t *)device->virtual + pos->offset;
|
|
*physical = gpuMemBase + (uint32_t)(*logical);/* device->physical + pos->offset; */
|
|
device->heap.free -= aligned_size;
|
|
|
|
*node = pos;
|
|
return VG_LITE_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* Out of memory. */
|
|
return VG_LITE_OUT_OF_MEMORY;
|
|
}
|
|
|
|
void vg_lite_hal_free_contiguous(void * memory_handle)
|
|
{
|
|
/* TODO: no list available in RTOS. */
|
|
heap_node_t * pos, * node;
|
|
|
|
/* Get pointer to node. */
|
|
node = memory_handle;
|
|
|
|
if (node->status != HEAP_NODE_USED) {
|
|
return;
|
|
}
|
|
|
|
/* Mark node as free. */
|
|
node->status = 0;
|
|
|
|
/* Add node size to free count. */
|
|
device->heap.free += node->size;
|
|
|
|
/* Check if next node is free. */
|
|
pos = node;
|
|
for (pos = (heap_node_t *)pos->list.next;
|
|
&pos->list != &device->heap.list;
|
|
pos = (heap_node_t *)pos->list.next) {
|
|
if (pos->status == 0) {
|
|
/* Merge the nodes. */
|
|
node->size += pos->size;
|
|
if(node->offset > pos->offset)
|
|
node->offset = pos->offset;
|
|
/* Delete the next node from the list. */
|
|
delete_list(&pos->list);
|
|
vg_lite_os_free(pos);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* Check if the previous node is free. */
|
|
pos = node;
|
|
for (pos = (heap_node_t *)pos->list.prev;
|
|
&pos->list != &device->heap.list;
|
|
pos = (heap_node_t *)pos->list.prev) {
|
|
if (pos->status == 0) {
|
|
/* Merge the nodes. */
|
|
pos->size += node->size;
|
|
if(pos->offset > node->offset)
|
|
pos->offset = node->offset;
|
|
/* Delete the current node from the list. */
|
|
delete_list(&node->list);
|
|
vg_lite_os_free(node);
|
|
}
|
|
break;
|
|
}
|
|
/* when release command buffer node and ts buffer node to exit,release the linked list*/
|
|
if(device->heap.list.next == device->heap.list.prev) {
|
|
delete_list(&pos->list);
|
|
vg_lite_os_free(pos);
|
|
}
|
|
}
|
|
|
|
void vg_lite_hal_free_os_heap(void)
|
|
{
|
|
struct heap_node *pos, *n;
|
|
|
|
/* Check for valid device. */
|
|
if (device != NULL) {
|
|
/* Process each node. */
|
|
for (pos = (heap_node_t *)device->heap.list.next,
|
|
n = (heap_node_t *)pos->list.next;
|
|
&pos->list != &device->heap.list;
|
|
pos = n, n = (heap_node_t *)n->list.next) {
|
|
/* Remove it from the linked list. */
|
|
delete_list(&pos->list);
|
|
/* Free up the memory. */
|
|
vg_lite_os_free(pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Portable: read register value. */
|
|
uint32_t vg_lite_hal_peek(uint32_t address)
|
|
{
|
|
/* Read data from the GPU register. */
|
|
return (uint32_t) (*(volatile uint32_t *) (device->gpu + address));
|
|
}
|
|
|
|
/* Portable: write register. */
|
|
void vg_lite_hal_poke(uint32_t address, uint32_t data)
|
|
{
|
|
/* Write data to the GPU register. */
|
|
uint32_t *LocalAddr = (uint32_t *)(device->gpu + address);
|
|
*LocalAddr = data;
|
|
}
|
|
|
|
vg_lite_error_t vg_lite_hal_query_mem(vg_lite_kernel_mem_t *mem)
|
|
{
|
|
if(device != NULL){
|
|
mem->bytes = device->heap.free;
|
|
return VG_LITE_SUCCESS;
|
|
}
|
|
mem->bytes = 0;
|
|
return VG_LITE_NO_CONTEXT;
|
|
}
|
|
|
|
void vg_lite_IRQHandler(void)
|
|
{
|
|
vg_lite_os_IRQHandler();
|
|
}
|
|
|
|
int32_t vg_lite_hal_wait_interrupt(uint32_t timeout, uint32_t mask, uint32_t * value)
|
|
{
|
|
return vg_lite_os_wait_interrupt(timeout,mask,value);
|
|
}
|
|
|
|
void * vg_lite_hal_map(unsigned long bytes, void * logical, uint32_t physical, uint32_t * gpu)
|
|
{
|
|
|
|
(void) bytes;
|
|
(void) logical;
|
|
(void) physical;
|
|
(void) gpu;
|
|
return (void *)0;
|
|
}
|
|
|
|
void vg_lite_hal_unmap(void * handle)
|
|
{
|
|
|
|
(void) handle;
|
|
}
|
|
|
|
#if !defined(VG_DRIVER_SINGLE_THREAD)
|
|
vg_lite_error_t vg_lite_hal_submit(uint32_t context,uint32_t physical, uint32_t offset, uint32_t size, vg_lite_os_async_event_t *event)
|
|
{
|
|
return (vg_lite_error_t)vg_lite_os_submit(context,physical,offset,size,event);
|
|
}
|
|
|
|
vg_lite_error_t vg_lite_hal_wait(uint32_t timeout, vg_lite_os_async_event_t *event)
|
|
{
|
|
return (vg_lite_error_t)vg_lite_os_wait(timeout,event);
|
|
}
|
|
#endif /* not defined(VG_DRIVER_SINGLE_THREAD) */
|
|
|
|
static void vg_lite_exit(void)
|
|
{
|
|
heap_node_t * pos;
|
|
heap_node_t * n;
|
|
|
|
/* Check for valid device. */
|
|
if (device != NULL) {
|
|
/* TODO: unmap register mem should be unnecessary. */
|
|
device->gpu = 0;
|
|
|
|
/* Process each node. */
|
|
for (pos = (heap_node_t *)device->heap.list.next, n = (heap_node_t *)pos->list.next;
|
|
&pos->list != &device->heap.list;
|
|
pos = n, n = (heap_node_t *)n->list.next) {
|
|
/* Remove it from the linked list. */
|
|
delete_list(&pos->list);
|
|
|
|
/* Free up the memory. */
|
|
vg_lite_os_free(pos);
|
|
}
|
|
|
|
/* Free up the device structure. */
|
|
vg_lite_os_free(device);
|
|
}
|
|
}
|
|
|
|
static int vg_lite_init(void)
|
|
{
|
|
heap_node_t * node;
|
|
|
|
/* Initialize memory and objects ***************************************/
|
|
/* Create device structure. */
|
|
device = &Device;
|
|
|
|
/* Zero out the enture structure. */
|
|
_memset(device, 0, sizeof(struct vg_lite_device));
|
|
|
|
/* Setup register memory. **********************************************/
|
|
device->gpu = registerMemBase;
|
|
|
|
/* Initialize contiguous memory. ***************************************/
|
|
/* Allocate the contiguous memory. */
|
|
device->heap_size = heap_size;
|
|
device->contiguous = (volatile void *)contiguousMem;
|
|
_memset((void *)device->contiguous, 0, heap_size);
|
|
/* Make 64byte aligned. */
|
|
while ((((uint32_t)device->contiguous) & 63) != 0)
|
|
{
|
|
device->contiguous = ((unsigned char*) device->contiguous) + 4;
|
|
device->heap_size -= 4;
|
|
}
|
|
|
|
/* Check if we allocated any contiguous memory or not. */
|
|
if (device->contiguous == NULL) {
|
|
vg_lite_exit();
|
|
return -1;
|
|
}
|
|
|
|
device->virtual = (void *)device->contiguous;
|
|
device->physical = gpuMemBase + (uint32_t)device->virtual;
|
|
device->size = device->heap_size;
|
|
|
|
/* Create the heap. */
|
|
INIT_LIST_HEAD(&device->heap.list);
|
|
device->heap.free = device->size;
|
|
|
|
node = (heap_node_t *)vg_lite_os_malloc(sizeof(heap_node_t));
|
|
|
|
if (node == NULL) {
|
|
vg_lite_exit();
|
|
return -1;
|
|
}
|
|
node->offset = 0;
|
|
node->size = device->size;
|
|
node->status = 0;
|
|
add_list(&node->list, &device->heap.list);
|
|
#if defined(VG_DRIVER_SINGLE_THREAD)
|
|
#if !_BAREMETAL /*for rt500*/
|
|
device->int_queue = rt_sem_create("diq", 0, RT_IPC_FLAG_PRIO);
|
|
device->int_flags = 0;
|
|
#endif
|
|
#endif /* VG_DRIVER_SINGLE_THREAD */
|
|
/* Success. */
|
|
return 0;
|
|
}
|