Commit 6eedddab authored by Sarah Walker's avatar Sarah Walker Committed by Maxime Ripard

drm/imagination: Implement free list and HWRT create and destroy ioctls

Implement ioctls to create and destroy free lists and HWRT datasets. Free
lists are used for GPU-side memory allocation during geometry processing.
HWRT datasets are the FW-side structures representing render targets.

Changes since v8:
- Corrected license identifiers

Changes since v6:
- Fix out-of-bounds shift in get_cr_multisamplectl_val()

Changes since v4:
- Remove use of drm_gem_shmem_get_pages()

Changes since v3:
- Support free list grow requests from FW
- Use drm_dev_{enter,exit}
Co-developed-by: default avatarBoris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: default avatarBoris Brezillon <boris.brezillon@collabora.com>
Co-developed-by: default avatarDonald Robson <donald.robson@imgtec.com>
Signed-off-by: default avatarDonald Robson <donald.robson@imgtec.com>
Signed-off-by: default avatarSarah Walker <sarah.walker@imgtec.com>
Link: https://lore.kernel.org/r/919358c5887a7628da588c455a5bb7e3ea4b47ae.1700668843.git.donald.robson@imgtec.comSigned-off-by: default avatarMaxime Ripard <mripard@kernel.org>
parent 927f3e02
......@@ -8,12 +8,14 @@ powervr-y := \
pvr_device.o \
pvr_device_info.o \
pvr_drv.o \
pvr_free_list.o \
pvr_fw.o \
pvr_fw_meta.o \
pvr_fw_mips.o \
pvr_fw_startstop.o \
pvr_fw_trace.o \
pvr_gem.o \
pvr_hwrt.o \
pvr_mmu.o \
pvr_power.o \
pvr_vm.o \
......
......@@ -4,6 +4,7 @@
#include "pvr_ccb.h"
#include "pvr_device.h"
#include "pvr_drv.h"
#include "pvr_free_list.h"
#include "pvr_fw.h"
#include "pvr_gem.h"
#include "pvr_power.h"
......@@ -139,6 +140,15 @@ process_fwccb_command(struct pvr_device *pvr_dev, struct rogue_fwif_fwccb_cmd *c
pvr_power_reset(pvr_dev, false);
break;
case ROGUE_FWIF_FWCCB_CMD_FREELISTS_RECONSTRUCTION:
pvr_free_list_process_reconstruct_req(pvr_dev,
&cmd->cmd_data.cmd_freelists_reconstruction);
break;
case ROGUE_FWIF_FWCCB_CMD_FREELIST_GROW:
pvr_free_list_process_grow_req(pvr_dev, &cmd->cmd_data.cmd_free_list_gs);
break;
default:
drm_info(from_pvr_device(pvr_dev), "Received unknown FWCCB command %x\n",
cmd->cmd_type);
......
......@@ -152,6 +152,14 @@ struct pvr_device {
*/
atomic_t mmu_flush_cache_flags;
/**
* @free_list_ids: Array of free lists belonging to this device. Array members
* are of type "struct pvr_free_list *".
*
* This array is used to allocate IDs used by the firmware.
*/
struct xarray free_list_ids;
struct {
/** @work: Work item for watchdog callback. */
struct delayed_work work;
......@@ -247,6 +255,22 @@ struct pvr_file {
*/
struct pvr_device *pvr_dev;
/**
* @free_list_handles: Array of free lists belonging to this file. Array
* members are of type "struct pvr_free_list *".
*
* This array is used to allocate handles returned to userspace.
*/
struct xarray free_list_handles;
/**
* @hwrt_handles: Array of HWRT datasets belonging to this file. Array
* members are of type "struct pvr_hwrt_dataset *".
*
* This array is used to allocate handles returned to userspace.
*/
struct xarray hwrt_handles;
/**
* @vm_ctx_handles: Array of VM contexts belonging to this file. Array
* members are of type "struct pvr_vm_context *".
......
......@@ -3,7 +3,9 @@
#include "pvr_device.h"
#include "pvr_drv.h"
#include "pvr_free_list.h"
#include "pvr_gem.h"
#include "pvr_hwrt.h"
#include "pvr_mmu.h"
#include "pvr_power.h"
#include "pvr_rogue_defs.h"
......@@ -711,7 +713,41 @@ static int
pvr_ioctl_create_free_list(struct drm_device *drm_dev, void *raw_args,
struct drm_file *file)
{
return -ENOTTY;
struct drm_pvr_ioctl_create_free_list_args *args = raw_args;
struct pvr_file *pvr_file = to_pvr_file(file);
struct pvr_free_list *free_list;
int idx;
int err;
if (!drm_dev_enter(drm_dev, &idx))
return -EIO;
free_list = pvr_free_list_create(pvr_file, args);
if (IS_ERR(free_list)) {
err = PTR_ERR(free_list);
goto err_drm_dev_exit;
}
/* Allocate object handle for userspace. */
err = xa_alloc(&pvr_file->free_list_handles,
&args->handle,
free_list,
xa_limit_32b,
GFP_KERNEL);
if (err < 0)
goto err_cleanup;
drm_dev_exit(idx);
return 0;
err_cleanup:
pvr_free_list_put(free_list);
err_drm_dev_exit:
drm_dev_exit(idx);
return err;
}
/**
......@@ -731,7 +767,19 @@ static int
pvr_ioctl_destroy_free_list(struct drm_device *drm_dev, void *raw_args,
struct drm_file *file)
{
return -ENOTTY;
struct drm_pvr_ioctl_destroy_free_list_args *args = raw_args;
struct pvr_file *pvr_file = to_pvr_file(file);
struct pvr_free_list *free_list;
if (args->_padding_4)
return -EINVAL;
free_list = xa_erase(&pvr_file->free_list_handles, args->handle);
if (!free_list)
return -EINVAL;
pvr_free_list_put(free_list);
return 0;
}
/**
......@@ -751,7 +799,41 @@ static int
pvr_ioctl_create_hwrt_dataset(struct drm_device *drm_dev, void *raw_args,
struct drm_file *file)
{
return -ENOTTY;
struct drm_pvr_ioctl_create_hwrt_dataset_args *args = raw_args;
struct pvr_file *pvr_file = to_pvr_file(file);
struct pvr_hwrt_dataset *hwrt;
int idx;
int err;
if (!drm_dev_enter(drm_dev, &idx))
return -EIO;
hwrt = pvr_hwrt_dataset_create(pvr_file, args);
if (IS_ERR(hwrt)) {
err = PTR_ERR(hwrt);
goto err_drm_dev_exit;
}
/* Allocate object handle for userspace. */
err = xa_alloc(&pvr_file->hwrt_handles,
&args->handle,
hwrt,
xa_limit_32b,
GFP_KERNEL);
if (err < 0)
goto err_cleanup;
drm_dev_exit(idx);
return 0;
err_cleanup:
pvr_hwrt_dataset_put(hwrt);
err_drm_dev_exit:
drm_dev_exit(idx);
return err;
}
/**
......@@ -771,7 +853,19 @@ static int
pvr_ioctl_destroy_hwrt_dataset(struct drm_device *drm_dev, void *raw_args,
struct drm_file *file)
{
return -ENOTTY;
struct drm_pvr_ioctl_destroy_hwrt_dataset_args *args = raw_args;
struct pvr_file *pvr_file = to_pvr_file(file);
struct pvr_hwrt_dataset *hwrt;
if (args->_padding_4)
return -EINVAL;
hwrt = xa_erase(&pvr_file->hwrt_handles, args->handle);
if (!hwrt)
return -EINVAL;
pvr_hwrt_dataset_put(hwrt);
return 0;
}
/**
......@@ -1195,6 +1289,8 @@ pvr_drm_driver_open(struct drm_device *drm_dev, struct drm_file *file)
*/
pvr_file->pvr_dev = pvr_dev;
xa_init_flags(&pvr_file->free_list_handles, XA_FLAGS_ALLOC1);
xa_init_flags(&pvr_file->hwrt_handles, XA_FLAGS_ALLOC1);
xa_init_flags(&pvr_file->vm_ctx_handles, XA_FLAGS_ALLOC1);
/*
......@@ -1223,6 +1319,8 @@ pvr_drm_driver_postclose(__always_unused struct drm_device *drm_dev,
struct pvr_file *pvr_file = to_pvr_file(file);
/* Drop references on any remaining objects. */
pvr_destroy_free_lists_for_file(pvr_file);
pvr_destroy_hwrt_datasets_for_file(pvr_file);
pvr_destroy_vm_contexts_for_file(pvr_file);
kfree(pvr_file);
......@@ -1281,6 +1379,8 @@ pvr_probe(struct platform_device *plat_dev)
if (err)
goto err_device_fini;
xa_init_flags(&pvr_dev->free_list_ids, XA_FLAGS_ALLOC1);
return 0;
err_device_fini:
......@@ -1298,6 +1398,10 @@ pvr_remove(struct platform_device *plat_dev)
struct drm_device *drm_dev = platform_get_drvdata(plat_dev);
struct pvr_device *pvr_dev = to_pvr_device(drm_dev);
WARN_ON(!xa_empty(&pvr_dev->free_list_ids));
xa_destroy(&pvr_dev->free_list_ids);
pm_runtime_suspend(drm_dev->dev);
pvr_device_fini(pvr_dev);
drm_dev_unplug(drm_dev);
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_FREE_LIST_H
#define PVR_FREE_LIST_H
#include <linux/compiler_attributes.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/xarray.h>
#include <uapi/drm/pvr_drm.h>
#include "pvr_device.h"
/* Forward declaration from pvr_gem.h. */
struct pvr_fw_object;
/* Forward declaration from pvr_gem.h. */
struct pvr_gem_object;
/* Forward declaration from pvr_hwrt.h. */
struct pvr_hwrt_data;
/**
* struct pvr_free_list_node - structure representing an allocation in the free
* list
*/
struct pvr_free_list_node {
/** @node: List node for &pvr_free_list.mem_block_list. */
struct list_head node;
/** @free_list: Pointer to owning free list. */
struct pvr_free_list *free_list;
/** @num_pages: Number of pages in this node. */
u32 num_pages;
/** @mem_obj: GEM object representing the pages in this node. */
struct pvr_gem_object *mem_obj;
};
/**
* struct pvr_free_list - structure representing a free list
*/
struct pvr_free_list {
/** @ref_count: Reference count of object. */
struct kref ref_count;
/** @pvr_dev: Pointer to device that owns this object. */
struct pvr_device *pvr_dev;
/** @obj: GEM object representing the free list. */
struct pvr_gem_object *obj;
/** @fw_obj: FW object representing the FW-side structure. */
struct pvr_fw_object *fw_obj;
/** @fw_data: Pointer to CPU mapping of the FW-side structure. */
struct rogue_fwif_freelist *fw_data;
/**
* @lock: Mutex protecting modification of the free list. Must be held when accessing any
* of the members below.
*/
struct mutex lock;
/** @fw_id: Firmware ID for this object. */
u32 fw_id;
/** @current_pages: Current number of pages in free list. */
u32 current_pages;
/** @max_pages: Maximum number of pages in free list. */
u32 max_pages;
/** @grow_pages: Pages to grow free list by per request. */
u32 grow_pages;
/**
* @grow_threshold: Percentage of FL memory used that should trigger a
* new grow request.
*/
u32 grow_threshold;
/**
* @ready_pages: Number of pages reserved for FW to use while a grow
* request is being processed.
*/
u32 ready_pages;
/** @mem_block_list: List of memory blocks in this free list. */
struct list_head mem_block_list;
/** @hwrt_list: List of HWRTs using this free list. */
struct list_head hwrt_list;
/** @initial_num_pages: Initial number of pages in free list. */
u32 initial_num_pages;
/** @free_list_gpu_addr: Address of free list in GPU address space. */
u64 free_list_gpu_addr;
};
struct pvr_free_list *
pvr_free_list_create(struct pvr_file *pvr_file,
struct drm_pvr_ioctl_create_free_list_args *args);
void
pvr_destroy_free_lists_for_file(struct pvr_file *pvr_file);
u32
pvr_get_free_list_min_pages(struct pvr_device *pvr_dev);
static __always_inline struct pvr_free_list *
pvr_free_list_get(struct pvr_free_list *free_list)
{
if (free_list)
kref_get(&free_list->ref_count);
return free_list;
}
/**
* pvr_free_list_lookup() - Lookup free list pointer from handle and file
* @pvr_file: Pointer to pvr_file structure.
* @handle: Object handle.
*
* Takes reference on free list object. Call pvr_free_list_put() to release.
*
* Returns:
* * The requested object on success, or
* * %NULL on failure (object does not exist in list, is not a free list, or
* does not belong to @pvr_file)
*/
static __always_inline struct pvr_free_list *
pvr_free_list_lookup(struct pvr_file *pvr_file, u32 handle)
{
struct pvr_free_list *free_list;
xa_lock(&pvr_file->free_list_handles);
free_list = pvr_free_list_get(xa_load(&pvr_file->free_list_handles, handle));
xa_unlock(&pvr_file->free_list_handles);
return free_list;
}
/**
* pvr_free_list_lookup_id() - Lookup free list pointer from FW ID
* @pvr_dev: Device pointer.
* @id: FW object ID.
*
* Takes reference on free list object. Call pvr_free_list_put() to release.
*
* Returns:
* * The requested object on success, or
* * %NULL on failure (object does not exist in list, or is not a free list)
*/
static __always_inline struct pvr_free_list *
pvr_free_list_lookup_id(struct pvr_device *pvr_dev, u32 id)
{
struct pvr_free_list *free_list;
xa_lock(&pvr_dev->free_list_ids);
/* Contexts are removed from the ctx_ids set in the context release path,
* meaning the ref_count reached zero before they get removed. We need
* to make sure we're not trying to acquire a context that's being
* destroyed.
*/
free_list = xa_load(&pvr_dev->free_list_ids, id);
if (free_list && !kref_get_unless_zero(&free_list->ref_count))
free_list = NULL;
xa_unlock(&pvr_dev->free_list_ids);
return free_list;
}
void
pvr_free_list_put(struct pvr_free_list *free_list);
void
pvr_free_list_add_hwrt(struct pvr_free_list *free_list, struct pvr_hwrt_data *hwrt_data);
void
pvr_free_list_remove_hwrt(struct pvr_free_list *free_list, struct pvr_hwrt_data *hwrt_data);
void pvr_free_list_process_grow_req(struct pvr_device *pvr_dev,
struct rogue_fwif_fwccb_cmd_freelist_gs_data *req);
void
pvr_free_list_process_reconstruct_req(struct pvr_device *pvr_dev,
struct rogue_fwif_fwccb_cmd_freelists_reconstruction_data *req);
#endif /* PVR_FREE_LIST_H */
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_HWRT_H
#define PVR_HWRT_H
#include <linux/compiler_attributes.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/types.h>
#include <linux/xarray.h>
#include <uapi/drm/pvr_drm.h>
#include "pvr_device.h"
#include "pvr_rogue_fwif_shared.h"
/* Forward declaration from pvr_free_list.h. */
struct pvr_free_list;
/* Forward declaration from pvr_gem.h. */
struct pvr_fw_object;
/**
* struct pvr_hwrt_data - structure representing HWRT data
*/
struct pvr_hwrt_data {
/** @fw_obj: FW object representing the FW-side structure. */
struct pvr_fw_object *fw_obj;
/** @data: Local copy of FW-side structure. */
struct rogue_fwif_hwrtdata data;
/** @freelist_node: List node connecting this HWRT to the local freelist. */
struct list_head freelist_node;
/**
* @srtc_obj: FW object representing shadow render target cache.
*
* Only valid if @max_rts > 1.
*/
struct pvr_fw_object *srtc_obj;
/**
* @raa_obj: FW object representing renders accumulation array.
*
* Only valid if @max_rts > 1.
*/
struct pvr_fw_object *raa_obj;
/** @hwrt_dataset: Back pointer to owning HWRT dataset. */
struct pvr_hwrt_dataset *hwrt_dataset;
};
/**
* struct pvr_hwrt_dataset - structure representing a HWRT data set.
*/
struct pvr_hwrt_dataset {
/** @ref_count: Reference count of object. */
struct kref ref_count;
/** @pvr_dev: Pointer to device that owns this object. */
struct pvr_device *pvr_dev;
/** @common_fw_obj: FW object representing common FW-side structure. */
struct pvr_fw_object *common_fw_obj;
struct rogue_fwif_hwrtdata_common common;
/** @data: HWRT data structures belonging to this set. */
struct pvr_hwrt_data data[ROGUE_FWIF_NUM_RTDATAS];
/** @free_lists: Free lists used by HWRT data set. */
struct pvr_free_list *free_lists[ROGUE_FWIF_NUM_RTDATA_FREELISTS];
/** @max_rts: Maximum render targets for this HWRT data set. */
u16 max_rts;
};
struct pvr_hwrt_dataset *
pvr_hwrt_dataset_create(struct pvr_file *pvr_file,
struct drm_pvr_ioctl_create_hwrt_dataset_args *args);
void
pvr_destroy_hwrt_datasets_for_file(struct pvr_file *pvr_file);
/**
* pvr_hwrt_dataset_lookup() - Lookup HWRT dataset pointer from handle
* @pvr_file: Pointer to pvr_file structure.
* @handle: Object handle.
*
* Takes reference on dataset object. Call pvr_hwrt_dataset_put() to release.
*
* Returns:
* * The requested object on success, or
* * %NULL on failure (object does not exist in list, or is not a HWRT
* dataset)
*/
static __always_inline struct pvr_hwrt_dataset *
pvr_hwrt_dataset_lookup(struct pvr_file *pvr_file, u32 handle)
{
struct pvr_hwrt_dataset *hwrt;
xa_lock(&pvr_file->hwrt_handles);
hwrt = xa_load(&pvr_file->hwrt_handles, handle);
if (hwrt)
kref_get(&hwrt->ref_count);
xa_unlock(&pvr_file->hwrt_handles);
return hwrt;
}
void
pvr_hwrt_dataset_put(struct pvr_hwrt_dataset *hwrt);
/**
* pvr_hwrt_data_lookup() - Lookup HWRT data pointer from handle and index
* @pvr_file: Pointer to pvr_file structure.
* @handle: Object handle.
* @index: Index of RT data within dataset.
*
* Takes reference on dataset object. Call pvr_hwrt_data_put() to release.
*
* Returns:
* * The requested object on success, or
* * %NULL on failure (object does not exist in list, or is not a HWRT
* dataset, or index is out of range)
*/
static __always_inline struct pvr_hwrt_data *
pvr_hwrt_data_lookup(struct pvr_file *pvr_file, u32 handle, u32 index)
{
struct pvr_hwrt_dataset *hwrt_dataset = pvr_hwrt_dataset_lookup(pvr_file, handle);
if (hwrt_dataset) {
if (index < ARRAY_SIZE(hwrt_dataset->data))
return &hwrt_dataset->data[index];
pvr_hwrt_dataset_put(hwrt_dataset);
}
return NULL;
}
/**
* pvr_hwrt_data_put() - Release reference on HWRT data
* @hwrt: Pointer to HWRT data to release reference on
*/
static __always_inline void
pvr_hwrt_data_put(struct pvr_hwrt_data *hwrt)
{
if (hwrt)
pvr_hwrt_dataset_put(hwrt->hwrt_dataset);
}
static __always_inline struct pvr_hwrt_data *
pvr_hwrt_data_get(struct pvr_hwrt_data *hwrt)
{
if (hwrt)
kref_get(&hwrt->hwrt_dataset->ref_count);
return hwrt;
}
#endif /* PVR_HWRT_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment