./drivers/gpu/drm/imagination/pvr_free_list.c:258:2-3: Unneeded semicolon Reported-by: Abaci Robot <abaci@linux.alibaba.com> Closes: https://bugzilla.openanolis.cn/show_bug.cgi?id=7635 Signed-off-by: Yang Li <yang.lee@linux.alibaba.com> Reviewed-by: Frank Binns <frank.binns@imgtec.com> Signed-off-by: Maxime Ripard <mripard@kernel.org> Link: https://patchwork.freedesktop.org/patch/msgid/20231127010430.119666-1-yang.lee@linux.alibaba.com
625 lines
17 KiB
C
625 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
|
/* Copyright (c) 2023 Imagination Technologies Ltd. */
|
|
|
|
#include "pvr_free_list.h"
|
|
#include "pvr_gem.h"
|
|
#include "pvr_hwrt.h"
|
|
#include "pvr_rogue_fwif.h"
|
|
#include "pvr_vm.h"
|
|
|
|
#include <drm/drm_gem.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/xarray.h>
|
|
#include <uapi/drm/pvr_drm.h>
|
|
|
|
#define FREE_LIST_ENTRY_SIZE sizeof(u32)
|
|
|
|
#define FREE_LIST_ALIGNMENT \
|
|
((ROGUE_BIF_PM_FREELIST_BASE_ADDR_ALIGNSIZE / FREE_LIST_ENTRY_SIZE) - 1)
|
|
|
|
#define FREE_LIST_MIN_PAGES 50
|
|
#define FREE_LIST_MIN_PAGES_BRN66011 40
|
|
#define FREE_LIST_MIN_PAGES_ROGUEXE 25
|
|
|
|
/**
|
|
* pvr_get_free_list_min_pages() - Get minimum free list size for this device
|
|
* @pvr_dev: Device pointer.
|
|
*
|
|
* Returns:
|
|
* * Minimum free list size, in PM physical pages.
|
|
*/
|
|
u32
|
|
pvr_get_free_list_min_pages(struct pvr_device *pvr_dev)
|
|
{
|
|
u32 value;
|
|
|
|
if (PVR_HAS_FEATURE(pvr_dev, roguexe)) {
|
|
if (PVR_HAS_QUIRK(pvr_dev, 66011))
|
|
value = FREE_LIST_MIN_PAGES_BRN66011;
|
|
else
|
|
value = FREE_LIST_MIN_PAGES_ROGUEXE;
|
|
} else {
|
|
value = FREE_LIST_MIN_PAGES;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static int
|
|
free_list_create_kernel_structure(struct pvr_file *pvr_file,
|
|
struct drm_pvr_ioctl_create_free_list_args *args,
|
|
struct pvr_free_list *free_list)
|
|
{
|
|
struct pvr_gem_object *free_list_obj;
|
|
struct pvr_vm_context *vm_ctx;
|
|
u64 free_list_size;
|
|
int err;
|
|
|
|
if (args->grow_threshold > 100 ||
|
|
args->initial_num_pages > args->max_num_pages ||
|
|
args->grow_num_pages > args->max_num_pages ||
|
|
args->max_num_pages == 0 ||
|
|
(args->initial_num_pages < args->max_num_pages && !args->grow_num_pages) ||
|
|
(args->initial_num_pages == args->max_num_pages && args->grow_num_pages))
|
|
return -EINVAL;
|
|
|
|
if ((args->initial_num_pages & FREE_LIST_ALIGNMENT) ||
|
|
(args->max_num_pages & FREE_LIST_ALIGNMENT) ||
|
|
(args->grow_num_pages & FREE_LIST_ALIGNMENT))
|
|
return -EINVAL;
|
|
|
|
vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle);
|
|
if (!vm_ctx)
|
|
return -EINVAL;
|
|
|
|
free_list_obj = pvr_vm_find_gem_object(vm_ctx, args->free_list_gpu_addr,
|
|
NULL, &free_list_size);
|
|
if (!free_list_obj) {
|
|
err = -EINVAL;
|
|
goto err_put_vm_context;
|
|
}
|
|
|
|
if ((free_list_obj->flags & DRM_PVR_BO_ALLOW_CPU_USERSPACE_ACCESS) ||
|
|
!(free_list_obj->flags & DRM_PVR_BO_PM_FW_PROTECT) ||
|
|
free_list_size < (args->max_num_pages * FREE_LIST_ENTRY_SIZE)) {
|
|
err = -EINVAL;
|
|
goto err_put_free_list_obj;
|
|
}
|
|
|
|
free_list->pvr_dev = pvr_file->pvr_dev;
|
|
free_list->current_pages = 0;
|
|
free_list->max_pages = args->max_num_pages;
|
|
free_list->grow_pages = args->grow_num_pages;
|
|
free_list->grow_threshold = args->grow_threshold;
|
|
free_list->obj = free_list_obj;
|
|
free_list->free_list_gpu_addr = args->free_list_gpu_addr;
|
|
free_list->initial_num_pages = args->initial_num_pages;
|
|
|
|
pvr_vm_context_put(vm_ctx);
|
|
|
|
return 0;
|
|
|
|
err_put_free_list_obj:
|
|
pvr_gem_object_put(free_list_obj);
|
|
|
|
err_put_vm_context:
|
|
pvr_vm_context_put(vm_ctx);
|
|
|
|
return err;
|
|
}
|
|
|
|
static void
|
|
free_list_destroy_kernel_structure(struct pvr_free_list *free_list)
|
|
{
|
|
WARN_ON(!list_empty(&free_list->hwrt_list));
|
|
|
|
pvr_gem_object_put(free_list->obj);
|
|
}
|
|
|
|
/**
|
|
* calculate_free_list_ready_pages_locked() - Function to work out the number of free
|
|
* list pages to reserve for growing within
|
|
* the FW without having to wait for the
|
|
* host to progress a grow request
|
|
* @free_list: Pointer to free list.
|
|
* @pages: Total pages currently in free list.
|
|
*
|
|
* If the threshold or grow size means less than the alignment size (4 pages on
|
|
* Rogue), then the feature is not used.
|
|
*
|
|
* Caller must hold &free_list->lock.
|
|
*
|
|
* Return: number of pages to reserve.
|
|
*/
|
|
static u32
|
|
calculate_free_list_ready_pages_locked(struct pvr_free_list *free_list, u32 pages)
|
|
{
|
|
u32 ready_pages;
|
|
|
|
lockdep_assert_held(&free_list->lock);
|
|
|
|
ready_pages = ((pages * free_list->grow_threshold) / 100);
|
|
|
|
/* The number of pages must be less than the grow size. */
|
|
ready_pages = min(ready_pages, free_list->grow_pages);
|
|
|
|
/*
|
|
* The number of pages must be a multiple of the free list align size.
|
|
*/
|
|
ready_pages &= ~FREE_LIST_ALIGNMENT;
|
|
|
|
return ready_pages;
|
|
}
|
|
|
|
static u32
|
|
calculate_free_list_ready_pages(struct pvr_free_list *free_list, u32 pages)
|
|
{
|
|
u32 ret;
|
|
|
|
mutex_lock(&free_list->lock);
|
|
|
|
ret = calculate_free_list_ready_pages_locked(free_list, pages);
|
|
|
|
mutex_unlock(&free_list->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
free_list_fw_init(void *cpu_ptr, void *priv)
|
|
{
|
|
struct rogue_fwif_freelist *fw_data = cpu_ptr;
|
|
struct pvr_free_list *free_list = priv;
|
|
u32 ready_pages;
|
|
|
|
/* Fill out FW structure */
|
|
ready_pages = calculate_free_list_ready_pages(free_list,
|
|
free_list->initial_num_pages);
|
|
|
|
fw_data->max_pages = free_list->max_pages;
|
|
fw_data->current_pages = free_list->initial_num_pages - ready_pages;
|
|
fw_data->grow_pages = free_list->grow_pages;
|
|
fw_data->ready_pages = ready_pages;
|
|
fw_data->freelist_id = free_list->fw_id;
|
|
fw_data->grow_pending = false;
|
|
fw_data->current_stack_top = fw_data->current_pages - 1;
|
|
fw_data->freelist_dev_addr = free_list->free_list_gpu_addr;
|
|
fw_data->current_dev_addr = (fw_data->freelist_dev_addr +
|
|
((fw_data->max_pages - fw_data->current_pages) *
|
|
FREE_LIST_ENTRY_SIZE)) &
|
|
~((u64)ROGUE_BIF_PM_FREELIST_BASE_ADDR_ALIGNSIZE - 1);
|
|
}
|
|
|
|
static int
|
|
free_list_create_fw_structure(struct pvr_file *pvr_file,
|
|
struct drm_pvr_ioctl_create_free_list_args *args,
|
|
struct pvr_free_list *free_list)
|
|
{
|
|
struct pvr_device *pvr_dev = pvr_file->pvr_dev;
|
|
|
|
/*
|
|
* Create and map the FW structure so we can initialise it. This is not
|
|
* accessed on the CPU side post-initialisation so the mapping lifetime
|
|
* is only for this function.
|
|
*/
|
|
free_list->fw_data = pvr_fw_object_create_and_map(pvr_dev, sizeof(*free_list->fw_data),
|
|
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
|
|
free_list_fw_init, free_list,
|
|
&free_list->fw_obj);
|
|
if (IS_ERR(free_list->fw_data))
|
|
return PTR_ERR(free_list->fw_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
free_list_destroy_fw_structure(struct pvr_free_list *free_list)
|
|
{
|
|
pvr_fw_object_unmap_and_destroy(free_list->fw_obj);
|
|
}
|
|
|
|
static int
|
|
pvr_free_list_insert_pages_locked(struct pvr_free_list *free_list,
|
|
struct sg_table *sgt, u32 offset, u32 num_pages)
|
|
{
|
|
struct sg_dma_page_iter dma_iter;
|
|
u32 *page_list;
|
|
|
|
lockdep_assert_held(&free_list->lock);
|
|
|
|
page_list = pvr_gem_object_vmap(free_list->obj);
|
|
if (IS_ERR(page_list))
|
|
return PTR_ERR(page_list);
|
|
|
|
offset /= FREE_LIST_ENTRY_SIZE;
|
|
/* clang-format off */
|
|
for_each_sgtable_dma_page(sgt, &dma_iter, 0) {
|
|
dma_addr_t dma_addr = sg_page_iter_dma_address(&dma_iter);
|
|
u64 dma_pfn = dma_addr >>
|
|
ROGUE_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT;
|
|
u32 dma_addr_offset;
|
|
|
|
BUILD_BUG_ON(ROGUE_BIF_PM_PHYSICAL_PAGE_SIZE > PAGE_SIZE);
|
|
|
|
for (dma_addr_offset = 0; dma_addr_offset < PAGE_SIZE;
|
|
dma_addr_offset += ROGUE_BIF_PM_PHYSICAL_PAGE_SIZE) {
|
|
WARN_ON_ONCE(dma_pfn >> 32);
|
|
|
|
page_list[offset++] = (u32)dma_pfn;
|
|
dma_pfn++;
|
|
|
|
num_pages--;
|
|
if (!num_pages)
|
|
break;
|
|
}
|
|
|
|
if (!num_pages)
|
|
break;
|
|
}
|
|
/* clang-format on */
|
|
|
|
/* Make sure our free_list update is flushed. */
|
|
wmb();
|
|
|
|
pvr_gem_object_vunmap(free_list->obj);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pvr_free_list_insert_node_locked(struct pvr_free_list_node *free_list_node)
|
|
{
|
|
struct pvr_free_list *free_list = free_list_node->free_list;
|
|
struct sg_table *sgt;
|
|
u32 start_page;
|
|
u32 offset;
|
|
int err;
|
|
|
|
lockdep_assert_held(&free_list->lock);
|
|
|
|
start_page = free_list->max_pages - free_list->current_pages -
|
|
free_list_node->num_pages;
|
|
offset = (start_page * FREE_LIST_ENTRY_SIZE) &
|
|
~((u64)ROGUE_BIF_PM_FREELIST_BASE_ADDR_ALIGNSIZE - 1);
|
|
|
|
sgt = drm_gem_shmem_get_pages_sgt(&free_list_node->mem_obj->base);
|
|
if (WARN_ON(IS_ERR(sgt)))
|
|
return PTR_ERR(sgt);
|
|
|
|
err = pvr_free_list_insert_pages_locked(free_list, sgt,
|
|
offset, free_list_node->num_pages);
|
|
if (!err)
|
|
free_list->current_pages += free_list_node->num_pages;
|
|
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
pvr_free_list_grow(struct pvr_free_list *free_list, u32 num_pages)
|
|
{
|
|
struct pvr_device *pvr_dev = free_list->pvr_dev;
|
|
struct pvr_free_list_node *free_list_node;
|
|
int err;
|
|
|
|
mutex_lock(&free_list->lock);
|
|
|
|
if (num_pages & FREE_LIST_ALIGNMENT) {
|
|
err = -EINVAL;
|
|
goto err_unlock;
|
|
}
|
|
|
|
free_list_node = kzalloc(sizeof(*free_list_node), GFP_KERNEL);
|
|
if (!free_list_node) {
|
|
err = -ENOMEM;
|
|
goto err_unlock;
|
|
}
|
|
|
|
free_list_node->num_pages = num_pages;
|
|
free_list_node->free_list = free_list;
|
|
|
|
free_list_node->mem_obj = pvr_gem_object_create(pvr_dev,
|
|
num_pages <<
|
|
ROGUE_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT,
|
|
PVR_BO_FW_FLAGS_DEVICE_CACHED);
|
|
if (IS_ERR(free_list_node->mem_obj)) {
|
|
err = PTR_ERR(free_list_node->mem_obj);
|
|
goto err_free;
|
|
}
|
|
|
|
err = pvr_free_list_insert_node_locked(free_list_node);
|
|
if (err)
|
|
goto err_destroy_gem_object;
|
|
|
|
list_add_tail(&free_list_node->node, &free_list->mem_block_list);
|
|
|
|
/*
|
|
* Reserve a number ready pages to allow the FW to process OOM quickly
|
|
* and asynchronously request a grow.
|
|
*/
|
|
free_list->ready_pages =
|
|
calculate_free_list_ready_pages_locked(free_list,
|
|
free_list->current_pages);
|
|
free_list->current_pages -= free_list->ready_pages;
|
|
|
|
mutex_unlock(&free_list->lock);
|
|
|
|
return 0;
|
|
|
|
err_destroy_gem_object:
|
|
pvr_gem_object_put(free_list_node->mem_obj);
|
|
|
|
err_free:
|
|
kfree(free_list_node);
|
|
|
|
err_unlock:
|
|
mutex_unlock(&free_list->lock);
|
|
|
|
return err;
|
|
}
|
|
|
|
void pvr_free_list_process_grow_req(struct pvr_device *pvr_dev,
|
|
struct rogue_fwif_fwccb_cmd_freelist_gs_data *req)
|
|
{
|
|
struct pvr_free_list *free_list = pvr_free_list_lookup_id(pvr_dev, req->freelist_id);
|
|
struct rogue_fwif_kccb_cmd resp_cmd = {
|
|
.cmd_type = ROGUE_FWIF_KCCB_CMD_FREELIST_GROW_UPDATE,
|
|
};
|
|
struct rogue_fwif_freelist_gs_data *resp = &resp_cmd.cmd_data.free_list_gs_data;
|
|
u32 grow_pages = 0;
|
|
|
|
/* If we don't have a freelist registered for this ID, we can't do much. */
|
|
if (WARN_ON(!free_list))
|
|
return;
|
|
|
|
/* Since the FW made the request, it has already consumed the ready pages,
|
|
* update the host struct.
|
|
*/
|
|
free_list->current_pages += free_list->ready_pages;
|
|
free_list->ready_pages = 0;
|
|
|
|
/* If the grow succeeds, update the grow_pages argument. */
|
|
if (!pvr_free_list_grow(free_list, free_list->grow_pages))
|
|
grow_pages = free_list->grow_pages;
|
|
|
|
/* Now prepare the response and send it back to the FW. */
|
|
pvr_fw_object_get_fw_addr(free_list->fw_obj, &resp->freelist_fw_addr);
|
|
resp->delta_pages = grow_pages;
|
|
resp->new_pages = free_list->current_pages + free_list->ready_pages;
|
|
resp->ready_pages = free_list->ready_pages;
|
|
pvr_free_list_put(free_list);
|
|
|
|
WARN_ON(pvr_kccb_send_cmd(pvr_dev, &resp_cmd, NULL));
|
|
}
|
|
|
|
static void
|
|
pvr_free_list_free_node(struct pvr_free_list_node *free_list_node)
|
|
{
|
|
pvr_gem_object_put(free_list_node->mem_obj);
|
|
|
|
kfree(free_list_node);
|
|
}
|
|
|
|
/**
|
|
* pvr_free_list_create() - Create a new free list and return an object pointer
|
|
* @pvr_file: Pointer to pvr_file structure.
|
|
* @args: Creation arguments from userspace.
|
|
*
|
|
* Return:
|
|
* * Pointer to new free_list, or
|
|
* * ERR_PTR(-%ENOMEM) on out of memory.
|
|
*/
|
|
struct pvr_free_list *
|
|
pvr_free_list_create(struct pvr_file *pvr_file,
|
|
struct drm_pvr_ioctl_create_free_list_args *args)
|
|
{
|
|
struct pvr_free_list *free_list;
|
|
int err;
|
|
|
|
/* Create and fill out the kernel structure */
|
|
free_list = kzalloc(sizeof(*free_list), GFP_KERNEL);
|
|
|
|
if (!free_list)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
kref_init(&free_list->ref_count);
|
|
INIT_LIST_HEAD(&free_list->mem_block_list);
|
|
INIT_LIST_HEAD(&free_list->hwrt_list);
|
|
mutex_init(&free_list->lock);
|
|
|
|
err = free_list_create_kernel_structure(pvr_file, args, free_list);
|
|
if (err < 0)
|
|
goto err_free;
|
|
|
|
/* Allocate global object ID for firmware. */
|
|
err = xa_alloc(&pvr_file->pvr_dev->free_list_ids,
|
|
&free_list->fw_id,
|
|
free_list,
|
|
xa_limit_32b,
|
|
GFP_KERNEL);
|
|
if (err)
|
|
goto err_destroy_kernel_structure;
|
|
|
|
err = free_list_create_fw_structure(pvr_file, args, free_list);
|
|
if (err < 0)
|
|
goto err_free_fw_id;
|
|
|
|
err = pvr_free_list_grow(free_list, args->initial_num_pages);
|
|
if (err < 0)
|
|
goto err_fw_struct_cleanup;
|
|
|
|
return free_list;
|
|
|
|
err_fw_struct_cleanup:
|
|
WARN_ON(pvr_fw_structure_cleanup(free_list->pvr_dev,
|
|
ROGUE_FWIF_CLEANUP_FREELIST,
|
|
free_list->fw_obj, 0));
|
|
|
|
err_free_fw_id:
|
|
xa_erase(&free_list->pvr_dev->free_list_ids, free_list->fw_id);
|
|
|
|
err_destroy_kernel_structure:
|
|
free_list_destroy_kernel_structure(free_list);
|
|
|
|
err_free:
|
|
mutex_destroy(&free_list->lock);
|
|
kfree(free_list);
|
|
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
static void
|
|
pvr_free_list_release(struct kref *ref_count)
|
|
{
|
|
struct pvr_free_list *free_list =
|
|
container_of(ref_count, struct pvr_free_list, ref_count);
|
|
struct list_head *pos, *n;
|
|
int err;
|
|
|
|
xa_erase(&free_list->pvr_dev->free_list_ids, free_list->fw_id);
|
|
|
|
err = pvr_fw_structure_cleanup(free_list->pvr_dev,
|
|
ROGUE_FWIF_CLEANUP_FREELIST,
|
|
free_list->fw_obj, 0);
|
|
if (err == -EBUSY) {
|
|
/* Flush the FWCCB to process any HWR or freelist reconstruction
|
|
* request that might keep the freelist busy, and try again.
|
|
*/
|
|
pvr_fwccb_process(free_list->pvr_dev);
|
|
err = pvr_fw_structure_cleanup(free_list->pvr_dev,
|
|
ROGUE_FWIF_CLEANUP_FREELIST,
|
|
free_list->fw_obj, 0);
|
|
}
|
|
|
|
WARN_ON(err);
|
|
|
|
/* clang-format off */
|
|
list_for_each_safe(pos, n, &free_list->mem_block_list) {
|
|
struct pvr_free_list_node *free_list_node =
|
|
container_of(pos, struct pvr_free_list_node, node);
|
|
|
|
list_del(pos);
|
|
pvr_free_list_free_node(free_list_node);
|
|
}
|
|
/* clang-format on */
|
|
|
|
free_list_destroy_kernel_structure(free_list);
|
|
free_list_destroy_fw_structure(free_list);
|
|
mutex_destroy(&free_list->lock);
|
|
kfree(free_list);
|
|
}
|
|
|
|
/**
|
|
* pvr_destroy_free_lists_for_file: Destroy any free lists associated with the
|
|
* given file.
|
|
* @pvr_file: Pointer to pvr_file structure.
|
|
*
|
|
* Removes all free lists associated with @pvr_file from the device free_list
|
|
* list and drops initial references. Free lists will then be destroyed once
|
|
* all outstanding references are dropped.
|
|
*/
|
|
void pvr_destroy_free_lists_for_file(struct pvr_file *pvr_file)
|
|
{
|
|
struct pvr_free_list *free_list;
|
|
unsigned long handle;
|
|
|
|
xa_for_each(&pvr_file->free_list_handles, handle, free_list) {
|
|
(void)free_list;
|
|
pvr_free_list_put(xa_erase(&pvr_file->free_list_handles, handle));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* pvr_free_list_put() - Release reference on free list
|
|
* @free_list: Pointer to list to release reference on
|
|
*/
|
|
void
|
|
pvr_free_list_put(struct pvr_free_list *free_list)
|
|
{
|
|
if (free_list)
|
|
kref_put(&free_list->ref_count, pvr_free_list_release);
|
|
}
|
|
|
|
void pvr_free_list_add_hwrt(struct pvr_free_list *free_list, struct pvr_hwrt_data *hwrt_data)
|
|
{
|
|
mutex_lock(&free_list->lock);
|
|
|
|
list_add_tail(&hwrt_data->freelist_node, &free_list->hwrt_list);
|
|
|
|
mutex_unlock(&free_list->lock);
|
|
}
|
|
|
|
void pvr_free_list_remove_hwrt(struct pvr_free_list *free_list, struct pvr_hwrt_data *hwrt_data)
|
|
{
|
|
mutex_lock(&free_list->lock);
|
|
|
|
list_del(&hwrt_data->freelist_node);
|
|
|
|
mutex_unlock(&free_list->lock);
|
|
}
|
|
|
|
static void
|
|
pvr_free_list_reconstruct(struct pvr_device *pvr_dev, u32 freelist_id)
|
|
{
|
|
struct pvr_free_list *free_list = pvr_free_list_lookup_id(pvr_dev, freelist_id);
|
|
struct pvr_free_list_node *free_list_node;
|
|
struct rogue_fwif_freelist *fw_data;
|
|
struct pvr_hwrt_data *hwrt_data;
|
|
|
|
if (!free_list)
|
|
return;
|
|
|
|
mutex_lock(&free_list->lock);
|
|
|
|
/* Rebuild the free list based on the memory block list. */
|
|
free_list->current_pages = 0;
|
|
|
|
list_for_each_entry(free_list_node, &free_list->mem_block_list, node)
|
|
WARN_ON(pvr_free_list_insert_node_locked(free_list_node));
|
|
|
|
/*
|
|
* Remove the ready pages, which are reserved to allow the FW to process OOM quickly and
|
|
* asynchronously request a grow.
|
|
*/
|
|
free_list->current_pages -= free_list->ready_pages;
|
|
|
|
fw_data = free_list->fw_data;
|
|
fw_data->current_stack_top = fw_data->current_pages - 1;
|
|
fw_data->allocated_page_count = 0;
|
|
fw_data->allocated_mmu_page_count = 0;
|
|
|
|
/* Reset the state of any associated HWRTs. */
|
|
list_for_each_entry(hwrt_data, &free_list->hwrt_list, freelist_node) {
|
|
struct rogue_fwif_hwrtdata *hwrt_fw_data = pvr_fw_object_vmap(hwrt_data->fw_obj);
|
|
|
|
if (!WARN_ON(IS_ERR(hwrt_fw_data))) {
|
|
hwrt_fw_data->state = ROGUE_FWIF_RTDATA_STATE_HWR;
|
|
hwrt_fw_data->hwrt_data_flags &= ~HWRTDATA_HAS_LAST_GEOM;
|
|
}
|
|
|
|
pvr_fw_object_vunmap(hwrt_data->fw_obj);
|
|
}
|
|
|
|
mutex_unlock(&free_list->lock);
|
|
|
|
pvr_free_list_put(free_list);
|
|
}
|
|
|
|
void
|
|
pvr_free_list_process_reconstruct_req(struct pvr_device *pvr_dev,
|
|
struct rogue_fwif_fwccb_cmd_freelists_reconstruction_data *req)
|
|
{
|
|
struct rogue_fwif_kccb_cmd resp_cmd = {
|
|
.cmd_type = ROGUE_FWIF_KCCB_CMD_FREELISTS_RECONSTRUCTION_UPDATE,
|
|
};
|
|
struct rogue_fwif_freelists_reconstruction_data *resp =
|
|
&resp_cmd.cmd_data.free_lists_reconstruction_data;
|
|
|
|
for (u32 i = 0; i < req->freelist_count; i++)
|
|
pvr_free_list_reconstruct(pvr_dev, req->freelist_ids[i]);
|
|
|
|
resp->freelist_count = req->freelist_count;
|
|
memcpy(resp->freelist_ids, req->freelist_ids,
|
|
req->freelist_count * sizeof(resp->freelist_ids[0]));
|
|
|
|
WARN_ON(pvr_kccb_send_cmd(pvr_dev, &resp_cmd, NULL));
|
|
}
|