drm-misc-next for 6.8:

UAPI Changes:
 
 Cross-subsystem Changes:
 
 Core Changes:
   - Drop deprecated drm_kms_helper.edid_firmware module parameter
 
 Driver Changes:
   - Convert platform drivers remove callback to return void
   - imagination: Introduction of the Imagination GPU Support
   - rockchip:
     - rk3066_hdmi: Convert to atomic
     - vop2: Support NV20 and NV30
   - panel:
     - elida-kd35t133: PM reworks
     - New panels: Powkiddy RK2023
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRcEzekXsqa64kGDp7j7w1vZxhRxQUCZV9MRgAKCRDj7w1vZxhR
 xcBKAQDug7+M77pZsHlXVGgaqaAC5ZVfgp9qZbYEWiUgOpKSRAEA3KbhhI3CczGy
 SI9aS3+70jGBKWJDxKQoAjCXekwQaA4=
 =6cNH
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-next-2023-11-23' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for 6.8:

UAPI Changes:

Cross-subsystem Changes:

Core Changes:
  - Drop deprecated drm_kms_helper.edid_firmware module parameter

Driver Changes:
  - Convert platform drivers remove callback to return void
  - imagination: Introduction of the Imagination GPU Support
  - rockchip:
    - rk3066_hdmi: Convert to atomic
    - vop2: Support NV20 and NV30
  - panel:
    - elida-kd35t133: PM reworks
    - New panels: Powkiddy RK2023

Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
From: Maxime Ripard <mripard@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/drzvrbsej2txf6a6npc4ukkpadj3wio7edkjbgsfdm4l33szpe@fgwtdy5z5ev7
This commit is contained in:
Daniel Vetter 2023-11-23 18:10:39 +01:00
commit b26ca73519
115 changed files with 34630 additions and 202 deletions

View file

@ -21,7 +21,7 @@ properties:
- enum:
- anbernic,rg351v-panel
- anbernic,rg353p-panel
- anbernic,rg353v-panel
- powkiddy,rk2023-panel
- const: newvision,nv3051d
reg: true

View file

@ -0,0 +1,73 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (c) 2023 Imagination Technologies Ltd.
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpu/img,powervr.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Imagination Technologies PowerVR and IMG GPU
maintainers:
- Frank Binns <frank.binns@imgtec.com>
properties:
compatible:
items:
- enum:
- ti,am62-gpu
- const: img,img-axe # IMG AXE GPU model/revision is fully discoverable
reg:
maxItems: 1
clocks:
minItems: 1
maxItems: 3
clock-names:
items:
- const: core
- const: mem
- const: sys
minItems: 1
interrupts:
maxItems: 1
power-domains:
maxItems: 1
required:
- compatible
- reg
- clocks
- clock-names
- interrupts
additionalProperties: false
allOf:
- if:
properties:
compatible:
contains:
const: ti,am62-gpu
then:
properties:
clocks:
maxItems: 1
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/soc/ti,sci_pm_domain.h>
gpu@fd00000 {
compatible = "ti,am62-gpu", "img,img-axe";
reg = <0x0fd00000 0x20000>;
clocks = <&k3_clks 187 0>;
clock-names = "core";
interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
power-domains = <&k3_pds 187 TI_SCI_PD_EXCLUSIVE>;
};

View file

@ -3,9 +3,11 @@ GPU Driver Documentation
========================
.. toctree::
:maxdepth: 3
amdgpu/index
i915
imagination/index
mcde
meson
pl111

View file

@ -0,0 +1,13 @@
=======================================
drm/imagination PowerVR Graphics Driver
=======================================
.. kernel-doc:: drivers/gpu/drm/imagination/pvr_drv.c
:doc: PowerVR Graphics Driver
Contents
========
.. toctree::
:maxdepth: 2
uapi

View file

@ -0,0 +1,174 @@
====
UAPI
====
The sources associated with this section can be found in ``pvr_drm.h``.
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: PowerVR UAPI
OBJECT ARRAYS
=============
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_obj_array
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: DRM_PVR_OBJ_ARRAY
IOCTLS
======
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: PowerVR IOCTL interface
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: PVR_IOCTL
DEV_QUERY
---------
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: PowerVR IOCTL DEV_QUERY interface
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_dev_query
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ioctl_dev_query_args
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_dev_query_gpu_info
drm_pvr_dev_query_runtime_info
drm_pvr_dev_query_hwrt_info
drm_pvr_dev_query_quirks
drm_pvr_dev_query_enhancements
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_heap_id
drm_pvr_heap
drm_pvr_dev_query_heap_info
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: Flags for DRM_PVR_DEV_QUERY_HEAP_INFO_GET.
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_static_data_area_usage
drm_pvr_static_data_area
drm_pvr_dev_query_static_data_areas
CREATE_BO
---------
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: PowerVR IOCTL CREATE_BO interface
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ioctl_create_bo_args
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: Flags for CREATE_BO
GET_BO_MMAP_OFFSET
------------------
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: PowerVR IOCTL GET_BO_MMAP_OFFSET interface
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ioctl_get_bo_mmap_offset_args
CREATE_VM_CONTEXT and DESTROY_VM_CONTEXT
----------------------------------------
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: PowerVR IOCTL CREATE_VM_CONTEXT and DESTROY_VM_CONTEXT interfaces
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ioctl_create_vm_context_args
drm_pvr_ioctl_destroy_vm_context_args
VM_MAP and VM_UNMAP
-------------------
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: PowerVR IOCTL VM_MAP and VM_UNMAP interfaces
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ioctl_vm_map_args
drm_pvr_ioctl_vm_unmap_args
CREATE_CONTEXT and DESTROY_CONTEXT
----------------------------------
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: PowerVR IOCTL CREATE_CONTEXT and DESTROY_CONTEXT interfaces
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ioctl_create_context_args
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ctx_priority
drm_pvr_ctx_type
drm_pvr_static_render_context_state
drm_pvr_static_render_context_state_format
drm_pvr_reset_framework
drm_pvr_reset_framework_format
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ioctl_destroy_context_args
CREATE_FREE_LIST and DESTROY_FREE_LIST
--------------------------------------
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: PowerVR IOCTL CREATE_FREE_LIST and DESTROY_FREE_LIST interfaces
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ioctl_create_free_list_args
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ioctl_destroy_free_list_args
CREATE_HWRT_DATASET and DESTROY_HWRT_DATASET
--------------------------------------
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: PowerVR IOCTL CREATE_HWRT_DATASET and DESTROY_HWRT_DATASET interfaces
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ioctl_create_hwrt_dataset_args
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_create_hwrt_geom_data_args
drm_pvr_create_hwrt_rt_data_args
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ioctl_destroy_hwrt_dataset_args
SUBMIT_JOBS
-----------
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: PowerVR IOCTL SUBMIT_JOBS interface
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: Flags for the drm_pvr_sync_op object.
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_ioctl_submit_jobs_args
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: Flags for SUBMIT_JOB ioctl geometry command.
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: Flags for SUBMIT_JOB ioctl fragment command.
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: Flags for SUBMIT_JOB ioctl compute command.
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:doc: Flags for SUBMIT_JOB ioctl transfer command.
.. kernel-doc:: include/uapi/drm/pvr_drm.h
:identifiers: drm_pvr_sync_op
drm_pvr_job_type
drm_pvr_hwrt_data_ref
drm_pvr_job
Internal notes
==============
.. kernel-doc:: drivers/gpu/drm/imagination/pvr_device.h
:doc: IOCTL validation helpers
.. kernel-doc:: drivers/gpu/drm/imagination/pvr_device.h
:identifiers: PVR_STATIC_ASSERT_64BIT_ALIGNED PVR_IOCTL_UNION_PADDING_CHECK
pvr_ioctl_union_padding_check

View file

@ -10391,6 +10391,16 @@ IMGTEC IR DECODER DRIVER
S: Orphan
F: drivers/media/rc/img-ir/
IMGTEC POWERVR DRM DRIVER
M: Frank Binns <frank.binns@imgtec.com>
M: Donald Robson <donald.robson@imgtec.com>
M: Matt Coster <matt.coster@imgtec.com>
S: Supported
F: Documentation/devicetree/bindings/gpu/img,powervr.yaml
F: Documentation/gpu/imagination/
F: drivers/gpu/drm/imagination/
F: include/uapi/drm/pvr_drm.h
IMON SOUNDGRAPH USB IR RECEIVER
M: Sean Young <sean@mess.org>
L: linux-media@vger.kernel.org

View file

@ -452,7 +452,7 @@ static int create_sgt(struct qaic_device *qdev, struct sg_table **sgt_out, u64 s
* later
*/
buf_extra = (PAGE_SIZE - size % PAGE_SIZE) % PAGE_SIZE;
max_order = min(MAX_ORDER - 1, get_order(size));
max_order = min(MAX_ORDER, get_order(size));
} else {
/* allocate a single page for book keeping */
nr_pages = 1;

View file

@ -46,12 +46,12 @@ static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen)
{
struct dma_buf *dmabuf;
char name[DMA_BUF_NAME_LEN];
size_t ret = 0;
ssize_t ret = 0;
dmabuf = dentry->d_fsdata;
spin_lock(&dmabuf->name_lock);
if (dmabuf->name)
ret = strlcpy(name, dmabuf->name, DMA_BUF_NAME_LEN);
ret = strscpy(name, dmabuf->name, sizeof(name));
spin_unlock(&dmabuf->name_lock);
return dynamic_dname(buffer, buflen, "/%s:%s",

View file

@ -394,6 +394,8 @@ source "drivers/gpu/drm/solomon/Kconfig"
source "drivers/gpu/drm/sprd/Kconfig"
source "drivers/gpu/drm/imagination/Kconfig"
config DRM_HYPERV
tristate "DRM Support for Hyper-V synthetic video device"
depends on DRM && PCI && MMU && HYPERV

View file

@ -199,3 +199,4 @@ obj-$(CONFIG_DRM_HYPERV) += hyperv/
obj-y += solomon/
obj-$(CONFIG_DRM_SPRD) += sprd/
obj-$(CONFIG_DRM_LOONGSON) += loongson/
obj-$(CONFIG_DRM_POWERVR) += imagination/

View file

@ -1066,10 +1066,9 @@ static int armada_lcd_probe(struct platform_device *pdev)
return component_add(&pdev->dev, &armada_lcd_ops);
}
static int armada_lcd_remove(struct platform_device *pdev)
static void armada_lcd_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &armada_lcd_ops);
return 0;
}
static const struct of_device_id armada_lcd_of_match[] = {
@ -1095,7 +1094,7 @@ MODULE_DEVICE_TABLE(platform, armada_lcd_platform_ids);
struct platform_driver armada_lcd_platform_driver = {
.probe = armada_lcd_probe,
.remove = armada_lcd_remove,
.remove_new = armada_lcd_remove,
.driver = {
.name = "armada-lcd",
.owner = THIS_MODULE,

View file

@ -226,10 +226,9 @@ static int armada_drm_probe(struct platform_device *pdev)
match);
}
static int armada_drm_remove(struct platform_device *pdev)
static void armada_drm_remove(struct platform_device *pdev)
{
component_master_del(&pdev->dev, &armada_master_ops);
return 0;
}
static void armada_drm_shutdown(struct platform_device *pdev)
@ -249,7 +248,7 @@ MODULE_DEVICE_TABLE(platform, armada_drm_platform_ids);
static struct platform_driver armada_drm_platform_driver = {
.probe = armada_drm_probe,
.remove = armada_drm_remove,
.remove_new = armada_drm_remove,
.shutdown = armada_drm_shutdown,
.driver = {
.name = "armada-drm",

View file

@ -2596,11 +2596,10 @@ static int cdns_mhdp_probe(struct platform_device *pdev)
return ret;
}
static int cdns_mhdp_remove(struct platform_device *pdev)
static void cdns_mhdp_remove(struct platform_device *pdev)
{
struct cdns_mhdp_device *mhdp = platform_get_drvdata(pdev);
unsigned long timeout = msecs_to_jiffies(100);
bool stop_fw = false;
int ret;
drm_bridge_remove(&mhdp->bridge);
@ -2608,18 +2607,19 @@ static int cdns_mhdp_remove(struct platform_device *pdev)
ret = wait_event_timeout(mhdp->fw_load_wq,
mhdp->hw_state == MHDP_HW_READY,
timeout);
if (ret == 0)
dev_err(mhdp->dev, "%s: Timeout waiting for fw loading\n",
__func__);
else
stop_fw = true;
spin_lock(&mhdp->start_lock);
mhdp->hw_state = MHDP_HW_STOPPED;
spin_unlock(&mhdp->start_lock);
if (stop_fw)
if (ret == 0) {
dev_err(mhdp->dev, "%s: Timeout waiting for fw loading\n",
__func__);
} else {
ret = cdns_mhdp_set_firmware_active(mhdp, false);
if (ret)
dev_err(mhdp->dev, "Failed to stop firmware (%pe)\n",
ERR_PTR(ret));
}
phy_exit(mhdp->phy);
@ -2634,8 +2634,6 @@ static int cdns_mhdp_remove(struct platform_device *pdev)
/* Ignoring mhdp->hdcp.check_work and mhdp->hdcp.prop_work here. */
clk_disable_unprepare(mhdp->clk);
return ret;
}
static const struct of_device_id mhdp_ids[] = {
@ -2658,7 +2656,7 @@ static struct platform_driver mhdp_driver = {
.of_match_table = mhdp_ids,
},
.probe = cdns_mhdp_probe,
.remove = cdns_mhdp_remove,
.remove_new = cdns_mhdp_remove,
};
module_platform_driver(mhdp_driver);

View file

@ -179,13 +179,11 @@ static int tpd12s015_probe(struct platform_device *pdev)
return 0;
}
static int __exit tpd12s015_remove(struct platform_device *pdev)
static void tpd12s015_remove(struct platform_device *pdev)
{
struct tpd12s015_device *tpd = platform_get_drvdata(pdev);
drm_bridge_remove(&tpd->bridge);
return 0;
}
static const struct of_device_id tpd12s015_of_match[] = {
@ -197,7 +195,7 @@ MODULE_DEVICE_TABLE(of, tpd12s015_of_match);
static struct platform_driver tpd12s015_driver = {
.probe = tpd12s015_probe,
.remove = __exit_p(tpd12s015_remove),
.remove_new = tpd12s015_remove,
.driver = {
.name = "tpd12s015",
.of_match_table = tpd12s015_of_match,

View file

@ -23,22 +23,6 @@ module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
"from built-in data or /lib/firmware instead. ");
/* Use only for backward compatibility with drm_kms_helper.edid_firmware */
int __drm_set_edid_firmware_path(const char *path)
{
scnprintf(edid_firmware, sizeof(edid_firmware), "%s", path);
return 0;
}
EXPORT_SYMBOL(__drm_set_edid_firmware_path);
/* Use only for backward compatibility with drm_kms_helper.edid_firmware */
int __drm_get_edid_firmware_path(char *buf, size_t bufsize)
{
return scnprintf(buf, bufsize, "%s", edid_firmware);
}
EXPORT_SYMBOL(__drm_get_edid_firmware_path);
#define GENERIC_EDIDS 6
static const char * const generic_edid_name[GENERIC_EDIDS] = {
"edid/800x600.bin",

View file

@ -27,38 +27,6 @@
#include <linux/module.h>
#include <drm/drm_edid.h>
#include <drm/drm_print.h>
#include "drm_crtc_helper_internal.h"
MODULE_AUTHOR("David Airlie, Jesse Barnes");
MODULE_DESCRIPTION("DRM KMS helper");
MODULE_LICENSE("GPL and additional rights");
#if IS_ENABLED(CONFIG_DRM_LOAD_EDID_FIRMWARE)
/* Backward compatibility for drm_kms_helper.edid_firmware */
static int edid_firmware_set(const char *val, const struct kernel_param *kp)
{
DRM_NOTE("drm_kms_helper.edid_firmware is deprecated, please use drm.edid_firmware instead.\n");
return __drm_set_edid_firmware_path(val);
}
static int edid_firmware_get(char *buffer, const struct kernel_param *kp)
{
return __drm_get_edid_firmware_path(buffer, PAGE_SIZE);
}
static const struct kernel_param_ops edid_firmware_ops = {
.set = edid_firmware_set,
.get = edid_firmware_get,
};
module_param_cb(edid_firmware, &edid_firmware_ops, NULL, 0644);
__MODULE_PARM_TYPE(edid_firmware, "charp");
MODULE_PARM_DESC(edid_firmware,
"DEPRECATED. Use drm.edid_firmware module parameter instead.");
#endif

View file

@ -640,16 +640,14 @@ static int etnaviv_pdev_probe(struct platform_device *pdev)
return component_master_add_with_match(dev, &etnaviv_master_ops, match);
}
static int etnaviv_pdev_remove(struct platform_device *pdev)
static void etnaviv_pdev_remove(struct platform_device *pdev)
{
component_master_del(&pdev->dev, &etnaviv_master_ops);
return 0;
}
static struct platform_driver etnaviv_platform_driver = {
.probe = etnaviv_pdev_probe,
.remove = etnaviv_pdev_remove,
.remove_new = etnaviv_pdev_remove,
.driver = {
.name = "etnaviv",
},

View file

@ -1904,11 +1904,10 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
return 0;
}
static int etnaviv_gpu_platform_remove(struct platform_device *pdev)
static void etnaviv_gpu_platform_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &gpu_ops);
pm_runtime_disable(&pdev->dev);
return 0;
}
static int etnaviv_gpu_rpm_suspend(struct device *dev)
@ -1970,6 +1969,6 @@ struct platform_driver etnaviv_gpu_driver = {
.of_match_table = etnaviv_gpu_match,
},
.probe = etnaviv_gpu_platform_probe,
.remove = etnaviv_gpu_platform_remove,
.remove_new = etnaviv_gpu_platform_remove,
.id_table = gpu_ids,
};

View file

@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0-only OR MIT
# Copyright (c) 2023 Imagination Technologies Ltd.
config DRM_POWERVR
tristate "Imagination Technologies PowerVR (Series 6 and later) & IMG Graphics"
depends on ARM64
depends on DRM
depends on PM
select DRM_EXEC
select DRM_GEM_SHMEM_HELPER
select DRM_SCHED
select DRM_GPUVM
select FW_LOADER
help
Choose this option if you have a system that has an Imagination
Technologies PowerVR (Series 6 or later) or IMG GPU.
If "M" is selected, the module will be called powervr.

View file

@ -0,0 +1,35 @@
# SPDX-License-Identifier: GPL-2.0-only OR MIT
# Copyright (c) 2023 Imagination Technologies Ltd.
subdir-ccflags-y := -I$(srctree)/$(src)
powervr-y := \
pvr_ccb.o \
pvr_cccb.o \
pvr_context.o \
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_job.o \
pvr_mmu.o \
pvr_params.o \
pvr_power.o \
pvr_queue.o \
pvr_stream.o \
pvr_stream_defs.o \
pvr_sync.o \
pvr_vm.o \
pvr_vm_mips.o
powervr-$(CONFIG_DEBUG_FS) += \
pvr_debugfs.o
obj-$(CONFIG_DRM_POWERVR) += powervr.o

View file

@ -0,0 +1,645 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#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"
#include <drm/drm_managed.h>
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#define RESERVE_SLOT_TIMEOUT (1 * HZ) /* 1s */
#define RESERVE_SLOT_MIN_RETRIES 10
static void
ccb_ctrl_init(void *cpu_ptr, void *priv)
{
struct rogue_fwif_ccb_ctl *ctrl = cpu_ptr;
struct pvr_ccb *pvr_ccb = priv;
ctrl->write_offset = 0;
ctrl->read_offset = 0;
ctrl->wrap_mask = pvr_ccb->num_cmds - 1;
ctrl->cmd_size = pvr_ccb->cmd_size;
}
/**
* pvr_ccb_init() - Initialise a CCB
* @pvr_dev: Device pointer.
* @pvr_ccb: Pointer to CCB structure to initialise.
* @num_cmds_log2: Log2 of number of commands in this CCB.
* @cmd_size: Command size for this CCB.
*
* Return:
* * Zero on success, or
* * Any error code returned by pvr_fw_object_create_and_map().
*/
static int
pvr_ccb_init(struct pvr_device *pvr_dev, struct pvr_ccb *pvr_ccb,
u32 num_cmds_log2, size_t cmd_size)
{
u32 num_cmds = 1 << num_cmds_log2;
u32 ccb_size = num_cmds * cmd_size;
int err;
pvr_ccb->num_cmds = num_cmds;
pvr_ccb->cmd_size = cmd_size;
err = drmm_mutex_init(from_pvr_device(pvr_dev), &pvr_ccb->lock);
if (err)
return err;
/*
* Map CCB and control structure as uncached, so we don't have to flush
* CPU cache repeatedly when polling for space.
*/
pvr_ccb->ctrl = pvr_fw_object_create_and_map(pvr_dev, sizeof(*pvr_ccb->ctrl),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
ccb_ctrl_init, pvr_ccb, &pvr_ccb->ctrl_obj);
if (IS_ERR(pvr_ccb->ctrl))
return PTR_ERR(pvr_ccb->ctrl);
pvr_ccb->ccb = pvr_fw_object_create_and_map(pvr_dev, ccb_size,
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
NULL, NULL, &pvr_ccb->ccb_obj);
if (IS_ERR(pvr_ccb->ccb)) {
err = PTR_ERR(pvr_ccb->ccb);
goto err_free_ctrl;
}
pvr_fw_object_get_fw_addr(pvr_ccb->ctrl_obj, &pvr_ccb->ctrl_fw_addr);
pvr_fw_object_get_fw_addr(pvr_ccb->ccb_obj, &pvr_ccb->ccb_fw_addr);
WRITE_ONCE(pvr_ccb->ctrl->write_offset, 0);
WRITE_ONCE(pvr_ccb->ctrl->read_offset, 0);
WRITE_ONCE(pvr_ccb->ctrl->wrap_mask, num_cmds - 1);
WRITE_ONCE(pvr_ccb->ctrl->cmd_size, cmd_size);
return 0;
err_free_ctrl:
pvr_fw_object_unmap_and_destroy(pvr_ccb->ctrl_obj);
return err;
}
/**
* pvr_ccb_fini() - Release CCB structure
* @pvr_ccb: CCB to release.
*/
void
pvr_ccb_fini(struct pvr_ccb *pvr_ccb)
{
pvr_fw_object_unmap_and_destroy(pvr_ccb->ccb_obj);
pvr_fw_object_unmap_and_destroy(pvr_ccb->ctrl_obj);
}
/**
* pvr_ccb_slot_available_locked() - Test whether any slots are available in CCB
* @pvr_ccb: CCB to test.
* @write_offset: Address to store number of next available slot. May be %NULL.
*
* Caller must hold @pvr_ccb->lock.
*
* Return:
* * %true if a slot is available, or
* * %false if no slot is available.
*/
static __always_inline bool
pvr_ccb_slot_available_locked(struct pvr_ccb *pvr_ccb, u32 *write_offset)
{
struct rogue_fwif_ccb_ctl *ctrl = pvr_ccb->ctrl;
u32 next_write_offset = (READ_ONCE(ctrl->write_offset) + 1) & READ_ONCE(ctrl->wrap_mask);
lockdep_assert_held(&pvr_ccb->lock);
if (READ_ONCE(ctrl->read_offset) != next_write_offset) {
if (write_offset)
*write_offset = next_write_offset;
return true;
}
return false;
}
static void
process_fwccb_command(struct pvr_device *pvr_dev, struct rogue_fwif_fwccb_cmd *cmd)
{
switch (cmd->cmd_type) {
case ROGUE_FWIF_FWCCB_CMD_REQUEST_GPU_RESTART:
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);
break;
}
}
/**
* pvr_fwccb_process() - Process any pending FWCCB commands
* @pvr_dev: Target PowerVR device
*/
void pvr_fwccb_process(struct pvr_device *pvr_dev)
{
struct rogue_fwif_fwccb_cmd *fwccb = pvr_dev->fwccb.ccb;
struct rogue_fwif_ccb_ctl *ctrl = pvr_dev->fwccb.ctrl;
u32 read_offset;
mutex_lock(&pvr_dev->fwccb.lock);
while ((read_offset = READ_ONCE(ctrl->read_offset)) != READ_ONCE(ctrl->write_offset)) {
struct rogue_fwif_fwccb_cmd cmd = fwccb[read_offset];
WRITE_ONCE(ctrl->read_offset, (read_offset + 1) & READ_ONCE(ctrl->wrap_mask));
/* Drop FWCCB lock while we process command. */
mutex_unlock(&pvr_dev->fwccb.lock);
process_fwccb_command(pvr_dev, &cmd);
mutex_lock(&pvr_dev->fwccb.lock);
}
mutex_unlock(&pvr_dev->fwccb.lock);
}
/**
* pvr_kccb_capacity() - Returns the maximum number of usable KCCB slots.
* @pvr_dev: Target PowerVR device
*
* Return:
* * The maximum number of active slots.
*/
static u32 pvr_kccb_capacity(struct pvr_device *pvr_dev)
{
/* Capacity is the number of slot minus one to cope with the wrapping
* mechanisms. If we were to use all slots, we might end up with
* read_offset == write_offset, which the FW considers as a KCCB-is-empty
* condition.
*/
return pvr_dev->kccb.slot_count - 1;
}
/**
* pvr_kccb_used_slot_count_locked() - Get the number of used slots
* @pvr_dev: Device pointer.
*
* KCCB lock must be held.
*
* Return:
* * The number of slots currently used.
*/
static u32
pvr_kccb_used_slot_count_locked(struct pvr_device *pvr_dev)
{
struct pvr_ccb *pvr_ccb = &pvr_dev->kccb.ccb;
struct rogue_fwif_ccb_ctl *ctrl = pvr_ccb->ctrl;
u32 wr_offset = READ_ONCE(ctrl->write_offset);
u32 rd_offset = READ_ONCE(ctrl->read_offset);
u32 used_count;
lockdep_assert_held(&pvr_ccb->lock);
if (wr_offset >= rd_offset)
used_count = wr_offset - rd_offset;
else
used_count = wr_offset + pvr_dev->kccb.slot_count - rd_offset;
return used_count;
}
/**
* pvr_kccb_send_cmd_reserved_powered() - Send command to the KCCB, with the PM ref
* held and a slot pre-reserved
* @pvr_dev: Device pointer.
* @cmd: Command to sent.
* @kccb_slot: Address to store the KCCB slot for this command. May be %NULL.
*/
void
pvr_kccb_send_cmd_reserved_powered(struct pvr_device *pvr_dev,
struct rogue_fwif_kccb_cmd *cmd,
u32 *kccb_slot)
{
struct pvr_ccb *pvr_ccb = &pvr_dev->kccb.ccb;
struct rogue_fwif_kccb_cmd *kccb = pvr_ccb->ccb;
struct rogue_fwif_ccb_ctl *ctrl = pvr_ccb->ctrl;
u32 old_write_offset;
u32 new_write_offset;
WARN_ON(pvr_dev->lost);
mutex_lock(&pvr_ccb->lock);
if (WARN_ON(!pvr_dev->kccb.reserved_count))
goto out_unlock;
old_write_offset = READ_ONCE(ctrl->write_offset);
/* We reserved the slot, we should have one available. */
if (WARN_ON(!pvr_ccb_slot_available_locked(pvr_ccb, &new_write_offset)))
goto out_unlock;
memcpy(&kccb[old_write_offset], cmd,
sizeof(struct rogue_fwif_kccb_cmd));
if (kccb_slot) {
*kccb_slot = old_write_offset;
/* Clear return status for this slot. */
WRITE_ONCE(pvr_dev->kccb.rtn[old_write_offset],
ROGUE_FWIF_KCCB_RTN_SLOT_NO_RESPONSE);
}
mb(); /* memory barrier */
WRITE_ONCE(ctrl->write_offset, new_write_offset);
pvr_dev->kccb.reserved_count--;
/* Kick MTS */
pvr_fw_mts_schedule(pvr_dev,
PVR_FWIF_DM_GP & ~ROGUE_CR_MTS_SCHEDULE_DM_CLRMSK);
out_unlock:
mutex_unlock(&pvr_ccb->lock);
}
/**
* pvr_kccb_try_reserve_slot() - Try to reserve a KCCB slot
* @pvr_dev: Device pointer.
*
* Return:
* * true if a KCCB slot was reserved, or
* * false otherwise.
*/
static bool pvr_kccb_try_reserve_slot(struct pvr_device *pvr_dev)
{
bool reserved = false;
u32 used_count;
mutex_lock(&pvr_dev->kccb.ccb.lock);
used_count = pvr_kccb_used_slot_count_locked(pvr_dev);
if (pvr_dev->kccb.reserved_count < pvr_kccb_capacity(pvr_dev) - used_count) {
pvr_dev->kccb.reserved_count++;
reserved = true;
}
mutex_unlock(&pvr_dev->kccb.ccb.lock);
return reserved;
}
/**
* pvr_kccb_reserve_slot_sync() - Try to reserve a slot synchronously
* @pvr_dev: Device pointer.
*
* Return:
* * 0 on success, or
* * -EBUSY if no slots were reserved after %RESERVE_SLOT_TIMEOUT, with a minimum of
* %RESERVE_SLOT_MIN_RETRIES retries.
*/
static int pvr_kccb_reserve_slot_sync(struct pvr_device *pvr_dev)
{
unsigned long start_timestamp = jiffies;
bool reserved = false;
u32 retries = 0;
while ((jiffies - start_timestamp) < (u32)RESERVE_SLOT_TIMEOUT ||
retries < RESERVE_SLOT_MIN_RETRIES) {
reserved = pvr_kccb_try_reserve_slot(pvr_dev);
if (reserved)
break;
usleep_range(1, 50);
if (retries < U32_MAX)
retries++;
}
return reserved ? 0 : -EBUSY;
}
/**
* pvr_kccb_send_cmd_powered() - Send command to the KCCB, with a PM ref held
* @pvr_dev: Device pointer.
* @cmd: Command to sent.
* @kccb_slot: Address to store the KCCB slot for this command. May be %NULL.
*
* Returns:
* * Zero on success, or
* * -EBUSY if timeout while waiting for a free KCCB slot.
*/
int
pvr_kccb_send_cmd_powered(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd *cmd,
u32 *kccb_slot)
{
int err;
err = pvr_kccb_reserve_slot_sync(pvr_dev);
if (err)
return err;
pvr_kccb_send_cmd_reserved_powered(pvr_dev, cmd, kccb_slot);
return 0;
}
/**
* pvr_kccb_send_cmd() - Send command to the KCCB
* @pvr_dev: Device pointer.
* @cmd: Command to sent.
* @kccb_slot: Address to store the KCCB slot for this command. May be %NULL.
*
* Returns:
* * Zero on success, or
* * -EBUSY if timeout while waiting for a free KCCB slot.
*/
int
pvr_kccb_send_cmd(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd *cmd,
u32 *kccb_slot)
{
int err;
err = pvr_power_get(pvr_dev);
if (err)
return err;
err = pvr_kccb_send_cmd_powered(pvr_dev, cmd, kccb_slot);
pvr_power_put(pvr_dev);
return err;
}
/**
* pvr_kccb_wait_for_completion() - Wait for a KCCB command to complete
* @pvr_dev: Device pointer.
* @slot_nr: KCCB slot to wait on.
* @timeout: Timeout length (in jiffies).
* @rtn_out: Location to store KCCB command result. May be %NULL.
*
* Returns:
* * Zero on success, or
* * -ETIMEDOUT on timeout.
*/
int
pvr_kccb_wait_for_completion(struct pvr_device *pvr_dev, u32 slot_nr,
u32 timeout, u32 *rtn_out)
{
int ret = wait_event_timeout(pvr_dev->kccb.rtn_q, READ_ONCE(pvr_dev->kccb.rtn[slot_nr]) &
ROGUE_FWIF_KCCB_RTN_SLOT_CMD_EXECUTED, timeout);
if (ret && rtn_out)
*rtn_out = READ_ONCE(pvr_dev->kccb.rtn[slot_nr]);
return ret ? 0 : -ETIMEDOUT;
}
/**
* pvr_kccb_is_idle() - Returns whether the device's KCCB is idle
* @pvr_dev: Device pointer
*
* Returns:
* * %true if the KCCB is idle (contains no commands), or
* * %false if the KCCB contains pending commands.
*/
bool
pvr_kccb_is_idle(struct pvr_device *pvr_dev)
{
struct rogue_fwif_ccb_ctl *ctrl = pvr_dev->kccb.ccb.ctrl;
bool idle;
mutex_lock(&pvr_dev->kccb.ccb.lock);
idle = (READ_ONCE(ctrl->write_offset) == READ_ONCE(ctrl->read_offset));
mutex_unlock(&pvr_dev->kccb.ccb.lock);
return idle;
}
static const char *
pvr_kccb_fence_get_driver_name(struct dma_fence *f)
{
return PVR_DRIVER_NAME;
}
static const char *
pvr_kccb_fence_get_timeline_name(struct dma_fence *f)
{
return "kccb";
}
static const struct dma_fence_ops pvr_kccb_fence_ops = {
.get_driver_name = pvr_kccb_fence_get_driver_name,
.get_timeline_name = pvr_kccb_fence_get_timeline_name,
};
/**
* struct pvr_kccb_fence - Fence object used to wait for a KCCB slot
*/
struct pvr_kccb_fence {
/** @base: Base dma_fence object. */
struct dma_fence base;
/** @node: Node used to insert the fence in the pvr_device::kccb::waiters list. */
struct list_head node;
};
/**
* pvr_kccb_wake_up_waiters() - Check the KCCB waiters
* @pvr_dev: Target PowerVR device
*
* Signal as many KCCB fences as we have slots available.
*/
void pvr_kccb_wake_up_waiters(struct pvr_device *pvr_dev)
{
struct pvr_kccb_fence *fence, *tmp_fence;
u32 used_count, available_count;
/* Wake up those waiting for KCCB slot execution. */
wake_up_all(&pvr_dev->kccb.rtn_q);
/* Then iterate over all KCCB fences and signal as many as we can. */
mutex_lock(&pvr_dev->kccb.ccb.lock);
used_count = pvr_kccb_used_slot_count_locked(pvr_dev);
if (WARN_ON(used_count + pvr_dev->kccb.reserved_count > pvr_kccb_capacity(pvr_dev)))
goto out_unlock;
available_count = pvr_kccb_capacity(pvr_dev) - used_count - pvr_dev->kccb.reserved_count;
list_for_each_entry_safe(fence, tmp_fence, &pvr_dev->kccb.waiters, node) {
if (!available_count)
break;
list_del(&fence->node);
pvr_dev->kccb.reserved_count++;
available_count--;
dma_fence_signal(&fence->base);
dma_fence_put(&fence->base);
}
out_unlock:
mutex_unlock(&pvr_dev->kccb.ccb.lock);
}
/**
* pvr_kccb_fini() - Cleanup device KCCB
* @pvr_dev: Target PowerVR device
*/
void pvr_kccb_fini(struct pvr_device *pvr_dev)
{
pvr_ccb_fini(&pvr_dev->kccb.ccb);
WARN_ON(!list_empty(&pvr_dev->kccb.waiters));
WARN_ON(pvr_dev->kccb.reserved_count);
}
/**
* pvr_kccb_init() - Initialise device KCCB
* @pvr_dev: Target PowerVR device
*
* Returns:
* * 0 on success, or
* * Any error returned by pvr_ccb_init().
*/
int
pvr_kccb_init(struct pvr_device *pvr_dev)
{
pvr_dev->kccb.slot_count = 1 << ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT;
INIT_LIST_HEAD(&pvr_dev->kccb.waiters);
pvr_dev->kccb.fence_ctx.id = dma_fence_context_alloc(1);
spin_lock_init(&pvr_dev->kccb.fence_ctx.lock);
return pvr_ccb_init(pvr_dev, &pvr_dev->kccb.ccb,
ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT,
sizeof(struct rogue_fwif_kccb_cmd));
}
/**
* pvr_kccb_fence_alloc() - Allocate a pvr_kccb_fence object
*
* Return:
* * NULL if the allocation fails, or
* * A valid dma_fence pointer otherwise.
*/
struct dma_fence *pvr_kccb_fence_alloc(void)
{
struct pvr_kccb_fence *kccb_fence;
kccb_fence = kzalloc(sizeof(*kccb_fence), GFP_KERNEL);
if (!kccb_fence)
return NULL;
return &kccb_fence->base;
}
/**
* pvr_kccb_fence_put() - Drop a KCCB fence reference
* @fence: The fence to drop the reference on.
*
* If the fence hasn't been initialized yet, dma_fence_free() is called. This
* way we have a single function taking care of both cases.
*/
void pvr_kccb_fence_put(struct dma_fence *fence)
{
if (!fence)
return;
if (!fence->ops) {
dma_fence_free(fence);
} else {
WARN_ON(fence->ops != &pvr_kccb_fence_ops);
dma_fence_put(fence);
}
}
/**
* pvr_kccb_reserve_slot() - Reserve a KCCB slot for later use
* @pvr_dev: Target PowerVR device
* @f: KCCB fence object previously allocated with pvr_kccb_fence_alloc()
*
* Try to reserve a KCCB slot, and if there's no slot available,
* initializes the fence object and queue it to the waiters list.
*
* If NULL is returned, that means the slot is reserved. In that case,
* the @f is freed and shouldn't be accessed after that point.
*
* Return:
* * NULL if a slot was available directly, or
* * A valid dma_fence object to wait on if no slot was available.
*/
struct dma_fence *
pvr_kccb_reserve_slot(struct pvr_device *pvr_dev, struct dma_fence *f)
{
struct pvr_kccb_fence *fence = container_of(f, struct pvr_kccb_fence, base);
struct dma_fence *out_fence = NULL;
u32 used_count;
mutex_lock(&pvr_dev->kccb.ccb.lock);
used_count = pvr_kccb_used_slot_count_locked(pvr_dev);
if (pvr_dev->kccb.reserved_count >= pvr_kccb_capacity(pvr_dev) - used_count) {
dma_fence_init(&fence->base, &pvr_kccb_fence_ops,
&pvr_dev->kccb.fence_ctx.lock,
pvr_dev->kccb.fence_ctx.id,
atomic_inc_return(&pvr_dev->kccb.fence_ctx.seqno));
out_fence = dma_fence_get(&fence->base);
list_add_tail(&fence->node, &pvr_dev->kccb.waiters);
} else {
pvr_kccb_fence_put(f);
pvr_dev->kccb.reserved_count++;
}
mutex_unlock(&pvr_dev->kccb.ccb.lock);
return out_fence;
}
/**
* pvr_kccb_release_slot() - Release a KCCB slot reserved with
* pvr_kccb_reserve_slot()
* @pvr_dev: Target PowerVR device
*
* Should only be called if something failed after the
* pvr_kccb_reserve_slot() call and you know you won't call
* pvr_kccb_send_cmd_reserved().
*/
void pvr_kccb_release_slot(struct pvr_device *pvr_dev)
{
mutex_lock(&pvr_dev->kccb.ccb.lock);
if (!WARN_ON(!pvr_dev->kccb.reserved_count))
pvr_dev->kccb.reserved_count--;
mutex_unlock(&pvr_dev->kccb.ccb.lock);
}
/**
* pvr_fwccb_init() - Initialise device FWCCB
* @pvr_dev: Target PowerVR device
*
* Returns:
* * 0 on success, or
* * Any error returned by pvr_ccb_init().
*/
int
pvr_fwccb_init(struct pvr_device *pvr_dev)
{
return pvr_ccb_init(pvr_dev, &pvr_dev->fwccb,
ROGUE_FWIF_FWCCB_NUMCMDS_LOG2,
sizeof(struct rogue_fwif_fwccb_cmd));
}

View file

@ -0,0 +1,71 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_CCB_H
#define PVR_CCB_H
#include "pvr_rogue_fwif.h"
#include <linux/mutex.h>
#include <linux/types.h>
/* Forward declaration from pvr_device.h. */
struct pvr_device;
/* Forward declaration from pvr_gem.h. */
struct pvr_fw_object;
struct pvr_ccb {
/** @ctrl_obj: FW object representing CCB control structure. */
struct pvr_fw_object *ctrl_obj;
/** @ccb_obj: FW object representing CCB. */
struct pvr_fw_object *ccb_obj;
/** @ctrl_fw_addr: FW virtual address of CCB control structure. */
u32 ctrl_fw_addr;
/** @ccb_fw_addr: FW virtual address of CCB. */
u32 ccb_fw_addr;
/** @num_cmds: Number of commands in this CCB. */
u32 num_cmds;
/** @cmd_size: Size of each command in this CCB, in bytes. */
u32 cmd_size;
/** @lock: Mutex protecting @ctrl and @ccb. */
struct mutex lock;
/**
* @ctrl: Kernel mapping of CCB control structure. @lock must be held
* when accessing.
*/
struct rogue_fwif_ccb_ctl *ctrl;
/** @ccb: Kernel mapping of CCB. @lock must be held when accessing. */
void *ccb;
};
int pvr_kccb_init(struct pvr_device *pvr_dev);
void pvr_kccb_fini(struct pvr_device *pvr_dev);
int pvr_fwccb_init(struct pvr_device *pvr_dev);
void pvr_ccb_fini(struct pvr_ccb *ccb);
void pvr_fwccb_process(struct pvr_device *pvr_dev);
struct dma_fence *pvr_kccb_fence_alloc(void);
void pvr_kccb_fence_put(struct dma_fence *fence);
struct dma_fence *
pvr_kccb_reserve_slot(struct pvr_device *pvr_dev, struct dma_fence *f);
void pvr_kccb_release_slot(struct pvr_device *pvr_dev);
int pvr_kccb_send_cmd(struct pvr_device *pvr_dev,
struct rogue_fwif_kccb_cmd *cmd, u32 *kccb_slot);
int pvr_kccb_send_cmd_powered(struct pvr_device *pvr_dev,
struct rogue_fwif_kccb_cmd *cmd,
u32 *kccb_slot);
void pvr_kccb_send_cmd_reserved_powered(struct pvr_device *pvr_dev,
struct rogue_fwif_kccb_cmd *cmd,
u32 *kccb_slot);
int pvr_kccb_wait_for_completion(struct pvr_device *pvr_dev, u32 slot_nr, u32 timeout,
u32 *rtn_out);
bool pvr_kccb_is_idle(struct pvr_device *pvr_dev);
void pvr_kccb_wake_up_waiters(struct pvr_device *pvr_dev);
#endif /* PVR_CCB_H */

View file

@ -0,0 +1,267 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_ccb.h"
#include "pvr_cccb.h"
#include "pvr_device.h"
#include "pvr_gem.h"
#include "pvr_hwrt.h"
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/mutex.h>
#include <linux/types.h>
static __always_inline u32
get_ccb_space(u32 w_off, u32 r_off, u32 ccb_size)
{
return (((r_off) - (w_off)) + ((ccb_size) - 1)) & ((ccb_size) - 1);
}
static void
cccb_ctrl_init(void *cpu_ptr, void *priv)
{
struct rogue_fwif_cccb_ctl *ctrl = cpu_ptr;
struct pvr_cccb *pvr_cccb = priv;
WRITE_ONCE(ctrl->write_offset, 0);
WRITE_ONCE(ctrl->read_offset, 0);
WRITE_ONCE(ctrl->dep_offset, 0);
WRITE_ONCE(ctrl->wrap_mask, pvr_cccb->wrap_mask);
}
/**
* pvr_cccb_init() - Initialise a Client CCB
* @pvr_dev: Device pointer.
* @pvr_cccb: Pointer to Client CCB structure to initialise.
* @size_log2: Log2 size of Client CCB in bytes.
* @name: Name of owner of Client CCB. Used for fence context.
*
* Return:
* * Zero on success, or
* * Any error code returned by pvr_fw_object_create_and_map().
*/
int
pvr_cccb_init(struct pvr_device *pvr_dev, struct pvr_cccb *pvr_cccb,
u32 size_log2, const char *name)
{
size_t size = 1 << size_log2;
int err;
pvr_cccb->size = size;
pvr_cccb->write_offset = 0;
pvr_cccb->wrap_mask = size - 1;
/*
* Map CCCB and control structure as uncached, so we don't have to flush
* CPU cache repeatedly when polling for space.
*/
pvr_cccb->ctrl = pvr_fw_object_create_and_map(pvr_dev, sizeof(*pvr_cccb->ctrl),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
cccb_ctrl_init, pvr_cccb,
&pvr_cccb->ctrl_obj);
if (IS_ERR(pvr_cccb->ctrl))
return PTR_ERR(pvr_cccb->ctrl);
pvr_cccb->cccb = pvr_fw_object_create_and_map(pvr_dev, size,
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
NULL, NULL, &pvr_cccb->cccb_obj);
if (IS_ERR(pvr_cccb->cccb)) {
err = PTR_ERR(pvr_cccb->cccb);
goto err_free_ctrl;
}
pvr_fw_object_get_fw_addr(pvr_cccb->ctrl_obj, &pvr_cccb->ctrl_fw_addr);
pvr_fw_object_get_fw_addr(pvr_cccb->cccb_obj, &pvr_cccb->cccb_fw_addr);
return 0;
err_free_ctrl:
pvr_fw_object_unmap_and_destroy(pvr_cccb->ctrl_obj);
return err;
}
/**
* pvr_cccb_fini() - Release Client CCB structure
* @pvr_cccb: Client CCB to release.
*/
void
pvr_cccb_fini(struct pvr_cccb *pvr_cccb)
{
pvr_fw_object_unmap_and_destroy(pvr_cccb->cccb_obj);
pvr_fw_object_unmap_and_destroy(pvr_cccb->ctrl_obj);
}
/**
* pvr_cccb_cmdseq_fits() - Check if a command sequence fits in the CCCB
* @pvr_cccb: Target Client CCB.
* @size: Size of the command sequence.
*
* Check if a command sequence fits in the CCCB we have at hand.
*
* Return:
* * true if the command sequence fits in the CCCB, or
* * false otherwise.
*/
bool pvr_cccb_cmdseq_fits(struct pvr_cccb *pvr_cccb, size_t size)
{
struct rogue_fwif_cccb_ctl *ctrl = pvr_cccb->ctrl;
u32 read_offset, remaining;
bool fits = false;
read_offset = READ_ONCE(ctrl->read_offset);
remaining = pvr_cccb->size - pvr_cccb->write_offset;
/* Always ensure we have enough room for a padding command at the end of the CCCB.
* If our command sequence does not fit, reserve the remaining space for a padding
* command.
*/
if (size + PADDING_COMMAND_SIZE > remaining)
size += remaining;
if (get_ccb_space(pvr_cccb->write_offset, read_offset, pvr_cccb->size) >= size)
fits = true;
return fits;
}
/**
* pvr_cccb_write_command_with_header() - Write a command + command header to a
* Client CCB
* @pvr_cccb: Target Client CCB.
* @cmd_type: Client CCB command type. Must be one of %ROGUE_FWIF_CCB_CMD_TYPE_*.
* @cmd_size: Size of command in bytes.
* @cmd_data: Pointer to command to write.
* @ext_job_ref: External job reference.
* @int_job_ref: Internal job reference.
*
* Caller must make sure there's enough space in CCCB to queue this command. This
* can be done by calling pvr_cccb_cmdseq_fits().
*
* This function is not protected by any lock. The caller must ensure there's
* no concurrent caller, which should be guaranteed by the drm_sched model (job
* submission is serialized in drm_sched_main()).
*/
void
pvr_cccb_write_command_with_header(struct pvr_cccb *pvr_cccb, u32 cmd_type, u32 cmd_size,
void *cmd_data, u32 ext_job_ref, u32 int_job_ref)
{
u32 sz_with_hdr = pvr_cccb_get_size_of_cmd_with_hdr(cmd_size);
struct rogue_fwif_ccb_cmd_header cmd_header = {
.cmd_type = cmd_type,
.cmd_size = ALIGN(cmd_size, 8),
.ext_job_ref = ext_job_ref,
.int_job_ref = int_job_ref,
};
struct rogue_fwif_cccb_ctl *ctrl = pvr_cccb->ctrl;
u32 remaining = pvr_cccb->size - pvr_cccb->write_offset;
u32 required_size, cccb_space, read_offset;
/*
* Always ensure we have enough room for a padding command at the end of
* the CCCB.
*/
if (remaining < sz_with_hdr + PADDING_COMMAND_SIZE) {
/*
* Command would need to wrap, so we need to pad the remainder
* of the CCCB.
*/
required_size = sz_with_hdr + remaining;
} else {
required_size = sz_with_hdr;
}
read_offset = READ_ONCE(ctrl->read_offset);
cccb_space = get_ccb_space(pvr_cccb->write_offset, read_offset, pvr_cccb->size);
if (WARN_ON(cccb_space < required_size))
return;
if (required_size != sz_with_hdr) {
/* Add padding command */
struct rogue_fwif_ccb_cmd_header pad_cmd = {
.cmd_type = ROGUE_FWIF_CCB_CMD_TYPE_PADDING,
.cmd_size = remaining - sizeof(pad_cmd),
};
memcpy(&pvr_cccb->cccb[pvr_cccb->write_offset], &pad_cmd, sizeof(pad_cmd));
pvr_cccb->write_offset = 0;
}
memcpy(&pvr_cccb->cccb[pvr_cccb->write_offset], &cmd_header, sizeof(cmd_header));
memcpy(&pvr_cccb->cccb[pvr_cccb->write_offset + sizeof(cmd_header)], cmd_data, cmd_size);
pvr_cccb->write_offset += sz_with_hdr;
}
static void fill_cmd_kick_data(struct pvr_cccb *cccb, u32 ctx_fw_addr,
struct pvr_hwrt_data *hwrt,
struct rogue_fwif_kccb_cmd_kick_data *k)
{
k->context_fw_addr = ctx_fw_addr;
k->client_woff_update = cccb->write_offset;
k->client_wrap_mask_update = cccb->wrap_mask;
if (hwrt) {
u32 cleanup_state_offset = offsetof(struct rogue_fwif_hwrtdata, cleanup_state);
pvr_fw_object_get_fw_addr_offset(hwrt->fw_obj, cleanup_state_offset,
&k->cleanup_ctl_fw_addr[k->num_cleanup_ctl++]);
}
}
/**
* pvr_cccb_send_kccb_kick: Send KCCB kick to trigger command processing
* @pvr_dev: Device pointer.
* @pvr_cccb: Pointer to CCCB to process.
* @cctx_fw_addr: FW virtual address for context owning this Client CCB.
* @hwrt: HWRT data set associated with this kick. May be %NULL.
*
* You must call pvr_kccb_reserve_slot() and wait for the returned fence to
* signal (if this function didn't return NULL) before calling
* pvr_cccb_send_kccb_kick().
*/
void
pvr_cccb_send_kccb_kick(struct pvr_device *pvr_dev,
struct pvr_cccb *pvr_cccb, u32 cctx_fw_addr,
struct pvr_hwrt_data *hwrt)
{
struct rogue_fwif_kccb_cmd cmd_kick = {
.cmd_type = ROGUE_FWIF_KCCB_CMD_KICK,
};
fill_cmd_kick_data(pvr_cccb, cctx_fw_addr, hwrt, &cmd_kick.cmd_data.cmd_kick_data);
/* Make sure the writes to the CCCB are flushed before sending the KICK. */
wmb();
pvr_kccb_send_cmd_reserved_powered(pvr_dev, &cmd_kick, NULL);
}
void
pvr_cccb_send_kccb_combined_kick(struct pvr_device *pvr_dev,
struct pvr_cccb *geom_cccb,
struct pvr_cccb *frag_cccb,
u32 geom_ctx_fw_addr,
u32 frag_ctx_fw_addr,
struct pvr_hwrt_data *hwrt,
bool frag_is_pr)
{
struct rogue_fwif_kccb_cmd cmd_kick = {
.cmd_type = ROGUE_FWIF_KCCB_CMD_COMBINED_GEOM_FRAG_KICK,
};
fill_cmd_kick_data(geom_cccb, geom_ctx_fw_addr, hwrt,
&cmd_kick.cmd_data.combined_geom_frag_cmd_kick_data.geom_cmd_kick_data);
/* If this is a partial-render job, we don't attach resources to cleanup-ctl array,
* because the resources are already retained by the geometry job.
*/
fill_cmd_kick_data(frag_cccb, frag_ctx_fw_addr, frag_is_pr ? NULL : hwrt,
&cmd_kick.cmd_data.combined_geom_frag_cmd_kick_data.frag_cmd_kick_data);
/* Make sure the writes to the CCCB are flushed before sending the KICK. */
wmb();
pvr_kccb_send_cmd_reserved_powered(pvr_dev, &cmd_kick, NULL);
}

View file

@ -0,0 +1,109 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_CCCB_H
#define PVR_CCCB_H
#include "pvr_rogue_fwif.h"
#include "pvr_rogue_fwif_shared.h"
#include <linux/mutex.h>
#include <linux/types.h>
#define PADDING_COMMAND_SIZE sizeof(struct rogue_fwif_ccb_cmd_header)
/* Forward declaration from pvr_device.h. */
struct pvr_device;
/* Forward declaration from pvr_gem.h. */
struct pvr_fw_object;
/* Forward declaration from pvr_hwrt.h. */
struct pvr_hwrt_data;
struct pvr_cccb {
/** @ctrl_obj: FW object representing CCCB control structure. */
struct pvr_fw_object *ctrl_obj;
/** @ccb_obj: FW object representing CCCB. */
struct pvr_fw_object *cccb_obj;
/**
* @ctrl: Kernel mapping of CCCB control structure. @lock must be held
* when accessing.
*/
struct rogue_fwif_cccb_ctl *ctrl;
/** @cccb: Kernel mapping of CCCB. @lock must be held when accessing.*/
u8 *cccb;
/** @ctrl_fw_addr: FW virtual address of CCCB control structure. */
u32 ctrl_fw_addr;
/** @ccb_fw_addr: FW virtual address of CCCB. */
u32 cccb_fw_addr;
/** @size: Size of CCCB in bytes. */
size_t size;
/** @write_offset: CCCB write offset. */
u32 write_offset;
/** @wrap_mask: CCCB wrap mask. */
u32 wrap_mask;
};
int pvr_cccb_init(struct pvr_device *pvr_dev, struct pvr_cccb *cccb,
u32 size_log2, const char *name);
void pvr_cccb_fini(struct pvr_cccb *cccb);
void pvr_cccb_write_command_with_header(struct pvr_cccb *pvr_cccb,
u32 cmd_type, u32 cmd_size, void *cmd_data,
u32 ext_job_ref, u32 int_job_ref);
void pvr_cccb_send_kccb_kick(struct pvr_device *pvr_dev,
struct pvr_cccb *pvr_cccb, u32 cctx_fw_addr,
struct pvr_hwrt_data *hwrt);
void pvr_cccb_send_kccb_combined_kick(struct pvr_device *pvr_dev,
struct pvr_cccb *geom_cccb,
struct pvr_cccb *frag_cccb,
u32 geom_ctx_fw_addr,
u32 frag_ctx_fw_addr,
struct pvr_hwrt_data *hwrt,
bool frag_is_pr);
bool pvr_cccb_cmdseq_fits(struct pvr_cccb *pvr_cccb, size_t size);
/**
* pvr_cccb_get_size_of_cmd_with_hdr() - Get the size of a command and its header.
* @cmd_size: Command size.
*
* Returns the size of the command and its header.
*/
static __always_inline u32
pvr_cccb_get_size_of_cmd_with_hdr(u32 cmd_size)
{
WARN_ON(!IS_ALIGNED(cmd_size, 8));
return sizeof(struct rogue_fwif_ccb_cmd_header) + ALIGN(cmd_size, 8);
}
/**
* pvr_cccb_cmdseq_can_fit() - Check if a command sequence can fit in the CCCB.
* @size: Command sequence size.
*
* Returns:
* * true it the CCCB is big enough to contain a command sequence, or
* * false otherwise.
*/
static __always_inline bool
pvr_cccb_cmdseq_can_fit(struct pvr_cccb *pvr_cccb, size_t size)
{
/* We divide the capacity by two to simplify our CCCB fencing logic:
* we want to be sure that, no matter what we had queued before, we
* are able to either queue our command sequence at the end or add a
* padding command and queue the command sequence at the beginning
* of the CCCB. If the command sequence size is bigger than half the
* CCCB capacity, we'd have to queue the padding command and make sure
* the FW is done processing it before queueing our command sequence.
*/
return size + PADDING_COMMAND_SIZE <= pvr_cccb->size / 2;
}
#endif /* PVR_CCCB_H */

View file

@ -0,0 +1,464 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_cccb.h"
#include "pvr_context.h"
#include "pvr_device.h"
#include "pvr_drv.h"
#include "pvr_gem.h"
#include "pvr_job.h"
#include "pvr_power.h"
#include "pvr_rogue_fwif.h"
#include "pvr_rogue_fwif_common.h"
#include "pvr_rogue_fwif_resetframework.h"
#include "pvr_stream.h"
#include "pvr_stream_defs.h"
#include "pvr_vm.h"
#include <drm/drm_auth.h>
#include <drm/drm_managed.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/xarray.h>
static int
remap_priority(struct pvr_file *pvr_file, s32 uapi_priority,
enum pvr_context_priority *priority_out)
{
switch (uapi_priority) {
case DRM_PVR_CTX_PRIORITY_LOW:
*priority_out = PVR_CTX_PRIORITY_LOW;
break;
case DRM_PVR_CTX_PRIORITY_NORMAL:
*priority_out = PVR_CTX_PRIORITY_MEDIUM;
break;
case DRM_PVR_CTX_PRIORITY_HIGH:
if (!capable(CAP_SYS_NICE) && !drm_is_current_master(from_pvr_file(pvr_file)))
return -EACCES;
*priority_out = PVR_CTX_PRIORITY_HIGH;
break;
default:
return -EINVAL;
}
return 0;
}
static int get_fw_obj_size(enum drm_pvr_ctx_type type)
{
switch (type) {
case DRM_PVR_CTX_TYPE_RENDER:
return sizeof(struct rogue_fwif_fwrendercontext);
case DRM_PVR_CTX_TYPE_COMPUTE:
return sizeof(struct rogue_fwif_fwcomputecontext);
case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
return sizeof(struct rogue_fwif_fwtransfercontext);
}
return -EINVAL;
}
static int
process_static_context_state(struct pvr_device *pvr_dev, const struct pvr_stream_cmd_defs *cmd_defs,
u64 stream_user_ptr, u32 stream_size, void *dest)
{
void *stream;
int err;
stream = kzalloc(stream_size, GFP_KERNEL);
if (!stream)
return -ENOMEM;
if (copy_from_user(stream, u64_to_user_ptr(stream_user_ptr), stream_size)) {
err = -EFAULT;
goto err_free;
}
err = pvr_stream_process(pvr_dev, cmd_defs, stream, stream_size, dest);
if (err)
goto err_free;
kfree(stream);
return 0;
err_free:
kfree(stream);
return err;
}
static int init_render_fw_objs(struct pvr_context *ctx,
struct drm_pvr_ioctl_create_context_args *args,
void *fw_ctx_map)
{
struct rogue_fwif_static_rendercontext_state *static_rendercontext_state;
struct rogue_fwif_fwrendercontext *fw_render_context = fw_ctx_map;
if (!args->static_context_state_len)
return -EINVAL;
static_rendercontext_state = &fw_render_context->static_render_context_state;
/* Copy static render context state from userspace. */
return process_static_context_state(ctx->pvr_dev,
&pvr_static_render_context_state_stream,
args->static_context_state,
args->static_context_state_len,
&static_rendercontext_state->ctxswitch_regs[0]);
}
static int init_compute_fw_objs(struct pvr_context *ctx,
struct drm_pvr_ioctl_create_context_args *args,
void *fw_ctx_map)
{
struct rogue_fwif_fwcomputecontext *fw_compute_context = fw_ctx_map;
struct rogue_fwif_cdm_registers_cswitch *ctxswitch_regs;
if (!args->static_context_state_len)
return -EINVAL;
ctxswitch_regs = &fw_compute_context->static_compute_context_state.ctxswitch_regs;
/* Copy static render context state from userspace. */
return process_static_context_state(ctx->pvr_dev,
&pvr_static_compute_context_state_stream,
args->static_context_state,
args->static_context_state_len,
ctxswitch_regs);
}
static int init_transfer_fw_objs(struct pvr_context *ctx,
struct drm_pvr_ioctl_create_context_args *args,
void *fw_ctx_map)
{
if (args->static_context_state_len)
return -EINVAL;
return 0;
}
static int init_fw_objs(struct pvr_context *ctx,
struct drm_pvr_ioctl_create_context_args *args,
void *fw_ctx_map)
{
switch (ctx->type) {
case DRM_PVR_CTX_TYPE_RENDER:
return init_render_fw_objs(ctx, args, fw_ctx_map);
case DRM_PVR_CTX_TYPE_COMPUTE:
return init_compute_fw_objs(ctx, args, fw_ctx_map);
case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
return init_transfer_fw_objs(ctx, args, fw_ctx_map);
}
return -EINVAL;
}
static void
ctx_fw_data_init(void *cpu_ptr, void *priv)
{
struct pvr_context *ctx = priv;
memcpy(cpu_ptr, ctx->data, ctx->data_size);
}
/**
* pvr_context_destroy_queues() - Destroy all queues attached to a context.
* @ctx: Context to destroy queues on.
*
* Should be called when the last reference to a context object is dropped.
* It releases all resources attached to the queues bound to this context.
*/
static void pvr_context_destroy_queues(struct pvr_context *ctx)
{
switch (ctx->type) {
case DRM_PVR_CTX_TYPE_RENDER:
pvr_queue_destroy(ctx->queues.fragment);
pvr_queue_destroy(ctx->queues.geometry);
break;
case DRM_PVR_CTX_TYPE_COMPUTE:
pvr_queue_destroy(ctx->queues.compute);
break;
case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
pvr_queue_destroy(ctx->queues.transfer);
break;
}
}
/**
* pvr_context_create_queues() - Create all queues attached to a context.
* @ctx: Context to create queues on.
* @args: Context creation arguments passed by userspace.
* @fw_ctx_map: CPU mapping of the FW context object.
*
* Return:
* * 0 on success, or
* * A negative error code otherwise.
*/
static int pvr_context_create_queues(struct pvr_context *ctx,
struct drm_pvr_ioctl_create_context_args *args,
void *fw_ctx_map)
{
int err;
switch (ctx->type) {
case DRM_PVR_CTX_TYPE_RENDER:
ctx->queues.geometry = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_GEOMETRY,
args, fw_ctx_map);
if (IS_ERR(ctx->queues.geometry)) {
err = PTR_ERR(ctx->queues.geometry);
ctx->queues.geometry = NULL;
goto err_destroy_queues;
}
ctx->queues.fragment = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_FRAGMENT,
args, fw_ctx_map);
if (IS_ERR(ctx->queues.fragment)) {
err = PTR_ERR(ctx->queues.fragment);
ctx->queues.fragment = NULL;
goto err_destroy_queues;
}
return 0;
case DRM_PVR_CTX_TYPE_COMPUTE:
ctx->queues.compute = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_COMPUTE,
args, fw_ctx_map);
if (IS_ERR(ctx->queues.compute)) {
err = PTR_ERR(ctx->queues.compute);
ctx->queues.compute = NULL;
goto err_destroy_queues;
}
return 0;
case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
ctx->queues.transfer = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_TRANSFER_FRAG,
args, fw_ctx_map);
if (IS_ERR(ctx->queues.transfer)) {
err = PTR_ERR(ctx->queues.transfer);
ctx->queues.transfer = NULL;
goto err_destroy_queues;
}
return 0;
}
return -EINVAL;
err_destroy_queues:
pvr_context_destroy_queues(ctx);
return err;
}
/**
* pvr_context_kill_queues() - Kill queues attached to context.
* @ctx: Context to kill queues on.
*
* Killing the queues implies making them unusable for future jobs, while still
* letting the currently submitted jobs a chance to finish. Queue resources will
* stay around until pvr_context_destroy_queues() is called.
*/
static void pvr_context_kill_queues(struct pvr_context *ctx)
{
switch (ctx->type) {
case DRM_PVR_CTX_TYPE_RENDER:
pvr_queue_kill(ctx->queues.fragment);
pvr_queue_kill(ctx->queues.geometry);
break;
case DRM_PVR_CTX_TYPE_COMPUTE:
pvr_queue_kill(ctx->queues.compute);
break;
case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
pvr_queue_kill(ctx->queues.transfer);
break;
}
}
/**
* pvr_context_create() - Create a context.
* @pvr_file: File to attach the created context to.
* @args: Context creation arguments.
*
* Return:
* * 0 on success, or
* * A negative error code on failure.
*/
int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_context_args *args)
{
struct pvr_device *pvr_dev = pvr_file->pvr_dev;
struct pvr_context *ctx;
int ctx_size;
int err;
/* Context creation flags are currently unused and must be zero. */
if (args->flags)
return -EINVAL;
ctx_size = get_fw_obj_size(args->type);
if (ctx_size < 0)
return ctx_size;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->data_size = ctx_size;
ctx->type = args->type;
ctx->flags = args->flags;
ctx->pvr_dev = pvr_dev;
kref_init(&ctx->ref_count);
err = remap_priority(pvr_file, args->priority, &ctx->priority);
if (err)
goto err_free_ctx;
ctx->vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle);
if (IS_ERR(ctx->vm_ctx)) {
err = PTR_ERR(ctx->vm_ctx);
goto err_free_ctx;
}
ctx->data = kzalloc(ctx_size, GFP_KERNEL);
if (!ctx->data) {
err = -ENOMEM;
goto err_put_vm;
}
err = pvr_context_create_queues(ctx, args, ctx->data);
if (err)
goto err_free_ctx_data;
err = init_fw_objs(ctx, args, ctx->data);
if (err)
goto err_destroy_queues;
err = pvr_fw_object_create(pvr_dev, ctx_size, PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
ctx_fw_data_init, ctx, &ctx->fw_obj);
if (err)
goto err_free_ctx_data;
err = xa_alloc(&pvr_dev->ctx_ids, &ctx->ctx_id, ctx, xa_limit_32b, GFP_KERNEL);
if (err)
goto err_destroy_fw_obj;
err = xa_alloc(&pvr_file->ctx_handles, &args->handle, ctx, xa_limit_32b, GFP_KERNEL);
if (err) {
/*
* It's possible that another thread could have taken a reference on the context at
* this point as it is in the ctx_ids xarray. Therefore instead of directly
* destroying the context, drop a reference instead.
*/
pvr_context_put(ctx);
return err;
}
return 0;
err_destroy_fw_obj:
pvr_fw_object_destroy(ctx->fw_obj);
err_destroy_queues:
pvr_context_destroy_queues(ctx);
err_free_ctx_data:
kfree(ctx->data);
err_put_vm:
pvr_vm_context_put(ctx->vm_ctx);
err_free_ctx:
kfree(ctx);
return err;
}
static void
pvr_context_release(struct kref *ref_count)
{
struct pvr_context *ctx =
container_of(ref_count, struct pvr_context, ref_count);
struct pvr_device *pvr_dev = ctx->pvr_dev;
xa_erase(&pvr_dev->ctx_ids, ctx->ctx_id);
pvr_context_destroy_queues(ctx);
pvr_fw_object_destroy(ctx->fw_obj);
kfree(ctx->data);
pvr_vm_context_put(ctx->vm_ctx);
kfree(ctx);
}
/**
* pvr_context_put() - Release reference on context
* @ctx: Target context.
*/
void
pvr_context_put(struct pvr_context *ctx)
{
if (ctx)
kref_put(&ctx->ref_count, pvr_context_release);
}
/**
* pvr_context_destroy() - Destroy context
* @pvr_file: Pointer to pvr_file structure.
* @handle: Userspace context handle.
*
* Removes context from context list and drops initial reference. Context will
* then be destroyed once all outstanding references are dropped.
*
* Return:
* * 0 on success, or
* * -%EINVAL if context not in context list.
*/
int
pvr_context_destroy(struct pvr_file *pvr_file, u32 handle)
{
struct pvr_context *ctx = xa_erase(&pvr_file->ctx_handles, handle);
if (!ctx)
return -EINVAL;
/* Make sure nothing can be queued to the queues after that point. */
pvr_context_kill_queues(ctx);
/* Release the reference held by the handle set. */
pvr_context_put(ctx);
return 0;
}
/**
* pvr_destroy_contexts_for_file: Destroy any contexts associated with the given file
* @pvr_file: Pointer to pvr_file structure.
*
* Removes all contexts associated with @pvr_file from the device context list and drops initial
* references. Contexts will then be destroyed once all outstanding references are dropped.
*/
void pvr_destroy_contexts_for_file(struct pvr_file *pvr_file)
{
struct pvr_context *ctx;
unsigned long handle;
xa_for_each(&pvr_file->ctx_handles, handle, ctx)
pvr_context_destroy(pvr_file, handle);
}
/**
* pvr_context_device_init() - Device level initialization for queue related resources.
* @pvr_dev: The device to initialize.
*/
void pvr_context_device_init(struct pvr_device *pvr_dev)
{
xa_init_flags(&pvr_dev->ctx_ids, XA_FLAGS_ALLOC1);
}
/**
* pvr_context_device_fini() - Device level cleanup for queue related resources.
* @pvr_dev: The device to cleanup.
*/
void pvr_context_device_fini(struct pvr_device *pvr_dev)
{
WARN_ON(!xa_empty(&pvr_dev->ctx_ids));
xa_destroy(&pvr_dev->ctx_ids);
}

View file

@ -0,0 +1,205 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_CONTEXT_H
#define PVR_CONTEXT_H
#include <drm/gpu_scheduler.h>
#include <linux/compiler_attributes.h>
#include <linux/dma-fence.h>
#include <linux/kref.h>
#include <linux/types.h>
#include <linux/xarray.h>
#include <uapi/drm/pvr_drm.h>
#include "pvr_cccb.h"
#include "pvr_device.h"
#include "pvr_queue.h"
/* Forward declaration from pvr_gem.h. */
struct pvr_fw_object;
enum pvr_context_priority {
PVR_CTX_PRIORITY_LOW = 0,
PVR_CTX_PRIORITY_MEDIUM,
PVR_CTX_PRIORITY_HIGH,
};
/**
* struct pvr_context - Context data
*/
struct pvr_context {
/** @ref_count: Refcount for context. */
struct kref ref_count;
/** @pvr_dev: Pointer to owning device. */
struct pvr_device *pvr_dev;
/** @vm_ctx: Pointer to associated VM context. */
struct pvr_vm_context *vm_ctx;
/** @type: Type of context. */
enum drm_pvr_ctx_type type;
/** @flags: Context flags. */
u32 flags;
/** @priority: Context priority*/
enum pvr_context_priority priority;
/** @fw_obj: FW object representing FW-side context data. */
struct pvr_fw_object *fw_obj;
/** @data: Pointer to local copy of FW context data. */
void *data;
/** @data_size: Size of FW context data, in bytes. */
u32 data_size;
/** @ctx_id: FW context ID. */
u32 ctx_id;
/**
* @faulty: Set to 1 when the context queues had unfinished job when
* a GPU reset happened.
*
* In that case, the context is in an inconsistent state and can't be
* used anymore.
*/
atomic_t faulty;
/** @queues: Union containing all kind of queues. */
union {
struct {
/** @geometry: Geometry queue. */
struct pvr_queue *geometry;
/** @fragment: Fragment queue. */
struct pvr_queue *fragment;
};
/** @compute: Compute queue. */
struct pvr_queue *compute;
/** @compute: Transfer queue. */
struct pvr_queue *transfer;
} queues;
};
static __always_inline struct pvr_queue *
pvr_context_get_queue_for_job(struct pvr_context *ctx, enum drm_pvr_job_type type)
{
switch (type) {
case DRM_PVR_JOB_TYPE_GEOMETRY:
return ctx->type == DRM_PVR_CTX_TYPE_RENDER ? ctx->queues.geometry : NULL;
case DRM_PVR_JOB_TYPE_FRAGMENT:
return ctx->type == DRM_PVR_CTX_TYPE_RENDER ? ctx->queues.fragment : NULL;
case DRM_PVR_JOB_TYPE_COMPUTE:
return ctx->type == DRM_PVR_CTX_TYPE_COMPUTE ? ctx->queues.compute : NULL;
case DRM_PVR_JOB_TYPE_TRANSFER_FRAG:
return ctx->type == DRM_PVR_CTX_TYPE_TRANSFER_FRAG ? ctx->queues.transfer : NULL;
}
return NULL;
}
/**
* pvr_context_get() - Take additional reference on context.
* @ctx: Context pointer.
*
* Call pvr_context_put() to release.
*
* Returns:
* * The requested context on success, or
* * %NULL if no context pointer passed.
*/
static __always_inline struct pvr_context *
pvr_context_get(struct pvr_context *ctx)
{
if (ctx)
kref_get(&ctx->ref_count);
return ctx;
}
/**
* pvr_context_lookup() - Lookup context pointer from handle and file.
* @pvr_file: Pointer to pvr_file structure.
* @handle: Context handle.
*
* Takes reference on context. Call pvr_context_put() to release.
*
* Return:
* * The requested context on success, or
* * %NULL on failure (context does not exist, or does not belong to @pvr_file).
*/
static __always_inline struct pvr_context *
pvr_context_lookup(struct pvr_file *pvr_file, u32 handle)
{
struct pvr_context *ctx;
/* Take the array lock to protect against context removal. */
xa_lock(&pvr_file->ctx_handles);
ctx = pvr_context_get(xa_load(&pvr_file->ctx_handles, handle));
xa_unlock(&pvr_file->ctx_handles);
return ctx;
}
/**
* pvr_context_lookup_id() - Lookup context pointer from ID.
* @pvr_dev: Device pointer.
* @id: FW context ID.
*
* Takes reference on context. Call pvr_context_put() to release.
*
* Return:
* * The requested context on success, or
* * %NULL on failure (context does not exist).
*/
static __always_inline struct pvr_context *
pvr_context_lookup_id(struct pvr_device *pvr_dev, u32 id)
{
struct pvr_context *ctx;
/* Take the array lock to protect against context removal. */
xa_lock(&pvr_dev->ctx_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.
*/
ctx = xa_load(&pvr_dev->ctx_ids, id);
if (!kref_get_unless_zero(&ctx->ref_count))
ctx = NULL;
xa_unlock(&pvr_dev->ctx_ids);
return ctx;
}
static __always_inline u32
pvr_context_get_fw_addr(struct pvr_context *ctx)
{
u32 ctx_fw_addr = 0;
pvr_fw_object_get_fw_addr(ctx->fw_obj, &ctx_fw_addr);
return ctx_fw_addr;
}
void pvr_context_put(struct pvr_context *ctx);
int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_context_args *args);
int pvr_context_destroy(struct pvr_file *pvr_file, u32 handle);
void pvr_destroy_contexts_for_file(struct pvr_file *pvr_file);
void pvr_context_device_init(struct pvr_device *pvr_dev);
void pvr_context_device_fini(struct pvr_device *pvr_dev);
#endif /* PVR_CONTEXT_H */

View file

@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_debugfs.h"
#include "pvr_device.h"
#include "pvr_fw_trace.h"
#include "pvr_params.h"
#include <linux/dcache.h>
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <drm/drm_device.h>
#include <drm/drm_file.h>
#include <drm/drm_print.h>
static const struct pvr_debugfs_entry pvr_debugfs_entries[] = {
{"pvr_params", pvr_params_debugfs_init},
{"pvr_fw", pvr_fw_trace_debugfs_init},
};
void
pvr_debugfs_init(struct drm_minor *minor)
{
struct drm_device *drm_dev = minor->dev;
struct pvr_device *pvr_dev = to_pvr_device(drm_dev);
struct dentry *root = minor->debugfs_root;
size_t i;
for (i = 0; i < ARRAY_SIZE(pvr_debugfs_entries); ++i) {
const struct pvr_debugfs_entry *entry = &pvr_debugfs_entries[i];
struct dentry *dir;
dir = debugfs_create_dir(entry->name, root);
if (IS_ERR(dir)) {
drm_warn(drm_dev,
"failed to create debugfs dir '%s' (err=%d)",
entry->name, (int)PTR_ERR(dir));
continue;
}
entry->init(pvr_dev, dir);
}
}
/*
* Since all entries are created under &drm_minor->debugfs_root, there's no
* need for a pvr_debugfs_fini() as DRM will clean up everything under its root
* automatically.
*/

View file

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_DEBUGFS_H
#define PVR_DEBUGFS_H
/* Forward declaration from <drm/drm_drv.h>. */
struct drm_minor;
#if defined(CONFIG_DEBUG_FS)
/* Forward declaration from "pvr_device.h". */
struct pvr_device;
/* Forward declaration from <linux/dcache.h>. */
struct dentry;
struct pvr_debugfs_entry {
const char *name;
void (*init)(struct pvr_device *pvr_dev, struct dentry *dir);
};
void pvr_debugfs_init(struct drm_minor *minor);
#else /* defined(CONFIG_DEBUG_FS) */
#include <linux/compiler_attributes.h>
static __always_inline void pvr_debugfs_init(struct drm_minor *minor) {}
#endif /* defined(CONFIG_DEBUG_FS) */
#endif /* PVR_DEBUGFS_H */

View file

@ -0,0 +1,658 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_device.h"
#include "pvr_device_info.h"
#include "pvr_fw.h"
#include "pvr_params.h"
#include "pvr_power.h"
#include "pvr_queue.h"
#include "pvr_rogue_cr_defs.h"
#include "pvr_stream.h"
#include "pvr_vm.h"
#include <drm/drm_print.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/compiler_attributes.h>
#include <linux/compiler_types.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/firmware.h>
#include <linux/gfp.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/workqueue.h>
/* Major number for the supported version of the firmware. */
#define PVR_FW_VERSION_MAJOR 1
/**
* pvr_device_reg_init() - Initialize kernel access to a PowerVR device's
* control registers.
* @pvr_dev: Target PowerVR device.
*
* Sets struct pvr_device->regs.
*
* This method of mapping the device control registers into memory ensures that
* they are unmapped when the driver is detached (i.e. no explicit cleanup is
* required).
*
* Return:
* * 0 on success, or
* * Any error returned by devm_platform_ioremap_resource().
*/
static int
pvr_device_reg_init(struct pvr_device *pvr_dev)
{
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
struct platform_device *plat_dev = to_platform_device(drm_dev->dev);
struct resource *regs_resource;
void __iomem *regs;
pvr_dev->regs_resource = NULL;
pvr_dev->regs = NULL;
regs = devm_platform_get_and_ioremap_resource(plat_dev, 0, &regs_resource);
if (IS_ERR(regs))
return dev_err_probe(drm_dev->dev, PTR_ERR(regs),
"failed to ioremap gpu registers\n");
pvr_dev->regs = regs;
pvr_dev->regs_resource = regs_resource;
return 0;
}
/**
* pvr_device_clk_init() - Initialize clocks required by a PowerVR device
* @pvr_dev: Target PowerVR device.
*
* Sets struct pvr_device->core_clk, struct pvr_device->sys_clk and
* struct pvr_device->mem_clk.
*
* Three clocks are required by the PowerVR device: core, sys and mem. On
* return, this function guarantees that the clocks are in one of the following
* states:
*
* * All successfully initialized,
* * Core errored, sys and mem uninitialized,
* * Core deinitialized, sys errored, mem uninitialized, or
* * Core and sys deinitialized, mem errored.
*
* Return:
* * 0 on success,
* * Any error returned by devm_clk_get(), or
* * Any error returned by devm_clk_get_optional().
*/
static int pvr_device_clk_init(struct pvr_device *pvr_dev)
{
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
struct clk *core_clk;
struct clk *sys_clk;
struct clk *mem_clk;
core_clk = devm_clk_get(drm_dev->dev, "core");
if (IS_ERR(core_clk))
return dev_err_probe(drm_dev->dev, PTR_ERR(core_clk),
"failed to get core clock\n");
sys_clk = devm_clk_get_optional(drm_dev->dev, "sys");
if (IS_ERR(sys_clk))
return dev_err_probe(drm_dev->dev, PTR_ERR(core_clk),
"failed to get sys clock\n");
mem_clk = devm_clk_get_optional(drm_dev->dev, "mem");
if (IS_ERR(mem_clk))
return dev_err_probe(drm_dev->dev, PTR_ERR(core_clk),
"failed to get mem clock\n");
pvr_dev->core_clk = core_clk;
pvr_dev->sys_clk = sys_clk;
pvr_dev->mem_clk = mem_clk;
return 0;
}
/**
* pvr_device_process_active_queues() - Process all queue related events.
* @pvr_dev: PowerVR device to check
*
* This is called any time we receive a FW event. It iterates over all
* active queues and calls pvr_queue_process() on them.
*/
void pvr_device_process_active_queues(struct pvr_device *pvr_dev)
{
struct pvr_queue *queue, *tmp_queue;
LIST_HEAD(active_queues);
mutex_lock(&pvr_dev->queues.lock);
/* Move all active queues to a temporary list. Queues that remain
* active after we're done processing them are re-inserted to
* the queues.active list by pvr_queue_process().
*/
list_splice_init(&pvr_dev->queues.active, &active_queues);
list_for_each_entry_safe(queue, tmp_queue, &active_queues, node)
pvr_queue_process(queue);
mutex_unlock(&pvr_dev->queues.lock);
}
static irqreturn_t pvr_device_irq_thread_handler(int irq, void *data)
{
struct pvr_device *pvr_dev = data;
irqreturn_t ret = IRQ_NONE;
/* We are in the threaded handler, we can keep dequeuing events until we
* don't see any. This should allow us to reduce the number of interrupts
* when the GPU is receiving a massive amount of short jobs.
*/
while (pvr_fw_irq_pending(pvr_dev)) {
pvr_fw_irq_clear(pvr_dev);
if (pvr_dev->fw_dev.booted) {
pvr_fwccb_process(pvr_dev);
pvr_kccb_wake_up_waiters(pvr_dev);
pvr_device_process_active_queues(pvr_dev);
}
pm_runtime_mark_last_busy(from_pvr_device(pvr_dev)->dev);
ret = IRQ_HANDLED;
}
/* Unmask FW irqs before returning, so new interrupts can be received. */
pvr_fw_irq_enable(pvr_dev);
return ret;
}
static irqreturn_t pvr_device_irq_handler(int irq, void *data)
{
struct pvr_device *pvr_dev = data;
if (!pvr_fw_irq_pending(pvr_dev))
return IRQ_NONE; /* Spurious IRQ - ignore. */
/* Mask the FW interrupts before waking up the thread. Will be unmasked
* when the thread handler is done processing events.
*/
pvr_fw_irq_disable(pvr_dev);
return IRQ_WAKE_THREAD;
}
/**
* pvr_device_irq_init() - Initialise IRQ required by a PowerVR device
* @pvr_dev: Target PowerVR device.
*
* Returns:
* * 0 on success,
* * Any error returned by platform_get_irq_byname(), or
* * Any error returned by request_irq().
*/
static int
pvr_device_irq_init(struct pvr_device *pvr_dev)
{
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
struct platform_device *plat_dev = to_platform_device(drm_dev->dev);
init_waitqueue_head(&pvr_dev->kccb.rtn_q);
pvr_dev->irq = platform_get_irq(plat_dev, 0);
if (pvr_dev->irq < 0)
return pvr_dev->irq;
/* Clear any pending events before requesting the IRQ line. */
pvr_fw_irq_clear(pvr_dev);
pvr_fw_irq_enable(pvr_dev);
return request_threaded_irq(pvr_dev->irq, pvr_device_irq_handler,
pvr_device_irq_thread_handler,
IRQF_SHARED, "gpu", pvr_dev);
}
/**
* pvr_device_irq_fini() - Deinitialise IRQ required by a PowerVR device
* @pvr_dev: Target PowerVR device.
*/
static void
pvr_device_irq_fini(struct pvr_device *pvr_dev)
{
free_irq(pvr_dev->irq, pvr_dev);
}
/**
* pvr_build_firmware_filename() - Construct a PowerVR firmware filename
* @pvr_dev: Target PowerVR device.
* @base: First part of the filename.
* @major: Major version number.
*
* A PowerVR firmware filename consists of three parts separated by underscores
* (``'_'``) along with a '.fw' file suffix. The first part is the exact value
* of @base, the second part is the hardware version string derived from @pvr_fw
* and the final part is the firmware version number constructed from @major with
* a 'v' prefix, e.g. powervr/rogue_4.40.2.51_v1.fw.
*
* The returned string will have been slab allocated and must be freed with
* kfree().
*
* Return:
* * The constructed filename on success, or
* * Any error returned by kasprintf().
*/
static char *
pvr_build_firmware_filename(struct pvr_device *pvr_dev, const char *base,
u8 major)
{
struct pvr_gpu_id *gpu_id = &pvr_dev->gpu_id;
return kasprintf(GFP_KERNEL, "%s_%d.%d.%d.%d_v%d.fw", base, gpu_id->b,
gpu_id->v, gpu_id->n, gpu_id->c, major);
}
static void
pvr_release_firmware(void *data)
{
struct pvr_device *pvr_dev = data;
release_firmware(pvr_dev->fw_dev.firmware);
}
/**
* pvr_request_firmware() - Load firmware for a PowerVR device
* @pvr_dev: Target PowerVR device.
*
* See pvr_build_firmware_filename() for details on firmware file naming.
*
* Return:
* * 0 on success,
* * Any error returned by pvr_build_firmware_filename(), or
* * Any error returned by request_firmware().
*/
static int
pvr_request_firmware(struct pvr_device *pvr_dev)
{
struct drm_device *drm_dev = &pvr_dev->base;
char *filename;
const struct firmware *fw;
int err;
filename = pvr_build_firmware_filename(pvr_dev, "powervr/rogue",
PVR_FW_VERSION_MAJOR);
if (IS_ERR(filename))
return PTR_ERR(filename);
/*
* This function takes a copy of &filename, meaning we can free our
* instance before returning.
*/
err = request_firmware(&fw, filename, pvr_dev->base.dev);
if (err) {
drm_err(drm_dev, "failed to load firmware %s (err=%d)\n",
filename, err);
goto err_free_filename;
}
drm_info(drm_dev, "loaded firmware %s\n", filename);
kfree(filename);
pvr_dev->fw_dev.firmware = fw;
return devm_add_action_or_reset(drm_dev->dev, pvr_release_firmware, pvr_dev);
err_free_filename:
kfree(filename);
return err;
}
/**
* pvr_load_gpu_id() - Load a PowerVR device's GPU ID (BVNC) from control registers.
*
* Sets struct pvr_dev.gpu_id.
*
* @pvr_dev: Target PowerVR device.
*/
static void
pvr_load_gpu_id(struct pvr_device *pvr_dev)
{
struct pvr_gpu_id *gpu_id = &pvr_dev->gpu_id;
u64 bvnc;
/*
* Try reading the BVNC using the newer (cleaner) method first. If the
* B value is zero, fall back to the older method.
*/
bvnc = pvr_cr_read64(pvr_dev, ROGUE_CR_CORE_ID__PBVNC);
gpu_id->b = PVR_CR_FIELD_GET(bvnc, CORE_ID__PBVNC__BRANCH_ID);
if (gpu_id->b != 0) {
gpu_id->v = PVR_CR_FIELD_GET(bvnc, CORE_ID__PBVNC__VERSION_ID);
gpu_id->n = PVR_CR_FIELD_GET(bvnc, CORE_ID__PBVNC__NUMBER_OF_SCALABLE_UNITS);
gpu_id->c = PVR_CR_FIELD_GET(bvnc, CORE_ID__PBVNC__CONFIG_ID);
} else {
u32 core_rev = pvr_cr_read32(pvr_dev, ROGUE_CR_CORE_REVISION);
u32 core_id = pvr_cr_read32(pvr_dev, ROGUE_CR_CORE_ID);
u16 core_id_config = PVR_CR_FIELD_GET(core_id, CORE_ID_CONFIG);
gpu_id->b = PVR_CR_FIELD_GET(core_rev, CORE_REVISION_MAJOR);
gpu_id->v = PVR_CR_FIELD_GET(core_rev, CORE_REVISION_MINOR);
gpu_id->n = FIELD_GET(0xFF00, core_id_config);
gpu_id->c = FIELD_GET(0x00FF, core_id_config);
}
}
/**
* pvr_set_dma_info() - Set PowerVR device DMA information
* @pvr_dev: Target PowerVR device.
*
* Sets the DMA mask and max segment size for the PowerVR device.
*
* Return:
* * 0 on success,
* * Any error returned by PVR_FEATURE_VALUE(), or
* * Any error returned by dma_set_mask().
*/
static int
pvr_set_dma_info(struct pvr_device *pvr_dev)
{
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
u16 phys_bus_width;
int err;
err = PVR_FEATURE_VALUE(pvr_dev, phys_bus_width, &phys_bus_width);
if (err) {
drm_err(drm_dev, "Failed to get device physical bus width\n");
return err;
}
err = dma_set_mask(drm_dev->dev, DMA_BIT_MASK(phys_bus_width));
if (err) {
drm_err(drm_dev, "Failed to set DMA mask (err=%d)\n", err);
return err;
}
dma_set_max_seg_size(drm_dev->dev, UINT_MAX);
return 0;
}
/**
* pvr_device_gpu_init() - GPU-specific initialization for a PowerVR device
* @pvr_dev: Target PowerVR device.
*
* The following steps are taken to ensure the device is ready:
*
* 1. Read the hardware version information from control registers,
* 2. Initialise the hardware feature information,
* 3. Setup the device DMA information,
* 4. Setup the device-scoped memory context, and
* 5. Load firmware into the device.
*
* Return:
* * 0 on success,
* * -%ENODEV if the GPU is not supported,
* * Any error returned by pvr_set_dma_info(),
* * Any error returned by pvr_memory_context_init(), or
* * Any error returned by pvr_request_firmware().
*/
static int
pvr_device_gpu_init(struct pvr_device *pvr_dev)
{
int err;
pvr_load_gpu_id(pvr_dev);
err = pvr_request_firmware(pvr_dev);
if (err)
return err;
err = pvr_fw_validate_init_device_info(pvr_dev);
if (err)
return err;
if (PVR_HAS_FEATURE(pvr_dev, meta))
pvr_dev->fw_dev.processor_type = PVR_FW_PROCESSOR_TYPE_META;
else if (PVR_HAS_FEATURE(pvr_dev, mips))
pvr_dev->fw_dev.processor_type = PVR_FW_PROCESSOR_TYPE_MIPS;
else if (PVR_HAS_FEATURE(pvr_dev, riscv_fw_processor))
pvr_dev->fw_dev.processor_type = PVR_FW_PROCESSOR_TYPE_RISCV;
else
return -EINVAL;
pvr_stream_create_musthave_masks(pvr_dev);
err = pvr_set_dma_info(pvr_dev);
if (err)
return err;
if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) {
pvr_dev->kernel_vm_ctx = pvr_vm_create_context(pvr_dev, false);
if (IS_ERR(pvr_dev->kernel_vm_ctx))
return PTR_ERR(pvr_dev->kernel_vm_ctx);
}
err = pvr_fw_init(pvr_dev);
if (err)
goto err_vm_ctx_put;
return 0;
err_vm_ctx_put:
if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) {
pvr_vm_context_put(pvr_dev->kernel_vm_ctx);
pvr_dev->kernel_vm_ctx = NULL;
}
return err;
}
/**
* pvr_device_gpu_fini() - GPU-specific deinitialization for a PowerVR device
* @pvr_dev: Target PowerVR device.
*/
static void
pvr_device_gpu_fini(struct pvr_device *pvr_dev)
{
pvr_fw_fini(pvr_dev);
if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) {
WARN_ON(!pvr_vm_context_put(pvr_dev->kernel_vm_ctx));
pvr_dev->kernel_vm_ctx = NULL;
}
}
/**
* pvr_device_init() - Initialize a PowerVR device
* @pvr_dev: Target PowerVR device.
*
* If this function returns successfully, the device will have been fully
* initialized. Otherwise, any parts of the device initialized before an error
* occurs will be de-initialized before returning.
*
* NOTE: The initialization steps currently taken are the bare minimum required
* to read from the control registers. The device is unlikely to function
* until further initialization steps are added. [This note should be
* removed when that happens.]
*
* Return:
* * 0 on success,
* * Any error returned by pvr_device_reg_init(),
* * Any error returned by pvr_device_clk_init(), or
* * Any error returned by pvr_device_gpu_init().
*/
int
pvr_device_init(struct pvr_device *pvr_dev)
{
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
struct device *dev = drm_dev->dev;
int err;
/*
* Setup device parameters. We do this first in case other steps
* depend on them.
*/
err = pvr_device_params_init(&pvr_dev->params);
if (err)
return err;
/* Enable and initialize clocks required for the device to operate. */
err = pvr_device_clk_init(pvr_dev);
if (err)
return err;
/* Explicitly power the GPU so we can access control registers before the FW is booted. */
err = pm_runtime_resume_and_get(dev);
if (err)
return err;
/* Map the control registers into memory. */
err = pvr_device_reg_init(pvr_dev);
if (err)
goto err_pm_runtime_put;
/* Perform GPU-specific initialization steps. */
err = pvr_device_gpu_init(pvr_dev);
if (err)
goto err_pm_runtime_put;
err = pvr_device_irq_init(pvr_dev);
if (err)
goto err_device_gpu_fini;
pm_runtime_put(dev);
return 0;
err_device_gpu_fini:
pvr_device_gpu_fini(pvr_dev);
err_pm_runtime_put:
pm_runtime_put_sync_suspend(dev);
return err;
}
/**
* pvr_device_fini() - Deinitialize a PowerVR device
* @pvr_dev: Target PowerVR device.
*/
void
pvr_device_fini(struct pvr_device *pvr_dev)
{
/*
* Deinitialization stages are performed in reverse order compared to
* the initialization stages in pvr_device_init().
*/
pvr_device_irq_fini(pvr_dev);
pvr_device_gpu_fini(pvr_dev);
}
bool
pvr_device_has_uapi_quirk(struct pvr_device *pvr_dev, u32 quirk)
{
switch (quirk) {
case 47217:
return PVR_HAS_QUIRK(pvr_dev, 47217);
case 48545:
return PVR_HAS_QUIRK(pvr_dev, 48545);
case 49927:
return PVR_HAS_QUIRK(pvr_dev, 49927);
case 51764:
return PVR_HAS_QUIRK(pvr_dev, 51764);
case 62269:
return PVR_HAS_QUIRK(pvr_dev, 62269);
default:
return false;
};
}
bool
pvr_device_has_uapi_enhancement(struct pvr_device *pvr_dev, u32 enhancement)
{
switch (enhancement) {
case 35421:
return PVR_HAS_ENHANCEMENT(pvr_dev, 35421);
case 42064:
return PVR_HAS_ENHANCEMENT(pvr_dev, 42064);
default:
return false;
};
}
/**
* pvr_device_has_feature() - Look up device feature based on feature definition
* @pvr_dev: Device pointer.
* @feature: Feature to look up. Should be one of %PVR_FEATURE_*.
*
* Returns:
* * %true if feature is present on device, or
* * %false if feature is not present on device.
*/
bool
pvr_device_has_feature(struct pvr_device *pvr_dev, u32 feature)
{
switch (feature) {
case PVR_FEATURE_CLUSTER_GROUPING:
return PVR_HAS_FEATURE(pvr_dev, cluster_grouping);
case PVR_FEATURE_COMPUTE_MORTON_CAPABLE:
return PVR_HAS_FEATURE(pvr_dev, compute_morton_capable);
case PVR_FEATURE_FB_CDC_V4:
return PVR_HAS_FEATURE(pvr_dev, fb_cdc_v4);
case PVR_FEATURE_GPU_MULTICORE_SUPPORT:
return PVR_HAS_FEATURE(pvr_dev, gpu_multicore_support);
case PVR_FEATURE_ISP_ZLS_D24_S8_PACKING_OGL_MODE:
return PVR_HAS_FEATURE(pvr_dev, isp_zls_d24_s8_packing_ogl_mode);
case PVR_FEATURE_S7_TOP_INFRASTRUCTURE:
return PVR_HAS_FEATURE(pvr_dev, s7_top_infrastructure);
case PVR_FEATURE_TESSELLATION:
return PVR_HAS_FEATURE(pvr_dev, tessellation);
case PVR_FEATURE_TPU_DM_GLOBAL_REGISTERS:
return PVR_HAS_FEATURE(pvr_dev, tpu_dm_global_registers);
case PVR_FEATURE_VDM_DRAWINDIRECT:
return PVR_HAS_FEATURE(pvr_dev, vdm_drawindirect);
case PVR_FEATURE_VDM_OBJECT_LEVEL_LLS:
return PVR_HAS_FEATURE(pvr_dev, vdm_object_level_lls);
case PVR_FEATURE_ZLS_SUBTILE:
return PVR_HAS_FEATURE(pvr_dev, zls_subtile);
/* Derived features. */
case PVR_FEATURE_CDM_USER_MODE_QUEUE: {
u8 cdm_control_stream_format = 0;
PVR_FEATURE_VALUE(pvr_dev, cdm_control_stream_format, &cdm_control_stream_format);
return (cdm_control_stream_format >= 2 && cdm_control_stream_format <= 4);
}
case PVR_FEATURE_REQUIRES_FB_CDC_ZLS_SETUP:
if (PVR_HAS_FEATURE(pvr_dev, fbcdc_algorithm)) {
u8 fbcdc_algorithm = 0;
PVR_FEATURE_VALUE(pvr_dev, fbcdc_algorithm, &fbcdc_algorithm);
return (fbcdc_algorithm < 3 || PVR_HAS_FEATURE(pvr_dev, fb_cdc_v4));
}
return false;
default:
WARN(true, "Looking up undefined feature %u\n", feature);
return false;
}
}

View file

@ -0,0 +1,710 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_DEVICE_H
#define PVR_DEVICE_H
#include "pvr_ccb.h"
#include "pvr_device_info.h"
#include "pvr_fw.h"
#include "pvr_params.h"
#include "pvr_rogue_fwif_stream.h"
#include "pvr_stream.h"
#include <drm/drm_device.h>
#include <drm/drm_file.h>
#include <drm/drm_mm.h>
#include <linux/bits.h>
#include <linux/compiler_attributes.h>
#include <linux/compiler_types.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/math.h>
#include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/xarray.h>
/* Forward declaration from <linux/clk.h>. */
struct clk;
/* Forward declaration from <linux/firmware.h>. */
struct firmware;
/**
* struct pvr_gpu_id - Hardware GPU ID information for a PowerVR device
* @b: Branch ID.
* @v: Version ID.
* @n: Number of scalable units.
* @c: Config ID.
*/
struct pvr_gpu_id {
u16 b, v, n, c;
};
/**
* struct pvr_fw_version - Firmware version information
* @major: Major version number.
* @minor: Minor version number.
*/
struct pvr_fw_version {
u16 major, minor;
};
/**
* struct pvr_device - powervr-specific wrapper for &struct drm_device
*/
struct pvr_device {
/**
* @base: The underlying &struct drm_device.
*
* Do not access this member directly, instead call
* from_pvr_device().
*/
struct drm_device base;
/** @gpu_id: GPU ID detected at runtime. */
struct pvr_gpu_id gpu_id;
/**
* @features: Hardware feature information.
*
* Do not access this member directly, instead use PVR_HAS_FEATURE()
* or PVR_FEATURE_VALUE() macros.
*/
struct pvr_device_features features;
/**
* @quirks: Hardware quirk information.
*
* Do not access this member directly, instead use PVR_HAS_QUIRK().
*/
struct pvr_device_quirks quirks;
/**
* @enhancements: Hardware enhancement information.
*
* Do not access this member directly, instead use
* PVR_HAS_ENHANCEMENT().
*/
struct pvr_device_enhancements enhancements;
/** @fw_version: Firmware version detected at runtime. */
struct pvr_fw_version fw_version;
/** @regs_resource: Resource representing device control registers. */
struct resource *regs_resource;
/**
* @regs: Device control registers.
*
* These are mapped into memory when the device is initialized; that
* location is where this pointer points.
*/
void __iomem *regs;
/**
* @core_clk: General core clock.
*
* This is the primary clock used by the entire GPU core.
*/
struct clk *core_clk;
/**
* @sys_clk: Optional system bus clock.
*
* This may be used on some platforms to provide an independent clock to the SoC Interface
* (SOCIF). If present, this needs to be enabled/disabled together with @core_clk.
*/
struct clk *sys_clk;
/**
* @mem_clk: Optional memory clock.
*
* This may be used on some platforms to provide an independent clock to the Memory
* Interface (MEMIF). If present, this needs to be enabled/disabled together with @core_clk.
*/
struct clk *mem_clk;
/** @irq: IRQ number. */
int irq;
/** @fwccb: Firmware CCB. */
struct pvr_ccb fwccb;
/**
* @kernel_vm_ctx: Virtual memory context used for kernel mappings.
*
* This is used for mappings in the firmware address region when a META firmware processor
* is in use.
*
* When a MIPS firmware processor is in use, this will be %NULL.
*/
struct pvr_vm_context *kernel_vm_ctx;
/** @fw_dev: Firmware related data. */
struct pvr_fw_device fw_dev;
/**
* @params: Device-specific parameters.
*
* The values of these parameters are initialized from the
* defaults specified as module parameters. They may be
* modified at runtime via debugfs (if enabled).
*/
struct pvr_device_params params;
/** @stream_musthave_quirks: Bit array of "must-have" quirks for stream commands. */
u32 stream_musthave_quirks[PVR_STREAM_TYPE_MAX][PVR_STREAM_EXTHDR_TYPE_MAX];
/**
* @mmu_flush_cache_flags: Records which MMU caches require flushing
* before submitting the next job.
*/
atomic_t mmu_flush_cache_flags;
/**
* @ctx_ids: Array of contexts belonging to this device. Array members
* are of type "struct pvr_context *".
*
* This array is used to allocate IDs used by the firmware.
*/
struct xarray ctx_ids;
/**
* @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;
/**
* @job_ids: Array of jobs belonging to this device. Array members
* are of type "struct pvr_job *".
*/
struct xarray job_ids;
/**
* @queues: Queue-related fields.
*/
struct {
/** @active: Active queue list. */
struct list_head active;
/** @idle: Idle queue list. */
struct list_head idle;
/** @lock: Lock protecting access to the active/idle lists. */
struct mutex lock;
} queues;
struct {
/** @work: Work item for watchdog callback. */
struct delayed_work work;
/** @old_kccb_cmds_executed: KCCB command execution count at last watchdog poll. */
u32 old_kccb_cmds_executed;
/** @kccb_stall_count: Number of watchdog polls KCCB has been stalled for. */
u32 kccb_stall_count;
} watchdog;
struct {
/** @ccb: Kernel CCB. */
struct pvr_ccb ccb;
/** @rtn_q: Waitqueue for KCCB command return waiters. */
wait_queue_head_t rtn_q;
/** @rtn_obj: Object representing KCCB return slots. */
struct pvr_fw_object *rtn_obj;
/**
* @rtn: Pointer to CPU mapping of KCCB return slots. Must be accessed by
* READ_ONCE()/WRITE_ONCE().
*/
u32 *rtn;
/** @slot_count: Total number of KCCB slots available. */
u32 slot_count;
/** @reserved_count: Number of KCCB slots reserved for future use. */
u32 reserved_count;
/**
* @waiters: List of KCCB slot waiters.
*/
struct list_head waiters;
/** @fence_ctx: KCCB fence context. */
struct {
/** @id: KCCB fence context ID allocated with dma_fence_context_alloc(). */
u64 id;
/** @seqno: Sequence number incremented each time a fence is created. */
atomic_t seqno;
/**
* @lock: Lock used to synchronize access to fences allocated by this
* context.
*/
spinlock_t lock;
} fence_ctx;
} kccb;
/**
* @lost: %true if the device has been lost.
*
* This variable is set if the device has become irretrievably unavailable, e.g. if the
* firmware processor has stopped responding and can not be revived via a hard reset.
*/
bool lost;
/**
* @reset_sem: Reset semaphore.
*
* GPU reset code will lock this for writing. Any code that submits commands to the firmware
* that isn't in an IRQ handler or on the scheduler workqueue must lock this for reading.
* Once this has been successfully locked, &pvr_dev->lost _must_ be checked, and -%EIO must
* be returned if it is set.
*/
struct rw_semaphore reset_sem;
/** @sched_wq: Workqueue for schedulers. */
struct workqueue_struct *sched_wq;
};
/**
* struct pvr_file - powervr-specific data to be assigned to &struct
* drm_file.driver_priv
*/
struct pvr_file {
/**
* @file: A reference to the parent &struct drm_file.
*
* Do not access this member directly, instead call from_pvr_file().
*/
struct drm_file *file;
/**
* @pvr_dev: A reference to the powervr-specific wrapper for the
* associated device. Saves on repeated calls to
* to_pvr_device().
*/
struct pvr_device *pvr_dev;
/**
* @ctx_handles: Array of contexts belonging to this file. Array members
* are of type "struct pvr_context *".
*
* This array is used to allocate handles returned to userspace.
*/
struct xarray ctx_handles;
/**
* @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 *".
*
* This array is used to allocate handles returned to userspace.
*/
struct xarray vm_ctx_handles;
};
/**
* PVR_HAS_FEATURE() - Tests whether a PowerVR device has a given feature
* @pvr_dev: [IN] Target PowerVR device.
* @feature: [IN] Hardware feature name.
*
* Feature names are derived from those found in &struct pvr_device_features by
* dropping the 'has_' prefix, which is applied by this macro.
*
* Return:
* * true if the named feature is present in the hardware
* * false if the named feature is not present in the hardware
*/
#define PVR_HAS_FEATURE(pvr_dev, feature) ((pvr_dev)->features.has_##feature)
/**
* PVR_FEATURE_VALUE() - Gets a PowerVR device feature value
* @pvr_dev: [IN] Target PowerVR device.
* @feature: [IN] Feature name.
* @value_out: [OUT] Feature value.
*
* This macro will get a feature value for those features that have values.
* If the feature is not present, nothing will be stored to @value_out.
*
* Feature names are derived from those found in &struct pvr_device_features by
* dropping the 'has_' prefix.
*
* Return:
* * 0 on success, or
* * -%EINVAL if the named feature is not present in the hardware
*/
#define PVR_FEATURE_VALUE(pvr_dev, feature, value_out) \
({ \
struct pvr_device *_pvr_dev = pvr_dev; \
int _ret = -EINVAL; \
if (_pvr_dev->features.has_##feature) { \
*(value_out) = _pvr_dev->features.feature; \
_ret = 0; \
} \
_ret; \
})
/**
* PVR_HAS_QUIRK() - Tests whether a physical device has a given quirk
* @pvr_dev: [IN] Target PowerVR device.
* @quirk: [IN] Hardware quirk name.
*
* Quirk numbers are derived from those found in #pvr_device_quirks by
* dropping the 'has_brn' prefix, which is applied by this macro.
*
* Returns
* * true if the quirk is present in the hardware, or
* * false if the quirk is not present in the hardware.
*/
#define PVR_HAS_QUIRK(pvr_dev, quirk) ((pvr_dev)->quirks.has_brn##quirk)
/**
* PVR_HAS_ENHANCEMENT() - Tests whether a physical device has a given
* enhancement
* @pvr_dev: [IN] Target PowerVR device.
* @enhancement: [IN] Hardware enhancement name.
*
* Enhancement numbers are derived from those found in #pvr_device_enhancements
* by dropping the 'has_ern' prefix, which is applied by this macro.
*
* Returns
* * true if the enhancement is present in the hardware, or
* * false if the enhancement is not present in the hardware.
*/
#define PVR_HAS_ENHANCEMENT(pvr_dev, enhancement) ((pvr_dev)->enhancements.has_ern##enhancement)
#define from_pvr_device(pvr_dev) (&(pvr_dev)->base)
#define to_pvr_device(drm_dev) container_of_const(drm_dev, struct pvr_device, base)
#define from_pvr_file(pvr_file) ((pvr_file)->file)
#define to_pvr_file(file) ((file)->driver_priv)
/**
* PVR_PACKED_BVNC() - Packs B, V, N and C values into a 64-bit unsigned integer
* @b: Branch ID.
* @v: Version ID.
* @n: Number of scalable units.
* @c: Config ID.
*
* The packed layout is as follows:
*
* +--------+--------+--------+-------+
* | 63..48 | 47..32 | 31..16 | 15..0 |
* +========+========+========+=======+
* | B | V | N | C |
* +--------+--------+--------+-------+
*
* pvr_gpu_id_to_packed_bvnc() should be used instead of this macro when a
* &struct pvr_gpu_id is available in order to ensure proper type checking.
*
* Return: Packed BVNC.
*/
/* clang-format off */
#define PVR_PACKED_BVNC(b, v, n, c) \
((((u64)(b) & GENMASK_ULL(15, 0)) << 48) | \
(((u64)(v) & GENMASK_ULL(15, 0)) << 32) | \
(((u64)(n) & GENMASK_ULL(15, 0)) << 16) | \
(((u64)(c) & GENMASK_ULL(15, 0)) << 0))
/* clang-format on */
/**
* pvr_gpu_id_to_packed_bvnc() - Packs B, V, N and C values into a 64-bit
* unsigned integer
* @gpu_id: GPU ID.
*
* The packed layout is as follows:
*
* +--------+--------+--------+-------+
* | 63..48 | 47..32 | 31..16 | 15..0 |
* +========+========+========+=======+
* | B | V | N | C |
* +--------+--------+--------+-------+
*
* This should be used in preference to PVR_PACKED_BVNC() when a &struct
* pvr_gpu_id is available in order to ensure proper type checking.
*
* Return: Packed BVNC.
*/
static __always_inline u64
pvr_gpu_id_to_packed_bvnc(struct pvr_gpu_id *gpu_id)
{
return PVR_PACKED_BVNC(gpu_id->b, gpu_id->v, gpu_id->n, gpu_id->c);
}
static __always_inline void
packed_bvnc_to_pvr_gpu_id(u64 bvnc, struct pvr_gpu_id *gpu_id)
{
gpu_id->b = (bvnc & GENMASK_ULL(63, 48)) >> 48;
gpu_id->v = (bvnc & GENMASK_ULL(47, 32)) >> 32;
gpu_id->n = (bvnc & GENMASK_ULL(31, 16)) >> 16;
gpu_id->c = bvnc & GENMASK_ULL(15, 0);
}
int pvr_device_init(struct pvr_device *pvr_dev);
void pvr_device_fini(struct pvr_device *pvr_dev);
void pvr_device_reset(struct pvr_device *pvr_dev);
bool
pvr_device_has_uapi_quirk(struct pvr_device *pvr_dev, u32 quirk);
bool
pvr_device_has_uapi_enhancement(struct pvr_device *pvr_dev, u32 enhancement);
bool
pvr_device_has_feature(struct pvr_device *pvr_dev, u32 feature);
/**
* PVR_CR_FIELD_GET() - Extract a single field from a PowerVR control register
* @val: Value of the target register.
* @field: Field specifier, as defined in "pvr_rogue_cr_defs.h".
*
* Return: The extracted field.
*/
#define PVR_CR_FIELD_GET(val, field) FIELD_GET(~ROGUE_CR_##field##_CLRMSK, val)
/**
* pvr_cr_read32() - Read a 32-bit register from a PowerVR device
* @pvr_dev: Target PowerVR device.
* @reg: Target register.
*
* Return: The value of the requested register.
*/
static __always_inline u32
pvr_cr_read32(struct pvr_device *pvr_dev, u32 reg)
{
return ioread32(pvr_dev->regs + reg);
}
/**
* pvr_cr_read64() - Read a 64-bit register from a PowerVR device
* @pvr_dev: Target PowerVR device.
* @reg: Target register.
*
* Return: The value of the requested register.
*/
static __always_inline u64
pvr_cr_read64(struct pvr_device *pvr_dev, u32 reg)
{
return ioread64(pvr_dev->regs + reg);
}
/**
* pvr_cr_write32() - Write to a 32-bit register in a PowerVR device
* @pvr_dev: Target PowerVR device.
* @reg: Target register.
* @val: Value to write.
*/
static __always_inline void
pvr_cr_write32(struct pvr_device *pvr_dev, u32 reg, u32 val)
{
iowrite32(val, pvr_dev->regs + reg);
}
/**
* pvr_cr_write64() - Write to a 64-bit register in a PowerVR device
* @pvr_dev: Target PowerVR device.
* @reg: Target register.
* @val: Value to write.
*/
static __always_inline void
pvr_cr_write64(struct pvr_device *pvr_dev, u32 reg, u64 val)
{
iowrite64(val, pvr_dev->regs + reg);
}
/**
* pvr_cr_poll_reg32() - Wait for a 32-bit register to match a given value by
* polling
* @pvr_dev: Target PowerVR device.
* @reg_addr: Address of register.
* @reg_value: Expected register value (after masking).
* @reg_mask: Mask of bits valid for comparison with @reg_value.
* @timeout_usec: Timeout length, in us.
*
* Returns:
* * 0 on success, or
* * -%ETIMEDOUT on timeout.
*/
static __always_inline int
pvr_cr_poll_reg32(struct pvr_device *pvr_dev, u32 reg_addr, u32 reg_value,
u32 reg_mask, u64 timeout_usec)
{
u32 value;
return readl_poll_timeout(pvr_dev->regs + reg_addr, value,
(value & reg_mask) == reg_value, 0, timeout_usec);
}
/**
* pvr_cr_poll_reg64() - Wait for a 64-bit register to match a given value by
* polling
* @pvr_dev: Target PowerVR device.
* @reg_addr: Address of register.
* @reg_value: Expected register value (after masking).
* @reg_mask: Mask of bits valid for comparison with @reg_value.
* @timeout_usec: Timeout length, in us.
*
* Returns:
* * 0 on success, or
* * -%ETIMEDOUT on timeout.
*/
static __always_inline int
pvr_cr_poll_reg64(struct pvr_device *pvr_dev, u32 reg_addr, u64 reg_value,
u64 reg_mask, u64 timeout_usec)
{
u64 value;
return readq_poll_timeout(pvr_dev->regs + reg_addr, value,
(value & reg_mask) == reg_value, 0, timeout_usec);
}
/**
* pvr_round_up_to_cacheline_size() - Round up a provided size to be cacheline
* aligned
* @pvr_dev: Target PowerVR device.
* @size: Initial size, in bytes.
*
* Returns:
* * Size aligned to cacheline size.
*/
static __always_inline size_t
pvr_round_up_to_cacheline_size(struct pvr_device *pvr_dev, size_t size)
{
u16 slc_cacheline_size_bits = 0;
u16 slc_cacheline_size_bytes;
WARN_ON(!PVR_HAS_FEATURE(pvr_dev, slc_cache_line_size_bits));
PVR_FEATURE_VALUE(pvr_dev, slc_cache_line_size_bits,
&slc_cacheline_size_bits);
slc_cacheline_size_bytes = slc_cacheline_size_bits / 8;
return round_up(size, slc_cacheline_size_bytes);
}
/**
* DOC: IOCTL validation helpers
*
* To validate the constraints imposed on IOCTL argument structs, a collection
* of macros and helper functions exist in ``pvr_device.h``.
*
* Of the current helpers, it should only be necessary to call
* PVR_IOCTL_UNION_PADDING_CHECK() directly. This macro should be used once in
* every code path which extracts a union member from a struct passed from
* userspace.
*/
/**
* pvr_ioctl_union_padding_check() - Validate that the implicit padding between
* the end of a union member and the end of the union itself is zeroed.
* @instance: Pointer to the instance of the struct to validate.
* @union_offset: Offset into the type of @instance of the target union. Must
* be 64-bit aligned.
* @union_size: Size of the target union in the type of @instance. Must be
* 64-bit aligned.
* @member_size: Size of the target member in the target union specified by
* @union_offset and @union_size. It is assumed that the offset of the target
* member is zero relative to @union_offset. Must be 64-bit aligned.
*
* You probably want to use PVR_IOCTL_UNION_PADDING_CHECK() instead of calling
* this function directly, since that macro abstracts away much of the setup,
* and also provides some static validation. See its docs for details.
*
* Return:
* * %true if every byte between the end of the used member of the union and
* the end of that union is zeroed, or
* * %false otherwise.
*/
static __always_inline bool
pvr_ioctl_union_padding_check(void *instance, size_t union_offset,
size_t union_size, size_t member_size)
{
/*
* void pointer arithmetic is technically illegal - cast to a byte
* pointer so this addition works safely.
*/
void *padding_start = ((u8 *)instance) + union_offset + member_size;
size_t padding_size = union_size - member_size;
return !memchr_inv(padding_start, 0, padding_size);
}
/**
* PVR_STATIC_ASSERT_64BIT_ALIGNED() - Inline assertion for 64-bit alignment.
* @static_expr_: Target expression to evaluate.
*
* If @static_expr_ does not evaluate to a constant integer which would be a
* 64-bit aligned address (i.e. a multiple of 8), compilation will fail.
*
* Return:
* The value of @static_expr_.
*/
#define PVR_STATIC_ASSERT_64BIT_ALIGNED(static_expr_) \
({ \
static_assert(((static_expr_) & (sizeof(u64) - 1)) == 0); \
(static_expr_); \
})
/**
* PVR_IOCTL_UNION_PADDING_CHECK() - Validate that the implicit padding between
* the end of a union member and the end of the union itself is zeroed.
* @struct_instance_: An expression which evaluates to a pointer to a UAPI data
* struct.
* @union_: The name of the union member of @struct_instance_ to check. If the
* union member is nested within the type of @struct_instance_, this may
* contain the member access operator (".").
* @member_: The name of the member of @union_ to assess.
*
* This is a wrapper around pvr_ioctl_union_padding_check() which performs
* alignment checks and simplifies things for the caller.
*
* Return:
* * %true if every byte in @struct_instance_ between the end of @member_ and
* the end of @union_ is zeroed, or
* * %false otherwise.
*/
#define PVR_IOCTL_UNION_PADDING_CHECK(struct_instance_, union_, member_) \
({ \
typeof(struct_instance_) __instance = (struct_instance_); \
size_t __union_offset = PVR_STATIC_ASSERT_64BIT_ALIGNED( \
offsetof(typeof(*__instance), union_)); \
size_t __union_size = PVR_STATIC_ASSERT_64BIT_ALIGNED( \
sizeof(__instance->union_)); \
size_t __member_size = PVR_STATIC_ASSERT_64BIT_ALIGNED( \
sizeof(__instance->union_.member_)); \
pvr_ioctl_union_padding_check(__instance, __union_offset, \
__union_size, __member_size); \
})
#define PVR_FW_PROCESSOR_TYPE_META 0
#define PVR_FW_PROCESSOR_TYPE_MIPS 1
#define PVR_FW_PROCESSOR_TYPE_RISCV 2
#endif /* PVR_DEVICE_H */

View file

@ -0,0 +1,254 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_device.h"
#include "pvr_device_info.h"
#include "pvr_rogue_fwif_dev_info.h"
#include <drm/drm_print.h>
#include <linux/bits.h>
#include <linux/minmax.h>
#include <linux/stddef.h>
#include <linux/types.h>
#define QUIRK_MAPPING(quirk) \
[PVR_FW_HAS_BRN_##quirk] = offsetof(struct pvr_device, quirks.has_brn##quirk)
static const uintptr_t quirks_mapping[] = {
QUIRK_MAPPING(44079),
QUIRK_MAPPING(47217),
QUIRK_MAPPING(48492),
QUIRK_MAPPING(48545),
QUIRK_MAPPING(49927),
QUIRK_MAPPING(50767),
QUIRK_MAPPING(51764),
QUIRK_MAPPING(62269),
QUIRK_MAPPING(63142),
QUIRK_MAPPING(63553),
QUIRK_MAPPING(66011),
QUIRK_MAPPING(71242),
};
#undef QUIRK_MAPPING
#define ENHANCEMENT_MAPPING(enhancement) \
[PVR_FW_HAS_ERN_##enhancement] = offsetof(struct pvr_device, \
enhancements.has_ern##enhancement)
static const uintptr_t enhancements_mapping[] = {
ENHANCEMENT_MAPPING(35421),
ENHANCEMENT_MAPPING(38020),
ENHANCEMENT_MAPPING(38748),
ENHANCEMENT_MAPPING(42064),
ENHANCEMENT_MAPPING(42290),
ENHANCEMENT_MAPPING(42606),
ENHANCEMENT_MAPPING(47025),
ENHANCEMENT_MAPPING(57596),
};
#undef ENHANCEMENT_MAPPING
static void pvr_device_info_set_common(struct pvr_device *pvr_dev, const u64 *bitmask,
u32 bitmask_size, const uintptr_t *mapping, u32 mapping_max)
{
const u32 mapping_max_size = (mapping_max + 63) >> 6;
const u32 nr_bits = min(bitmask_size * 64, mapping_max);
/* Warn if any unsupported values in the bitmask. */
if (bitmask_size > mapping_max_size) {
if (mapping == quirks_mapping)
drm_warn(from_pvr_device(pvr_dev), "Unsupported quirks in firmware image");
else
drm_warn(from_pvr_device(pvr_dev),
"Unsupported enhancements in firmware image");
} else if (bitmask_size == mapping_max_size && (mapping_max & 63)) {
u64 invalid_mask = ~0ull << (mapping_max & 63);
if (bitmask[bitmask_size - 1] & invalid_mask) {
if (mapping == quirks_mapping)
drm_warn(from_pvr_device(pvr_dev),
"Unsupported quirks in firmware image");
else
drm_warn(from_pvr_device(pvr_dev),
"Unsupported enhancements in firmware image");
}
}
for (u32 i = 0; i < nr_bits; i++) {
if (bitmask[i >> 6] & BIT_ULL(i & 63))
*(bool *)((u8 *)pvr_dev + mapping[i]) = true;
}
}
/**
* pvr_device_info_set_quirks() - Set device quirks from device information in firmware
* @pvr_dev: Device pointer.
* @quirks: Pointer to quirks mask in device information.
* @quirks_size: Size of quirks mask, in u64s.
*/
void pvr_device_info_set_quirks(struct pvr_device *pvr_dev, const u64 *quirks, u32 quirks_size)
{
BUILD_BUG_ON(ARRAY_SIZE(quirks_mapping) != PVR_FW_HAS_BRN_MAX);
pvr_device_info_set_common(pvr_dev, quirks, quirks_size, quirks_mapping,
ARRAY_SIZE(quirks_mapping));
}
/**
* pvr_device_info_set_enhancements() - Set device enhancements from device information in firmware
* @pvr_dev: Device pointer.
* @enhancements: Pointer to enhancements mask in device information.
* @enhancements_size: Size of enhancements mask, in u64s.
*/
void pvr_device_info_set_enhancements(struct pvr_device *pvr_dev, const u64 *enhancements,
u32 enhancements_size)
{
BUILD_BUG_ON(ARRAY_SIZE(enhancements_mapping) != PVR_FW_HAS_ERN_MAX);
pvr_device_info_set_common(pvr_dev, enhancements, enhancements_size,
enhancements_mapping, ARRAY_SIZE(enhancements_mapping));
}
#define FEATURE_MAPPING(fw_feature, feature) \
[PVR_FW_HAS_FEATURE_##fw_feature] = { \
.flag_offset = offsetof(struct pvr_device, features.has_##feature), \
.value_offset = 0 \
}
#define FEATURE_MAPPING_VALUE(fw_feature, feature) \
[PVR_FW_HAS_FEATURE_##fw_feature] = { \
.flag_offset = offsetof(struct pvr_device, features.has_##feature), \
.value_offset = offsetof(struct pvr_device, features.feature) \
}
static const struct {
uintptr_t flag_offset;
uintptr_t value_offset;
} features_mapping[] = {
FEATURE_MAPPING(AXI_ACELITE, axi_acelite),
FEATURE_MAPPING_VALUE(CDM_CONTROL_STREAM_FORMAT, cdm_control_stream_format),
FEATURE_MAPPING(CLUSTER_GROUPING, cluster_grouping),
FEATURE_MAPPING_VALUE(COMMON_STORE_SIZE_IN_DWORDS, common_store_size_in_dwords),
FEATURE_MAPPING(COMPUTE, compute),
FEATURE_MAPPING(COMPUTE_MORTON_CAPABLE, compute_morton_capable),
FEATURE_MAPPING(COMPUTE_OVERLAP, compute_overlap),
FEATURE_MAPPING(COREID_PER_OS, coreid_per_os),
FEATURE_MAPPING(DYNAMIC_DUST_POWER, dynamic_dust_power),
FEATURE_MAPPING_VALUE(ECC_RAMS, ecc_rams),
FEATURE_MAPPING_VALUE(FBCDC, fbcdc),
FEATURE_MAPPING_VALUE(FBCDC_ALGORITHM, fbcdc_algorithm),
FEATURE_MAPPING_VALUE(FBCDC_ARCHITECTURE, fbcdc_architecture),
FEATURE_MAPPING_VALUE(FBC_MAX_DEFAULT_DESCRIPTORS, fbc_max_default_descriptors),
FEATURE_MAPPING_VALUE(FBC_MAX_LARGE_DESCRIPTORS, fbc_max_large_descriptors),
FEATURE_MAPPING(FB_CDC_V4, fb_cdc_v4),
FEATURE_MAPPING(GPU_MULTICORE_SUPPORT, gpu_multicore_support),
FEATURE_MAPPING(GPU_VIRTUALISATION, gpu_virtualisation),
FEATURE_MAPPING(GS_RTA_SUPPORT, gs_rta_support),
FEATURE_MAPPING(IRQ_PER_OS, irq_per_os),
FEATURE_MAPPING_VALUE(ISP_MAX_TILES_IN_FLIGHT, isp_max_tiles_in_flight),
FEATURE_MAPPING_VALUE(ISP_SAMPLES_PER_PIXEL, isp_samples_per_pixel),
FEATURE_MAPPING(ISP_ZLS_D24_S8_PACKING_OGL_MODE, isp_zls_d24_s8_packing_ogl_mode),
FEATURE_MAPPING_VALUE(LAYOUT_MARS, layout_mars),
FEATURE_MAPPING_VALUE(MAX_PARTITIONS, max_partitions),
FEATURE_MAPPING_VALUE(META, meta),
FEATURE_MAPPING_VALUE(META_COREMEM_SIZE, meta_coremem_size),
FEATURE_MAPPING(MIPS, mips),
FEATURE_MAPPING_VALUE(NUM_CLUSTERS, num_clusters),
FEATURE_MAPPING_VALUE(NUM_ISP_IPP_PIPES, num_isp_ipp_pipes),
FEATURE_MAPPING_VALUE(NUM_OSIDS, num_osids),
FEATURE_MAPPING_VALUE(NUM_RASTER_PIPES, num_raster_pipes),
FEATURE_MAPPING(PBE2_IN_XE, pbe2_in_xe),
FEATURE_MAPPING(PBVNC_COREID_REG, pbvnc_coreid_reg),
FEATURE_MAPPING(PERFBUS, perfbus),
FEATURE_MAPPING(PERF_COUNTER_BATCH, perf_counter_batch),
FEATURE_MAPPING_VALUE(PHYS_BUS_WIDTH, phys_bus_width),
FEATURE_MAPPING(RISCV_FW_PROCESSOR, riscv_fw_processor),
FEATURE_MAPPING(ROGUEXE, roguexe),
FEATURE_MAPPING(S7_TOP_INFRASTRUCTURE, s7_top_infrastructure),
FEATURE_MAPPING(SIMPLE_INTERNAL_PARAMETER_FORMAT, simple_internal_parameter_format),
FEATURE_MAPPING(SIMPLE_INTERNAL_PARAMETER_FORMAT_V2, simple_internal_parameter_format_v2),
FEATURE_MAPPING_VALUE(SIMPLE_PARAMETER_FORMAT_VERSION, simple_parameter_format_version),
FEATURE_MAPPING_VALUE(SLC_BANKS, slc_banks),
FEATURE_MAPPING_VALUE(SLC_CACHE_LINE_SIZE_BITS, slc_cache_line_size_bits),
FEATURE_MAPPING(SLC_SIZE_CONFIGURABLE, slc_size_configurable),
FEATURE_MAPPING_VALUE(SLC_SIZE_IN_KILOBYTES, slc_size_in_kilobytes),
FEATURE_MAPPING(SOC_TIMER, soc_timer),
FEATURE_MAPPING(SYS_BUS_SECURE_RESET, sys_bus_secure_reset),
FEATURE_MAPPING(TESSELLATION, tessellation),
FEATURE_MAPPING(TILE_REGION_PROTECTION, tile_region_protection),
FEATURE_MAPPING_VALUE(TILE_SIZE_X, tile_size_x),
FEATURE_MAPPING_VALUE(TILE_SIZE_Y, tile_size_y),
FEATURE_MAPPING(TLA, tla),
FEATURE_MAPPING(TPU_CEM_DATAMASTER_GLOBAL_REGISTERS, tpu_cem_datamaster_global_registers),
FEATURE_MAPPING(TPU_DM_GLOBAL_REGISTERS, tpu_dm_global_registers),
FEATURE_MAPPING(TPU_FILTERING_MODE_CONTROL, tpu_filtering_mode_control),
FEATURE_MAPPING_VALUE(USC_MIN_OUTPUT_REGISTERS_PER_PIX, usc_min_output_registers_per_pix),
FEATURE_MAPPING(VDM_DRAWINDIRECT, vdm_drawindirect),
FEATURE_MAPPING(VDM_OBJECT_LEVEL_LLS, vdm_object_level_lls),
FEATURE_MAPPING_VALUE(VIRTUAL_ADDRESS_SPACE_BITS, virtual_address_space_bits),
FEATURE_MAPPING(WATCHDOG_TIMER, watchdog_timer),
FEATURE_MAPPING(WORKGROUP_PROTECTION, workgroup_protection),
FEATURE_MAPPING_VALUE(XE_ARCHITECTURE, xe_architecture),
FEATURE_MAPPING(XE_MEMORY_HIERARCHY, xe_memory_hierarchy),
FEATURE_MAPPING(XE_TPU2, xe_tpu2),
FEATURE_MAPPING_VALUE(XPU_MAX_REGBANKS_ADDR_WIDTH, xpu_max_regbanks_addr_width),
FEATURE_MAPPING_VALUE(XPU_MAX_SLAVES, xpu_max_slaves),
FEATURE_MAPPING_VALUE(XPU_REGISTER_BROADCAST, xpu_register_broadcast),
FEATURE_MAPPING(XT_TOP_INFRASTRUCTURE, xt_top_infrastructure),
FEATURE_MAPPING(ZLS_SUBTILE, zls_subtile),
};
#undef FEATURE_MAPPING_VALUE
#undef FEATURE_MAPPING
/**
* pvr_device_info_set_features() - Set device features from device information in firmware
* @pvr_dev: Device pointer.
* @features: Pointer to features mask in device information.
* @features_size: Size of features mask, in u64s.
* @feature_param_size: Size of feature parameters, in u64s.
*
* Returns:
* * 0 on success, or
* * -%EINVAL on malformed stream.
*/
int pvr_device_info_set_features(struct pvr_device *pvr_dev, const u64 *features, u32 features_size,
u32 feature_param_size)
{
const u32 mapping_max = ARRAY_SIZE(features_mapping);
const u32 mapping_max_size = (mapping_max + 63) >> 6;
const u32 nr_bits = min(features_size * 64, mapping_max);
const u64 *feature_params = features + features_size;
u32 param_idx = 0;
BUILD_BUG_ON(ARRAY_SIZE(features_mapping) != PVR_FW_HAS_FEATURE_MAX);
/* Verify no unsupported values in the bitmask. */
if (features_size > mapping_max_size) {
drm_warn(from_pvr_device(pvr_dev), "Unsupported features in firmware image");
} else if (features_size == mapping_max_size && (mapping_max & 63)) {
u64 invalid_mask = ~0ull << (mapping_max & 63);
if (features[features_size - 1] & invalid_mask)
drm_warn(from_pvr_device(pvr_dev),
"Unsupported features in firmware image");
}
for (u32 i = 0; i < nr_bits; i++) {
if (features[i >> 6] & BIT_ULL(i & 63)) {
*(bool *)((u8 *)pvr_dev + features_mapping[i].flag_offset) = true;
if (features_mapping[i].value_offset) {
if (param_idx >= feature_param_size)
return -EINVAL;
*(u64 *)((u8 *)pvr_dev + features_mapping[i].value_offset) =
feature_params[param_idx];
param_idx++;
}
}
}
return 0;
}

View file

@ -0,0 +1,186 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_DEVICE_INFO_H
#define PVR_DEVICE_INFO_H
#include <linux/types.h>
struct pvr_device;
/*
* struct pvr_device_features - Hardware feature information
*/
struct pvr_device_features {
bool has_axi_acelite;
bool has_cdm_control_stream_format;
bool has_cluster_grouping;
bool has_common_store_size_in_dwords;
bool has_compute;
bool has_compute_morton_capable;
bool has_compute_overlap;
bool has_coreid_per_os;
bool has_dynamic_dust_power;
bool has_ecc_rams;
bool has_fb_cdc_v4;
bool has_fbc_max_default_descriptors;
bool has_fbc_max_large_descriptors;
bool has_fbcdc;
bool has_fbcdc_algorithm;
bool has_fbcdc_architecture;
bool has_gpu_multicore_support;
bool has_gpu_virtualisation;
bool has_gs_rta_support;
bool has_irq_per_os;
bool has_isp_max_tiles_in_flight;
bool has_isp_samples_per_pixel;
bool has_isp_zls_d24_s8_packing_ogl_mode;
bool has_layout_mars;
bool has_max_partitions;
bool has_meta;
bool has_meta_coremem_size;
bool has_mips;
bool has_num_clusters;
bool has_num_isp_ipp_pipes;
bool has_num_osids;
bool has_num_raster_pipes;
bool has_pbe2_in_xe;
bool has_pbvnc_coreid_reg;
bool has_perfbus;
bool has_perf_counter_batch;
bool has_phys_bus_width;
bool has_riscv_fw_processor;
bool has_roguexe;
bool has_s7_top_infrastructure;
bool has_simple_internal_parameter_format;
bool has_simple_internal_parameter_format_v2;
bool has_simple_parameter_format_version;
bool has_slc_banks;
bool has_slc_cache_line_size_bits;
bool has_slc_size_configurable;
bool has_slc_size_in_kilobytes;
bool has_soc_timer;
bool has_sys_bus_secure_reset;
bool has_tessellation;
bool has_tile_region_protection;
bool has_tile_size_x;
bool has_tile_size_y;
bool has_tla;
bool has_tpu_cem_datamaster_global_registers;
bool has_tpu_dm_global_registers;
bool has_tpu_filtering_mode_control;
bool has_usc_min_output_registers_per_pix;
bool has_vdm_drawindirect;
bool has_vdm_object_level_lls;
bool has_virtual_address_space_bits;
bool has_watchdog_timer;
bool has_workgroup_protection;
bool has_xe_architecture;
bool has_xe_memory_hierarchy;
bool has_xe_tpu2;
bool has_xpu_max_regbanks_addr_width;
bool has_xpu_max_slaves;
bool has_xpu_register_broadcast;
bool has_xt_top_infrastructure;
bool has_zls_subtile;
u64 cdm_control_stream_format;
u64 common_store_size_in_dwords;
u64 ecc_rams;
u64 fbc_max_default_descriptors;
u64 fbc_max_large_descriptors;
u64 fbcdc;
u64 fbcdc_algorithm;
u64 fbcdc_architecture;
u64 isp_max_tiles_in_flight;
u64 isp_samples_per_pixel;
u64 layout_mars;
u64 max_partitions;
u64 meta;
u64 meta_coremem_size;
u64 num_clusters;
u64 num_isp_ipp_pipes;
u64 num_osids;
u64 num_raster_pipes;
u64 phys_bus_width;
u64 simple_parameter_format_version;
u64 slc_banks;
u64 slc_cache_line_size_bits;
u64 slc_size_in_kilobytes;
u64 tile_size_x;
u64 tile_size_y;
u64 usc_min_output_registers_per_pix;
u64 virtual_address_space_bits;
u64 xe_architecture;
u64 xpu_max_regbanks_addr_width;
u64 xpu_max_slaves;
u64 xpu_register_broadcast;
};
/*
* struct pvr_device_quirks - Hardware quirk information
*/
struct pvr_device_quirks {
bool has_brn44079;
bool has_brn47217;
bool has_brn48492;
bool has_brn48545;
bool has_brn49927;
bool has_brn50767;
bool has_brn51764;
bool has_brn62269;
bool has_brn63142;
bool has_brn63553;
bool has_brn66011;
bool has_brn71242;
};
/*
* struct pvr_device_enhancements - Hardware enhancement information
*/
struct pvr_device_enhancements {
bool has_ern35421;
bool has_ern38020;
bool has_ern38748;
bool has_ern42064;
bool has_ern42290;
bool has_ern42606;
bool has_ern47025;
bool has_ern57596;
};
void pvr_device_info_set_quirks(struct pvr_device *pvr_dev, const u64 *bitmask,
u32 bitmask_len);
void pvr_device_info_set_enhancements(struct pvr_device *pvr_dev, const u64 *bitmask,
u32 bitmask_len);
int pvr_device_info_set_features(struct pvr_device *pvr_dev, const u64 *features, u32 features_size,
u32 feature_param_size);
/*
* Meta cores
*
* These are the values for the 'meta' feature when the feature is present
* (as per &struct pvr_device_features)/
*/
#define PVR_META_MTP218 (1)
#define PVR_META_MTP219 (2)
#define PVR_META_LTP218 (3)
#define PVR_META_LTP217 (4)
enum {
PVR_FEATURE_CDM_USER_MODE_QUEUE,
PVR_FEATURE_CLUSTER_GROUPING,
PVR_FEATURE_COMPUTE_MORTON_CAPABLE,
PVR_FEATURE_FB_CDC_V4,
PVR_FEATURE_GPU_MULTICORE_SUPPORT,
PVR_FEATURE_ISP_ZLS_D24_S8_PACKING_OGL_MODE,
PVR_FEATURE_REQUIRES_FB_CDC_ZLS_SETUP,
PVR_FEATURE_S7_TOP_INFRASTRUCTURE,
PVR_FEATURE_TESSELLATION,
PVR_FEATURE_TPU_DM_GLOBAL_REGISTERS,
PVR_FEATURE_VDM_DRAWINDIRECT,
PVR_FEATURE_VDM_OBJECT_LEVEL_LLS,
PVR_FEATURE_ZLS_SUBTILE,
};
#endif /* PVR_DEVICE_INFO_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,129 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_DRV_H
#define PVR_DRV_H
#include "linux/compiler_attributes.h"
#include <uapi/drm/pvr_drm.h>
#define PVR_DRIVER_NAME "powervr"
#define PVR_DRIVER_DESC "Imagination PowerVR (Series 6 and later) & IMG Graphics"
#define PVR_DRIVER_DATE "20230904"
/*
* Driver interface version:
* - 1.0: Initial interface
*/
#define PVR_DRIVER_MAJOR 1
#define PVR_DRIVER_MINOR 0
#define PVR_DRIVER_PATCHLEVEL 0
int pvr_get_uobj(u64 usr_ptr, u32 usr_size, u32 min_size, u32 obj_size, void *out);
int pvr_set_uobj(u64 usr_ptr, u32 usr_size, u32 min_size, u32 obj_size, const void *in);
int pvr_get_uobj_array(const struct drm_pvr_obj_array *in, u32 min_stride, u32 obj_size,
void **out);
int pvr_set_uobj_array(const struct drm_pvr_obj_array *out, u32 min_stride, u32 obj_size,
const void *in);
#define PVR_UOBJ_MIN_SIZE_INTERNAL(_typename, _last_mandatory_field) \
(offsetof(_typename, _last_mandatory_field) + \
sizeof(((_typename *)NULL)->_last_mandatory_field))
/* NOLINTBEGIN(bugprone-macro-parentheses) */
#define PVR_UOBJ_DECL(_typename, _last_mandatory_field) \
, _typename : PVR_UOBJ_MIN_SIZE_INTERNAL(_typename, _last_mandatory_field)
/* NOLINTEND(bugprone-macro-parentheses) */
/**
* DOC: PVR user objects.
*
* Macros used to aid copying structured and array data to and from
* userspace. Objects can differ in size, provided the minimum size
* allowed is specified (using the last mandatory field in the struct).
* All types used with PVR_UOBJ_GET/SET macros must be listed here under
* PVR_UOBJ_MIN_SIZE, with the last mandatory struct field specified.
*/
/**
* PVR_UOBJ_MIN_SIZE() - Fetch the minimum copy size of a compatible type object.
* @_obj_name: The name of the object. Cannot be a typename - this is deduced.
*
* This cannot fail. Using the macro with an incompatible type will result in a
* compiler error.
*
* To add compatibility for a type, list it within the macro in an orderly
* fashion. The second argument is the name of the last mandatory field of the
* struct type, which is used to calculate the size. See also PVR_UOBJ_DECL().
*
* Return: The minimum copy size.
*/
#define PVR_UOBJ_MIN_SIZE(_obj_name) _Generic(_obj_name \
PVR_UOBJ_DECL(struct drm_pvr_job, hwrt) \
PVR_UOBJ_DECL(struct drm_pvr_sync_op, value) \
PVR_UOBJ_DECL(struct drm_pvr_dev_query_gpu_info, num_phantoms) \
PVR_UOBJ_DECL(struct drm_pvr_dev_query_runtime_info, cdm_max_local_mem_size_regs) \
PVR_UOBJ_DECL(struct drm_pvr_dev_query_quirks, _padding_c) \
PVR_UOBJ_DECL(struct drm_pvr_dev_query_enhancements, _padding_c) \
PVR_UOBJ_DECL(struct drm_pvr_heap, page_size_log2) \
PVR_UOBJ_DECL(struct drm_pvr_dev_query_heap_info, heaps) \
PVR_UOBJ_DECL(struct drm_pvr_static_data_area, offset) \
PVR_UOBJ_DECL(struct drm_pvr_dev_query_static_data_areas, static_data_areas) \
)
/**
* PVR_UOBJ_GET() - Copies from _src_usr_ptr to &_dest_obj.
* @_dest_obj: The destination container object in kernel space.
* @_usr_size: The size of the source container in user space.
* @_src_usr_ptr: __u64 raw pointer to the source container in user space.
*
* Return: Error code. See pvr_get_uobj().
*/
#define PVR_UOBJ_GET(_dest_obj, _usr_size, _src_usr_ptr) \
pvr_get_uobj(_src_usr_ptr, _usr_size, \
PVR_UOBJ_MIN_SIZE(_dest_obj), \
sizeof(_dest_obj), &(_dest_obj))
/**
* PVR_UOBJ_SET() - Copies from &_src_obj to _dest_usr_ptr.
* @_dest_usr_ptr: __u64 raw pointer to the destination container in user space.
* @_usr_size: The size of the destination container in user space.
* @_src_obj: The source container object in kernel space.
*
* Return: Error code. See pvr_set_uobj().
*/
#define PVR_UOBJ_SET(_dest_usr_ptr, _usr_size, _src_obj) \
pvr_set_uobj(_dest_usr_ptr, _usr_size, \
PVR_UOBJ_MIN_SIZE(_src_obj), \
sizeof(_src_obj), &(_src_obj))
/**
* PVR_UOBJ_GET_ARRAY() - Copies from @_src_drm_pvr_obj_array.array to
* alloced memory and returns a pointer in _dest_array.
* @_dest_array: The destination C array object in kernel space.
* @_src_drm_pvr_obj_array: The &struct drm_pvr_obj_array containing a __u64 raw
* pointer to the source C array in user space and the size of each array
* element in user space (the 'stride').
*
* Return: Error code. See pvr_get_uobj_array().
*/
#define PVR_UOBJ_GET_ARRAY(_dest_array, _src_drm_pvr_obj_array) \
pvr_get_uobj_array(_src_drm_pvr_obj_array, \
PVR_UOBJ_MIN_SIZE((_dest_array)[0]), \
sizeof((_dest_array)[0]), (void **)&(_dest_array))
/**
* PVR_UOBJ_SET_ARRAY() - Copies from _src_array to @_dest_drm_pvr_obj_array.array.
* @_dest_drm_pvr_obj_array: The &struct drm_pvr_obj_array containing a __u64 raw
* pointer to the destination C array in user space and the size of each array
* element in user space (the 'stride').
* @_src_array: The source C array object in kernel space.
*
* Return: Error code. See pvr_set_uobj_array().
*/
#define PVR_UOBJ_SET_ARRAY(_dest_drm_pvr_obj_array, _src_array) \
pvr_set_uobj_array(_dest_drm_pvr_obj_array, \
PVR_UOBJ_MIN_SIZE((_src_array)[0]), \
sizeof((_src_array)[0]), _src_array)
#endif /* PVR_DRV_H */

View file

@ -0,0 +1,625 @@
// 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));
}

View file

@ -0,0 +1,195 @@
/* 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 */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,508 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_FW_H
#define PVR_FW_H
#include "pvr_fw_info.h"
#include "pvr_fw_trace.h"
#include "pvr_gem.h"
#include <drm/drm_mm.h>
#include <linux/types.h>
/* Forward declarations from "pvr_device.h". */
struct pvr_device;
struct pvr_file;
/* Forward declaration from "pvr_vm.h". */
struct pvr_vm_context;
#define ROGUE_FWIF_FWCCB_NUMCMDS_LOG2 5
#define ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT 7
/**
* struct pvr_fw_object - container for firmware memory allocations
*/
struct pvr_fw_object {
/** @ref_count: FW object reference counter. */
struct kref ref_count;
/** @gem: GEM object backing the FW object. */
struct pvr_gem_object *gem;
/**
* @fw_mm_node: Node representing mapping in FW address space. @pvr_obj->lock must
* be held when writing.
*/
struct drm_mm_node fw_mm_node;
/**
* @fw_addr_offset: Virtual address offset of firmware mapping. Only
* valid if @flags has %PVR_GEM_OBJECT_FLAGS_FW_MAPPED
* set.
*/
u32 fw_addr_offset;
/**
* @init: Initialisation callback. Will be called on object creation and FW hard reset.
* Object will have been zeroed before this is called.
*/
void (*init)(void *cpu_ptr, void *priv);
/** @init_priv: Private data for initialisation callback. */
void *init_priv;
/** @node: Node for firmware object list. */
struct list_head node;
};
/**
* struct pvr_fw_defs - FW processor function table and static definitions
*/
struct pvr_fw_defs {
/**
* @init:
*
* FW processor specific initialisation.
* @pvr_dev: Target PowerVR device.
*
* This function must call pvr_fw_heap_calculate() to initialise the firmware heap for this
* FW processor.
*
* This function is mandatory.
*
* Returns:
* * 0 on success, or
* * Any appropriate error on failure.
*/
int (*init)(struct pvr_device *pvr_dev);
/**
* @fini:
*
* FW processor specific finalisation.
* @pvr_dev: Target PowerVR device.
*
* This function is optional.
*/
void (*fini)(struct pvr_device *pvr_dev);
/**
* @fw_process:
*
* Load and process firmware image.
* @pvr_dev: Target PowerVR device.
* @fw: Pointer to firmware image.
* @fw_code_ptr: Pointer to firmware code section.
* @fw_data_ptr: Pointer to firmware data section.
* @fw_core_code_ptr: Pointer to firmware core code section. May be %NULL.
* @fw_core_data_ptr: Pointer to firmware core data section. May be %NULL.
* @core_code_alloc_size: Total allocation size of core code section.
*
* This function is mandatory.
*
* Returns:
* * 0 on success, or
* * Any appropriate error on failure.
*/
int (*fw_process)(struct pvr_device *pvr_dev, const u8 *fw,
u8 *fw_code_ptr, u8 *fw_data_ptr, u8 *fw_core_code_ptr,
u8 *fw_core_data_ptr, u32 core_code_alloc_size);
/**
* @vm_map:
*
* Map FW object into FW processor address space.
* @pvr_dev: Target PowerVR device.
* @fw_obj: FW object to map.
*
* This function is mandatory.
*
* Returns:
* * 0 on success, or
* * Any appropriate error on failure.
*/
int (*vm_map)(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj);
/**
* @vm_unmap:
*
* Unmap FW object from FW processor address space.
* @pvr_dev: Target PowerVR device.
* @fw_obj: FW object to map.
*
* This function is mandatory.
*/
void (*vm_unmap)(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj);
/**
* @get_fw_addr_with_offset:
*
* Called to get address of object in firmware address space, with offset.
* @fw_obj: Pointer to object.
* @offset: Desired offset from start of object.
*
* This function is mandatory.
*
* Returns:
* * Address in firmware address space.
*/
u32 (*get_fw_addr_with_offset)(struct pvr_fw_object *fw_obj, u32 offset);
/**
* @wrapper_init:
*
* Called to initialise FW wrapper.
* @pvr_dev: Target PowerVR device.
*
* This function is mandatory.
*
* Returns:
* * 0 on success.
* * Any appropriate error on failure.
*/
int (*wrapper_init)(struct pvr_device *pvr_dev);
/**
* @has_fixed_data_addr:
*
* Called to check if firmware fixed data must be loaded at the address given by the
* firmware layout table.
*
* This function is mandatory.
*
* Returns:
* * %true if firmware fixed data must be loaded at the address given by the firmware
* layout table.
* * %false otherwise.
*/
bool (*has_fixed_data_addr)(void);
/**
* @irq: FW Interrupt information.
*
* Those are processor dependent, and should be initialized by the
* processor backend in pvr_fw_funcs::init().
*/
struct {
/** @enable_reg: FW interrupt enable register. */
u32 enable_reg;
/** @status_reg: FW interrupt status register. */
u32 status_reg;
/**
* @clear_reg: FW interrupt clear register.
*
* If @status_reg == @clear_reg, we clear by write a bit to zero,
* otherwise we clear by writing a bit to one.
*/
u32 clear_reg;
/** @event_mask: Bitmask of events to listen for. */
u32 event_mask;
/** @clear_mask: Value to write to the clear_reg in order to clear FW IRQs. */
u32 clear_mask;
} irq;
};
/**
* struct pvr_fw_mem - FW memory allocations
*/
struct pvr_fw_mem {
/** @code_obj: Object representing firmware code. */
struct pvr_fw_object *code_obj;
/** @data_obj: Object representing firmware data. */
struct pvr_fw_object *data_obj;
/**
* @core_code_obj: Object representing firmware core code. May be
* %NULL if firmware does not contain this section.
*/
struct pvr_fw_object *core_code_obj;
/**
* @core_data_obj: Object representing firmware core data. May be
* %NULL if firmware does not contain this section.
*/
struct pvr_fw_object *core_data_obj;
/** @code: Driver-side copy of firmware code. */
u8 *code;
/** @data: Driver-side copy of firmware data. */
u8 *data;
/**
* @core_code: Driver-side copy of firmware core code. May be %NULL if firmware does not
* contain this section.
*/
u8 *core_code;
/**
* @core_data: Driver-side copy of firmware core data. May be %NULL if firmware does not
* contain this section.
*/
u8 *core_data;
/** @code_alloc_size: Allocation size of firmware code section. */
u32 code_alloc_size;
/** @data_alloc_size: Allocation size of firmware data section. */
u32 data_alloc_size;
/** @core_code_alloc_size: Allocation size of firmware core code section. */
u32 core_code_alloc_size;
/** @core_data_alloc_size: Allocation size of firmware core data section. */
u32 core_data_alloc_size;
/**
* @fwif_connection_ctl_obj: Object representing FWIF connection control
* structure.
*/
struct pvr_fw_object *fwif_connection_ctl_obj;
/** @osinit_obj: Object representing FW OSINIT structure. */
struct pvr_fw_object *osinit_obj;
/** @sysinit_obj: Object representing FW SYSINIT structure. */
struct pvr_fw_object *sysinit_obj;
/** @osdata_obj: Object representing FW OSDATA structure. */
struct pvr_fw_object *osdata_obj;
/** @hwrinfobuf_obj: Object representing FW hwrinfobuf structure. */
struct pvr_fw_object *hwrinfobuf_obj;
/** @sysdata_obj: Object representing FW SYSDATA structure. */
struct pvr_fw_object *sysdata_obj;
/** @power_sync_obj: Object representing power sync state. */
struct pvr_fw_object *power_sync_obj;
/** @fault_page_obj: Object representing FW fault page. */
struct pvr_fw_object *fault_page_obj;
/** @gpu_util_fwcb_obj: Object representing FW GPU utilisation control structure. */
struct pvr_fw_object *gpu_util_fwcb_obj;
/** @runtime_cfg_obj: Object representing FW runtime config structure. */
struct pvr_fw_object *runtime_cfg_obj;
/** @mmucache_sync_obj: Object used as the sync parameter in an MMU cache operation. */
struct pvr_fw_object *mmucache_sync_obj;
};
struct pvr_fw_device {
/** @firmware: Handle to the firmware loaded into the device. */
const struct firmware *firmware;
/** @header: Pointer to firmware header. */
const struct pvr_fw_info_header *header;
/** @layout_entries: Pointer to firmware layout. */
const struct pvr_fw_layout_entry *layout_entries;
/** @mem: Structure containing objects representing firmware memory allocations. */
struct pvr_fw_mem mem;
/** @booted: %true if the firmware has been booted, %false otherwise. */
bool booted;
/**
* @processor_type: FW processor type for this device. Must be one of
* %PVR_FW_PROCESSOR_TYPE_*.
*/
u16 processor_type;
/** @funcs: Function table for the FW processor used by this device. */
const struct pvr_fw_defs *defs;
/** @processor_data: Pointer to data specific to FW processor. */
union {
/** @mips_data: Pointer to MIPS-specific data. */
struct pvr_fw_mips_data *mips_data;
} processor_data;
/** @fw_heap_info: Firmware heap information. */
struct {
/** @gpu_addr: Base address of firmware heap in GPU address space. */
u64 gpu_addr;
/** @size: Size of main area of heap. */
u32 size;
/** @offset_mask: Mask for offsets within FW heap. */
u32 offset_mask;
/** @raw_size: Raw size of heap, including reserved areas. */
u32 raw_size;
/** @log2_size: Log2 of raw size of heap. */
u32 log2_size;
/** @config_offset: Offset of config area within heap. */
u32 config_offset;
/** @reserved_size: Size of reserved area in heap. */
u32 reserved_size;
} fw_heap_info;
/** @fw_mm: Firmware address space allocator. */
struct drm_mm fw_mm;
/** @fw_mm_lock: Lock protecting access to &fw_mm. */
spinlock_t fw_mm_lock;
/** @fw_mm_base: Base address of address space managed by @fw_mm. */
u64 fw_mm_base;
/**
* @fwif_connection_ctl: Pointer to CPU mapping of FWIF connection
* control structure.
*/
struct rogue_fwif_connection_ctl *fwif_connection_ctl;
/** @fwif_sysinit: Pointer to CPU mapping of FW SYSINIT structure. */
struct rogue_fwif_sysinit *fwif_sysinit;
/** @fwif_sysdata: Pointer to CPU mapping of FW SYSDATA structure. */
struct rogue_fwif_sysdata *fwif_sysdata;
/** @fwif_osinit: Pointer to CPU mapping of FW OSINIT structure. */
struct rogue_fwif_osinit *fwif_osinit;
/** @fwif_osdata: Pointer to CPU mapping of FW OSDATA structure. */
struct rogue_fwif_osdata *fwif_osdata;
/** @power_sync: Pointer to CPU mapping of power sync state. */
u32 *power_sync;
/** @hwrinfobuf: Pointer to CPU mapping of FW HWR info buffer. */
struct rogue_fwif_hwrinfobuf *hwrinfobuf;
/** @fw_trace: Device firmware trace buffer state. */
struct pvr_fw_trace fw_trace;
/** @fw_objs: Structure tracking FW objects. */
struct {
/** @list: Head of FW object list. */
struct list_head list;
/** @lock: Lock protecting access to FW object list. */
struct mutex lock;
} fw_objs;
};
#define pvr_fw_irq_read_reg(pvr_dev, name) \
pvr_cr_read32((pvr_dev), (pvr_dev)->fw_dev.defs->irq.name ## _reg)
#define pvr_fw_irq_write_reg(pvr_dev, name, value) \
pvr_cr_write32((pvr_dev), (pvr_dev)->fw_dev.defs->irq.name ## _reg, value)
#define pvr_fw_irq_pending(pvr_dev) \
(pvr_fw_irq_read_reg(pvr_dev, status) & (pvr_dev)->fw_dev.defs->irq.event_mask)
#define pvr_fw_irq_clear(pvr_dev) \
pvr_fw_irq_write_reg(pvr_dev, clear, (pvr_dev)->fw_dev.defs->irq.clear_mask)
#define pvr_fw_irq_enable(pvr_dev) \
pvr_fw_irq_write_reg(pvr_dev, enable, (pvr_dev)->fw_dev.defs->irq.event_mask)
#define pvr_fw_irq_disable(pvr_dev) \
pvr_fw_irq_write_reg(pvr_dev, enable, 0)
extern const struct pvr_fw_defs pvr_fw_defs_meta;
extern const struct pvr_fw_defs pvr_fw_defs_mips;
int pvr_fw_validate_init_device_info(struct pvr_device *pvr_dev);
int pvr_fw_init(struct pvr_device *pvr_dev);
void pvr_fw_fini(struct pvr_device *pvr_dev);
int pvr_wait_for_fw_boot(struct pvr_device *pvr_dev);
int
pvr_fw_hard_reset(struct pvr_device *pvr_dev);
void pvr_fw_mts_schedule(struct pvr_device *pvr_dev, u32 val);
void
pvr_fw_heap_info_init(struct pvr_device *pvr_dev, u32 log2_size, u32 reserved_size);
const struct pvr_fw_layout_entry *
pvr_fw_find_layout_entry(struct pvr_device *pvr_dev, enum pvr_fw_section_id id);
int
pvr_fw_find_mmu_segment(struct pvr_device *pvr_dev, u32 addr, u32 size, void *fw_code_ptr,
void *fw_data_ptr, void *fw_core_code_ptr, void *fw_core_data_ptr,
void **host_addr_out);
int
pvr_fw_structure_cleanup(struct pvr_device *pvr_dev, u32 type, struct pvr_fw_object *fw_obj,
u32 offset);
int pvr_fw_object_create(struct pvr_device *pvr_dev, size_t size, u64 flags,
void (*init)(void *cpu_ptr, void *priv), void *init_priv,
struct pvr_fw_object **pvr_obj_out);
void *pvr_fw_object_create_and_map(struct pvr_device *pvr_dev, size_t size, u64 flags,
void (*init)(void *cpu_ptr, void *priv),
void *init_priv, struct pvr_fw_object **pvr_obj_out);
void *
pvr_fw_object_create_and_map_offset(struct pvr_device *pvr_dev, u32 dev_offset, size_t size,
u64 flags, void (*init)(void *cpu_ptr, void *priv),
void *init_priv, struct pvr_fw_object **pvr_obj_out);
static __always_inline void *
pvr_fw_object_vmap(struct pvr_fw_object *fw_obj)
{
return pvr_gem_object_vmap(fw_obj->gem);
}
static __always_inline void
pvr_fw_object_vunmap(struct pvr_fw_object *fw_obj)
{
pvr_gem_object_vunmap(fw_obj->gem);
}
void pvr_fw_object_destroy(struct pvr_fw_object *fw_obj);
static __always_inline void
pvr_fw_object_unmap_and_destroy(struct pvr_fw_object *fw_obj)
{
pvr_fw_object_vunmap(fw_obj);
pvr_fw_object_destroy(fw_obj);
}
/**
* pvr_fw_get_dma_addr() - Get DMA address for given offset in firmware object
* @fw_obj: Pointer to object to lookup address in.
* @offset: Offset within object to lookup address at.
* @dma_addr_out: Pointer to location to store DMA address.
*
* Returns:
* * 0 on success, or
* * -%EINVAL if object is not currently backed, or if @offset is out of valid
* range for this object.
*/
static __always_inline int
pvr_fw_object_get_dma_addr(struct pvr_fw_object *fw_obj, u32 offset, dma_addr_t *dma_addr_out)
{
return pvr_gem_get_dma_addr(fw_obj->gem, offset, dma_addr_out);
}
void pvr_fw_object_get_fw_addr_offset(struct pvr_fw_object *fw_obj, u32 offset, u32 *fw_addr_out);
static __always_inline void
pvr_fw_object_get_fw_addr(struct pvr_fw_object *fw_obj, u32 *fw_addr_out)
{
pvr_fw_object_get_fw_addr_offset(fw_obj, 0, fw_addr_out);
}
#endif /* PVR_FW_H */

View file

@ -0,0 +1,135 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_FW_INFO_H
#define PVR_FW_INFO_H
#include <linux/bits.h>
#include <linux/sizes.h>
#include <linux/types.h>
/*
* Firmware binary block unit in bytes.
* Raw data stored in FW binary will be aligned to this size.
*/
#define FW_BLOCK_SIZE SZ_4K
/* Maximum number of entries in firmware layout table. */
#define PVR_FW_INFO_MAX_NUM_ENTRIES 8
enum pvr_fw_section_id {
META_CODE = 0,
META_PRIVATE_DATA,
META_COREMEM_CODE,
META_COREMEM_DATA,
MIPS_CODE,
MIPS_EXCEPTIONS_CODE,
MIPS_BOOT_CODE,
MIPS_PRIVATE_DATA,
MIPS_BOOT_DATA,
MIPS_STACK,
RISCV_UNCACHED_CODE,
RISCV_CACHED_CODE,
RISCV_PRIVATE_DATA,
RISCV_COREMEM_CODE,
RISCV_COREMEM_DATA,
};
enum pvr_fw_section_type {
NONE = 0,
FW_CODE,
FW_DATA,
FW_COREMEM_CODE,
FW_COREMEM_DATA,
};
/*
* FW binary format with FW info attached:
*
* Contents Offset
* +-----------------+
* | | 0
* | |
* | Original binary |
* | file |
* | (.ldr/.elf) |
* | |
* | |
* +-----------------+
* | Device info | FILE_SIZE - 4K - device_info_size
* +-----------------+
* | FW info header | FILE_SIZE - 4K
* +-----------------+
* | |
* | FW layout table |
* | |
* +-----------------+
* FILE_SIZE
*/
#define PVR_FW_INFO_VERSION 3
#define PVR_FW_FLAGS_OPEN_SOURCE BIT(0)
/** struct pvr_fw_info_header - Firmware header */
struct pvr_fw_info_header {
/** @info_version: FW info header version. */
u32 info_version;
/** @header_len: Header length. */
u32 header_len;
/** @layout_entry_num: Number of entries in the layout table. */
u32 layout_entry_num;
/** @layout_entry_size: Size of an entry in the layout table. */
u32 layout_entry_size;
/** @bvnc: GPU ID supported by firmware. */
aligned_u64 bvnc;
/** @fw_page_size: Page size of processor on which firmware executes. */
u32 fw_page_size;
/** @flags: Compatibility flags. */
u32 flags;
/** @fw_version_major: Firmware major version number. */
u16 fw_version_major;
/** @fw_version_minor: Firmware minor version number. */
u16 fw_version_minor;
/** @fw_version_build: Firmware build number. */
u32 fw_version_build;
/** @device_info_size: Size of device info structure. */
u32 device_info_size;
/** @padding: Padding. */
u32 padding;
};
/**
* struct pvr_fw_layout_entry - Entry in firmware layout table, describing a
* section of the firmware image
*/
struct pvr_fw_layout_entry {
/** @id: Section ID. */
enum pvr_fw_section_id id;
/** @type: Section type. */
enum pvr_fw_section_type type;
/** @base_addr: Base address of section in FW address space. */
u32 base_addr;
/** @max_size: Maximum size of section, in bytes. */
u32 max_size;
/** @alloc_size: Allocation size of section, in bytes. */
u32 alloc_size;
/** @alloc_offset: Allocation offset of section. */
u32 alloc_offset;
};
/**
* struct pvr_fw_device_info_header - Device information header.
*/
struct pvr_fw_device_info_header {
/* BRN Mask size (in u64s). */
u64 brn_mask_size;
/* ERN Mask size (in u64s). */
u64 ern_mask_size;
/* Feature Mask size (in u64s). */
u64 feature_mask_size;
/* Feature Parameter size (in u64s). */
u64 feature_param_size;
};
#endif /* PVR_FW_INFO_H */

View file

@ -0,0 +1,554 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_device.h"
#include "pvr_fw.h"
#include "pvr_fw_info.h"
#include "pvr_gem.h"
#include "pvr_rogue_cr_defs.h"
#include "pvr_rogue_meta.h"
#include "pvr_vm.h"
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/ktime.h>
#include <linux/types.h>
#define ROGUE_FW_HEAP_META_SHIFT 25 /* 32 MB */
#define POLL_TIMEOUT_USEC 1000000
/**
* pvr_meta_cr_read32() - Read a META register via the Slave Port
* @pvr_dev: Device pointer.
* @reg_addr: Address of register to read.
* @reg_value_out: Pointer to location to store register value.
*
* Returns:
* * 0 on success, or
* * Any error returned by pvr_cr_poll_reg32().
*/
int
pvr_meta_cr_read32(struct pvr_device *pvr_dev, u32 reg_addr, u32 *reg_value_out)
{
int err;
/* Wait for Slave Port to be Ready. */
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_META_SP_MSLVCTRL1,
ROGUE_CR_META_SP_MSLVCTRL1_READY_EN |
ROGUE_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN,
ROGUE_CR_META_SP_MSLVCTRL1_READY_EN |
ROGUE_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN,
POLL_TIMEOUT_USEC);
if (err)
return err;
/* Issue a Read. */
pvr_cr_write32(pvr_dev, ROGUE_CR_META_SP_MSLVCTRL0,
reg_addr | ROGUE_CR_META_SP_MSLVCTRL0_RD_EN);
(void)pvr_cr_read32(pvr_dev, ROGUE_CR_META_SP_MSLVCTRL0); /* Fence write. */
/* Wait for Slave Port to be Ready. */
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_META_SP_MSLVCTRL1,
ROGUE_CR_META_SP_MSLVCTRL1_READY_EN |
ROGUE_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN,
ROGUE_CR_META_SP_MSLVCTRL1_READY_EN |
ROGUE_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN,
POLL_TIMEOUT_USEC);
if (err)
return err;
*reg_value_out = pvr_cr_read32(pvr_dev, ROGUE_CR_META_SP_MSLVDATAX);
return 0;
}
static int
pvr_meta_wrapper_init(struct pvr_device *pvr_dev)
{
u64 garten_config;
/* Configure META to Master boot. */
pvr_cr_write64(pvr_dev, ROGUE_CR_META_BOOT, ROGUE_CR_META_BOOT_MODE_EN);
/* Set Garten IDLE to META idle and Set the Garten Wrapper BIF Fence address. */
/* Garten IDLE bit controlled by META. */
garten_config = ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_IDLE_CTRL_META;
/* The fence addr is set during the fw init sequence. */
/* Set PC = 0 for fences. */
garten_config &=
ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_PC_BASE_CLRMSK;
garten_config |=
(u64)MMU_CONTEXT_MAPPING_FWPRIV
<< ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_PC_BASE_SHIFT;
/* Set SLC DM=META. */
garten_config |= ((u64)ROGUE_FW_SEGMMU_META_BIFDM_ID)
<< ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_DM_SHIFT;
pvr_cr_write64(pvr_dev, ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG, garten_config);
return 0;
}
static __always_inline void
add_boot_arg(u32 **boot_conf, u32 param, u32 data)
{
*(*boot_conf)++ = param;
*(*boot_conf)++ = data;
}
static int
meta_ldr_cmd_loadmem(struct drm_device *drm_dev, const u8 *fw,
struct rogue_meta_ldr_l1_data_blk *l1_data, u32 coremem_size, u8 *fw_code_ptr,
u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr, const u32 fw_size)
{
struct rogue_meta_ldr_l2_data_blk *l2_block =
(struct rogue_meta_ldr_l2_data_blk *)(fw +
l1_data->cmd_data[1]);
struct pvr_device *pvr_dev = to_pvr_device(drm_dev);
u32 offset = l1_data->cmd_data[0];
u32 data_size;
void *write_addr;
int err;
/* Verify header is within bounds. */
if (((u8 *)l2_block - fw) >= fw_size || ((u8 *)(l2_block + 1) - fw) >= fw_size)
return -EINVAL;
data_size = l2_block->length - 6 /* L2 Tag length and checksum */;
/* Verify data is within bounds. */
if (((u8 *)l2_block->block_data - fw) >= fw_size ||
((((u8 *)l2_block->block_data) + data_size) - fw) >= fw_size)
return -EINVAL;
if (!ROGUE_META_IS_COREMEM_CODE(offset, coremem_size) &&
!ROGUE_META_IS_COREMEM_DATA(offset, coremem_size)) {
/* Global range is aliased to local range */
offset &= ~META_MEM_GLOBAL_RANGE_BIT;
}
err = pvr_fw_find_mmu_segment(pvr_dev, offset, data_size, fw_code_ptr, fw_data_ptr,
fw_core_code_ptr, fw_core_data_ptr, &write_addr);
if (err) {
drm_err(drm_dev,
"Addr 0x%x (size: %d) not found in any firmware segment",
offset, data_size);
return err;
}
memcpy(write_addr, l2_block->block_data, data_size);
return 0;
}
static int
meta_ldr_cmd_zeromem(struct drm_device *drm_dev,
struct rogue_meta_ldr_l1_data_blk *l1_data, u32 coremem_size,
u8 *fw_code_ptr, u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr)
{
struct pvr_device *pvr_dev = to_pvr_device(drm_dev);
u32 offset = l1_data->cmd_data[0];
u32 byte_count = l1_data->cmd_data[1];
void *write_addr;
int err;
if (ROGUE_META_IS_COREMEM_DATA(offset, coremem_size)) {
/* cannot zero coremem directly */
return 0;
}
/* Global range is aliased to local range */
offset &= ~META_MEM_GLOBAL_RANGE_BIT;
err = pvr_fw_find_mmu_segment(pvr_dev, offset, byte_count, fw_code_ptr, fw_data_ptr,
fw_core_code_ptr, fw_core_data_ptr, &write_addr);
if (err) {
drm_err(drm_dev,
"Addr 0x%x (size: %d) not found in any firmware segment",
offset, byte_count);
return err;
}
memset(write_addr, 0, byte_count);
return 0;
}
static int
meta_ldr_cmd_config(struct drm_device *drm_dev, const u8 *fw,
struct rogue_meta_ldr_l1_data_blk *l1_data,
const u32 fw_size, u32 **boot_conf_ptr)
{
struct rogue_meta_ldr_l2_data_blk *l2_block =
(struct rogue_meta_ldr_l2_data_blk *)(fw +
l1_data->cmd_data[0]);
struct rogue_meta_ldr_cfg_blk *config_command;
u32 l2_block_size;
u32 curr_block_size = 0;
u32 *boot_conf = boot_conf_ptr ? *boot_conf_ptr : NULL;
/* Verify block header is within bounds. */
if (((u8 *)l2_block - fw) >= fw_size || ((u8 *)(l2_block + 1) - fw) >= fw_size)
return -EINVAL;
l2_block_size = l2_block->length - 6 /* L2 Tag length and checksum */;
config_command = (struct rogue_meta_ldr_cfg_blk *)l2_block->block_data;
if (((u8 *)config_command - fw) >= fw_size ||
((((u8 *)config_command) + l2_block_size) - fw) >= fw_size)
return -EINVAL;
while (l2_block_size >= 12) {
if (config_command->type != ROGUE_META_LDR_CFG_WRITE)
return -EINVAL;
/*
* Only write to bootloader if we got a valid pointer to the FW
* code allocation.
*/
if (boot_conf) {
u32 register_offset = config_command->block_data[0];
u32 register_value = config_command->block_data[1];
/* Do register write */
add_boot_arg(&boot_conf, register_offset,
register_value);
}
curr_block_size = 12;
l2_block_size -= curr_block_size;
config_command = (struct rogue_meta_ldr_cfg_blk
*)((uintptr_t)config_command +
curr_block_size);
}
if (boot_conf_ptr)
*boot_conf_ptr = boot_conf;
return 0;
}
/**
* process_ldr_command_stream() - Process LDR firmware image and populate
* firmware sections
* @pvr_dev: Device pointer.
* @fw: Pointer to firmware image.
* @fw_code_ptr: Pointer to FW code section.
* @fw_data_ptr: Pointer to FW data section.
* @fw_core_code_ptr: Pointer to FW coremem code section.
* @fw_core_data_ptr: Pointer to FW coremem data section.
* @boot_conf_ptr: Pointer to boot config argument pointer.
*
* Returns :
* * 0 on success, or
* * -EINVAL on any error in LDR command stream.
*/
static int
process_ldr_command_stream(struct pvr_device *pvr_dev, const u8 *fw, u8 *fw_code_ptr,
u8 *fw_data_ptr, u8 *fw_core_code_ptr,
u8 *fw_core_data_ptr, u32 **boot_conf_ptr)
{
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
struct rogue_meta_ldr_block_hdr *ldr_header =
(struct rogue_meta_ldr_block_hdr *)fw;
struct rogue_meta_ldr_l1_data_blk *l1_data =
(struct rogue_meta_ldr_l1_data_blk *)(fw + ldr_header->sl_data);
const u32 fw_size = pvr_dev->fw_dev.firmware->size;
int err;
u32 *boot_conf = boot_conf_ptr ? *boot_conf_ptr : NULL;
u32 coremem_size;
err = PVR_FEATURE_VALUE(pvr_dev, meta_coremem_size, &coremem_size);
if (err)
return err;
coremem_size *= SZ_1K;
while (l1_data) {
/* Verify block header is within bounds. */
if (((u8 *)l1_data - fw) >= fw_size || ((u8 *)(l1_data + 1) - fw) >= fw_size)
return -EINVAL;
if (ROGUE_META_LDR_BLK_IS_COMMENT(l1_data->cmd)) {
/* Don't process comment blocks */
goto next_block;
}
switch (l1_data->cmd & ROGUE_META_LDR_CMD_MASK)
case ROGUE_META_LDR_CMD_LOADMEM: {
err = meta_ldr_cmd_loadmem(drm_dev, fw, l1_data,
coremem_size,
fw_code_ptr, fw_data_ptr,
fw_core_code_ptr,
fw_core_data_ptr, fw_size);
if (err)
return err;
break;
case ROGUE_META_LDR_CMD_START_THREADS:
/* Don't process this block */
break;
case ROGUE_META_LDR_CMD_ZEROMEM:
err = meta_ldr_cmd_zeromem(drm_dev, l1_data,
coremem_size,
fw_code_ptr, fw_data_ptr,
fw_core_code_ptr,
fw_core_data_ptr);
if (err)
return err;
break;
case ROGUE_META_LDR_CMD_CONFIG:
err = meta_ldr_cmd_config(drm_dev, fw, l1_data, fw_size,
&boot_conf);
if (err)
return err;
break;
default:
return -EINVAL;
}
next_block:
if (l1_data->next == 0xFFFFFFFF)
break;
l1_data = (struct rogue_meta_ldr_l1_data_blk *)(fw +
l1_data->next);
}
if (boot_conf_ptr)
*boot_conf_ptr = boot_conf;
return 0;
}
static void
configure_seg_id(u64 seg_out_addr, u32 seg_base, u32 seg_limit, u32 seg_id,
u32 **boot_conf_ptr)
{
u32 seg_out_addr0 = seg_out_addr & 0x00000000FFFFFFFFUL;
u32 seg_out_addr1 = (seg_out_addr >> 32) & 0x00000000FFFFFFFFUL;
u32 *boot_conf = *boot_conf_ptr;
/* META segments have a minimum size. */
u32 limit_off = max(seg_limit, ROGUE_FW_SEGMMU_ALIGN);
/* The limit is an offset, therefore off = size - 1. */
limit_off -= 1;
seg_base |= ROGUE_FW_SEGMMU_ALLTHRS_WRITEABLE;
add_boot_arg(&boot_conf, META_CR_MMCU_SEGMENT_N_BASE(seg_id), seg_base);
add_boot_arg(&boot_conf, META_CR_MMCU_SEGMENT_N_LIMIT(seg_id), limit_off);
add_boot_arg(&boot_conf, META_CR_MMCU_SEGMENT_N_OUTA0(seg_id), seg_out_addr0);
add_boot_arg(&boot_conf, META_CR_MMCU_SEGMENT_N_OUTA1(seg_id), seg_out_addr1);
*boot_conf_ptr = boot_conf;
}
static u64 get_fw_obj_gpu_addr(struct pvr_fw_object *fw_obj)
{
struct pvr_device *pvr_dev = to_pvr_device(gem_from_pvr_gem(fw_obj->gem)->dev);
struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
return fw_obj->fw_addr_offset + fw_dev->fw_heap_info.gpu_addr;
}
static void
configure_seg_mmu(struct pvr_device *pvr_dev, u32 **boot_conf_ptr)
{
const struct pvr_fw_layout_entry *layout_entries = pvr_dev->fw_dev.layout_entries;
u32 num_layout_entries = pvr_dev->fw_dev.header->layout_entry_num;
u64 seg_out_addr_top;
u32 i;
seg_out_addr_top =
ROGUE_FW_SEGMMU_OUTADDR_TOP_SLC(MMU_CONTEXT_MAPPING_FWPRIV,
ROGUE_FW_SEGMMU_META_BIFDM_ID);
for (i = 0; i < num_layout_entries; i++) {
/*
* FW code is using the bootloader segment which is already
* configured on boot. FW coremem code and data don't use the
* segment MMU. Only the FW data segment needs to be configured.
*/
if (layout_entries[i].type == FW_DATA) {
u32 seg_id = ROGUE_FW_SEGMMU_DATA_ID;
u64 seg_out_addr = get_fw_obj_gpu_addr(pvr_dev->fw_dev.mem.data_obj);
seg_out_addr += layout_entries[i].alloc_offset;
seg_out_addr |= seg_out_addr_top;
/* Write the sequence to the bootldr. */
configure_seg_id(seg_out_addr,
layout_entries[i].base_addr,
layout_entries[i].alloc_size, seg_id,
boot_conf_ptr);
break;
}
}
}
static void
configure_meta_caches(u32 **boot_conf_ptr)
{
u32 *boot_conf = *boot_conf_ptr;
u32 d_cache_t0, i_cache_t0;
u32 d_cache_t1, i_cache_t1;
u32 d_cache_t2, i_cache_t2;
u32 d_cache_t3, i_cache_t3;
/* Initialise I/Dcache settings */
d_cache_t0 = META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE;
d_cache_t1 = META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE;
d_cache_t2 = META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE;
d_cache_t3 = META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE;
i_cache_t0 = 0;
i_cache_t1 = 0;
i_cache_t2 = 0;
i_cache_t3 = 0;
d_cache_t0 |= META_CR_SYSC_XCPARTX_LOCAL_ADDR_FULL_CACHE;
i_cache_t0 |= META_CR_SYSC_XCPARTX_LOCAL_ADDR_FULL_CACHE;
/* Local region MMU enhanced bypass: WIN-3 mode for code and data caches */
add_boot_arg(&boot_conf, META_CR_MMCU_LOCAL_EBCTRL,
META_CR_MMCU_LOCAL_EBCTRL_ICWIN |
META_CR_MMCU_LOCAL_EBCTRL_DCWIN);
/* Data cache partitioning thread 0 to 3 */
add_boot_arg(&boot_conf, META_CR_SYSC_DCPART(0), d_cache_t0);
add_boot_arg(&boot_conf, META_CR_SYSC_DCPART(1), d_cache_t1);
add_boot_arg(&boot_conf, META_CR_SYSC_DCPART(2), d_cache_t2);
add_boot_arg(&boot_conf, META_CR_SYSC_DCPART(3), d_cache_t3);
/* Enable data cache hits */
add_boot_arg(&boot_conf, META_CR_MMCU_DCACHE_CTRL,
META_CR_MMCU_XCACHE_CTRL_CACHE_HITS_EN);
/* Instruction cache partitioning thread 0 to 3 */
add_boot_arg(&boot_conf, META_CR_SYSC_ICPART(0), i_cache_t0);
add_boot_arg(&boot_conf, META_CR_SYSC_ICPART(1), i_cache_t1);
add_boot_arg(&boot_conf, META_CR_SYSC_ICPART(2), i_cache_t2);
add_boot_arg(&boot_conf, META_CR_SYSC_ICPART(3), i_cache_t3);
/* Enable instruction cache hits */
add_boot_arg(&boot_conf, META_CR_MMCU_ICACHE_CTRL,
META_CR_MMCU_XCACHE_CTRL_CACHE_HITS_EN);
add_boot_arg(&boot_conf, 0x040000C0, 0);
*boot_conf_ptr = boot_conf;
}
static int
pvr_meta_fw_process(struct pvr_device *pvr_dev, const u8 *fw,
u8 *fw_code_ptr, u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr,
u32 core_code_alloc_size)
{
struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
u32 *boot_conf;
int err;
boot_conf = ((u32 *)fw_code_ptr) + ROGUE_FW_BOOTLDR_CONF_OFFSET;
/* Slave port and JTAG accesses are privileged. */
add_boot_arg(&boot_conf, META_CR_SYSC_JTAG_THREAD,
META_CR_SYSC_JTAG_THREAD_PRIV_EN);
configure_seg_mmu(pvr_dev, &boot_conf);
/* Populate FW sections from LDR image. */
err = process_ldr_command_stream(pvr_dev, fw, fw_code_ptr, fw_data_ptr, fw_core_code_ptr,
fw_core_data_ptr, &boot_conf);
if (err)
return err;
configure_meta_caches(&boot_conf);
/* End argument list. */
add_boot_arg(&boot_conf, 0, 0);
if (fw_dev->mem.core_code_obj) {
u32 core_code_fw_addr;
pvr_fw_object_get_fw_addr(fw_dev->mem.core_code_obj, &core_code_fw_addr);
add_boot_arg(&boot_conf, core_code_fw_addr, core_code_alloc_size);
} else {
add_boot_arg(&boot_conf, 0, 0);
}
/* None of the cores supported by this driver have META DMA. */
add_boot_arg(&boot_conf, 0, 0);
return 0;
}
static int
pvr_meta_init(struct pvr_device *pvr_dev)
{
pvr_fw_heap_info_init(pvr_dev, ROGUE_FW_HEAP_META_SHIFT, 0);
return 0;
}
static u32
pvr_meta_get_fw_addr_with_offset(struct pvr_fw_object *fw_obj, u32 offset)
{
u32 fw_addr = fw_obj->fw_addr_offset + offset + ROGUE_FW_SEGMMU_DATA_BASE_ADDRESS;
/* META cacheability is determined by address. */
if (fw_obj->gem->flags & PVR_BO_FW_FLAGS_DEVICE_UNCACHED)
fw_addr |= ROGUE_FW_SEGMMU_DATA_META_UNCACHED |
ROGUE_FW_SEGMMU_DATA_VIVT_SLC_UNCACHED;
return fw_addr;
}
static int
pvr_meta_vm_map(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj)
{
struct pvr_gem_object *pvr_obj = fw_obj->gem;
return pvr_vm_map(pvr_dev->kernel_vm_ctx, pvr_obj, 0, fw_obj->fw_mm_node.start,
pvr_gem_object_size(pvr_obj));
}
static void
pvr_meta_vm_unmap(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj)
{
pvr_vm_unmap(pvr_dev->kernel_vm_ctx, fw_obj->fw_mm_node.start,
fw_obj->fw_mm_node.size);
}
static bool
pvr_meta_has_fixed_data_addr(void)
{
return false;
}
const struct pvr_fw_defs pvr_fw_defs_meta = {
.init = pvr_meta_init,
.fw_process = pvr_meta_fw_process,
.vm_map = pvr_meta_vm_map,
.vm_unmap = pvr_meta_vm_unmap,
.get_fw_addr_with_offset = pvr_meta_get_fw_addr_with_offset,
.wrapper_init = pvr_meta_wrapper_init,
.has_fixed_data_addr = pvr_meta_has_fixed_data_addr,
.irq = {
.enable_reg = ROGUE_CR_META_SP_MSLVIRQENABLE,
.status_reg = ROGUE_CR_META_SP_MSLVIRQSTATUS,
.clear_reg = ROGUE_CR_META_SP_MSLVIRQSTATUS,
.event_mask = ROGUE_CR_META_SP_MSLVIRQSTATUS_TRIGVECT2_EN,
.clear_mask = ROGUE_CR_META_SP_MSLVIRQSTATUS_TRIGVECT2_CLRMSK,
},
};

View file

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_FW_META_H
#define PVR_FW_META_H
#include <linux/types.h>
/* Forward declaration from pvr_device.h */
struct pvr_device;
int pvr_meta_cr_read32(struct pvr_device *pvr_dev, u32 reg_addr, u32 *reg_value_out);
#endif /* PVR_FW_META_H */

View file

@ -0,0 +1,252 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_device.h"
#include "pvr_fw.h"
#include "pvr_fw_mips.h"
#include "pvr_gem.h"
#include "pvr_rogue_mips.h"
#include "pvr_vm_mips.h"
#include <linux/elf.h>
#include <linux/err.h>
#include <linux/types.h>
#define ROGUE_FW_HEAP_MIPS_BASE 0xC0000000
#define ROGUE_FW_HEAP_MIPS_SHIFT 24 /* 16 MB */
#define ROGUE_FW_HEAP_MIPS_RESERVED_SIZE SZ_1M
/**
* process_elf_command_stream() - Process ELF firmware image and populate
* firmware sections
* @pvr_dev: Device pointer.
* @fw: Pointer to firmware image.
* @fw_code_ptr: Pointer to FW code section.
* @fw_data_ptr: Pointer to FW data section.
* @fw_core_code_ptr: Pointer to FW coremem code section.
* @fw_core_data_ptr: Pointer to FW coremem data section.
*
* Returns :
* * 0 on success, or
* * -EINVAL on any error in ELF command stream.
*/
static int
process_elf_command_stream(struct pvr_device *pvr_dev, const u8 *fw, u8 *fw_code_ptr,
u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr)
{
struct elf32_hdr *header = (struct elf32_hdr *)fw;
struct elf32_phdr *program_header = (struct elf32_phdr *)(fw + header->e_phoff);
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
u32 entry;
int err;
for (entry = 0; entry < header->e_phnum; entry++, program_header++) {
void *write_addr;
/* Only consider loadable entries in the ELF segment table */
if (program_header->p_type != PT_LOAD)
continue;
err = pvr_fw_find_mmu_segment(pvr_dev, program_header->p_vaddr,
program_header->p_memsz, fw_code_ptr, fw_data_ptr,
fw_core_code_ptr, fw_core_data_ptr, &write_addr);
if (err) {
drm_err(drm_dev,
"Addr 0x%x (size: %d) not found in any firmware segment",
program_header->p_vaddr, program_header->p_memsz);
return err;
}
/* Write to FW allocation only if available */
if (write_addr) {
memcpy(write_addr, fw + program_header->p_offset,
program_header->p_filesz);
memset((u8 *)write_addr + program_header->p_filesz, 0,
program_header->p_memsz - program_header->p_filesz);
}
}
return 0;
}
static int
pvr_mips_init(struct pvr_device *pvr_dev)
{
pvr_fw_heap_info_init(pvr_dev, ROGUE_FW_HEAP_MIPS_SHIFT, ROGUE_FW_HEAP_MIPS_RESERVED_SIZE);
return pvr_vm_mips_init(pvr_dev);
}
static void
pvr_mips_fini(struct pvr_device *pvr_dev)
{
pvr_vm_mips_fini(pvr_dev);
}
static int
pvr_mips_fw_process(struct pvr_device *pvr_dev, const u8 *fw,
u8 *fw_code_ptr, u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr,
u32 core_code_alloc_size)
{
struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
struct pvr_fw_mips_data *mips_data = fw_dev->processor_data.mips_data;
const struct pvr_fw_layout_entry *boot_code_entry;
const struct pvr_fw_layout_entry *boot_data_entry;
const struct pvr_fw_layout_entry *exception_code_entry;
const struct pvr_fw_layout_entry *stack_entry;
struct rogue_mipsfw_boot_data *boot_data;
dma_addr_t dma_addr;
u32 page_nr;
int err;
err = process_elf_command_stream(pvr_dev, fw, fw_code_ptr, fw_data_ptr, fw_core_code_ptr,
fw_core_data_ptr);
if (err)
return err;
boot_code_entry = pvr_fw_find_layout_entry(pvr_dev, MIPS_BOOT_CODE);
boot_data_entry = pvr_fw_find_layout_entry(pvr_dev, MIPS_BOOT_DATA);
exception_code_entry = pvr_fw_find_layout_entry(pvr_dev, MIPS_EXCEPTIONS_CODE);
if (!boot_code_entry || !boot_data_entry || !exception_code_entry)
return -EINVAL;
WARN_ON(pvr_gem_get_dma_addr(fw_dev->mem.code_obj->gem, boot_code_entry->alloc_offset,
&mips_data->boot_code_dma_addr));
WARN_ON(pvr_gem_get_dma_addr(fw_dev->mem.data_obj->gem, boot_data_entry->alloc_offset,
&mips_data->boot_data_dma_addr));
WARN_ON(pvr_gem_get_dma_addr(fw_dev->mem.code_obj->gem,
exception_code_entry->alloc_offset,
&mips_data->exception_code_dma_addr));
stack_entry = pvr_fw_find_layout_entry(pvr_dev, MIPS_STACK);
if (!stack_entry)
return -EINVAL;
boot_data = (struct rogue_mipsfw_boot_data *)(fw_data_ptr + boot_data_entry->alloc_offset +
ROGUE_MIPSFW_BOOTLDR_CONF_OFFSET);
WARN_ON(pvr_fw_object_get_dma_addr(fw_dev->mem.data_obj, stack_entry->alloc_offset,
&dma_addr));
boot_data->stack_phys_addr = dma_addr;
boot_data->reg_base = pvr_dev->regs_resource->start;
for (page_nr = 0; page_nr < ARRAY_SIZE(boot_data->pt_phys_addr); page_nr++) {
/* Firmware expects 4k pages, but host page size might be different. */
u32 src_page_nr = (page_nr * ROGUE_MIPSFW_PAGE_SIZE_4K) >> PAGE_SHIFT;
u32 page_offset = (page_nr * ROGUE_MIPSFW_PAGE_SIZE_4K) & ~PAGE_MASK;
boot_data->pt_phys_addr[page_nr] = mips_data->pt_dma_addr[src_page_nr] +
page_offset;
}
boot_data->pt_log2_page_size = ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K;
boot_data->pt_num_pages = ROGUE_MIPSFW_MAX_NUM_PAGETABLE_PAGES;
boot_data->reserved1 = 0;
boot_data->reserved2 = 0;
return 0;
}
static int
pvr_mips_wrapper_init(struct pvr_device *pvr_dev)
{
struct pvr_fw_mips_data *mips_data = pvr_dev->fw_dev.processor_data.mips_data;
const u64 remap_settings = ROGUE_MIPSFW_BOOT_REMAP_LOG2_SEGMENT_SIZE;
u32 phys_bus_width;
int err = PVR_FEATURE_VALUE(pvr_dev, phys_bus_width, &phys_bus_width);
if (WARN_ON(err))
return err;
/* Currently MIPS FW only supported with physical bus width > 32 bits. */
if (WARN_ON(phys_bus_width <= 32))
return -EINVAL;
pvr_cr_write32(pvr_dev, ROGUE_CR_MIPS_WRAPPER_CONFIG,
(ROGUE_MIPSFW_REGISTERS_VIRTUAL_BASE >>
ROGUE_MIPSFW_WRAPPER_CONFIG_REGBANK_ADDR_ALIGN) |
ROGUE_CR_MIPS_WRAPPER_CONFIG_BOOT_ISA_MODE_MICROMIPS);
/* Configure remap for boot code, boot data and exceptions code areas. */
pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG1,
ROGUE_MIPSFW_BOOT_REMAP_PHYS_ADDR_IN |
ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG1_MODE_ENABLE_EN);
pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2,
(mips_data->boot_code_dma_addr &
~ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2_ADDR_OUT_CLRMSK) | remap_settings);
if (PVR_HAS_QUIRK(pvr_dev, 63553)) {
/*
* WA always required on 36 bit cores, to avoid continuous unmapped memory accesses
* to address 0x0.
*/
WARN_ON(phys_bus_width != 36);
pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG1,
ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG1_MODE_ENABLE_EN);
pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2,
(mips_data->boot_code_dma_addr &
~ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2_ADDR_OUT_CLRMSK) |
remap_settings);
}
pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG1,
ROGUE_MIPSFW_DATA_REMAP_PHYS_ADDR_IN |
ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG1_MODE_ENABLE_EN);
pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2,
(mips_data->boot_data_dma_addr &
~ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2_ADDR_OUT_CLRMSK) | remap_settings);
pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG1,
ROGUE_MIPSFW_CODE_REMAP_PHYS_ADDR_IN |
ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG1_MODE_ENABLE_EN);
pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2,
(mips_data->exception_code_dma_addr &
~ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2_ADDR_OUT_CLRMSK) | remap_settings);
/* Garten IDLE bit controlled by MIPS. */
pvr_cr_write64(pvr_dev, ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG,
ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_IDLE_CTRL_META);
/* Turn on the EJTAG probe. */
pvr_cr_write32(pvr_dev, ROGUE_CR_MIPS_DEBUG_CONFIG, 0);
return 0;
}
static u32
pvr_mips_get_fw_addr_with_offset(struct pvr_fw_object *fw_obj, u32 offset)
{
struct pvr_device *pvr_dev = to_pvr_device(gem_from_pvr_gem(fw_obj->gem)->dev);
/* MIPS cacheability is determined by page table. */
return ((fw_obj->fw_addr_offset + offset) & pvr_dev->fw_dev.fw_heap_info.offset_mask) |
ROGUE_FW_HEAP_MIPS_BASE;
}
static bool
pvr_mips_has_fixed_data_addr(void)
{
return true;
}
const struct pvr_fw_defs pvr_fw_defs_mips = {
.init = pvr_mips_init,
.fini = pvr_mips_fini,
.fw_process = pvr_mips_fw_process,
.vm_map = pvr_vm_mips_map,
.vm_unmap = pvr_vm_mips_unmap,
.get_fw_addr_with_offset = pvr_mips_get_fw_addr_with_offset,
.wrapper_init = pvr_mips_wrapper_init,
.has_fixed_data_addr = pvr_mips_has_fixed_data_addr,
.irq = {
.enable_reg = ROGUE_CR_MIPS_WRAPPER_IRQ_ENABLE,
.status_reg = ROGUE_CR_MIPS_WRAPPER_IRQ_STATUS,
.clear_reg = ROGUE_CR_MIPS_WRAPPER_IRQ_CLEAR,
.event_mask = ROGUE_CR_MIPS_WRAPPER_IRQ_STATUS_EVENT_EN,
.clear_mask = ROGUE_CR_MIPS_WRAPPER_IRQ_CLEAR_EVENT_EN,
},
};

View file

@ -0,0 +1,48 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_FW_MIPS_H
#define PVR_FW_MIPS_H
#include "pvr_rogue_mips.h"
#include <asm/page.h>
#include <linux/types.h>
/* Forward declaration from pvr_gem.h. */
struct pvr_gem_object;
#define PVR_MIPS_PT_PAGE_COUNT ((ROGUE_MIPSFW_MAX_NUM_PAGETABLE_PAGES * ROGUE_MIPSFW_PAGE_SIZE_4K) \
>> PAGE_SHIFT)
/**
* struct pvr_fw_mips_data - MIPS-specific data
*/
struct pvr_fw_mips_data {
/**
* @pt_pages: Pages containing MIPS pagetable.
*/
struct page *pt_pages[PVR_MIPS_PT_PAGE_COUNT];
/** @pt: Pointer to CPU mapping of MIPS pagetable. */
u32 *pt;
/** @pt_dma_addr: DMA mappings of MIPS pagetable. */
dma_addr_t pt_dma_addr[PVR_MIPS_PT_PAGE_COUNT];
/** @boot_code_dma_addr: DMA address of MIPS boot code. */
dma_addr_t boot_code_dma_addr;
/** @boot_data_dma_addr: DMA address of MIPS boot data. */
dma_addr_t boot_data_dma_addr;
/** @exception_code_dma_addr: DMA address of MIPS exception code. */
dma_addr_t exception_code_dma_addr;
/** @cache_policy: Cache policy for this processor. */
u32 cache_policy;
/** @pfn_mask: PFN mask for MIPS pagetable. */
u32 pfn_mask;
};
#endif /* PVR_FW_MIPS_H */

View file

@ -0,0 +1,306 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_device.h"
#include "pvr_fw.h"
#include "pvr_fw_meta.h"
#include "pvr_fw_startstop.h"
#include "pvr_rogue_cr_defs.h"
#include "pvr_rogue_meta.h"
#include "pvr_vm.h"
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/ktime.h>
#include <linux/types.h>
#define POLL_TIMEOUT_USEC 1000000
static void
rogue_axi_ace_list_init(struct pvr_device *pvr_dev)
{
/* Setup AXI-ACE config. Set everything to outer cache. */
u64 reg_val =
(3U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_NON_SNOOPING_SHIFT) |
(3U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_NON_SNOOPING_SHIFT) |
(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_CACHE_MAINTENANCE_SHIFT) |
(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_COHERENT_SHIFT) |
(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_COHERENT_SHIFT) |
(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWCACHE_COHERENT_SHIFT) |
(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_COHERENT_SHIFT) |
(2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_CACHE_MAINTENANCE_SHIFT);
pvr_cr_write64(pvr_dev, ROGUE_CR_AXI_ACE_LITE_CONFIGURATION, reg_val);
}
static void
rogue_bif_init(struct pvr_device *pvr_dev)
{
dma_addr_t pc_dma_addr;
u64 pc_addr;
/* Acquire the address of the Kernel Page Catalogue. */
pc_dma_addr = pvr_vm_get_page_table_root_addr(pvr_dev->kernel_vm_ctx);
/* Write the kernel catalogue base. */
pc_addr = ((((u64)pc_dma_addr >> ROGUE_CR_BIF_CAT_BASE0_ADDR_ALIGNSHIFT)
<< ROGUE_CR_BIF_CAT_BASE0_ADDR_SHIFT) &
~ROGUE_CR_BIF_CAT_BASE0_ADDR_CLRMSK);
pvr_cr_write64(pvr_dev, BIF_CAT_BASEX(MMU_CONTEXT_MAPPING_FWPRIV),
pc_addr);
}
static int
rogue_slc_init(struct pvr_device *pvr_dev)
{
u16 slc_cache_line_size_bits;
u32 reg_val;
int err;
/*
* SLC Misc control.
*
* Note: This is a 64bit register and we set only the lower 32bits
* leaving the top 32bits (ROGUE_CR_SLC_CTRL_MISC_SCRAMBLE_BITS)
* unchanged from the HW default.
*/
reg_val = (pvr_cr_read32(pvr_dev, ROGUE_CR_SLC_CTRL_MISC) &
ROGUE_CR_SLC_CTRL_MISC_ENABLE_PSG_HAZARD_CHECK_EN) |
ROGUE_CR_SLC_CTRL_MISC_ADDR_DECODE_MODE_PVR_HASH1;
err = PVR_FEATURE_VALUE(pvr_dev, slc_cache_line_size_bits, &slc_cache_line_size_bits);
if (err)
return err;
/* Bypass burst combiner if SLC line size is smaller than 1024 bits. */
if (slc_cache_line_size_bits < 1024)
reg_val |= ROGUE_CR_SLC_CTRL_MISC_BYPASS_BURST_COMBINER_EN;
if (PVR_HAS_QUIRK(pvr_dev, 71242) && !PVR_HAS_FEATURE(pvr_dev, gpu_multicore_support))
reg_val |= ROGUE_CR_SLC_CTRL_MISC_LAZYWB_OVERRIDE_EN;
pvr_cr_write32(pvr_dev, ROGUE_CR_SLC_CTRL_MISC, reg_val);
return 0;
}
/**
* pvr_fw_start() - Start FW processor and boot firmware
* @pvr_dev: Target PowerVR device.
*
* Returns:
* * 0 on success, or
* * Any error returned by rogue_slc_init().
*/
int
pvr_fw_start(struct pvr_device *pvr_dev)
{
bool has_reset2 = PVR_HAS_FEATURE(pvr_dev, xe_tpu2);
u64 soft_reset_mask;
int err;
if (PVR_HAS_FEATURE(pvr_dev, pbe2_in_xe))
soft_reset_mask = ROGUE_CR_SOFT_RESET__PBE2_XE__MASKFULL;
else
soft_reset_mask = ROGUE_CR_SOFT_RESET_MASKFULL;
if (PVR_HAS_FEATURE(pvr_dev, sys_bus_secure_reset)) {
/*
* Disable the default sys_bus_secure protection to perform
* minimal setup.
*/
pvr_cr_write32(pvr_dev, ROGUE_CR_SYS_BUS_SECURE, 0);
(void)pvr_cr_read32(pvr_dev, ROGUE_CR_SYS_BUS_SECURE); /* Fence write */
}
/* Set Rogue in soft-reset. */
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, soft_reset_mask);
if (has_reset2)
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET2, ROGUE_CR_SOFT_RESET2_MASKFULL);
/* Read soft-reset to fence previous write in order to clear the SOCIF pipeline. */
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET);
if (has_reset2)
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET2);
/* Take Rascal and Dust out of reset. */
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET,
soft_reset_mask ^ ROGUE_CR_SOFT_RESET_RASCALDUSTS_EN);
if (has_reset2)
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET2, 0);
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET);
if (has_reset2)
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET2);
/* Take everything out of reset but the FW processor. */
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, ROGUE_CR_SOFT_RESET_GARTEN_EN);
if (has_reset2)
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET2, 0);
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET);
if (has_reset2)
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET2);
err = rogue_slc_init(pvr_dev);
if (err)
goto err_reset;
/* Initialise Firmware wrapper. */
pvr_dev->fw_dev.defs->wrapper_init(pvr_dev);
/* We must init the AXI-ACE interface before first BIF transaction. */
rogue_axi_ace_list_init(pvr_dev);
if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) {
/* Initialise BIF. */
rogue_bif_init(pvr_dev);
}
/* Need to wait for at least 16 cycles before taking the FW processor out of reset ... */
udelay(3);
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, 0x0);
(void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET);
/* ... and afterwards. */
udelay(3);
return 0;
err_reset:
/* Put everything back into soft-reset. */
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, soft_reset_mask);
return err;
}
/**
* pvr_fw_stop() - Stop FW processor
* @pvr_dev: Target PowerVR device.
*
* Returns:
* * 0 on success, or
* * Any error returned by pvr_cr_poll_reg32().
*/
int
pvr_fw_stop(struct pvr_device *pvr_dev)
{
const u32 sidekick_idle_mask = ROGUE_CR_SIDEKICK_IDLE_MASKFULL &
~(ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN |
ROGUE_CR_SIDEKICK_IDLE_SOCIF_EN |
ROGUE_CR_SIDEKICK_IDLE_HOSTIF_EN);
bool skip_garten_idle = false;
u32 reg_value;
int err;
/*
* Wait for Sidekick/Jones to signal IDLE except for the Garten Wrapper.
* For cores with the LAYOUT_MARS feature, SIDEKICK would have been
* powered down by the FW.
*/
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE, sidekick_idle_mask,
sidekick_idle_mask, POLL_TIMEOUT_USEC);
if (err)
return err;
/* Unset MTS DM association with threads. */
pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC,
ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC_MASKFULL &
ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC_DM_ASSOC_CLRMSK);
pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC,
ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_MASKFULL &
ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_DM_ASSOC_CLRMSK);
pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC,
ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_MASKFULL &
ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_DM_ASSOC_CLRMSK);
pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC,
ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC_MASKFULL &
ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC_DM_ASSOC_CLRMSK);
/* Extra Idle checks. */
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIF_STATUS_MMU, 0,
ROGUE_CR_BIF_STATUS_MMU_MASKFULL,
POLL_TIMEOUT_USEC);
if (err)
return err;
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIFPM_STATUS_MMU, 0,
ROGUE_CR_BIFPM_STATUS_MMU_MASKFULL,
POLL_TIMEOUT_USEC);
if (err)
return err;
if (!PVR_HAS_FEATURE(pvr_dev, xt_top_infrastructure)) {
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIF_READS_EXT_STATUS, 0,
ROGUE_CR_BIF_READS_EXT_STATUS_MASKFULL,
POLL_TIMEOUT_USEC);
if (err)
return err;
}
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIFPM_READS_EXT_STATUS, 0,
ROGUE_CR_BIFPM_READS_EXT_STATUS_MASKFULL,
POLL_TIMEOUT_USEC);
if (err)
return err;
err = pvr_cr_poll_reg64(pvr_dev, ROGUE_CR_SLC_STATUS1, 0,
ROGUE_CR_SLC_STATUS1_MASKFULL,
POLL_TIMEOUT_USEC);
if (err)
return err;
/*
* Wait for SLC to signal IDLE.
* For cores with the LAYOUT_MARS feature, SLC would have been powered
* down by the FW.
*/
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SLC_IDLE,
ROGUE_CR_SLC_IDLE_MASKFULL,
ROGUE_CR_SLC_IDLE_MASKFULL, POLL_TIMEOUT_USEC);
if (err)
return err;
/*
* Wait for Sidekick/Jones to signal IDLE except for the Garten Wrapper.
* For cores with the LAYOUT_MARS feature, SIDEKICK would have been powered
* down by the FW.
*/
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE, sidekick_idle_mask,
sidekick_idle_mask, POLL_TIMEOUT_USEC);
if (err)
return err;
if (pvr_dev->fw_dev.processor_type == PVR_FW_PROCESSOR_TYPE_META) {
err = pvr_meta_cr_read32(pvr_dev, META_CR_TxVECINT_BHALT, &reg_value);
if (err)
return err;
/*
* Wait for Sidekick/Jones to signal IDLE including the Garten
* Wrapper if there is no debugger attached (TxVECINT_BHALT =
* 0x0).
*/
if (reg_value)
skip_garten_idle = true;
}
if (!skip_garten_idle) {
err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE,
ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN,
ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN,
POLL_TIMEOUT_USEC);
if (err)
return err;
}
if (PVR_HAS_FEATURE(pvr_dev, pbe2_in_xe))
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET,
ROGUE_CR_SOFT_RESET__PBE2_XE__MASKFULL);
else
pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, ROGUE_CR_SOFT_RESET_MASKFULL);
return 0;
}

View file

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_FW_STARTSTOP_H
#define PVR_FW_STARTSTOP_H
/* Forward declaration from pvr_device.h. */
struct pvr_device;
int pvr_fw_start(struct pvr_device *pvr_dev);
int pvr_fw_stop(struct pvr_device *pvr_dev);
#endif /* PVR_FW_STARTSTOP_H */

View file

@ -0,0 +1,515 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_device.h"
#include "pvr_gem.h"
#include "pvr_rogue_fwif.h"
#include "pvr_rogue_fwif_sf.h"
#include "pvr_fw_trace.h"
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <linux/build_bug.h>
#include <linux/dcache.h>
#include <linux/sysfs.h>
#include <linux/types.h>
static void
tracebuf_ctrl_init(void *cpu_ptr, void *priv)
{
struct rogue_fwif_tracebuf *tracebuf_ctrl = cpu_ptr;
struct pvr_fw_trace *fw_trace = priv;
u32 thread_nr;
tracebuf_ctrl->tracebuf_size_in_dwords = ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS;
tracebuf_ctrl->tracebuf_flags = 0;
if (fw_trace->group_mask)
tracebuf_ctrl->log_type = fw_trace->group_mask | ROGUE_FWIF_LOG_TYPE_TRACE;
else
tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_NONE;
for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
struct rogue_fwif_tracebuf_space *tracebuf_space =
&tracebuf_ctrl->tracebuf[thread_nr];
struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
pvr_fw_object_get_fw_addr(trace_buffer->buf_obj,
&tracebuf_space->trace_buffer_fw_addr);
tracebuf_space->trace_buffer = trace_buffer->buf;
tracebuf_space->trace_pointer = 0;
}
}
int pvr_fw_trace_init(struct pvr_device *pvr_dev)
{
struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace;
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
u32 thread_nr;
int err;
for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
trace_buffer->buf =
pvr_fw_object_create_and_map(pvr_dev,
ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS *
sizeof(*trace_buffer->buf),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED |
PVR_BO_FW_NO_CLEAR_ON_RESET,
NULL, NULL, &trace_buffer->buf_obj);
if (IS_ERR(trace_buffer->buf)) {
drm_err(drm_dev, "Unable to allocate trace buffer\n");
err = PTR_ERR(trace_buffer->buf);
trace_buffer->buf = NULL;
goto err_free_buf;
}
}
/* TODO: Provide control of group mask. */
fw_trace->group_mask = 0;
fw_trace->tracebuf_ctrl =
pvr_fw_object_create_and_map(pvr_dev,
sizeof(*fw_trace->tracebuf_ctrl),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED |
PVR_BO_FW_NO_CLEAR_ON_RESET,
tracebuf_ctrl_init, fw_trace,
&fw_trace->tracebuf_ctrl_obj);
if (IS_ERR(fw_trace->tracebuf_ctrl)) {
drm_err(drm_dev, "Unable to allocate trace buffer control structure\n");
err = PTR_ERR(fw_trace->tracebuf_ctrl);
goto err_free_buf;
}
BUILD_BUG_ON(ARRAY_SIZE(fw_trace->tracebuf_ctrl->tracebuf) !=
ARRAY_SIZE(fw_trace->buffers));
for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
struct rogue_fwif_tracebuf_space *tracebuf_space =
&fw_trace->tracebuf_ctrl->tracebuf[thread_nr];
struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
trace_buffer->tracebuf_space = tracebuf_space;
}
return 0;
err_free_buf:
for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
if (trace_buffer->buf)
pvr_fw_object_unmap_and_destroy(trace_buffer->buf_obj);
}
return err;
}
void pvr_fw_trace_fini(struct pvr_device *pvr_dev)
{
struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace;
u32 thread_nr;
for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
pvr_fw_object_unmap_and_destroy(trace_buffer->buf_obj);
}
pvr_fw_object_unmap_and_destroy(fw_trace->tracebuf_ctrl_obj);
}
/**
* update_logtype() - Send KCCB command to trigger FW to update logtype
* @pvr_dev: Target PowerVR device
* @group_mask: New log group mask.
*
* Returns:
* * 0 on success,
* * Any error returned by pvr_kccb_send_cmd(), or
* * -%EIO if the device is lost.
*/
static int
update_logtype(struct pvr_device *pvr_dev, u32 group_mask)
{
struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace;
struct rogue_fwif_kccb_cmd cmd;
int idx;
int err;
if (group_mask)
fw_trace->tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_TRACE | group_mask;
else
fw_trace->tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_NONE;
fw_trace->group_mask = group_mask;
down_read(&pvr_dev->reset_sem);
if (!drm_dev_enter(from_pvr_device(pvr_dev), &idx)) {
err = -EIO;
goto err_up_read;
}
cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_LOGTYPE_UPDATE;
cmd.kccb_flags = 0;
err = pvr_kccb_send_cmd(pvr_dev, &cmd, NULL);
drm_dev_exit(idx);
err_up_read:
up_read(&pvr_dev->reset_sem);
return err;
}
#if defined(CONFIG_DEBUG_FS)
static int fw_trace_group_mask_show(struct seq_file *m, void *data)
{
struct pvr_device *pvr_dev = m->private;
seq_printf(m, "%08x\n", pvr_dev->fw_dev.fw_trace.group_mask);
return 0;
}
static int fw_trace_group_mask_open(struct inode *inode, struct file *file)
{
return single_open(file, fw_trace_group_mask_show, inode->i_private);
}
static ssize_t fw_trace_group_mask_write(struct file *file, const char __user *ubuf, size_t len,
loff_t *offp)
{
struct seq_file *m = file->private_data;
struct pvr_device *pvr_dev = m->private;
u32 new_group_mask;
int err;
err = kstrtouint_from_user(ubuf, len, 0, &new_group_mask);
if (err)
return err;
err = update_logtype(pvr_dev, new_group_mask);
if (err)
return err;
pvr_dev->fw_dev.fw_trace.group_mask = new_group_mask;
return (ssize_t)len;
}
static const struct file_operations pvr_fw_trace_group_mask_fops = {
.owner = THIS_MODULE,
.open = fw_trace_group_mask_open,
.read = seq_read,
.write = fw_trace_group_mask_write,
.llseek = default_llseek,
.release = single_release,
};
struct pvr_fw_trace_seq_data {
/** @buffer: Pointer to copy of trace data. */
u32 *buffer;
/** @start_offset: Starting offset in trace data, as reported by FW. */
u32 start_offset;
/** @idx: Current index into trace data. */
u32 idx;
/** @assert_buf: Trace assert buffer, as reported by FW. */
struct rogue_fwif_file_info_buf assert_buf;
};
static u32 find_sfid(u32 id)
{
u32 i;
for (i = 0; i < ARRAY_SIZE(stid_fmts); i++) {
if (stid_fmts[i].id == id)
return i;
}
return ROGUE_FW_SF_LAST;
}
static u32 read_fw_trace(struct pvr_fw_trace_seq_data *trace_seq_data, u32 offset)
{
u32 idx;
idx = trace_seq_data->idx + offset;
if (idx >= ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS)
return 0;
idx = (idx + trace_seq_data->start_offset) % ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS;
return trace_seq_data->buffer[idx];
}
/**
* fw_trace_get_next() - Advance trace index to next entry
* @trace_seq_data: Trace sequence data.
*
* Returns:
* * %true if trace index is now pointing to a valid entry, or
* * %false if trace index is pointing to an invalid entry, or has hit the end
* of the trace.
*/
static bool fw_trace_get_next(struct pvr_fw_trace_seq_data *trace_seq_data)
{
u32 id, sf_id;
while (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) {
id = read_fw_trace(trace_seq_data, 0);
trace_seq_data->idx++;
if (!ROGUE_FW_LOG_VALIDID(id))
continue;
if (id == ROGUE_FW_SF_MAIN_ASSERT_FAILED) {
/* Assertion failure marks the end of the trace. */
return false;
}
sf_id = find_sfid(id);
if (sf_id == ROGUE_FW_SF_FIRST)
continue;
if (sf_id == ROGUE_FW_SF_LAST) {
/*
* Could not match with an ID in the SF table, trace is
* most likely corrupt from this point.
*/
return false;
}
/* Skip over the timestamp, and any parameters. */
trace_seq_data->idx += 2 + ROGUE_FW_SF_PARAMNUM(id);
/* Ensure index is now pointing to a valid trace entry. */
id = read_fw_trace(trace_seq_data, 0);
if (!ROGUE_FW_LOG_VALIDID(id))
continue;
return true;
};
/* Hit end of trace data. */
return false;
}
/**
* fw_trace_get_first() - Find first valid entry in trace
* @trace_seq_data: Trace sequence data.
*
* Skips over invalid (usually zero) and ROGUE_FW_SF_FIRST entries.
*
* If the trace has no valid entries, this function will exit with the trace
* index pointing to the end of the trace. trace_seq_show() will return an error
* in this state.
*/
static void fw_trace_get_first(struct pvr_fw_trace_seq_data *trace_seq_data)
{
trace_seq_data->idx = 0;
while (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) {
u32 id = read_fw_trace(trace_seq_data, 0);
if (ROGUE_FW_LOG_VALIDID(id)) {
u32 sf_id = find_sfid(id);
if (sf_id != ROGUE_FW_SF_FIRST)
break;
}
trace_seq_data->idx++;
}
}
static void *fw_trace_seq_start(struct seq_file *s, loff_t *pos)
{
struct pvr_fw_trace_seq_data *trace_seq_data = s->private;
u32 i;
/* Reset trace index, then advance to *pos. */
fw_trace_get_first(trace_seq_data);
for (i = 0; i < *pos; i++) {
if (!fw_trace_get_next(trace_seq_data))
return NULL;
}
return (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) ? pos : NULL;
}
static void *fw_trace_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
struct pvr_fw_trace_seq_data *trace_seq_data = s->private;
(*pos)++;
if (!fw_trace_get_next(trace_seq_data))
return NULL;
return (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) ? pos : NULL;
}
static void fw_trace_seq_stop(struct seq_file *s, void *v)
{
}
static int fw_trace_seq_show(struct seq_file *s, void *v)
{
struct pvr_fw_trace_seq_data *trace_seq_data = s->private;
u64 timestamp;
u32 id;
u32 sf_id;
if (trace_seq_data->idx >= ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS)
return -EINVAL;
id = read_fw_trace(trace_seq_data, 0);
/* Index is not pointing at a valid entry. */
if (!ROGUE_FW_LOG_VALIDID(id))
return -EINVAL;
sf_id = find_sfid(id);
/* Index is not pointing at a valid entry. */
if (sf_id == ROGUE_FW_SF_LAST)
return -EINVAL;
timestamp = read_fw_trace(trace_seq_data, 1) |
((u64)read_fw_trace(trace_seq_data, 2) << 32);
timestamp = (timestamp & ~ROGUE_FWT_TIMESTAMP_TIME_CLRMSK) >>
ROGUE_FWT_TIMESTAMP_TIME_SHIFT;
seq_printf(s, "[%llu] : ", timestamp);
if (id == ROGUE_FW_SF_MAIN_ASSERT_FAILED) {
seq_printf(s, "ASSERTION %s failed at %s:%u",
trace_seq_data->assert_buf.info,
trace_seq_data->assert_buf.path,
trace_seq_data->assert_buf.line_num);
} else {
seq_printf(s, stid_fmts[sf_id].name,
read_fw_trace(trace_seq_data, 3),
read_fw_trace(trace_seq_data, 4),
read_fw_trace(trace_seq_data, 5),
read_fw_trace(trace_seq_data, 6),
read_fw_trace(trace_seq_data, 7),
read_fw_trace(trace_seq_data, 8),
read_fw_trace(trace_seq_data, 9),
read_fw_trace(trace_seq_data, 10),
read_fw_trace(trace_seq_data, 11),
read_fw_trace(trace_seq_data, 12),
read_fw_trace(trace_seq_data, 13),
read_fw_trace(trace_seq_data, 14),
read_fw_trace(trace_seq_data, 15),
read_fw_trace(trace_seq_data, 16),
read_fw_trace(trace_seq_data, 17),
read_fw_trace(trace_seq_data, 18),
read_fw_trace(trace_seq_data, 19),
read_fw_trace(trace_seq_data, 20),
read_fw_trace(trace_seq_data, 21),
read_fw_trace(trace_seq_data, 22));
}
seq_puts(s, "\n");
return 0;
}
static const struct seq_operations pvr_fw_trace_seq_ops = {
.start = fw_trace_seq_start,
.next = fw_trace_seq_next,
.stop = fw_trace_seq_stop,
.show = fw_trace_seq_show
};
static int fw_trace_open(struct inode *inode, struct file *file)
{
struct pvr_fw_trace_buffer *trace_buffer = inode->i_private;
struct rogue_fwif_tracebuf_space *tracebuf_space =
trace_buffer->tracebuf_space;
struct pvr_fw_trace_seq_data *trace_seq_data;
int err;
trace_seq_data = kzalloc(sizeof(*trace_seq_data), GFP_KERNEL);
if (!trace_seq_data)
return -ENOMEM;
trace_seq_data->buffer = kcalloc(ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS,
sizeof(*trace_seq_data->buffer), GFP_KERNEL);
if (!trace_seq_data->buffer) {
err = -ENOMEM;
goto err_free_data;
}
/*
* Take a local copy of the trace buffer, as firmware may still be
* writing to it. This will exist as long as this file is open.
*/
memcpy(trace_seq_data->buffer, trace_buffer->buf,
ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS * sizeof(u32));
trace_seq_data->start_offset = READ_ONCE(tracebuf_space->trace_pointer);
trace_seq_data->assert_buf = tracebuf_space->assert_buf;
fw_trace_get_first(trace_seq_data);
err = seq_open(file, &pvr_fw_trace_seq_ops);
if (err)
goto err_free_buffer;
((struct seq_file *)file->private_data)->private = trace_seq_data;
return 0;
err_free_buffer:
kfree(trace_seq_data->buffer);
err_free_data:
kfree(trace_seq_data);
return err;
}
static int fw_trace_release(struct inode *inode, struct file *file)
{
struct pvr_fw_trace_seq_data *trace_seq_data =
((struct seq_file *)file->private_data)->private;
seq_release(inode, file);
kfree(trace_seq_data->buffer);
kfree(trace_seq_data);
return 0;
}
static const struct file_operations pvr_fw_trace_fops = {
.owner = THIS_MODULE,
.open = fw_trace_open,
.read = seq_read,
.llseek = seq_lseek,
.release = fw_trace_release,
};
void
pvr_fw_trace_mask_update(struct pvr_device *pvr_dev, u32 old_mask, u32 new_mask)
{
if (old_mask != new_mask)
update_logtype(pvr_dev, new_mask);
}
void
pvr_fw_trace_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir)
{
struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace;
u32 thread_nr;
static_assert(ARRAY_SIZE(fw_trace->buffers) <= 10,
"The filename buffer is only large enough for a single-digit thread count");
for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); ++thread_nr) {
char filename[8];
snprintf(filename, ARRAY_SIZE(filename), "trace_%u", thread_nr);
debugfs_create_file(filename, 0400, dir,
&fw_trace->buffers[thread_nr],
&pvr_fw_trace_fops);
}
}
#endif

View file

@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_FW_TRACE_H
#define PVR_FW_TRACE_H
#include <drm/drm_file.h>
#include <linux/types.h>
#include "pvr_rogue_fwif.h"
/* Forward declaration from pvr_device.h. */
struct pvr_device;
/* Forward declaration from pvr_gem.h. */
struct pvr_fw_object;
/* Forward declarations from pvr_rogue_fwif.h */
struct rogue_fwif_tracebuf;
struct rogue_fwif_tracebuf_space;
/**
* struct pvr_fw_trace_buffer - Structure representing a trace buffer
*/
struct pvr_fw_trace_buffer {
/** @buf_obj: FW buffer object representing trace buffer. */
struct pvr_fw_object *buf_obj;
/** @buf: Pointer to CPU mapping of trace buffer. */
u32 *buf;
/**
* @tracebuf_space: Pointer to FW tracebuf_space structure for this
* trace buffer.
*/
struct rogue_fwif_tracebuf_space *tracebuf_space;
};
/**
* struct pvr_fw_trace - Device firmware trace data
*/
struct pvr_fw_trace {
/**
* @tracebuf_ctrl_obj: Object representing FW trace buffer control
* structure.
*/
struct pvr_fw_object *tracebuf_ctrl_obj;
/**
* @tracebuf_ctrl: Pointer to CPU mapping of FW trace buffer control
* structure.
*/
struct rogue_fwif_tracebuf *tracebuf_ctrl;
/**
* @buffers: Array representing the actual trace buffers owned by this
* device.
*/
struct pvr_fw_trace_buffer buffers[ROGUE_FW_THREAD_MAX];
/** @group_mask: Mask of enabled trace groups. */
u32 group_mask;
};
int pvr_fw_trace_init(struct pvr_device *pvr_dev);
void pvr_fw_trace_fini(struct pvr_device *pvr_dev);
#if defined(CONFIG_DEBUG_FS)
/* Forward declaration from <linux/dcache.h>. */
struct dentry;
void pvr_fw_trace_mask_update(struct pvr_device *pvr_dev, u32 old_mask,
u32 new_mask);
void pvr_fw_trace_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir);
#endif /* defined(CONFIG_DEBUG_FS) */
#endif /* PVR_FW_TRACE_H */

View file

@ -0,0 +1,414 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_device.h"
#include "pvr_gem.h"
#include "pvr_vm.h"
#include <drm/drm_gem.h>
#include <drm/drm_prime.h>
#include <linux/compiler.h>
#include <linux/compiler_attributes.h>
#include <linux/dma-buf.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/gfp.h>
#include <linux/iosys-map.h>
#include <linux/log2.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <linux/refcount.h>
#include <linux/scatterlist.h>
static void pvr_gem_object_free(struct drm_gem_object *obj)
{
drm_gem_shmem_object_free(obj);
}
static int pvr_gem_mmap(struct drm_gem_object *gem_obj, struct vm_area_struct *vma)
{
struct pvr_gem_object *pvr_obj = gem_to_pvr_gem(gem_obj);
struct drm_gem_shmem_object *shmem_obj = shmem_gem_from_pvr_gem(pvr_obj);
if (!(pvr_obj->flags & DRM_PVR_BO_ALLOW_CPU_USERSPACE_ACCESS))
return -EINVAL;
return drm_gem_shmem_mmap(shmem_obj, vma);
}
static const struct drm_gem_object_funcs pvr_gem_object_funcs = {
.free = pvr_gem_object_free,
.print_info = drm_gem_shmem_object_print_info,
.pin = drm_gem_shmem_object_pin,
.unpin = drm_gem_shmem_object_unpin,
.get_sg_table = drm_gem_shmem_object_get_sg_table,
.vmap = drm_gem_shmem_object_vmap,
.vunmap = drm_gem_shmem_object_vunmap,
.mmap = pvr_gem_mmap,
.vm_ops = &drm_gem_shmem_vm_ops,
};
/**
* pvr_gem_object_flags_validate() - Verify that a collection of PowerVR GEM
* mapping and/or creation flags form a valid combination.
* @flags: PowerVR GEM mapping/creation flags to validate.
*
* This function explicitly allows kernel-only flags. All ioctl entrypoints
* should do their own validation as well as relying on this function.
*
* Return:
* * %true if @flags contains valid mapping and/or creation flags, or
* * %false otherwise.
*/
static bool
pvr_gem_object_flags_validate(u64 flags)
{
static const u64 invalid_combinations[] = {
/*
* Memory flagged as PM/FW-protected cannot be mapped to
* userspace. To make this explicit, we require that the two
* flags allowing each of these respective features are never
* specified together.
*/
(DRM_PVR_BO_PM_FW_PROTECT |
DRM_PVR_BO_ALLOW_CPU_USERSPACE_ACCESS),
};
int i;
/*
* Check for bits set in undefined regions. Reserved regions refer to
* options that can only be set by the kernel. These are explicitly
* allowed in most cases, and must be checked specifically in IOCTL
* callback code.
*/
if ((flags & PVR_BO_UNDEFINED_MASK) != 0)
return false;
/*
* Check for all combinations of flags marked as invalid in the array
* above.
*/
for (i = 0; i < ARRAY_SIZE(invalid_combinations); ++i) {
u64 combo = invalid_combinations[i];
if ((flags & combo) == combo)
return false;
}
return true;
}
/**
* pvr_gem_object_into_handle() - Convert a reference to an object into a
* userspace-accessible handle.
* @pvr_obj: [IN] Target PowerVR-specific object.
* @pvr_file: [IN] File to associate the handle with.
* @handle: [OUT] Pointer to store the created handle in. Remains unmodified if
* an error is encountered.
*
* If an error is encountered, ownership of @pvr_obj will not have been
* transferred. If this function succeeds, however, further use of @pvr_obj is
* considered undefined behaviour unless another reference to it is explicitly
* held.
*
* Return:
* * 0 on success, or
* * Any error encountered while attempting to allocate a handle on @pvr_file.
*/
int
pvr_gem_object_into_handle(struct pvr_gem_object *pvr_obj,
struct pvr_file *pvr_file, u32 *handle)
{
struct drm_gem_object *gem_obj = gem_from_pvr_gem(pvr_obj);
struct drm_file *file = from_pvr_file(pvr_file);
u32 new_handle;
int err;
err = drm_gem_handle_create(file, gem_obj, &new_handle);
if (err)
return err;
/*
* Release our reference to @pvr_obj, effectively transferring
* ownership to the handle.
*/
pvr_gem_object_put(pvr_obj);
/*
* Do not store the new handle in @handle until no more errors can
* occur.
*/
*handle = new_handle;
return 0;
}
/**
* pvr_gem_object_from_handle() - Obtain a reference to an object from a
* userspace handle.
* @pvr_file: PowerVR-specific file to which @handle is associated.
* @handle: Userspace handle referencing the target object.
*
* On return, @handle always maintains its reference to the requested object
* (if it had one in the first place). If this function succeeds, the returned
* object will hold an additional reference. When the caller is finished with
* the returned object, they should call pvr_gem_object_put() on it to release
* this reference.
*
* Return:
* * A pointer to the requested PowerVR-specific object on success, or
* * %NULL otherwise.
*/
struct pvr_gem_object *
pvr_gem_object_from_handle(struct pvr_file *pvr_file, u32 handle)
{
struct drm_file *file = from_pvr_file(pvr_file);
struct drm_gem_object *gem_obj;
gem_obj = drm_gem_object_lookup(file, handle);
if (!gem_obj)
return NULL;
return gem_to_pvr_gem(gem_obj);
}
/**
* pvr_gem_object_vmap() - Map a PowerVR GEM object into CPU virtual address
* space.
* @pvr_obj: Target PowerVR GEM object.
*
* Once the caller is finished with the CPU mapping, they must call
* pvr_gem_object_vunmap() on @pvr_obj.
*
* If @pvr_obj is CPU-cached, dma_sync_sgtable_for_cpu() is called to make
* sure the CPU mapping is consistent.
*
* Return:
* * A pointer to the CPU mapping on success,
* * -%ENOMEM if the mapping fails, or
* * Any error encountered while attempting to acquire a reference to the
* backing pages for @pvr_obj.
*/
void *
pvr_gem_object_vmap(struct pvr_gem_object *pvr_obj)
{
struct drm_gem_shmem_object *shmem_obj = shmem_gem_from_pvr_gem(pvr_obj);
struct drm_gem_object *obj = gem_from_pvr_gem(pvr_obj);
struct iosys_map map;
int err;
dma_resv_lock(obj->resv, NULL);
err = drm_gem_shmem_vmap(shmem_obj, &map);
if (err)
goto err_unlock;
if (pvr_obj->flags & PVR_BO_CPU_CACHED) {
struct device *dev = shmem_obj->base.dev->dev;
/* If shmem_obj->sgt is NULL, that means the buffer hasn't been mapped
* in GPU space yet.
*/
if (shmem_obj->sgt)
dma_sync_sgtable_for_cpu(dev, shmem_obj->sgt, DMA_BIDIRECTIONAL);
}
dma_resv_unlock(obj->resv);
return map.vaddr;
err_unlock:
dma_resv_unlock(obj->resv);
return ERR_PTR(err);
}
/**
* pvr_gem_object_vunmap() - Unmap a PowerVR memory object from CPU virtual
* address space.
* @pvr_obj: Target PowerVR GEM object.
*
* If @pvr_obj is CPU-cached, dma_sync_sgtable_for_device() is called to make
* sure the GPU mapping is consistent.
*/
void
pvr_gem_object_vunmap(struct pvr_gem_object *pvr_obj)
{
struct drm_gem_shmem_object *shmem_obj = shmem_gem_from_pvr_gem(pvr_obj);
struct iosys_map map = IOSYS_MAP_INIT_VADDR(shmem_obj->vaddr);
struct drm_gem_object *obj = gem_from_pvr_gem(pvr_obj);
if (WARN_ON(!map.vaddr))
return;
dma_resv_lock(obj->resv, NULL);
if (pvr_obj->flags & PVR_BO_CPU_CACHED) {
struct device *dev = shmem_obj->base.dev->dev;
/* If shmem_obj->sgt is NULL, that means the buffer hasn't been mapped
* in GPU space yet.
*/
if (shmem_obj->sgt)
dma_sync_sgtable_for_device(dev, shmem_obj->sgt, DMA_BIDIRECTIONAL);
}
drm_gem_shmem_vunmap(shmem_obj, &map);
dma_resv_unlock(obj->resv);
}
/**
* pvr_gem_object_zero() - Zeroes the physical memory behind an object.
* @pvr_obj: Target PowerVR GEM object.
*
* Return:
* * 0 on success, or
* * Any error encountered while attempting to map @pvr_obj to the CPU (see
* pvr_gem_object_vmap()).
*/
static int
pvr_gem_object_zero(struct pvr_gem_object *pvr_obj)
{
void *cpu_ptr;
cpu_ptr = pvr_gem_object_vmap(pvr_obj);
if (IS_ERR(cpu_ptr))
return PTR_ERR(cpu_ptr);
memset(cpu_ptr, 0, pvr_gem_object_size(pvr_obj));
/* Make sure the zero-ing is done before vumap-ing the object. */
wmb();
pvr_gem_object_vunmap(pvr_obj);
return 0;
}
/**
* pvr_gem_create_object() - Allocate and pre-initializes a pvr_gem_object
* @drm_dev: DRM device creating this object.
* @size: Size of the object to allocate in bytes.
*
* Return:
* * The new pre-initialized GEM object on success,
* * -ENOMEM if the allocation failed.
*/
struct drm_gem_object *pvr_gem_create_object(struct drm_device *drm_dev, size_t size)
{
struct drm_gem_object *gem_obj;
struct pvr_gem_object *pvr_obj;
pvr_obj = kzalloc(sizeof(*pvr_obj), GFP_KERNEL);
if (!pvr_obj)
return ERR_PTR(-ENOMEM);
gem_obj = gem_from_pvr_gem(pvr_obj);
gem_obj->funcs = &pvr_gem_object_funcs;
return gem_obj;
}
/**
* pvr_gem_object_create() - Creates a PowerVR-specific buffer object.
* @pvr_dev: Target PowerVR device.
* @size: Size of the object to allocate in bytes. Must be greater than zero.
* Any value which is not an exact multiple of the system page size will be
* rounded up to satisfy this condition.
* @flags: Options which affect both this operation and future mapping
* operations performed on the returned object. Must be a combination of
* DRM_PVR_BO_* and/or PVR_BO_* flags.
*
* The created object may be larger than @size, but can never be smaller. To
* get the exact size, call pvr_gem_object_size() on the returned pointer.
*
* Return:
* * The newly-minted PowerVR-specific buffer object on success,
* * -%EINVAL if @size is zero or @flags is not valid,
* * -%ENOMEM if sufficient physical memory cannot be allocated, or
* * Any other error returned by drm_gem_create_mmap_offset().
*/
struct pvr_gem_object *
pvr_gem_object_create(struct pvr_device *pvr_dev, size_t size, u64 flags)
{
struct drm_gem_shmem_object *shmem_obj;
struct pvr_gem_object *pvr_obj;
struct sg_table *sgt;
int err;
/* Verify @size and @flags before continuing. */
if (size == 0 || !pvr_gem_object_flags_validate(flags))
return ERR_PTR(-EINVAL);
shmem_obj = drm_gem_shmem_create(from_pvr_device(pvr_dev), size);
if (IS_ERR(shmem_obj))
return ERR_CAST(shmem_obj);
shmem_obj->pages_mark_dirty_on_put = true;
shmem_obj->map_wc = !(flags & PVR_BO_CPU_CACHED);
pvr_obj = shmem_gem_to_pvr_gem(shmem_obj);
pvr_obj->flags = flags;
sgt = drm_gem_shmem_get_pages_sgt(shmem_obj);
if (IS_ERR(sgt)) {
err = PTR_ERR(sgt);
goto err_shmem_object_free;
}
dma_sync_sgtable_for_device(shmem_obj->base.dev->dev, sgt,
DMA_BIDIRECTIONAL);
/*
* Do this last because pvr_gem_object_zero() requires a fully
* configured instance of struct pvr_gem_object.
*/
pvr_gem_object_zero(pvr_obj);
return pvr_obj;
err_shmem_object_free:
drm_gem_shmem_free(shmem_obj);
return ERR_PTR(err);
}
/**
* pvr_gem_get_dma_addr() - Get DMA address for given offset in object
* @pvr_obj: Pointer to object to lookup address in.
* @offset: Offset within object to lookup address at.
* @dma_addr_out: Pointer to location to store DMA address.
*
* Returns:
* * 0 on success, or
* * -%EINVAL if object is not currently backed, or if @offset is out of valid
* range for this object.
*/
int
pvr_gem_get_dma_addr(struct pvr_gem_object *pvr_obj, u32 offset,
dma_addr_t *dma_addr_out)
{
struct drm_gem_shmem_object *shmem_obj = shmem_gem_from_pvr_gem(pvr_obj);
u32 accumulated_offset = 0;
struct scatterlist *sgl;
unsigned int sgt_idx;
WARN_ON(!shmem_obj->sgt);
for_each_sgtable_dma_sg(shmem_obj->sgt, sgl, sgt_idx) {
u32 new_offset = accumulated_offset + sg_dma_len(sgl);
if (offset >= accumulated_offset && offset < new_offset) {
*dma_addr_out = sg_dma_address(sgl) +
(offset - accumulated_offset);
return 0;
}
accumulated_offset = new_offset;
}
return -EINVAL;
}

View file

@ -0,0 +1,170 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_GEM_H
#define PVR_GEM_H
#include "pvr_rogue_heap_config.h"
#include "pvr_rogue_meta.h"
#include <uapi/drm/pvr_drm.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_mm.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/const.h>
#include <linux/compiler_attributes.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/refcount.h>
#include <linux/scatterlist.h>
#include <linux/sizes.h>
#include <linux/types.h>
/* Forward declaration from "pvr_device.h". */
struct pvr_device;
struct pvr_file;
/**
* DOC: Flags for DRM_IOCTL_PVR_CREATE_BO (kernel-only)
*
* Kernel-only values allowed in &pvr_gem_object->flags. The majority of options
* for this field are specified in the UAPI header "pvr_drm.h" with a
* DRM_PVR_BO_ prefix. To distinguish these internal options (which must exist
* in ranges marked as "reserved" in the UAPI header), we drop the DRM prefix.
* The public options should be used directly, DRM prefix and all.
*
* To avoid potentially confusing gaps in the UAPI options, these kernel-only
* options are specified "in reverse", starting at bit 63.
*
* We use "reserved" to refer to bits defined here and not exposed in the UAPI.
* Bits not defined anywhere are "undefined".
*
* CPU mapping options
* :PVR_BO_CPU_CACHED: By default, all GEM objects are mapped write-combined on the CPU. Set this
* flag to override this behaviour and map the object cached.
*
* Firmware options
* :PVR_BO_FW_NO_CLEAR_ON_RESET: By default, all FW objects are cleared and reinitialised on hard
* reset. Set this flag to override this behaviour and preserve buffer contents on reset.
*/
#define PVR_BO_CPU_CACHED BIT_ULL(63)
#define PVR_BO_FW_NO_CLEAR_ON_RESET BIT_ULL(62)
#define PVR_BO_KERNEL_FLAGS_MASK (PVR_BO_CPU_CACHED | PVR_BO_FW_NO_CLEAR_ON_RESET)
/* Bits 61..3 are undefined. */
/* Bits 2..0 are defined in the UAPI. */
/* Other utilities. */
#define PVR_BO_UNDEFINED_MASK ~(PVR_BO_KERNEL_FLAGS_MASK | DRM_PVR_BO_FLAGS_MASK)
/*
* All firmware-mapped memory uses (mostly) the same flags. Specifically,
* firmware-mapped memory should be:
* * Read/write on the device,
* * Read/write on the CPU, and
* * Write-combined on the CPU.
*
* The only variation is in caching on the device.
*/
#define PVR_BO_FW_FLAGS_DEVICE_CACHED (ULL(0))
#define PVR_BO_FW_FLAGS_DEVICE_UNCACHED DRM_PVR_BO_BYPASS_DEVICE_CACHE
/**
* struct pvr_gem_object - powervr-specific wrapper for &struct drm_gem_object
*/
struct pvr_gem_object {
/**
* @base: The underlying &struct drm_gem_shmem_object.
*
* Do not access this member directly, instead call
* shem_gem_from_pvr_gem().
*/
struct drm_gem_shmem_object base;
/**
* @flags: Options set at creation-time. Some of these options apply to
* the creation operation itself (which are stored here for reference)
* with the remainder used for mapping options to both the device and
* CPU. These are used every time this object is mapped, but may be
* changed after creation.
*
* Must be a combination of DRM_PVR_BO_* and/or PVR_BO_* flags.
*
* .. note::
*
* This member is declared const to indicate that none of these
* options may change or be changed throughout the object's
* lifetime.
*/
u64 flags;
};
static_assert(offsetof(struct pvr_gem_object, base) == 0,
"offsetof(struct pvr_gem_object, base) not zero");
#define shmem_gem_from_pvr_gem(pvr_obj) (&(pvr_obj)->base)
#define shmem_gem_to_pvr_gem(shmem_obj) container_of_const(shmem_obj, struct pvr_gem_object, base)
#define gem_from_pvr_gem(pvr_obj) (&(pvr_obj)->base.base)
#define gem_to_pvr_gem(gem_obj) container_of_const(gem_obj, struct pvr_gem_object, base.base)
/* Functions defined in pvr_gem.c */
struct drm_gem_object *pvr_gem_create_object(struct drm_device *drm_dev, size_t size);
struct pvr_gem_object *pvr_gem_object_create(struct pvr_device *pvr_dev,
size_t size, u64 flags);
int pvr_gem_object_into_handle(struct pvr_gem_object *pvr_obj,
struct pvr_file *pvr_file, u32 *handle);
struct pvr_gem_object *pvr_gem_object_from_handle(struct pvr_file *pvr_file,
u32 handle);
static __always_inline struct sg_table *
pvr_gem_object_get_pages_sgt(struct pvr_gem_object *pvr_obj)
{
return drm_gem_shmem_get_pages_sgt(shmem_gem_from_pvr_gem(pvr_obj));
}
void *pvr_gem_object_vmap(struct pvr_gem_object *pvr_obj);
void pvr_gem_object_vunmap(struct pvr_gem_object *pvr_obj);
int pvr_gem_get_dma_addr(struct pvr_gem_object *pvr_obj, u32 offset,
dma_addr_t *dma_addr_out);
/**
* pvr_gem_object_get() - Acquire reference on pvr_gem_object
* @pvr_obj: Pointer to object to acquire reference on.
*/
static __always_inline void
pvr_gem_object_get(struct pvr_gem_object *pvr_obj)
{
drm_gem_object_get(gem_from_pvr_gem(pvr_obj));
}
/**
* pvr_gem_object_put() - Release reference on pvr_gem_object
* @pvr_obj: Pointer to object to release reference on.
*/
static __always_inline void
pvr_gem_object_put(struct pvr_gem_object *pvr_obj)
{
drm_gem_object_put(gem_from_pvr_gem(pvr_obj));
}
static __always_inline size_t
pvr_gem_object_size(struct pvr_gem_object *pvr_obj)
{
return gem_from_pvr_gem(pvr_obj)->size;
}
#endif /* PVR_GEM_H */

View file

@ -0,0 +1,549 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_free_list.h"
#include "pvr_hwrt.h"
#include "pvr_gem.h"
#include "pvr_rogue_cr_defs_client.h"
#include "pvr_rogue_fwif.h"
#include <drm/drm_gem.h>
#include <linux/bitops.h>
#include <linux/math.h>
#include <linux/slab.h>
#include <linux/xarray.h>
#include <uapi/drm/pvr_drm.h>
static_assert(ROGUE_FWIF_NUM_RTDATAS == 2);
static_assert(ROGUE_FWIF_NUM_GEOMDATAS == 1);
static_assert(ROGUE_FWIF_NUM_RTDATA_FREELISTS == 2);
/*
* struct pvr_rt_mtile_info - Render target macrotile information
*/
struct pvr_rt_mtile_info {
u32 mtile_x[3];
u32 mtile_y[3];
u32 tile_max_x;
u32 tile_max_y;
u32 tile_size_x;
u32 tile_size_y;
u32 num_tiles_x;
u32 num_tiles_y;
};
/* Size of Shadow Render Target Cache entry */
#define SRTC_ENTRY_SIZE sizeof(u32)
/* Size of Renders Accumulation Array entry */
#define RAA_ENTRY_SIZE sizeof(u32)
static int
hwrt_init_kernel_structure(struct pvr_file *pvr_file,
struct drm_pvr_ioctl_create_hwrt_dataset_args *args,
struct pvr_hwrt_dataset *hwrt)
{
struct pvr_device *pvr_dev = pvr_file->pvr_dev;
int err;
int i;
hwrt->pvr_dev = pvr_dev;
hwrt->max_rts = args->layers;
/* Get pointers to the free lists */
for (i = 0; i < ARRAY_SIZE(hwrt->free_lists); i++) {
hwrt->free_lists[i] = pvr_free_list_lookup(pvr_file, args->free_list_handles[i]);
if (!hwrt->free_lists[i]) {
err = -EINVAL;
goto err_put_free_lists;
}
}
if (hwrt->free_lists[ROGUE_FW_LOCAL_FREELIST]->current_pages <
pvr_get_free_list_min_pages(pvr_dev)) {
err = -EINVAL;
goto err_put_free_lists;
}
return 0;
err_put_free_lists:
for (i = 0; i < ARRAY_SIZE(hwrt->free_lists); i++) {
pvr_free_list_put(hwrt->free_lists[i]);
hwrt->free_lists[i] = NULL;
}
return err;
}
static void
hwrt_fini_kernel_structure(struct pvr_hwrt_dataset *hwrt)
{
int i;
for (i = 0; i < ARRAY_SIZE(hwrt->free_lists); i++) {
pvr_free_list_put(hwrt->free_lists[i]);
hwrt->free_lists[i] = NULL;
}
}
static void
hwrt_fini_common_fw_structure(struct pvr_hwrt_dataset *hwrt)
{
pvr_fw_object_destroy(hwrt->common_fw_obj);
}
static int
get_cr_isp_mtile_size_val(struct pvr_device *pvr_dev, u32 samples,
struct pvr_rt_mtile_info *info, u32 *value_out)
{
u32 x = info->mtile_x[0];
u32 y = info->mtile_y[0];
u32 samples_per_pixel;
int err;
err = PVR_FEATURE_VALUE(pvr_dev, isp_samples_per_pixel, &samples_per_pixel);
if (err)
return err;
if (samples_per_pixel == 1) {
if (samples >= 4)
x <<= 1;
if (samples >= 2)
y <<= 1;
} else if (samples_per_pixel == 2) {
if (samples >= 8)
x <<= 1;
if (samples >= 4)
y <<= 1;
} else if (samples_per_pixel == 4) {
if (samples >= 8)
y <<= 1;
} else {
WARN(true, "Unsupported ISP samples per pixel value");
return -EINVAL;
}
*value_out = ((x << ROGUE_CR_ISP_MTILE_SIZE_X_SHIFT) & ~ROGUE_CR_ISP_MTILE_SIZE_X_CLRMSK) |
((y << ROGUE_CR_ISP_MTILE_SIZE_Y_SHIFT) & ~ROGUE_CR_ISP_MTILE_SIZE_Y_CLRMSK);
return 0;
}
static int
get_cr_multisamplectl_val(u32 samples, bool y_flip, u64 *value_out)
{
static const struct {
u8 x[8];
u8 y[8];
} sample_positions[4] = {
/* 1 sample */
{
.x = { 8 },
.y = { 8 },
},
/* 2 samples */
{
.x = { 12, 4 },
.y = { 12, 4 },
},
/* 4 samples */
{
.x = { 6, 14, 2, 10 },
.y = { 2, 6, 10, 14 },
},
/* 8 samples */
{
.x = { 9, 7, 13, 5, 3, 1, 11, 15 },
.y = { 5, 11, 9, 3, 13, 7, 15, 1 },
},
};
const int idx = fls(samples) - 1;
u64 value = 0;
if (idx < 0 || idx > 3)
return -EINVAL;
for (u32 i = 0; i < 8; i++) {
value |= ((u64)sample_positions[idx].x[i]) << (i * 8);
if (y_flip)
value |= (((u64)(16 - sample_positions[idx].y[i]) & 0xf)) << (i * 8 + 4);
else
value |= ((u64)sample_positions[idx].y[i]) << (i * 8 + 4);
}
*value_out = value;
return 0;
}
static int
get_cr_te_aa_val(struct pvr_device *pvr_dev, u32 samples, u32 *value_out)
{
u32 samples_per_pixel;
u32 value = 0;
int err = 0;
err = PVR_FEATURE_VALUE(pvr_dev, isp_samples_per_pixel, &samples_per_pixel);
if (err)
return err;
switch (samples_per_pixel) {
case 1:
if (samples >= 2)
value |= ROGUE_CR_TE_AA_Y_EN;
if (samples >= 4)
value |= ROGUE_CR_TE_AA_X_EN;
break;
case 2:
if (samples >= 2)
value |= ROGUE_CR_TE_AA_X2_EN;
if (samples >= 4)
value |= ROGUE_CR_TE_AA_Y_EN;
if (samples >= 8)
value |= ROGUE_CR_TE_AA_X_EN;
break;
case 4:
if (samples >= 2)
value |= ROGUE_CR_TE_AA_X2_EN;
if (samples >= 4)
value |= ROGUE_CR_TE_AA_Y2_EN;
if (samples >= 8)
value |= ROGUE_CR_TE_AA_Y_EN;
break;
default:
WARN(true, "Unsupported ISP samples per pixel value");
return -EINVAL;
}
*value_out = value;
return 0;
}
static void
hwrtdata_common_init(void *cpu_ptr, void *priv)
{
struct pvr_hwrt_dataset *hwrt = priv;
memcpy(cpu_ptr, &hwrt->common, sizeof(hwrt->common));
}
static int
hwrt_init_common_fw_structure(struct pvr_file *pvr_file,
struct drm_pvr_ioctl_create_hwrt_dataset_args *args,
struct pvr_hwrt_dataset *hwrt)
{
struct drm_pvr_create_hwrt_geom_data_args *geom_data_args = &args->geom_data_args;
struct pvr_device *pvr_dev = pvr_file->pvr_dev;
struct pvr_rt_mtile_info info;
int err;
err = PVR_FEATURE_VALUE(pvr_dev, tile_size_x, &info.tile_size_x);
if (WARN_ON(err))
return err;
err = PVR_FEATURE_VALUE(pvr_dev, tile_size_y, &info.tile_size_y);
if (WARN_ON(err))
return err;
info.num_tiles_x = DIV_ROUND_UP(args->width, info.tile_size_x);
info.num_tiles_y = DIV_ROUND_UP(args->height, info.tile_size_y);
if (PVR_HAS_FEATURE(pvr_dev, simple_parameter_format_version)) {
u32 parameter_format;
err = PVR_FEATURE_VALUE(pvr_dev, simple_parameter_format_version,
&parameter_format);
if (WARN_ON(err))
return err;
WARN_ON(parameter_format != 2);
/*
* Set up 16 macrotiles with a multiple of 2x2 tiles per macrotile, which is
* aligned to a tile group.
*/
info.mtile_x[0] = DIV_ROUND_UP(info.num_tiles_x, 8) * 2;
info.mtile_y[0] = DIV_ROUND_UP(info.num_tiles_y, 8) * 2;
info.mtile_x[1] = 0;
info.mtile_y[1] = 0;
info.mtile_x[2] = 0;
info.mtile_y[2] = 0;
info.tile_max_x = round_up(info.num_tiles_x, 2) - 1;
info.tile_max_y = round_up(info.num_tiles_y, 2) - 1;
} else {
/* Set up 16 macrotiles with a multiple of 4x4 tiles per macrotile. */
info.mtile_x[0] = round_up(DIV_ROUND_UP(info.num_tiles_x, 4), 4);
info.mtile_y[0] = round_up(DIV_ROUND_UP(info.num_tiles_y, 4), 4);
info.mtile_x[1] = info.mtile_x[0] * 2;
info.mtile_y[1] = info.mtile_y[0] * 2;
info.mtile_x[2] = info.mtile_x[0] * 3;
info.mtile_y[2] = info.mtile_y[0] * 3;
info.tile_max_x = info.num_tiles_x - 1;
info.tile_max_y = info.num_tiles_y - 1;
}
hwrt->common.geom_caches_need_zeroing = false;
hwrt->common.isp_merge_lower_x = args->isp_merge_lower_x;
hwrt->common.isp_merge_lower_y = args->isp_merge_lower_y;
hwrt->common.isp_merge_upper_x = args->isp_merge_upper_x;
hwrt->common.isp_merge_upper_y = args->isp_merge_upper_y;
hwrt->common.isp_merge_scale_x = args->isp_merge_scale_x;
hwrt->common.isp_merge_scale_y = args->isp_merge_scale_y;
err = get_cr_multisamplectl_val(args->samples, false,
&hwrt->common.multi_sample_ctl);
if (err)
return err;
err = get_cr_multisamplectl_val(args->samples, true,
&hwrt->common.flipped_multi_sample_ctl);
if (err)
return err;
hwrt->common.mtile_stride = info.mtile_x[0] * info.mtile_y[0];
err = get_cr_te_aa_val(pvr_dev, args->samples, &hwrt->common.teaa);
if (err)
return err;
hwrt->common.screen_pixel_max =
(((args->width - 1) << ROGUE_CR_PPP_SCREEN_PIXXMAX_SHIFT) &
~ROGUE_CR_PPP_SCREEN_PIXXMAX_CLRMSK) |
(((args->height - 1) << ROGUE_CR_PPP_SCREEN_PIXYMAX_SHIFT) &
~ROGUE_CR_PPP_SCREEN_PIXYMAX_CLRMSK);
hwrt->common.te_screen =
((info.tile_max_x << ROGUE_CR_TE_SCREEN_XMAX_SHIFT) &
~ROGUE_CR_TE_SCREEN_XMAX_CLRMSK) |
((info.tile_max_y << ROGUE_CR_TE_SCREEN_YMAX_SHIFT) &
~ROGUE_CR_TE_SCREEN_YMAX_CLRMSK);
hwrt->common.te_mtile1 =
((info.mtile_x[0] << ROGUE_CR_TE_MTILE1_X1_SHIFT) & ~ROGUE_CR_TE_MTILE1_X1_CLRMSK) |
((info.mtile_x[1] << ROGUE_CR_TE_MTILE1_X2_SHIFT) & ~ROGUE_CR_TE_MTILE1_X2_CLRMSK) |
((info.mtile_x[2] << ROGUE_CR_TE_MTILE1_X3_SHIFT) & ~ROGUE_CR_TE_MTILE1_X3_CLRMSK);
hwrt->common.te_mtile2 =
((info.mtile_y[0] << ROGUE_CR_TE_MTILE2_Y1_SHIFT) & ~ROGUE_CR_TE_MTILE2_Y1_CLRMSK) |
((info.mtile_y[1] << ROGUE_CR_TE_MTILE2_Y2_SHIFT) & ~ROGUE_CR_TE_MTILE2_Y2_CLRMSK) |
((info.mtile_y[2] << ROGUE_CR_TE_MTILE2_Y3_SHIFT) & ~ROGUE_CR_TE_MTILE2_Y3_CLRMSK);
err = get_cr_isp_mtile_size_val(pvr_dev, args->samples, &info,
&hwrt->common.isp_mtile_size);
if (err)
return err;
hwrt->common.tpc_stride = geom_data_args->tpc_stride;
hwrt->common.tpc_size = geom_data_args->tpc_size;
hwrt->common.rgn_header_size = args->region_header_size;
err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_hwrtdata_common),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED, hwrtdata_common_init, hwrt,
&hwrt->common_fw_obj);
return err;
}
static void
hwrt_fw_data_init(void *cpu_ptr, void *priv)
{
struct pvr_hwrt_data *hwrt_data = priv;
memcpy(cpu_ptr, &hwrt_data->data, sizeof(hwrt_data->data));
}
static int
hwrt_data_init_fw_structure(struct pvr_file *pvr_file,
struct pvr_hwrt_dataset *hwrt,
struct drm_pvr_ioctl_create_hwrt_dataset_args *args,
struct drm_pvr_create_hwrt_rt_data_args *rt_data_args,
struct pvr_hwrt_data *hwrt_data)
{
struct drm_pvr_create_hwrt_geom_data_args *geom_data_args = &args->geom_data_args;
struct pvr_device *pvr_dev = pvr_file->pvr_dev;
struct rogue_fwif_rta_ctl *rta_ctl;
int free_list_i;
int err;
pvr_fw_object_get_fw_addr(hwrt->common_fw_obj,
&hwrt_data->data.hwrt_data_common_fw_addr);
for (free_list_i = 0; free_list_i < ARRAY_SIZE(hwrt->free_lists); free_list_i++) {
pvr_fw_object_get_fw_addr(hwrt->free_lists[free_list_i]->fw_obj,
&hwrt_data->data.freelists_fw_addr[free_list_i]);
}
hwrt_data->data.tail_ptrs_dev_addr = geom_data_args->tpc_dev_addr;
hwrt_data->data.vheap_table_dev_addr = geom_data_args->vheap_table_dev_addr;
hwrt_data->data.rtc_dev_addr = geom_data_args->rtc_dev_addr;
hwrt_data->data.pm_mlist_dev_addr = rt_data_args->pm_mlist_dev_addr;
hwrt_data->data.macrotile_array_dev_addr = rt_data_args->macrotile_array_dev_addr;
hwrt_data->data.rgn_header_dev_addr = rt_data_args->region_header_dev_addr;
rta_ctl = &hwrt_data->data.rta_ctl;
rta_ctl->render_target_index = 0;
rta_ctl->active_render_targets = 0;
rta_ctl->valid_render_targets_fw_addr = 0;
rta_ctl->rta_num_partial_renders_fw_addr = 0;
rta_ctl->max_rts = args->layers;
if (args->layers > 1) {
err = pvr_fw_object_create(pvr_dev, args->layers * SRTC_ENTRY_SIZE,
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
NULL, NULL, &hwrt_data->srtc_obj);
if (err)
return err;
pvr_fw_object_get_fw_addr(hwrt_data->srtc_obj,
&rta_ctl->valid_render_targets_fw_addr);
err = pvr_fw_object_create(pvr_dev, args->layers * RAA_ENTRY_SIZE,
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
NULL, NULL, &hwrt_data->raa_obj);
if (err)
goto err_put_shadow_rt_cache;
pvr_fw_object_get_fw_addr(hwrt_data->raa_obj,
&rta_ctl->rta_num_partial_renders_fw_addr);
}
err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_hwrtdata),
PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
hwrt_fw_data_init, hwrt_data, &hwrt_data->fw_obj);
if (err)
goto err_put_raa_obj;
pvr_free_list_add_hwrt(hwrt->free_lists[0], hwrt_data);
return 0;
err_put_raa_obj:
if (args->layers > 1)
pvr_fw_object_destroy(hwrt_data->raa_obj);
err_put_shadow_rt_cache:
if (args->layers > 1)
pvr_fw_object_destroy(hwrt_data->srtc_obj);
return err;
}
static void
hwrt_data_fini_fw_structure(struct pvr_hwrt_dataset *hwrt, int hwrt_nr)
{
struct pvr_hwrt_data *hwrt_data = &hwrt->data[hwrt_nr];
pvr_free_list_remove_hwrt(hwrt->free_lists[0], hwrt_data);
if (hwrt->max_rts > 1) {
pvr_fw_object_destroy(hwrt_data->raa_obj);
pvr_fw_object_destroy(hwrt_data->srtc_obj);
}
pvr_fw_object_destroy(hwrt_data->fw_obj);
}
/**
* pvr_hwrt_dataset_create() - Create a new HWRT dataset
* @pvr_file: Pointer to pvr_file structure.
* @args: Creation arguments from userspace.
*
* Return:
* * Pointer to new HWRT, or
* * ERR_PTR(-%ENOMEM) on out of memory.
*/
struct pvr_hwrt_dataset *
pvr_hwrt_dataset_create(struct pvr_file *pvr_file,
struct drm_pvr_ioctl_create_hwrt_dataset_args *args)
{
struct pvr_hwrt_dataset *hwrt;
int err;
/* Create and fill out the kernel structure */
hwrt = kzalloc(sizeof(*hwrt), GFP_KERNEL);
if (!hwrt)
return ERR_PTR(-ENOMEM);
kref_init(&hwrt->ref_count);
err = hwrt_init_kernel_structure(pvr_file, args, hwrt);
if (err < 0)
goto err_free;
err = hwrt_init_common_fw_structure(pvr_file, args, hwrt);
if (err < 0)
goto err_free;
for (int i = 0; i < ARRAY_SIZE(hwrt->data); i++) {
err = hwrt_data_init_fw_structure(pvr_file, hwrt, args,
&args->rt_data_args[i],
&hwrt->data[i]);
if (err < 0) {
i--;
/* Destroy already created structures. */
for (; i >= 0; i--)
hwrt_data_fini_fw_structure(hwrt, i);
goto err_free;
}
hwrt->data[i].hwrt_dataset = hwrt;
}
return hwrt;
err_free:
pvr_hwrt_dataset_put(hwrt);
return ERR_PTR(err);
}
static void
pvr_hwrt_dataset_release(struct kref *ref_count)
{
struct pvr_hwrt_dataset *hwrt =
container_of(ref_count, struct pvr_hwrt_dataset, ref_count);
for (int i = ARRAY_SIZE(hwrt->data) - 1; i >= 0; i--) {
WARN_ON(pvr_fw_structure_cleanup(hwrt->pvr_dev, ROGUE_FWIF_CLEANUP_HWRTDATA,
hwrt->data[i].fw_obj, 0));
hwrt_data_fini_fw_structure(hwrt, i);
}
hwrt_fini_common_fw_structure(hwrt);
hwrt_fini_kernel_structure(hwrt);
kfree(hwrt);
}
/**
* pvr_destroy_hwrt_datasets_for_file: Destroy any HWRT datasets associated
* with the given file.
* @pvr_file: Pointer to pvr_file structure.
*
* Removes all HWRT datasets associated with @pvr_file from the device
* hwrt_dataset list and drops initial references. HWRT datasets will then be
* destroyed once all outstanding references are dropped.
*/
void pvr_destroy_hwrt_datasets_for_file(struct pvr_file *pvr_file)
{
struct pvr_hwrt_dataset *hwrt;
unsigned long handle;
xa_for_each(&pvr_file->hwrt_handles, handle, hwrt) {
(void)hwrt;
pvr_hwrt_dataset_put(xa_erase(&pvr_file->hwrt_handles, handle));
}
}
/**
* pvr_hwrt_dataset_put() - Release reference on HWRT dataset
* @hwrt: Pointer to HWRT dataset to release reference on
*/
void
pvr_hwrt_dataset_put(struct pvr_hwrt_dataset *hwrt)
{
if (hwrt)
kref_put(&hwrt->ref_count, pvr_hwrt_dataset_release);
}

View file

@ -0,0 +1,165 @@
/* 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 */

View file

@ -0,0 +1,788 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_context.h"
#include "pvr_device.h"
#include "pvr_drv.h"
#include "pvr_gem.h"
#include "pvr_hwrt.h"
#include "pvr_job.h"
#include "pvr_mmu.h"
#include "pvr_power.h"
#include "pvr_rogue_fwif.h"
#include "pvr_rogue_fwif_client.h"
#include "pvr_stream.h"
#include "pvr_stream_defs.h"
#include "pvr_sync.h"
#include <drm/drm_exec.h>
#include <drm/drm_gem.h>
#include <linux/types.h>
#include <uapi/drm/pvr_drm.h>
static void pvr_job_release(struct kref *kref)
{
struct pvr_job *job = container_of(kref, struct pvr_job, ref_count);
xa_erase(&job->pvr_dev->job_ids, job->id);
pvr_hwrt_data_put(job->hwrt);
pvr_context_put(job->ctx);
WARN_ON(job->paired_job);
pvr_queue_job_cleanup(job);
pvr_job_release_pm_ref(job);
kfree(job->cmd);
kfree(job);
}
/**
* pvr_job_put() - Release reference on job
* @job: Target job.
*/
void
pvr_job_put(struct pvr_job *job)
{
if (job)
kref_put(&job->ref_count, pvr_job_release);
}
/**
* pvr_job_process_stream() - Build job FW structure from stream
* @pvr_dev: Device pointer.
* @cmd_defs: Stream definition.
* @stream: Pointer to command stream.
* @stream_size: Size of command stream, in bytes.
* @job: Pointer to job.
*
* Caller is responsible for freeing the output structure.
*
* Returns:
* * 0 on success,
* * -%ENOMEM on out of memory, or
* * -%EINVAL on malformed stream.
*/
static int
pvr_job_process_stream(struct pvr_device *pvr_dev, const struct pvr_stream_cmd_defs *cmd_defs,
void *stream, u32 stream_size, struct pvr_job *job)
{
int err;
job->cmd = kzalloc(cmd_defs->dest_size, GFP_KERNEL);
if (!job->cmd)
return -ENOMEM;
job->cmd_len = cmd_defs->dest_size;
err = pvr_stream_process(pvr_dev, cmd_defs, stream, stream_size, job->cmd);
if (err)
kfree(job->cmd);
return err;
}
static int pvr_fw_cmd_init(struct pvr_device *pvr_dev, struct pvr_job *job,
const struct pvr_stream_cmd_defs *stream_def,
u64 stream_userptr, u32 stream_len)
{
void *stream;
int err;
stream = kzalloc(stream_len, GFP_KERNEL);
if (!stream)
return -ENOMEM;
if (copy_from_user(stream, u64_to_user_ptr(stream_userptr), stream_len)) {
err = -EFAULT;
goto err_free_stream;
}
err = pvr_job_process_stream(pvr_dev, stream_def, stream, stream_len, job);
err_free_stream:
kfree(stream);
return err;
}
static u32
convert_geom_flags(u32 in_flags)
{
u32 out_flags = 0;
if (in_flags & DRM_PVR_SUBMIT_JOB_GEOM_CMD_FIRST)
out_flags |= ROGUE_GEOM_FLAGS_FIRSTKICK;
if (in_flags & DRM_PVR_SUBMIT_JOB_GEOM_CMD_LAST)
out_flags |= ROGUE_GEOM_FLAGS_LASTKICK;
if (in_flags & DRM_PVR_SUBMIT_JOB_GEOM_CMD_SINGLE_CORE)
out_flags |= ROGUE_GEOM_FLAGS_SINGLE_CORE;
return out_flags;
}
static u32
convert_frag_flags(u32 in_flags)
{
u32 out_flags = 0;
if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_SINGLE_CORE)
out_flags |= ROGUE_FRAG_FLAGS_SINGLE_CORE;
if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_DEPTHBUFFER)
out_flags |= ROGUE_FRAG_FLAGS_DEPTHBUFFER;
if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_STENCILBUFFER)
out_flags |= ROGUE_FRAG_FLAGS_STENCILBUFFER;
if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_PREVENT_CDM_OVERLAP)
out_flags |= ROGUE_FRAG_FLAGS_PREVENT_CDM_OVERLAP;
if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_SCRATCHBUFFER)
out_flags |= ROGUE_FRAG_FLAGS_SCRATCHBUFFER;
if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_GET_VIS_RESULTS)
out_flags |= ROGUE_FRAG_FLAGS_GET_VIS_RESULTS;
if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_DISABLE_PIXELMERGE)
out_flags |= ROGUE_FRAG_FLAGS_DISABLE_PIXELMERGE;
return out_flags;
}
static int
pvr_geom_job_fw_cmd_init(struct pvr_job *job,
struct drm_pvr_job *args)
{
struct rogue_fwif_cmd_geom *cmd;
int err;
if (args->flags & ~DRM_PVR_SUBMIT_JOB_GEOM_CMD_FLAGS_MASK)
return -EINVAL;
if (job->ctx->type != DRM_PVR_CTX_TYPE_RENDER)
return -EINVAL;
if (!job->hwrt)
return -EINVAL;
job->fw_ccb_cmd_type = ROGUE_FWIF_CCB_CMD_TYPE_GEOM;
err = pvr_fw_cmd_init(job->pvr_dev, job, &pvr_cmd_geom_stream,
args->cmd_stream, args->cmd_stream_len);
if (err)
return err;
cmd = job->cmd;
cmd->cmd_shared.cmn.frame_num = 0;
cmd->flags = convert_geom_flags(args->flags);
pvr_fw_object_get_fw_addr(job->hwrt->fw_obj, &cmd->cmd_shared.hwrt_data_fw_addr);
return 0;
}
static int
pvr_frag_job_fw_cmd_init(struct pvr_job *job,
struct drm_pvr_job *args)
{
struct rogue_fwif_cmd_frag *cmd;
int err;
if (args->flags & ~DRM_PVR_SUBMIT_JOB_FRAG_CMD_FLAGS_MASK)
return -EINVAL;
if (job->ctx->type != DRM_PVR_CTX_TYPE_RENDER)
return -EINVAL;
if (!job->hwrt)
return -EINVAL;
job->fw_ccb_cmd_type = (args->flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_PARTIAL_RENDER) ?
ROGUE_FWIF_CCB_CMD_TYPE_FRAG_PR :
ROGUE_FWIF_CCB_CMD_TYPE_FRAG;
err = pvr_fw_cmd_init(job->pvr_dev, job, &pvr_cmd_frag_stream,
args->cmd_stream, args->cmd_stream_len);
if (err)
return err;
cmd = job->cmd;
cmd->cmd_shared.cmn.frame_num = 0;
cmd->flags = convert_frag_flags(args->flags);
pvr_fw_object_get_fw_addr(job->hwrt->fw_obj, &cmd->cmd_shared.hwrt_data_fw_addr);
return 0;
}
static u32
convert_compute_flags(u32 in_flags)
{
u32 out_flags = 0;
if (in_flags & DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_PREVENT_ALL_OVERLAP)
out_flags |= ROGUE_COMPUTE_FLAG_PREVENT_ALL_OVERLAP;
if (in_flags & DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_SINGLE_CORE)
out_flags |= ROGUE_COMPUTE_FLAG_SINGLE_CORE;
return out_flags;
}
static int
pvr_compute_job_fw_cmd_init(struct pvr_job *job,
struct drm_pvr_job *args)
{
struct rogue_fwif_cmd_compute *cmd;
int err;
if (args->flags & ~DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_FLAGS_MASK)
return -EINVAL;
if (job->ctx->type != DRM_PVR_CTX_TYPE_COMPUTE)
return -EINVAL;
job->fw_ccb_cmd_type = ROGUE_FWIF_CCB_CMD_TYPE_CDM;
err = pvr_fw_cmd_init(job->pvr_dev, job, &pvr_cmd_compute_stream,
args->cmd_stream, args->cmd_stream_len);
if (err)
return err;
cmd = job->cmd;
cmd->common.frame_num = 0;
cmd->flags = convert_compute_flags(args->flags);
return 0;
}
static u32
convert_transfer_flags(u32 in_flags)
{
u32 out_flags = 0;
if (in_flags & DRM_PVR_SUBMIT_JOB_TRANSFER_CMD_SINGLE_CORE)
out_flags |= ROGUE_TRANSFER_FLAGS_SINGLE_CORE;
return out_flags;
}
static int
pvr_transfer_job_fw_cmd_init(struct pvr_job *job,
struct drm_pvr_job *args)
{
struct rogue_fwif_cmd_transfer *cmd;
int err;
if (args->flags & ~DRM_PVR_SUBMIT_JOB_TRANSFER_CMD_FLAGS_MASK)
return -EINVAL;
if (job->ctx->type != DRM_PVR_CTX_TYPE_TRANSFER_FRAG)
return -EINVAL;
job->fw_ccb_cmd_type = ROGUE_FWIF_CCB_CMD_TYPE_TQ_3D;
err = pvr_fw_cmd_init(job->pvr_dev, job, &pvr_cmd_transfer_stream,
args->cmd_stream, args->cmd_stream_len);
if (err)
return err;
cmd = job->cmd;
cmd->common.frame_num = 0;
cmd->flags = convert_transfer_flags(args->flags);
return 0;
}
static int
pvr_job_fw_cmd_init(struct pvr_job *job,
struct drm_pvr_job *args)
{
switch (args->type) {
case DRM_PVR_JOB_TYPE_GEOMETRY:
return pvr_geom_job_fw_cmd_init(job, args);
case DRM_PVR_JOB_TYPE_FRAGMENT:
return pvr_frag_job_fw_cmd_init(job, args);
case DRM_PVR_JOB_TYPE_COMPUTE:
return pvr_compute_job_fw_cmd_init(job, args);
case DRM_PVR_JOB_TYPE_TRANSFER_FRAG:
return pvr_transfer_job_fw_cmd_init(job, args);
default:
return -EINVAL;
}
}
/**
* struct pvr_job_data - Helper container for pairing jobs with the
* sync_ops supplied for them by the user.
*/
struct pvr_job_data {
/** @job: Pointer to the job. */
struct pvr_job *job;
/** @sync_ops: Pointer to the sync_ops associated with @job. */
struct drm_pvr_sync_op *sync_ops;
/** @sync_op_count: Number of members of @sync_ops. */
u32 sync_op_count;
};
/**
* prepare_job_syncs() - Prepare all sync objects for a single job.
* @pvr_file: PowerVR file.
* @job_data: Precreated job and sync_ops array.
* @signal_array: xarray to receive signal sync objects.
*
* Returns:
* * 0 on success, or
* * Any error code returned by pvr_sync_signal_array_collect_ops(),
* pvr_sync_add_deps_to_job(), drm_sched_job_add_resv_dependencies() or
* pvr_sync_signal_array_update_fences().
*/
static int
prepare_job_syncs(struct pvr_file *pvr_file,
struct pvr_job_data *job_data,
struct xarray *signal_array)
{
struct dma_fence *done_fence;
int err = pvr_sync_signal_array_collect_ops(signal_array,
from_pvr_file(pvr_file),
job_data->sync_op_count,
job_data->sync_ops);
if (err)
return err;
err = pvr_sync_add_deps_to_job(pvr_file, &job_data->job->base,
job_data->sync_op_count,
job_data->sync_ops, signal_array);
if (err)
return err;
if (job_data->job->hwrt) {
/* The geometry job writes the HWRT region headers, which are
* then read by the fragment job.
*/
struct drm_gem_object *obj =
gem_from_pvr_gem(job_data->job->hwrt->fw_obj->gem);
enum dma_resv_usage usage =
dma_resv_usage_rw(job_data->job->type ==
DRM_PVR_JOB_TYPE_GEOMETRY);
dma_resv_lock(obj->resv, NULL);
err = drm_sched_job_add_resv_dependencies(&job_data->job->base,
obj->resv, usage);
dma_resv_unlock(obj->resv);
if (err)
return err;
}
/* We need to arm the job to get the job done fence. */
done_fence = pvr_queue_job_arm(job_data->job);
err = pvr_sync_signal_array_update_fences(signal_array,
job_data->sync_op_count,
job_data->sync_ops,
done_fence);
return err;
}
/**
* prepare_job_syncs_for_each() - Prepare all sync objects for an array of jobs.
* @file: PowerVR file.
* @job_data: Array of precreated jobs and their sync_ops.
* @job_count: Number of jobs.
* @signal_array: xarray to receive signal sync objects.
*
* Returns:
* * 0 on success, or
* * Any error code returned by pvr_vm_bind_job_prepare_syncs().
*/
static int
prepare_job_syncs_for_each(struct pvr_file *pvr_file,
struct pvr_job_data *job_data,
u32 *job_count,
struct xarray *signal_array)
{
for (u32 i = 0; i < *job_count; i++) {
int err = prepare_job_syncs(pvr_file, &job_data[i],
signal_array);
if (err) {
*job_count = i;
return err;
}
}
return 0;
}
static struct pvr_job *
create_job(struct pvr_device *pvr_dev,
struct pvr_file *pvr_file,
struct drm_pvr_job *args)
{
struct pvr_job *job = NULL;
int err;
if (!args->cmd_stream || !args->cmd_stream_len)
return ERR_PTR(-EINVAL);
if (args->type != DRM_PVR_JOB_TYPE_GEOMETRY &&
args->type != DRM_PVR_JOB_TYPE_FRAGMENT &&
(args->hwrt.set_handle || args->hwrt.data_index))
return ERR_PTR(-EINVAL);
job = kzalloc(sizeof(*job), GFP_KERNEL);
if (!job)
return ERR_PTR(-ENOMEM);
kref_init(&job->ref_count);
job->type = args->type;
job->pvr_dev = pvr_dev;
err = xa_alloc(&pvr_dev->job_ids, &job->id, job, xa_limit_32b, GFP_KERNEL);
if (err)
goto err_put_job;
job->ctx = pvr_context_lookup(pvr_file, args->context_handle);
if (!job->ctx) {
err = -EINVAL;
goto err_put_job;
}
if (args->hwrt.set_handle) {
job->hwrt = pvr_hwrt_data_lookup(pvr_file, args->hwrt.set_handle,
args->hwrt.data_index);
if (!job->hwrt) {
err = -EINVAL;
goto err_put_job;
}
}
err = pvr_job_fw_cmd_init(job, args);
if (err)
goto err_put_job;
err = pvr_queue_job_init(job);
if (err)
goto err_put_job;
return job;
err_put_job:
pvr_job_put(job);
return ERR_PTR(err);
}
/**
* pvr_job_data_fini() - Cleanup all allocs used to set up job submission.
* @job_data: Job data array.
* @job_count: Number of members of @job_data.
*/
static void
pvr_job_data_fini(struct pvr_job_data *job_data, u32 job_count)
{
for (u32 i = 0; i < job_count; i++) {
pvr_job_put(job_data[i].job);
kvfree(job_data[i].sync_ops);
}
}
/**
* pvr_job_data_init() - Init an array of created jobs, associating them with
* the appropriate sync_ops args, which will be copied in.
* @pvr_dev: Target PowerVR device.
* @pvr_file: Pointer to PowerVR file structure.
* @job_args: Job args array copied from user.
* @job_count: Number of members of @job_args.
* @job_data_out: Job data array.
*/
static int pvr_job_data_init(struct pvr_device *pvr_dev,
struct pvr_file *pvr_file,
struct drm_pvr_job *job_args,
u32 *job_count,
struct pvr_job_data *job_data_out)
{
int err = 0, i = 0;
for (; i < *job_count; i++) {
job_data_out[i].job =
create_job(pvr_dev, pvr_file, &job_args[i]);
err = PTR_ERR_OR_ZERO(job_data_out[i].job);
if (err) {
*job_count = i;
job_data_out[i].job = NULL;
goto err_cleanup;
}
err = PVR_UOBJ_GET_ARRAY(job_data_out[i].sync_ops,
&job_args[i].sync_ops);
if (err) {
*job_count = i;
/* Ensure the job created above is also cleaned up. */
i++;
goto err_cleanup;
}
job_data_out[i].sync_op_count = job_args[i].sync_ops.count;
}
return 0;
err_cleanup:
pvr_job_data_fini(job_data_out, i);
return err;
}
static void
push_jobs(struct pvr_job_data *job_data, u32 job_count)
{
for (u32 i = 0; i < job_count; i++)
pvr_queue_job_push(job_data[i].job);
}
static int
prepare_fw_obj_resv(struct drm_exec *exec, struct pvr_fw_object *fw_obj)
{
return drm_exec_prepare_obj(exec, gem_from_pvr_gem(fw_obj->gem), 1);
}
static int
jobs_lock_all_objs(struct drm_exec *exec, struct pvr_job_data *job_data,
u32 job_count)
{
for (u32 i = 0; i < job_count; i++) {
struct pvr_job *job = job_data[i].job;
/* Grab a lock on a the context, to guard against
* concurrent submission to the same queue.
*/
int err = drm_exec_lock_obj(exec,
gem_from_pvr_gem(job->ctx->fw_obj->gem));
if (err)
return err;
if (job->hwrt) {
err = prepare_fw_obj_resv(exec,
job->hwrt->fw_obj);
if (err)
return err;
}
}
return 0;
}
static int
prepare_job_resvs_for_each(struct drm_exec *exec, struct pvr_job_data *job_data,
u32 job_count)
{
drm_exec_until_all_locked(exec) {
int err = jobs_lock_all_objs(exec, job_data, job_count);
drm_exec_retry_on_contention(exec);
if (err)
return err;
}
return 0;
}
static void
update_job_resvs(struct pvr_job *job)
{
if (job->hwrt) {
enum dma_resv_usage usage = job->type == DRM_PVR_JOB_TYPE_GEOMETRY ?
DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ;
struct drm_gem_object *obj = gem_from_pvr_gem(job->hwrt->fw_obj->gem);
dma_resv_add_fence(obj->resv, &job->base.s_fence->finished, usage);
}
}
static void
update_job_resvs_for_each(struct pvr_job_data *job_data, u32 job_count)
{
for (u32 i = 0; i < job_count; i++)
update_job_resvs(job_data[i].job);
}
static bool can_combine_jobs(struct pvr_job *a, struct pvr_job *b)
{
struct pvr_job *geom_job = a, *frag_job = b;
struct dma_fence *fence;
unsigned long index;
/* Geometry and fragment jobs can be combined if they are queued to the
* same context and targeting the same HWRT.
*/
if (a->type != DRM_PVR_JOB_TYPE_GEOMETRY ||
b->type != DRM_PVR_JOB_TYPE_FRAGMENT ||
a->ctx != b->ctx ||
a->hwrt != b->hwrt)
return false;
xa_for_each(&frag_job->base.dependencies, index, fence) {
/* We combine when we see an explicit geom -> frag dep. */
if (&geom_job->base.s_fence->scheduled == fence)
return true;
}
return false;
}
static struct dma_fence *
get_last_queued_job_scheduled_fence(struct pvr_queue *queue,
struct pvr_job_data *job_data,
u32 cur_job_pos)
{
/* We iterate over the current job array in reverse order to grab the
* last to-be-queued job targeting the same queue.
*/
for (u32 i = cur_job_pos; i > 0; i--) {
struct pvr_job *job = job_data[i - 1].job;
if (job->ctx == queue->ctx && job->type == queue->type)
return dma_fence_get(&job->base.s_fence->scheduled);
}
/* If we didn't find any, we just return the last queued job scheduled
* fence attached to the queue.
*/
return dma_fence_get(queue->last_queued_job_scheduled_fence);
}
static int
pvr_jobs_link_geom_frag(struct pvr_job_data *job_data, u32 *job_count)
{
for (u32 i = 0; i < *job_count - 1; i++) {
struct pvr_job *geom_job = job_data[i].job;
struct pvr_job *frag_job = job_data[i + 1].job;
struct pvr_queue *frag_queue;
struct dma_fence *f;
if (!can_combine_jobs(job_data[i].job, job_data[i + 1].job))
continue;
/* The fragment job will be submitted by the geometry queue. We
* need to make sure it comes after all the other fragment jobs
* queued before it.
*/
frag_queue = pvr_context_get_queue_for_job(frag_job->ctx,
frag_job->type);
f = get_last_queued_job_scheduled_fence(frag_queue, job_data,
i);
if (f) {
int err = drm_sched_job_add_dependency(&geom_job->base,
f);
if (err) {
*job_count = i;
return err;
}
}
/* The KCCB slot will be reserved by the geometry job, so we can
* drop the KCCB fence on the fragment job.
*/
pvr_kccb_fence_put(frag_job->kccb_fence);
frag_job->kccb_fence = NULL;
geom_job->paired_job = frag_job;
frag_job->paired_job = geom_job;
/* Skip the fragment job we just paired to the geometry job. */
i++;
}
return 0;
}
/**
* pvr_submit_jobs() - Submit jobs to the GPU
* @pvr_dev: Target PowerVR device.
* @pvr_file: Pointer to PowerVR file structure.
* @args: Ioctl args.
* @job_count: Number of jobs in @jobs_args. On error this will be updated
* with the index into @jobs_args where the error occurred.
*
* This initial implementation is entirely synchronous; on return the GPU will
* be idle. This will not be the case for future implementations.
*
* Returns:
* * 0 on success,
* * -%EFAULT if arguments can not be copied from user space, or
* * -%EINVAL on invalid arguments, or
* * Any other error.
*/
int
pvr_submit_jobs(struct pvr_device *pvr_dev, struct pvr_file *pvr_file,
struct drm_pvr_ioctl_submit_jobs_args *args)
{
struct pvr_job_data *job_data = NULL;
struct drm_pvr_job *job_args;
struct xarray signal_array;
u32 jobs_alloced = 0;
struct drm_exec exec;
int err;
if (!args->jobs.count)
return -EINVAL;
err = PVR_UOBJ_GET_ARRAY(job_args, &args->jobs);
if (err)
return err;
job_data = kvmalloc_array(args->jobs.count, sizeof(*job_data),
GFP_KERNEL | __GFP_ZERO);
if (!job_data) {
err = -ENOMEM;
goto out_free;
}
err = pvr_job_data_init(pvr_dev, pvr_file, job_args, &args->jobs.count,
job_data);
if (err)
goto out_free;
jobs_alloced = args->jobs.count;
/*
* Flush MMU if needed - this has been deferred until now to avoid
* overuse of this expensive operation.
*/
err = pvr_mmu_flush_exec(pvr_dev, false);
if (err)
goto out_job_data_cleanup;
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT | DRM_EXEC_IGNORE_DUPLICATES);
xa_init_flags(&signal_array, XA_FLAGS_ALLOC);
err = prepare_job_syncs_for_each(pvr_file, job_data, &args->jobs.count,
&signal_array);
if (err)
goto out_exec_fini;
err = prepare_job_resvs_for_each(&exec, job_data, args->jobs.count);
if (err)
goto out_exec_fini;
err = pvr_jobs_link_geom_frag(job_data, &args->jobs.count);
if (err)
goto out_exec_fini;
/* Anything after that point must succeed because we start exposing job
* finished fences to the outside world.
*/
update_job_resvs_for_each(job_data, args->jobs.count);
push_jobs(job_data, args->jobs.count);
pvr_sync_signal_array_push_fences(&signal_array);
err = 0;
out_exec_fini:
drm_exec_fini(&exec);
pvr_sync_signal_array_cleanup(&signal_array);
out_job_data_cleanup:
pvr_job_data_fini(job_data, jobs_alloced);
out_free:
kvfree(job_data);
kvfree(job_args);
return err;
}

View file

@ -0,0 +1,161 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_JOB_H
#define PVR_JOB_H
#include <uapi/drm/pvr_drm.h>
#include <linux/kref.h>
#include <linux/types.h>
#include <drm/drm_gem.h>
#include <drm/gpu_scheduler.h>
#include "pvr_power.h"
/* Forward declaration from "pvr_context.h". */
struct pvr_context;
/* Forward declarations from "pvr_device.h". */
struct pvr_device;
struct pvr_file;
/* Forward declarations from "pvr_hwrt.h". */
struct pvr_hwrt_data;
/* Forward declaration from "pvr_queue.h". */
struct pvr_queue;
struct pvr_job {
/** @base: drm_sched_job object. */
struct drm_sched_job base;
/** @ref_count: Refcount for job. */
struct kref ref_count;
/** @type: Type of job. */
enum drm_pvr_job_type type;
/** @id: Job ID number. */
u32 id;
/**
* @paired_job: Job paired to this job.
*
* This field is only meaningful for geometry and fragment jobs.
*
* Paired jobs are executed on the same context, and need to be submitted
* atomically to the FW, to make sure the partial render logic has a
* fragment job to execute when the Parameter Manager runs out of memory.
*
* The geometry job should point to the fragment job it's paired with,
* and the fragment job should point to the geometry job it's paired with.
*/
struct pvr_job *paired_job;
/** @cccb_fence: Fence used to wait for CCCB space. */
struct dma_fence *cccb_fence;
/** @kccb_fence: Fence used to wait for KCCB space. */
struct dma_fence *kccb_fence;
/** @done_fence: Fence to signal when the job is done. */
struct dma_fence *done_fence;
/** @pvr_dev: Device pointer. */
struct pvr_device *pvr_dev;
/** @ctx: Pointer to owning context. */
struct pvr_context *ctx;
/** @cmd: Command data. Format depends on @type. */
void *cmd;
/** @cmd_len: Length of command data, in bytes. */
u32 cmd_len;
/**
* @fw_ccb_cmd_type: Firmware CCB command type. Must be one of %ROGUE_FWIF_CCB_CMD_TYPE_*.
*/
u32 fw_ccb_cmd_type;
/** @hwrt: HWRT object. Will be NULL for compute and transfer jobs. */
struct pvr_hwrt_data *hwrt;
/**
* @has_pm_ref: True if the job has a power ref, thus forcing the GPU to stay on until
* the job is done.
*/
bool has_pm_ref;
};
/**
* pvr_job_get() - Take additional reference on job.
* @job: Job pointer.
*
* Call pvr_job_put() to release.
*
* Returns:
* * The requested job on success, or
* * %NULL if no job pointer passed.
*/
static __always_inline struct pvr_job *
pvr_job_get(struct pvr_job *job)
{
if (job)
kref_get(&job->ref_count);
return job;
}
void pvr_job_put(struct pvr_job *job);
/**
* pvr_job_release_pm_ref() - Release the PM ref if the job acquired it.
* @job: The job to release the PM ref on.
*/
static __always_inline void
pvr_job_release_pm_ref(struct pvr_job *job)
{
if (job->has_pm_ref) {
pvr_power_put(job->pvr_dev);
job->has_pm_ref = false;
}
}
/**
* pvr_job_get_pm_ref() - Get a PM ref and attach it to the job.
* @job: The job to attach the PM ref to.
*
* Return:
* * 0 on success, or
* * Any error returned by pvr_power_get() otherwise.
*/
static __always_inline int
pvr_job_get_pm_ref(struct pvr_job *job)
{
int err;
if (job->has_pm_ref)
return 0;
err = pvr_power_get(job->pvr_dev);
if (!err)
job->has_pm_ref = true;
return err;
}
int pvr_job_wait_first_non_signaled_native_dep(struct pvr_job *job);
bool pvr_job_non_native_deps_done(struct pvr_job *job);
int pvr_job_fits_in_cccb(struct pvr_job *job, unsigned long native_dep_count);
void pvr_job_submit(struct pvr_job *job);
int pvr_submit_jobs(struct pvr_device *pvr_dev, struct pvr_file *pvr_file,
struct drm_pvr_ioctl_submit_jobs_args *args);
#endif /* PVR_JOB_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,108 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_MMU_H
#define PVR_MMU_H
#include <linux/memory.h>
#include <linux/types.h>
/* Forward declaration from "pvr_device.h" */
struct pvr_device;
/* Forward declaration from "pvr_mmu.c" */
struct pvr_mmu_context;
struct pvr_mmu_op_context;
/* Forward declaration from "pvr_vm.c" */
struct pvr_vm_context;
/* Forward declaration from <linux/scatterlist.h> */
struct sg_table;
/**
* DOC: Public API (constants)
*
* .. c:macro:: PVR_DEVICE_PAGE_SIZE
*
* Fixed page size referenced by leaf nodes in the page table tree
* structure. In the current implementation, this value is pegged to the
* CPU page size (%PAGE_SIZE). It is therefore an error to specify a CPU
* page size which is not also a supported device page size. The supported
* device page sizes are: 4KiB, 16KiB, 64KiB, 256KiB, 1MiB and 2MiB.
*
* .. c:macro:: PVR_DEVICE_PAGE_SHIFT
*
* Shift value used to efficiently multiply or divide by
* %PVR_DEVICE_PAGE_SIZE.
*
* This value is derived from %PVR_DEVICE_PAGE_SIZE.
*
* .. c:macro:: PVR_DEVICE_PAGE_MASK
*
* Mask used to round a value down to the nearest multiple of
* %PVR_DEVICE_PAGE_SIZE. When bitwise negated, it will indicate whether a
* value is already a multiple of %PVR_DEVICE_PAGE_SIZE.
*
* This value is derived from %PVR_DEVICE_PAGE_SIZE.
*/
/* PVR_DEVICE_PAGE_SIZE determines the page size */
#define PVR_DEVICE_PAGE_SIZE (PAGE_SIZE)
#define PVR_DEVICE_PAGE_SHIFT (PAGE_SHIFT)
#define PVR_DEVICE_PAGE_MASK (PAGE_MASK)
/**
* DOC: Page table index utilities (constants)
*
* .. c:macro:: PVR_PAGE_TABLE_ADDR_SPACE_SIZE
*
* Size of device-virtual address space which can be represented in the page
* table structure.
*
* This value is checked at runtime against
* &pvr_device_features.virtual_address_space_bits by
* pvr_vm_create_context(), which will return an error if the feature value
* does not match this constant.
*
* .. admonition:: Future work
*
* It should be possible to support other values of
* &pvr_device_features.virtual_address_space_bits, but so far no
* hardware has been created which advertises an unsupported value.
*
* .. c:macro:: PVR_PAGE_TABLE_ADDR_BITS
*
* Number of bits needed to represent any value less than
* %PVR_PAGE_TABLE_ADDR_SPACE_SIZE exactly.
*
* .. c:macro:: PVR_PAGE_TABLE_ADDR_MASK
*
* Bitmask of device-virtual addresses which are valid in the page table
* structure.
*
* This value is derived from %PVR_PAGE_TABLE_ADDR_SPACE_SIZE, so the same
* notes on that constant apply here.
*/
#define PVR_PAGE_TABLE_ADDR_SPACE_SIZE SZ_1T
#define PVR_PAGE_TABLE_ADDR_BITS __ffs(PVR_PAGE_TABLE_ADDR_SPACE_SIZE)
#define PVR_PAGE_TABLE_ADDR_MASK (PVR_PAGE_TABLE_ADDR_SPACE_SIZE - 1)
void pvr_mmu_flush_request_all(struct pvr_device *pvr_dev);
int pvr_mmu_flush_exec(struct pvr_device *pvr_dev, bool wait);
struct pvr_mmu_context *pvr_mmu_context_create(struct pvr_device *pvr_dev);
void pvr_mmu_context_destroy(struct pvr_mmu_context *ctx);
dma_addr_t pvr_mmu_get_root_table_dma_addr(struct pvr_mmu_context *ctx);
void pvr_mmu_op_context_destroy(struct pvr_mmu_op_context *op_ctx);
struct pvr_mmu_op_context *
pvr_mmu_op_context_create(struct pvr_mmu_context *ctx,
struct sg_table *sgt, u64 sgt_offset, u64 size);
int pvr_mmu_map(struct pvr_mmu_op_context *op_ctx, u64 size, u64 flags,
u64 device_addr);
int pvr_mmu_unmap(struct pvr_mmu_op_context *op_ctx, u64 device_addr, u64 size);
#endif /* PVR_MMU_H */

View file

@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_params.h"
#include <linux/cache.h>
#include <linux/moduleparam.h>
static struct pvr_device_params pvr_device_param_defaults __read_mostly = {
#define X(type_, name_, value_, desc_, ...) .name_ = (value_),
PVR_DEVICE_PARAMS
#undef X
};
#define PVR_DEVICE_PARAM_NAMED(name_, type_, desc_) \
module_param_named(name_, pvr_device_param_defaults.name_, type_, \
0400); \
MODULE_PARM_DESC(name_, desc_);
/*
* This list of defines must contain every type specified in "pvr_params.h" as
* ``PVR_PARAM_TYPE_*_C``.
*/
#define PVR_PARAM_TYPE_X32_MODPARAM uint
#define X(type_, name_, value_, desc_, ...) \
PVR_DEVICE_PARAM_NAMED(name_, PVR_PARAM_TYPE_##type_##_MODPARAM, desc_);
PVR_DEVICE_PARAMS
#undef X
int
pvr_device_params_init(struct pvr_device_params *params)
{
/*
* If heap-allocated parameters are added in the future (e.g.
* modparam's charp type), they must be handled specially here (via
* kstrdup() in the case of charp). Since that's not necessary yet,
* a straight copy will do for now. This change will also require a
* pvr_device_params_fini() function to free any heap-allocated copies.
*/
*params = pvr_device_param_defaults;
return 0;
}
#if defined(CONFIG_DEBUG_FS)
#include "pvr_device.h"
#include <linux/dcache.h>
#include <linux/debugfs.h>
#include <linux/export.h>
#include <linux/fs.h>
#include <linux/stddef.h>
/*
* This list of defines must contain every type specified in "pvr_params.h" as
* ``PVR_PARAM_TYPE_*_C``.
*/
#define PVR_PARAM_TYPE_X32_FMT "0x%08llx"
#define X_SET(name_, mode_) X_SET_##mode_(name_)
#define X_SET_DEF(name_, update_, mode_) X_SET_DEF_##mode_(name_, update_)
#define X_SET_RO(name_) NULL
#define X_SET_RW(name_) __pvr_device_param_##name_##set
#define X_SET_DEF_RO(name_, update_)
#define X_SET_DEF_RW(name_, update_) \
static int \
X_SET_RW(name_)(void *data, u64 val) \
{ \
struct pvr_device *pvr_dev = data; \
/* This is not just (update_) to suppress -Waddress. */ \
if ((void *)(update_) != NULL) \
(update_)(pvr_dev, pvr_dev->params.name_, val); \
pvr_dev->params.name_ = val; \
return 0; \
}
#define X(type_, name_, value_, desc_, mode_, update_) \
static int \
__pvr_device_param_##name_##_get(void *data, u64 *val) \
{ \
struct pvr_device *pvr_dev = data; \
*val = pvr_dev->params.name_; \
return 0; \
} \
X_SET_DEF(name_, update_, mode_) \
static int \
__pvr_device_param_##name_##_open(struct inode *inode, \
struct file *file) \
{ \
__simple_attr_check_format(PVR_PARAM_TYPE_##type_##_FMT, \
0ull); \
return simple_attr_open(inode, file, \
__pvr_device_param_##name_##_get, \
X_SET(name_, mode_), \
PVR_PARAM_TYPE_##type_##_FMT); \
}
PVR_DEVICE_PARAMS
#undef X
#undef X_SET
#undef X_SET_RO
#undef X_SET_RW
#undef X_SET_DEF
#undef X_SET_DEF_RO
#undef X_SET_DEF_RW
static struct {
#define X(type_, name_, value_, desc_, mode_, update_) \
const struct file_operations name_;
PVR_DEVICE_PARAMS
#undef X
} pvr_device_param_debugfs_fops = {
#define X(type_, name_, value_, desc_, mode_, update_) \
.name_ = { \
.owner = THIS_MODULE, \
.open = __pvr_device_param_##name_##_open, \
.release = simple_attr_release, \
.read = simple_attr_read, \
.write = simple_attr_write, \
.llseek = generic_file_llseek, \
},
PVR_DEVICE_PARAMS
#undef X
};
void
pvr_params_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir)
{
#define X_MODE(mode_) X_MODE_##mode_
#define X_MODE_RO 0400
#define X_MODE_RW 0600
#define X(type_, name_, value_, desc_, mode_, update_) \
debugfs_create_file(#name_, X_MODE(mode_), dir, pvr_dev, \
&pvr_device_param_debugfs_fops.name_);
PVR_DEVICE_PARAMS
#undef X
#undef X_MODE
#undef X_MODE_RO
#undef X_MODE_RW
}
#endif

View file

@ -0,0 +1,72 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_PARAMS_H
#define PVR_PARAMS_H
#include "pvr_rogue_fwif.h"
#include <linux/cache.h>
#include <linux/compiler_attributes.h>
/*
* This is the definitive list of types allowed in the definition of
* %PVR_DEVICE_PARAMS.
*/
#define PVR_PARAM_TYPE_X32_C u32
/*
* This macro defines all device-specific parameters; that is parameters which
* are set independently per device.
*
* The X-macro accepts the following arguments. Arguments marked with [debugfs]
* are ignored when debugfs is disabled; values used for these arguments may
* safely be gated behind CONFIG_DEBUG_FS.
*
* @type_: The definitive list of allowed values is PVR_PARAM_TYPE_*_C.
* @name_: Name of the parameter. This is used both as the field name in C and
* stringified as the parameter name.
* @value_: Initial/default value.
* @desc_: String literal used as help text to describe the usage of this
* parameter.
* @mode_: [debugfs] One of {RO,RW}. The access mode of the debugfs entry for
* this parameter.
* @update_: [debugfs] When debugfs support is enabled, parameters may be
* updated at runtime. When this happens, this function will be
* called to allow changes to propagate. The signature of this
* function is:
*
* void (*)(struct pvr_device *pvr_dev, T old_val, T new_val)
*
* Where T is the C type associated with @type_.
*
* If @mode_ does not allow write access, this function will never be
* called. In this case, or if no update callback is required, you
* should specify NULL for this argument.
*/
#define PVR_DEVICE_PARAMS \
X(X32, fw_trace_mask, ROGUE_FWIF_LOG_TYPE_NONE, \
"Enable FW trace for the specified groups. Specifying 0 disables " \
"all FW tracing.", \
RW, pvr_fw_trace_mask_update)
struct pvr_device_params {
#define X(type_, name_, value_, desc_, ...) \
PVR_PARAM_TYPE_##type_##_C name_;
PVR_DEVICE_PARAMS
#undef X
};
int pvr_device_params_init(struct pvr_device_params *params);
#if defined(CONFIG_DEBUG_FS)
/* Forward declaration from "pvr_device.h". */
struct pvr_device;
/* Forward declaration from <linux/dcache.h>. */
struct dentry;
void pvr_params_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir);
#endif /* defined(CONFIG_DEBUG_FS) */
#endif /* PVR_PARAMS_H */

View file

@ -0,0 +1,433 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_device.h"
#include "pvr_fw.h"
#include "pvr_fw_startstop.h"
#include "pvr_power.h"
#include "pvr_queue.h"
#include "pvr_rogue_fwif.h"
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#define POWER_SYNC_TIMEOUT_US (1000000) /* 1s */
#define WATCHDOG_TIME_MS (500)
/**
* pvr_device_lost() - Mark GPU device as lost
* @pvr_dev: Target PowerVR device.
*
* This will cause the DRM device to be unplugged.
*/
void
pvr_device_lost(struct pvr_device *pvr_dev)
{
if (!pvr_dev->lost) {
pvr_dev->lost = true;
drm_dev_unplug(from_pvr_device(pvr_dev));
}
}
static int
pvr_power_send_command(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd *pow_cmd)
{
struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
u32 slot_nr;
u32 value;
int err;
WRITE_ONCE(*fw_dev->power_sync, 0);
err = pvr_kccb_send_cmd_powered(pvr_dev, pow_cmd, &slot_nr);
if (err)
return err;
/* Wait for FW to acknowledge. */
return readl_poll_timeout(pvr_dev->fw_dev.power_sync, value, value != 0, 100,
POWER_SYNC_TIMEOUT_US);
}
static int
pvr_power_request_idle(struct pvr_device *pvr_dev)
{
struct rogue_fwif_kccb_cmd pow_cmd;
/* Send FORCED_IDLE request to FW. */
pow_cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_POW;
pow_cmd.cmd_data.pow_data.pow_type = ROGUE_FWIF_POW_FORCED_IDLE_REQ;
pow_cmd.cmd_data.pow_data.power_req_data.pow_request_type = ROGUE_FWIF_POWER_FORCE_IDLE;
return pvr_power_send_command(pvr_dev, &pow_cmd);
}
static int
pvr_power_request_pwr_off(struct pvr_device *pvr_dev)
{
struct rogue_fwif_kccb_cmd pow_cmd;
/* Send POW_OFF request to firmware. */
pow_cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_POW;
pow_cmd.cmd_data.pow_data.pow_type = ROGUE_FWIF_POW_OFF_REQ;
pow_cmd.cmd_data.pow_data.power_req_data.forced = true;
return pvr_power_send_command(pvr_dev, &pow_cmd);
}
static int
pvr_power_fw_disable(struct pvr_device *pvr_dev, bool hard_reset)
{
if (!hard_reset) {
int err;
cancel_delayed_work_sync(&pvr_dev->watchdog.work);
err = pvr_power_request_idle(pvr_dev);
if (err)
return err;
err = pvr_power_request_pwr_off(pvr_dev);
if (err)
return err;
}
return pvr_fw_stop(pvr_dev);
}
static int
pvr_power_fw_enable(struct pvr_device *pvr_dev)
{
int err;
err = pvr_fw_start(pvr_dev);
if (err)
return err;
err = pvr_wait_for_fw_boot(pvr_dev);
if (err) {
drm_err(from_pvr_device(pvr_dev), "Firmware failed to boot\n");
pvr_fw_stop(pvr_dev);
return err;
}
queue_delayed_work(pvr_dev->sched_wq, &pvr_dev->watchdog.work,
msecs_to_jiffies(WATCHDOG_TIME_MS));
return 0;
}
bool
pvr_power_is_idle(struct pvr_device *pvr_dev)
{
/*
* FW power state can be out of date if a KCCB command has been submitted but the FW hasn't
* started processing it yet. So also check the KCCB status.
*/
enum rogue_fwif_pow_state pow_state = READ_ONCE(pvr_dev->fw_dev.fwif_sysdata->pow_state);
bool kccb_idle = pvr_kccb_is_idle(pvr_dev);
return (pow_state == ROGUE_FWIF_POW_IDLE) && kccb_idle;
}
static bool
pvr_watchdog_kccb_stalled(struct pvr_device *pvr_dev)
{
/* Check KCCB commands are progressing. */
u32 kccb_cmds_executed = pvr_dev->fw_dev.fwif_osdata->kccb_cmds_executed;
bool kccb_is_idle = pvr_kccb_is_idle(pvr_dev);
if (pvr_dev->watchdog.old_kccb_cmds_executed == kccb_cmds_executed && !kccb_is_idle) {
pvr_dev->watchdog.kccb_stall_count++;
/*
* If we have commands pending with no progress for 2 consecutive polls then
* consider KCCB command processing stalled.
*/
if (pvr_dev->watchdog.kccb_stall_count == 2) {
pvr_dev->watchdog.kccb_stall_count = 0;
return true;
}
} else if (pvr_dev->watchdog.old_kccb_cmds_executed == kccb_cmds_executed) {
bool has_active_contexts;
mutex_lock(&pvr_dev->queues.lock);
has_active_contexts = list_empty(&pvr_dev->queues.active);
mutex_unlock(&pvr_dev->queues.lock);
if (has_active_contexts) {
/* Send a HEALTH_CHECK command so we can verify FW is still alive. */
struct rogue_fwif_kccb_cmd health_check_cmd;
health_check_cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_HEALTH_CHECK;
pvr_kccb_send_cmd_powered(pvr_dev, &health_check_cmd, NULL);
}
} else {
pvr_dev->watchdog.old_kccb_cmds_executed = kccb_cmds_executed;
pvr_dev->watchdog.kccb_stall_count = 0;
}
return false;
}
static void
pvr_watchdog_worker(struct work_struct *work)
{
struct pvr_device *pvr_dev = container_of(work, struct pvr_device,
watchdog.work.work);
bool stalled;
if (pvr_dev->lost)
return;
if (pm_runtime_get_if_in_use(from_pvr_device(pvr_dev)->dev) <= 0)
goto out_requeue;
if (!pvr_dev->fw_dev.booted)
goto out_pm_runtime_put;
stalled = pvr_watchdog_kccb_stalled(pvr_dev);
if (stalled) {
drm_err(from_pvr_device(pvr_dev), "FW stalled, trying hard reset");
pvr_power_reset(pvr_dev, true);
/* Device may be lost at this point. */
}
out_pm_runtime_put:
pm_runtime_put(from_pvr_device(pvr_dev)->dev);
out_requeue:
if (!pvr_dev->lost) {
queue_delayed_work(pvr_dev->sched_wq, &pvr_dev->watchdog.work,
msecs_to_jiffies(WATCHDOG_TIME_MS));
}
}
/**
* pvr_watchdog_init() - Initialise watchdog for device
* @pvr_dev: Target PowerVR device.
*
* Returns:
* * 0 on success, or
* * -%ENOMEM on out of memory.
*/
int
pvr_watchdog_init(struct pvr_device *pvr_dev)
{
INIT_DELAYED_WORK(&pvr_dev->watchdog.work, pvr_watchdog_worker);
return 0;
}
int
pvr_power_device_suspend(struct device *dev)
{
struct platform_device *plat_dev = to_platform_device(dev);
struct drm_device *drm_dev = platform_get_drvdata(plat_dev);
struct pvr_device *pvr_dev = to_pvr_device(drm_dev);
int err = 0;
int idx;
if (!drm_dev_enter(drm_dev, &idx))
return -EIO;
if (pvr_dev->fw_dev.booted) {
err = pvr_power_fw_disable(pvr_dev, false);
if (err)
goto err_drm_dev_exit;
}
clk_disable_unprepare(pvr_dev->mem_clk);
clk_disable_unprepare(pvr_dev->sys_clk);
clk_disable_unprepare(pvr_dev->core_clk);
err_drm_dev_exit:
drm_dev_exit(idx);
return err;
}
int
pvr_power_device_resume(struct device *dev)
{
struct platform_device *plat_dev = to_platform_device(dev);
struct drm_device *drm_dev = platform_get_drvdata(plat_dev);
struct pvr_device *pvr_dev = to_pvr_device(drm_dev);
int idx;
int err;
if (!drm_dev_enter(drm_dev, &idx))
return -EIO;
err = clk_prepare_enable(pvr_dev->core_clk);
if (err)
goto err_drm_dev_exit;
err = clk_prepare_enable(pvr_dev->sys_clk);
if (err)
goto err_core_clk_disable;
err = clk_prepare_enable(pvr_dev->mem_clk);
if (err)
goto err_sys_clk_disable;
if (pvr_dev->fw_dev.booted) {
err = pvr_power_fw_enable(pvr_dev);
if (err)
goto err_mem_clk_disable;
}
drm_dev_exit(idx);
return 0;
err_mem_clk_disable:
clk_disable_unprepare(pvr_dev->mem_clk);
err_sys_clk_disable:
clk_disable_unprepare(pvr_dev->sys_clk);
err_core_clk_disable:
clk_disable_unprepare(pvr_dev->core_clk);
err_drm_dev_exit:
drm_dev_exit(idx);
return err;
}
int
pvr_power_device_idle(struct device *dev)
{
struct platform_device *plat_dev = to_platform_device(dev);
struct drm_device *drm_dev = platform_get_drvdata(plat_dev);
struct pvr_device *pvr_dev = to_pvr_device(drm_dev);
return pvr_power_is_idle(pvr_dev) ? 0 : -EBUSY;
}
/**
* pvr_power_reset() - Reset the GPU
* @pvr_dev: Device pointer
* @hard_reset: %true for hard reset, %false for soft reset
*
* If @hard_reset is %false and the FW processor fails to respond during the reset process, this
* function will attempt a hard reset.
*
* If a hard reset fails then the GPU device is reported as lost.
*
* Returns:
* * 0 on success, or
* * Any error code returned by pvr_power_get, pvr_power_fw_disable or pvr_power_fw_enable().
*/
int
pvr_power_reset(struct pvr_device *pvr_dev, bool hard_reset)
{
bool queues_disabled = false;
int err;
/*
* Take a power reference during the reset. This should prevent any interference with the
* power state during reset.
*/
WARN_ON(pvr_power_get(pvr_dev));
down_write(&pvr_dev->reset_sem);
if (pvr_dev->lost) {
err = -EIO;
goto err_up_write;
}
/* Disable IRQs for the duration of the reset. */
disable_irq(pvr_dev->irq);
do {
if (hard_reset) {
pvr_queue_device_pre_reset(pvr_dev);
queues_disabled = true;
}
err = pvr_power_fw_disable(pvr_dev, hard_reset);
if (!err) {
if (hard_reset) {
pvr_dev->fw_dev.booted = false;
WARN_ON(pm_runtime_force_suspend(from_pvr_device(pvr_dev)->dev));
err = pvr_fw_hard_reset(pvr_dev);
if (err)
goto err_device_lost;
err = pm_runtime_force_resume(from_pvr_device(pvr_dev)->dev);
pvr_dev->fw_dev.booted = true;
if (err)
goto err_device_lost;
} else {
/* Clear the FW faulted flags. */
pvr_dev->fw_dev.fwif_sysdata->hwr_state_flags &=
~(ROGUE_FWIF_HWR_FW_FAULT |
ROGUE_FWIF_HWR_RESTART_REQUESTED);
}
pvr_fw_irq_clear(pvr_dev);
err = pvr_power_fw_enable(pvr_dev);
}
if (err && hard_reset)
goto err_device_lost;
if (err && !hard_reset) {
drm_err(from_pvr_device(pvr_dev), "FW stalled, trying hard reset");
hard_reset = true;
}
} while (err);
if (queues_disabled)
pvr_queue_device_post_reset(pvr_dev);
enable_irq(pvr_dev->irq);
up_write(&pvr_dev->reset_sem);
pvr_power_put(pvr_dev);
return 0;
err_device_lost:
drm_err(from_pvr_device(pvr_dev), "GPU device lost");
pvr_device_lost(pvr_dev);
/* Leave IRQs disabled if the device is lost. */
if (queues_disabled)
pvr_queue_device_post_reset(pvr_dev);
err_up_write:
up_write(&pvr_dev->reset_sem);
pvr_power_put(pvr_dev);
return err;
}
/**
* pvr_watchdog_fini() - Shutdown watchdog for device
* @pvr_dev: Target PowerVR device.
*/
void
pvr_watchdog_fini(struct pvr_device *pvr_dev)
{
cancel_delayed_work_sync(&pvr_dev->watchdog.work);
}

View file

@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_POWER_H
#define PVR_POWER_H
#include "pvr_device.h"
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
int pvr_watchdog_init(struct pvr_device *pvr_dev);
void pvr_watchdog_fini(struct pvr_device *pvr_dev);
void pvr_device_lost(struct pvr_device *pvr_dev);
bool pvr_power_is_idle(struct pvr_device *pvr_dev);
int pvr_power_device_suspend(struct device *dev);
int pvr_power_device_resume(struct device *dev);
int pvr_power_device_idle(struct device *dev);
int pvr_power_reset(struct pvr_device *pvr_dev, bool hard_reset);
static __always_inline int
pvr_power_get(struct pvr_device *pvr_dev)
{
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
return pm_runtime_resume_and_get(drm_dev->dev);
}
static __always_inline int
pvr_power_put(struct pvr_device *pvr_dev)
{
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
return pm_runtime_put(drm_dev->dev);
}
#endif /* PVR_POWER_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,169 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_QUEUE_H
#define PVR_QUEUE_H
#include <drm/gpu_scheduler.h>
#include "pvr_cccb.h"
#include "pvr_device.h"
struct pvr_context;
struct pvr_queue;
/**
* struct pvr_queue_fence_ctx - Queue fence context
*
* Used to implement dma_fence_ops for pvr_job::{done,cccb}_fence.
*/
struct pvr_queue_fence_ctx {
/** @id: Fence context ID allocated with dma_fence_context_alloc(). */
u64 id;
/** @seqno: Sequence number incremented each time a fence is created. */
atomic_t seqno;
/** @lock: Lock used to synchronize access to fences allocated by this context. */
spinlock_t lock;
};
/**
* struct pvr_queue_cccb_fence_ctx - CCCB fence context
*
* Context used to manage fences controlling access to the CCCB. No fences are
* issued if there's enough space in the CCCB to push job commands.
*/
struct pvr_queue_cccb_fence_ctx {
/** @base: Base queue fence context. */
struct pvr_queue_fence_ctx base;
/**
* @job: Job waiting for CCCB space.
*
* Thanks to the serializationg done at the drm_sched_entity level,
* there's no more than one job waiting for CCCB at a given time.
*
* This field is NULL if no jobs are currently waiting for CCCB space.
*
* Must be accessed with @job_lock held.
*/
struct pvr_job *job;
/** @lock: Lock protecting access to the job object. */
struct mutex job_lock;
};
/**
* struct pvr_queue_fence - Queue fence object
*/
struct pvr_queue_fence {
/** @base: Base dma_fence. */
struct dma_fence base;
/** @queue: Queue that created this fence. */
struct pvr_queue *queue;
};
/**
* struct pvr_queue - Job queue
*
* Used to queue and track execution of pvr_job objects.
*/
struct pvr_queue {
/** @scheduler: Single entity scheduler use to push jobs to this queue. */
struct drm_gpu_scheduler scheduler;
/** @entity: Scheduling entity backing this queue. */
struct drm_sched_entity entity;
/** @type: Type of jobs queued to this queue. */
enum drm_pvr_job_type type;
/** @ctx: Context object this queue is bound to. */
struct pvr_context *ctx;
/** @node: Used to add the queue to the active/idle queue list. */
struct list_head node;
/**
* @in_flight_job_count: Number of jobs submitted to the CCCB that
* have not been processed yet.
*/
atomic_t in_flight_job_count;
/**
* @cccb_fence_ctx: CCCB fence context.
*
* Used to control access to the CCCB is full, such that we don't
* end up trying to push commands to the CCCB if there's not enough
* space to receive all commands needed for a job to complete.
*/
struct pvr_queue_cccb_fence_ctx cccb_fence_ctx;
/** @job_fence_ctx: Job fence context object. */
struct pvr_queue_fence_ctx job_fence_ctx;
/** @timeline_ufo: Timeline UFO for the context queue. */
struct {
/** @fw_obj: FW object representing the UFO value. */
struct pvr_fw_object *fw_obj;
/** @value: CPU mapping of the UFO value. */
u32 *value;
} timeline_ufo;
/**
* last_queued_job_scheduled_fence: The scheduled fence of the last
* job queued to this queue.
*
* We use it to insert frag -> geom dependencies when issuing combined
* geom+frag jobs, to guarantee that the fragment job that's part of
* the combined operation comes after all fragment jobs that were queued
* before it.
*/
struct dma_fence *last_queued_job_scheduled_fence;
/** @cccb: Client Circular Command Buffer. */
struct pvr_cccb cccb;
/** @reg_state_obj: FW object representing the register state of this queue. */
struct pvr_fw_object *reg_state_obj;
/** @ctx_offset: Offset of the queue context in the FW context object. */
u32 ctx_offset;
/** @callstack_addr: Initial call stack address for register state object. */
u64 callstack_addr;
};
bool pvr_queue_fence_is_ufo_backed(struct dma_fence *f);
int pvr_queue_job_init(struct pvr_job *job);
void pvr_queue_job_cleanup(struct pvr_job *job);
void pvr_queue_job_push(struct pvr_job *job);
struct dma_fence *pvr_queue_job_arm(struct pvr_job *job);
struct pvr_queue *pvr_queue_create(struct pvr_context *ctx,
enum drm_pvr_job_type type,
struct drm_pvr_ioctl_create_context_args *args,
void *fw_ctx_map);
void pvr_queue_kill(struct pvr_queue *queue);
void pvr_queue_destroy(struct pvr_queue *queue);
void pvr_queue_process(struct pvr_queue *queue);
void pvr_queue_device_pre_reset(struct pvr_device *pvr_dev);
void pvr_queue_device_post_reset(struct pvr_device *pvr_dev);
int pvr_queue_device_init(struct pvr_device *pvr_dev);
void pvr_queue_device_fini(struct pvr_device *pvr_dev);
#endif /* PVR_QUEUE_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,159 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_CR_DEFS_CLIENT_H
#define PVR_ROGUE_CR_DEFS_CLIENT_H
/* clang-format off */
/*
* This register controls the anti-aliasing mode of the Tiling Co-Processor, independent control is
* provided in both X & Y axis.
* This register needs to be set based on the ISP Samples Per Pixel a core supports.
*
* When ISP Samples Per Pixel = 1:
* 2xmsaa is achieved by enabling Y - TE does AA on Y plane only
* 4xmsaa is achieved by enabling Y and X - TE does AA on X and Y plane
* 8xmsaa not supported by XE cores
*
* When ISP Samples Per Pixel = 2:
* 2xmsaa is achieved by enabling X2 - does not affect TE
* 4xmsaa is achieved by enabling Y and X2 - TE does AA on Y plane only
* 8xmsaa is achieved by enabling Y, X and X2 - TE does AA on X and Y plane
* 8xmsaa not supported by XE cores
*
* When ISP Samples Per Pixel = 4:
* 2xmsaa is achieved by enabling X2 - does not affect TE
* 4xmsaa is achieved by enabling Y2 and X2 - TE does AA on Y plane only
* 8xmsaa not supported by XE cores
*/
/* Register ROGUE_CR_TE_AA */
#define ROGUE_CR_TE_AA 0x0C00U
#define ROGUE_CR_TE_AA_MASKFULL 0x000000000000000Full
/* Y2
* Indicates 4xmsaa when X2 and Y2 are set to 1. This does not affect TE and is only used within
* TPW.
*/
#define ROGUE_CR_TE_AA_Y2_SHIFT 3
#define ROGUE_CR_TE_AA_Y2_CLRMSK 0xFFFFFFF7
#define ROGUE_CR_TE_AA_Y2_EN 0x00000008
/* Y
* Anti-Aliasing in Y Plane Enabled
*/
#define ROGUE_CR_TE_AA_Y_SHIFT 2
#define ROGUE_CR_TE_AA_Y_CLRMSK 0xFFFFFFFB
#define ROGUE_CR_TE_AA_Y_EN 0x00000004
/* X
* Anti-Aliasing in X Plane Enabled
*/
#define ROGUE_CR_TE_AA_X_SHIFT 1
#define ROGUE_CR_TE_AA_X_CLRMSK 0xFFFFFFFD
#define ROGUE_CR_TE_AA_X_EN 0x00000002
/* X2
* 2x Anti-Aliasing Enabled, affects PPP only
*/
#define ROGUE_CR_TE_AA_X2_SHIFT (0U)
#define ROGUE_CR_TE_AA_X2_CLRMSK (0xFFFFFFFEU)
#define ROGUE_CR_TE_AA_X2_EN (0x00000001U)
/* MacroTile Boundaries X Plane */
/* Register ROGUE_CR_TE_MTILE1 */
#define ROGUE_CR_TE_MTILE1 0x0C08
#define ROGUE_CR_TE_MTILE1_MASKFULL 0x0000000007FFFFFFull
/* X1 default: 0x00000004
* X1 MacroTile boundary, left tile X for second column of macrotiles (16MT mode) - 32 pixels across
* tile
*/
#define ROGUE_CR_TE_MTILE1_X1_SHIFT 18
#define ROGUE_CR_TE_MTILE1_X1_CLRMSK 0xF803FFFF
/* X2 default: 0x00000008
* X2 MacroTile boundary, left tile X for third(16MT) column of macrotiles - 32 pixels across tile
*/
#define ROGUE_CR_TE_MTILE1_X2_SHIFT 9U
#define ROGUE_CR_TE_MTILE1_X2_CLRMSK 0xFFFC01FF
/* X3 default: 0x0000000c
* X3 MacroTile boundary, left tile X for fourth column of macrotiles (16MT) - 32 pixels across tile
*/
#define ROGUE_CR_TE_MTILE1_X3_SHIFT 0
#define ROGUE_CR_TE_MTILE1_X3_CLRMSK 0xFFFFFE00
/* MacroTile Boundaries Y Plane. */
/* Register ROGUE_CR_TE_MTILE2 */
#define ROGUE_CR_TE_MTILE2 0x0C10
#define ROGUE_CR_TE_MTILE2_MASKFULL 0x0000000007FFFFFFull
/* Y1 default: 0x00000004
* X1 MacroTile boundary, ltop tile Y for second column of macrotiles (16MT mode) - 32 pixels tile
* height
*/
#define ROGUE_CR_TE_MTILE2_Y1_SHIFT 18
#define ROGUE_CR_TE_MTILE2_Y1_CLRMSK 0xF803FFFF
/* Y2 default: 0x00000008
* X2 MacroTile boundary, top tile Y for third(16MT) column of macrotiles - 32 pixels tile height
*/
#define ROGUE_CR_TE_MTILE2_Y2_SHIFT 9
#define ROGUE_CR_TE_MTILE2_Y2_CLRMSK 0xFFFC01FF
/* Y3 default: 0x0000000c
* X3 MacroTile boundary, top tile Y for fourth column of macrotiles (16MT) - 32 pixels tile height
*/
#define ROGUE_CR_TE_MTILE2_Y3_SHIFT 0
#define ROGUE_CR_TE_MTILE2_Y3_CLRMSK 0xFFFFFE00
/*
* In order to perform the tiling operation and generate the display list the maximum screen size
* must be configured in terms of the number of tiles in X & Y axis.
*/
/* Register ROGUE_CR_TE_SCREEN */
#define ROGUE_CR_TE_SCREEN 0x0C18U
#define ROGUE_CR_TE_SCREEN_MASKFULL 0x00000000001FF1FFull
/* YMAX default: 0x00000010
* Maximum Y tile address visible on screen, 32 pixel tile height, 16Kx16K max screen size
*/
#define ROGUE_CR_TE_SCREEN_YMAX_SHIFT 12
#define ROGUE_CR_TE_SCREEN_YMAX_CLRMSK 0xFFE00FFF
/* XMAX default: 0x00000010
* Maximum X tile address visible on screen, 32 pixel tile width, 16Kx16K max screen size
*/
#define ROGUE_CR_TE_SCREEN_XMAX_SHIFT 0
#define ROGUE_CR_TE_SCREEN_XMAX_CLRMSK 0xFFFFFE00
/*
* In order to perform the tiling operation and generate the display list the maximum screen size
* must be configured in terms of the number of pixels in X & Y axis since this may not be the same
* as the number of tiles defined in the RGX_CR_TE_SCREEN register.
*/
/* Register ROGUE_CR_PPP_SCREEN */
#define ROGUE_CR_PPP_SCREEN 0x0C98
#define ROGUE_CR_PPP_SCREEN_MASKFULL 0x000000007FFF7FFFull
/* PIXYMAX
* Screen height in pixels. (16K x 16K max screen size)
*/
#define ROGUE_CR_PPP_SCREEN_PIXYMAX_SHIFT 16
#define ROGUE_CR_PPP_SCREEN_PIXYMAX_CLRMSK 0x8000FFFF
/* PIXXMAX
* Screen width in pixels.(16K x 16K max screen size)
*/
#define ROGUE_CR_PPP_SCREEN_PIXXMAX_SHIFT 0
#define ROGUE_CR_PPP_SCREEN_PIXXMAX_CLRMSK 0xFFFF8000
/* Register ROGUE_CR_ISP_MTILE_SIZE */
#define ROGUE_CR_ISP_MTILE_SIZE 0x0F18
#define ROGUE_CR_ISP_MTILE_SIZE_MASKFULL 0x0000000003FF03FFull
/* X
* Macrotile width, in tiles. A value of zero corresponds to the maximum size
*/
#define ROGUE_CR_ISP_MTILE_SIZE_X_SHIFT 16
#define ROGUE_CR_ISP_MTILE_SIZE_X_CLRMSK 0xFC00FFFF
#define ROGUE_CR_ISP_MTILE_SIZE_X_ALIGNSHIFT 0
#define ROGUE_CR_ISP_MTILE_SIZE_X_ALIGNSIZE 1
/* Y
* Macrotile height, in tiles. A value of zero corresponds to the maximum size
*/
#define ROGUE_CR_ISP_MTILE_SIZE_Y_SHIFT 0
#define ROGUE_CR_ISP_MTILE_SIZE_Y_CLRMSK 0xFFFFFC00
#define ROGUE_CR_ISP_MTILE_SIZE_Y_ALIGNSHIFT 0
#define ROGUE_CR_ISP_MTILE_SIZE_Y_ALIGNSIZE 1
/* clang-format on */
#endif /* PVR_ROGUE_CR_DEFS_CLIENT_H */

View file

@ -0,0 +1,179 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_DEFS_H
#define PVR_ROGUE_DEFS_H
#include "pvr_rogue_cr_defs.h"
#include <linux/bits.h>
/*
******************************************************************************
* ROGUE Defines
******************************************************************************
*/
#define ROGUE_FW_MAX_NUM_OS (8U)
#define ROGUE_FW_HOST_OS (0U)
#define ROGUE_FW_GUEST_OSID_START (1U)
#define ROGUE_FW_THREAD_0 (0U)
#define ROGUE_FW_THREAD_1 (1U)
#define GET_ROGUE_CACHE_LINE_SIZE(x) ((((s32)(x)) > 0) ? ((x) / 8) : (0))
#define MAX_HW_GEOM_FRAG_CONTEXTS 2U
#define ROGUE_CR_CLK_CTRL_ALL_ON \
(0x5555555555555555ull & ROGUE_CR_CLK_CTRL_MASKFULL)
#define ROGUE_CR_CLK_CTRL_ALL_AUTO \
(0xaaaaaaaaaaaaaaaaull & ROGUE_CR_CLK_CTRL_MASKFULL)
#define ROGUE_CR_CLK_CTRL2_ALL_ON \
(0x5555555555555555ull & ROGUE_CR_CLK_CTRL2_MASKFULL)
#define ROGUE_CR_CLK_CTRL2_ALL_AUTO \
(0xaaaaaaaaaaaaaaaaull & ROGUE_CR_CLK_CTRL2_MASKFULL)
#define ROGUE_CR_SOFT_RESET_DUST_n_CORE_EN \
(ROGUE_CR_SOFT_RESET_DUST_A_CORE_EN | \
ROGUE_CR_SOFT_RESET_DUST_B_CORE_EN | \
ROGUE_CR_SOFT_RESET_DUST_C_CORE_EN | \
ROGUE_CR_SOFT_RESET_DUST_D_CORE_EN | \
ROGUE_CR_SOFT_RESET_DUST_E_CORE_EN | \
ROGUE_CR_SOFT_RESET_DUST_F_CORE_EN | \
ROGUE_CR_SOFT_RESET_DUST_G_CORE_EN | \
ROGUE_CR_SOFT_RESET_DUST_H_CORE_EN)
/* SOFT_RESET Rascal and DUSTs bits */
#define ROGUE_CR_SOFT_RESET_RASCALDUSTS_EN \
(ROGUE_CR_SOFT_RESET_RASCAL_CORE_EN | \
ROGUE_CR_SOFT_RESET_DUST_n_CORE_EN)
/* SOFT_RESET steps as defined in the TRM */
#define ROGUE_S7_SOFT_RESET_DUSTS (ROGUE_CR_SOFT_RESET_DUST_n_CORE_EN)
#define ROGUE_S7_SOFT_RESET_JONES \
(ROGUE_CR_SOFT_RESET_PM_EN | ROGUE_CR_SOFT_RESET_VDM_EN | \
ROGUE_CR_SOFT_RESET_ISP_EN)
#define ROGUE_S7_SOFT_RESET_JONES_ALL \
(ROGUE_S7_SOFT_RESET_JONES | ROGUE_CR_SOFT_RESET_BIF_EN | \
ROGUE_CR_SOFT_RESET_SLC_EN | ROGUE_CR_SOFT_RESET_GARTEN_EN)
#define ROGUE_S7_SOFT_RESET2 \
(ROGUE_CR_SOFT_RESET2_BLACKPEARL_EN | ROGUE_CR_SOFT_RESET2_PIXEL_EN | \
ROGUE_CR_SOFT_RESET2_CDM_EN | ROGUE_CR_SOFT_RESET2_VERTEX_EN)
#define ROGUE_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT (12U)
#define ROGUE_BIF_PM_PHYSICAL_PAGE_SIZE \
BIT(ROGUE_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT)
#define ROGUE_BIF_PM_VIRTUAL_PAGE_ALIGNSHIFT (14U)
#define ROGUE_BIF_PM_VIRTUAL_PAGE_SIZE BIT(ROGUE_BIF_PM_VIRTUAL_PAGE_ALIGNSHIFT)
#define ROGUE_BIF_PM_FREELIST_BASE_ADDR_ALIGNSIZE (16U)
/*
* To get the number of required Dusts, divide the number of
* clusters by 2 and round up
*/
#define ROGUE_REQ_NUM_DUSTS(CLUSTERS) (((CLUSTERS) + 1U) / 2U)
/*
* To get the number of required Bernado/Phantom(s), divide
* the number of clusters by 4 and round up
*/
#define ROGUE_REQ_NUM_PHANTOMS(CLUSTERS) (((CLUSTERS) + 3U) / 4U)
#define ROGUE_REQ_NUM_BERNADOS(CLUSTERS) (((CLUSTERS) + 3U) / 4U)
#define ROGUE_REQ_NUM_BLACKPEARLS(CLUSTERS) (((CLUSTERS) + 3U) / 4U)
/*
* FW MMU contexts
*/
#define MMU_CONTEXT_MAPPING_FWPRIV (0x0) /* FW code/private data */
#define MMU_CONTEXT_MAPPING_FWIF (0x0) /* Host/FW data */
/*
* Utility macros to calculate CAT_BASE register addresses
*/
#define BIF_CAT_BASEX(n) \
(ROGUE_CR_BIF_CAT_BASE0 + \
(n) * (ROGUE_CR_BIF_CAT_BASE1 - ROGUE_CR_BIF_CAT_BASE0))
#define FWCORE_MEM_CAT_BASEX(n) \
(ROGUE_CR_FWCORE_MEM_CAT_BASE0 + \
(n) * (ROGUE_CR_FWCORE_MEM_CAT_BASE1 - \
ROGUE_CR_FWCORE_MEM_CAT_BASE0))
/*
* FWCORE wrapper register defines
*/
#define FWCORE_ADDR_REMAP_CONFIG0_MMU_CONTEXT_SHIFT \
ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_CBASE_SHIFT
#define FWCORE_ADDR_REMAP_CONFIG0_MMU_CONTEXT_CLRMSK \
ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_CBASE_CLRMSK
#define FWCORE_ADDR_REMAP_CONFIG0_SIZE_ALIGNSHIFT (12U)
#define ROGUE_MAX_COMPUTE_SHARED_REGISTERS (2 * 1024)
#define ROGUE_MAX_VERTEX_SHARED_REGISTERS 1024
#define ROGUE_MAX_PIXEL_SHARED_REGISTERS 1024
#define ROGUE_CSRM_LINE_SIZE_IN_DWORDS (64 * 4 * 4)
#define ROGUE_CDMCTRL_USC_COMMON_SIZE_ALIGNSIZE 64
#define ROGUE_CDMCTRL_USC_COMMON_SIZE_UPPER 256
/*
* The maximum amount of local memory which can be allocated by a single kernel
* (in dwords/32-bit registers).
*
* ROGUE_CDMCTRL_USC_COMMON_SIZE_ALIGNSIZE is in bytes so we divide by four.
*/
#define ROGUE_MAX_PER_KERNEL_LOCAL_MEM_SIZE_REGS ((ROGUE_CDMCTRL_USC_COMMON_SIZE_ALIGNSIZE * \
ROGUE_CDMCTRL_USC_COMMON_SIZE_UPPER) >> 2)
/*
******************************************************************************
* WA HWBRNs
******************************************************************************
*/
/* GPU CR timer tick in GPU cycles */
#define ROGUE_CRTIME_TICK_IN_CYCLES (256U)
/* for nohw multicore return max cores possible to client */
#define ROGUE_MULTICORE_MAX_NOHW_CORES (4U)
/*
* If the size of the SLC is less than this value then the TPU bypasses the SLC.
*/
#define ROGUE_TPU_CACHED_SLC_SIZE_THRESHOLD (128U * 1024U)
/*
* If the size of the SLC is bigger than this value then the TCU must not be
* bypassed in the SLC.
* In XE_MEMORY_HIERARCHY cores, the TCU is bypassed by default.
*/
#define ROGUE_TCU_CACHED_SLC_SIZE_THRESHOLD (32U * 1024U)
/*
* Register used by the FW to track the current boot stage (not used in MIPS)
*/
#define ROGUE_FW_BOOT_STAGE_REGISTER (ROGUE_CR_POWER_ESTIMATE_RESULT)
/*
* Virtualisation definitions
*/
#define ROGUE_VIRTUALISATION_REG_SIZE_PER_OS \
(ROGUE_CR_MTS_SCHEDULE1 - ROGUE_CR_MTS_SCHEDULE)
/*
* Macro used to indicate which version of HWPerf is active
*/
#define ROGUE_FEATURE_HWPERF_ROGUE
/*
* Maximum number of cores supported by TRP
*/
#define ROGUE_TRP_MAX_NUM_CORES (4U)
#endif /* PVR_ROGUE_DEFS_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,493 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_FWIF_CHECK_H
#define PVR_ROGUE_FWIF_CHECK_H
#include <linux/build_bug.h>
#define OFFSET_CHECK(type, member, offset) \
static_assert(offsetof(type, member) == (offset), \
"offsetof(" #type ", " #member ") incorrect")
#define SIZE_CHECK(type, size) \
static_assert(sizeof(type) == (size), #type " is incorrect size")
OFFSET_CHECK(struct rogue_fwif_file_info_buf, path, 0);
OFFSET_CHECK(struct rogue_fwif_file_info_buf, info, 200);
OFFSET_CHECK(struct rogue_fwif_file_info_buf, line_num, 400);
SIZE_CHECK(struct rogue_fwif_file_info_buf, 408);
OFFSET_CHECK(struct rogue_fwif_tracebuf_space, trace_pointer, 0);
OFFSET_CHECK(struct rogue_fwif_tracebuf_space, trace_buffer_fw_addr, 4);
OFFSET_CHECK(struct rogue_fwif_tracebuf_space, trace_buffer, 8);
OFFSET_CHECK(struct rogue_fwif_tracebuf_space, assert_buf, 16);
SIZE_CHECK(struct rogue_fwif_tracebuf_space, 424);
OFFSET_CHECK(struct rogue_fwif_tracebuf, log_type, 0);
OFFSET_CHECK(struct rogue_fwif_tracebuf, tracebuf, 8);
OFFSET_CHECK(struct rogue_fwif_tracebuf, tracebuf_size_in_dwords, 856);
OFFSET_CHECK(struct rogue_fwif_tracebuf, tracebuf_flags, 860);
SIZE_CHECK(struct rogue_fwif_tracebuf, 864);
OFFSET_CHECK(struct rogue_fw_fault_info, cr_timer, 0);
OFFSET_CHECK(struct rogue_fw_fault_info, os_timer, 8);
OFFSET_CHECK(struct rogue_fw_fault_info, data, 16);
OFFSET_CHECK(struct rogue_fw_fault_info, reserved, 20);
OFFSET_CHECK(struct rogue_fw_fault_info, fault_buf, 24);
SIZE_CHECK(struct rogue_fw_fault_info, 432);
OFFSET_CHECK(struct rogue_fwif_sysdata, config_flags, 0);
OFFSET_CHECK(struct rogue_fwif_sysdata, config_flags_ext, 4);
OFFSET_CHECK(struct rogue_fwif_sysdata, pow_state, 8);
OFFSET_CHECK(struct rogue_fwif_sysdata, hw_perf_ridx, 12);
OFFSET_CHECK(struct rogue_fwif_sysdata, hw_perf_widx, 16);
OFFSET_CHECK(struct rogue_fwif_sysdata, hw_perf_wrap_count, 20);
OFFSET_CHECK(struct rogue_fwif_sysdata, hw_perf_size, 24);
OFFSET_CHECK(struct rogue_fwif_sysdata, hw_perf_drop_count, 28);
OFFSET_CHECK(struct rogue_fwif_sysdata, hw_perf_ut, 32);
OFFSET_CHECK(struct rogue_fwif_sysdata, first_drop_ordinal, 36);
OFFSET_CHECK(struct rogue_fwif_sysdata, last_drop_ordinal, 40);
OFFSET_CHECK(struct rogue_fwif_sysdata, os_runtime_flags_mirror, 44);
OFFSET_CHECK(struct rogue_fwif_sysdata, fault_info, 80);
OFFSET_CHECK(struct rogue_fwif_sysdata, fw_faults, 3536);
OFFSET_CHECK(struct rogue_fwif_sysdata, cr_poll_addr, 3540);
OFFSET_CHECK(struct rogue_fwif_sysdata, cr_poll_mask, 3548);
OFFSET_CHECK(struct rogue_fwif_sysdata, cr_poll_count, 3556);
OFFSET_CHECK(struct rogue_fwif_sysdata, start_idle_time, 3568);
OFFSET_CHECK(struct rogue_fwif_sysdata, hwr_state_flags, 3576);
OFFSET_CHECK(struct rogue_fwif_sysdata, hwr_recovery_flags, 3580);
OFFSET_CHECK(struct rogue_fwif_sysdata, fw_sys_data_flags, 3616);
OFFSET_CHECK(struct rogue_fwif_sysdata, mc_config, 3620);
SIZE_CHECK(struct rogue_fwif_sysdata, 3624);
OFFSET_CHECK(struct rogue_fwif_slr_entry, timestamp, 0);
OFFSET_CHECK(struct rogue_fwif_slr_entry, fw_ctx_addr, 8);
OFFSET_CHECK(struct rogue_fwif_slr_entry, num_ufos, 12);
OFFSET_CHECK(struct rogue_fwif_slr_entry, ccb_name, 16);
SIZE_CHECK(struct rogue_fwif_slr_entry, 48);
OFFSET_CHECK(struct rogue_fwif_osdata, fw_os_config_flags, 0);
OFFSET_CHECK(struct rogue_fwif_osdata, fw_sync_check_mark, 4);
OFFSET_CHECK(struct rogue_fwif_osdata, host_sync_check_mark, 8);
OFFSET_CHECK(struct rogue_fwif_osdata, forced_updates_requested, 12);
OFFSET_CHECK(struct rogue_fwif_osdata, slr_log_wp, 16);
OFFSET_CHECK(struct rogue_fwif_osdata, slr_log_first, 24);
OFFSET_CHECK(struct rogue_fwif_osdata, slr_log, 72);
OFFSET_CHECK(struct rogue_fwif_osdata, last_forced_update_time, 552);
OFFSET_CHECK(struct rogue_fwif_osdata, interrupt_count, 560);
OFFSET_CHECK(struct rogue_fwif_osdata, kccb_cmds_executed, 568);
OFFSET_CHECK(struct rogue_fwif_osdata, power_sync_fw_addr, 572);
OFFSET_CHECK(struct rogue_fwif_osdata, fw_os_data_flags, 576);
SIZE_CHECK(struct rogue_fwif_osdata, 584);
OFFSET_CHECK(struct rogue_bifinfo, bif_req_status, 0);
OFFSET_CHECK(struct rogue_bifinfo, bif_mmu_status, 8);
OFFSET_CHECK(struct rogue_bifinfo, pc_address, 16);
OFFSET_CHECK(struct rogue_bifinfo, reserved, 24);
SIZE_CHECK(struct rogue_bifinfo, 32);
OFFSET_CHECK(struct rogue_eccinfo, fault_gpu, 0);
SIZE_CHECK(struct rogue_eccinfo, 4);
OFFSET_CHECK(struct rogue_mmuinfo, mmu_status, 0);
OFFSET_CHECK(struct rogue_mmuinfo, pc_address, 16);
OFFSET_CHECK(struct rogue_mmuinfo, reserved, 24);
SIZE_CHECK(struct rogue_mmuinfo, 32);
OFFSET_CHECK(struct rogue_pollinfo, thread_num, 0);
OFFSET_CHECK(struct rogue_pollinfo, cr_poll_addr, 4);
OFFSET_CHECK(struct rogue_pollinfo, cr_poll_mask, 8);
OFFSET_CHECK(struct rogue_pollinfo, cr_poll_last_value, 12);
OFFSET_CHECK(struct rogue_pollinfo, reserved, 16);
SIZE_CHECK(struct rogue_pollinfo, 24);
OFFSET_CHECK(struct rogue_tlbinfo, bad_addr, 0);
OFFSET_CHECK(struct rogue_tlbinfo, entry_lo, 4);
SIZE_CHECK(struct rogue_tlbinfo, 8);
OFFSET_CHECK(struct rogue_hwrinfo, hwr_data, 0);
OFFSET_CHECK(struct rogue_hwrinfo, cr_timer, 32);
OFFSET_CHECK(struct rogue_hwrinfo, os_timer, 40);
OFFSET_CHECK(struct rogue_hwrinfo, frame_num, 48);
OFFSET_CHECK(struct rogue_hwrinfo, pid, 52);
OFFSET_CHECK(struct rogue_hwrinfo, active_hwrt_data, 56);
OFFSET_CHECK(struct rogue_hwrinfo, hwr_number, 60);
OFFSET_CHECK(struct rogue_hwrinfo, event_status, 64);
OFFSET_CHECK(struct rogue_hwrinfo, hwr_recovery_flags, 68);
OFFSET_CHECK(struct rogue_hwrinfo, hwr_type, 72);
OFFSET_CHECK(struct rogue_hwrinfo, dm, 76);
OFFSET_CHECK(struct rogue_hwrinfo, core_id, 80);
OFFSET_CHECK(struct rogue_hwrinfo, cr_time_of_kick, 88);
OFFSET_CHECK(struct rogue_hwrinfo, cr_time_hw_reset_start, 96);
OFFSET_CHECK(struct rogue_hwrinfo, cr_time_hw_reset_finish, 104);
OFFSET_CHECK(struct rogue_hwrinfo, cr_time_freelist_ready, 112);
OFFSET_CHECK(struct rogue_hwrinfo, reserved, 120);
SIZE_CHECK(struct rogue_hwrinfo, 136);
OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_info, 0);
OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_counter, 2176);
OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, write_index, 2180);
OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, dd_req_count, 2184);
OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_info_buf_flags, 2188);
OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_dm_locked_up_count, 2192);
OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_dm_overran_count, 2228);
OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_dm_recovered_count, 2264);
OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_dm_false_detect_count, 2300);
SIZE_CHECK(struct rogue_fwif_hwrinfobuf, 2336);
OFFSET_CHECK(struct rogue_fwif_fwmemcontext, pc_dev_paddr, 0);
OFFSET_CHECK(struct rogue_fwif_fwmemcontext, page_cat_base_reg_set, 8);
OFFSET_CHECK(struct rogue_fwif_fwmemcontext, breakpoint_addr, 12);
OFFSET_CHECK(struct rogue_fwif_fwmemcontext, bp_handler_addr, 16);
OFFSET_CHECK(struct rogue_fwif_fwmemcontext, breakpoint_ctl, 20);
OFFSET_CHECK(struct rogue_fwif_fwmemcontext, fw_mem_ctx_flags, 24);
SIZE_CHECK(struct rogue_fwif_fwmemcontext, 32);
OFFSET_CHECK(struct rogue_fwif_geom_ctx_state_per_geom, geom_reg_vdm_call_stack_pointer, 0);
OFFSET_CHECK(struct rogue_fwif_geom_ctx_state_per_geom, geom_reg_vdm_call_stack_pointer_init, 8);
OFFSET_CHECK(struct rogue_fwif_geom_ctx_state_per_geom, geom_reg_vbs_so_prim, 16);
OFFSET_CHECK(struct rogue_fwif_geom_ctx_state_per_geom, geom_current_idx, 32);
SIZE_CHECK(struct rogue_fwif_geom_ctx_state_per_geom, 40);
OFFSET_CHECK(struct rogue_fwif_geom_ctx_state, geom_core, 0);
SIZE_CHECK(struct rogue_fwif_geom_ctx_state, 160);
OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_pm_deallocated_mask_status, 0);
OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_dm_pds_mtilefree_status, 4);
OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, ctx_state_flags, 8);
OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_isp_store, 12);
SIZE_CHECK(struct rogue_fwif_frag_ctx_state, 16);
OFFSET_CHECK(struct rogue_fwif_compute_ctx_state, ctx_state_flags, 0);
SIZE_CHECK(struct rogue_fwif_compute_ctx_state, 4);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, ccbctl_fw_addr, 0);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, ccb_fw_addr, 4);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, ccb_meta_dma_addr, 8);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, context_state_addr, 24);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, fw_com_ctx_flags, 28);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, priority, 32);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, priority_seq_num, 36);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, rf_cmd_addr, 40);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, stats_pending, 44);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, stats_num_stores, 48);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, stats_num_out_of_memory, 52);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, stats_num_partial_renders, 56);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, dm, 60);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, wait_signal_address, 64);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, wait_signal_node, 72);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, buf_stalled_node, 80);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, cbuf_queue_ctrl_addr, 88);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, robustness_address, 96);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, max_deadline_ms, 104);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, read_offset_needs_reset, 108);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, waiting_node, 112);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, run_node, 120);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, last_failed_ufo, 128);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, fw_mem_context_fw_addr, 136);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, server_common_context_id, 140);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, pid, 144);
OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, geom_oom_disabled, 148);
SIZE_CHECK(struct rogue_fwif_fwcommoncontext, 152);
OFFSET_CHECK(struct rogue_fwif_ccb_ctl, write_offset, 0);
OFFSET_CHECK(struct rogue_fwif_ccb_ctl, padding, 4);
OFFSET_CHECK(struct rogue_fwif_ccb_ctl, read_offset, 128);
OFFSET_CHECK(struct rogue_fwif_ccb_ctl, wrap_mask, 132);
OFFSET_CHECK(struct rogue_fwif_ccb_ctl, cmd_size, 136);
OFFSET_CHECK(struct rogue_fwif_ccb_ctl, padding2, 140);
SIZE_CHECK(struct rogue_fwif_ccb_ctl, 144);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd_kick_data, context_fw_addr, 0);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd_kick_data, client_woff_update, 4);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd_kick_data, client_wrap_mask_update, 8);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd_kick_data, num_cleanup_ctl, 12);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd_kick_data, cleanup_ctl_fw_addr, 16);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd_kick_data, work_est_cmd_header_offset, 28);
SIZE_CHECK(struct rogue_fwif_kccb_cmd_kick_data, 32);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd_combined_geom_frag_kick_data, geom_cmd_kick_data, 0);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd_combined_geom_frag_kick_data, frag_cmd_kick_data, 32);
SIZE_CHECK(struct rogue_fwif_kccb_cmd_combined_geom_frag_kick_data, 64);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd_force_update_data, context_fw_addr, 0);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd_force_update_data, ccb_fence_offset, 4);
SIZE_CHECK(struct rogue_fwif_kccb_cmd_force_update_data, 8);
OFFSET_CHECK(struct rogue_fwif_cleanup_request, cleanup_type, 0);
OFFSET_CHECK(struct rogue_fwif_cleanup_request, cleanup_data, 4);
SIZE_CHECK(struct rogue_fwif_cleanup_request, 8);
OFFSET_CHECK(struct rogue_fwif_power_request, pow_type, 0);
OFFSET_CHECK(struct rogue_fwif_power_request, power_req_data, 4);
SIZE_CHECK(struct rogue_fwif_power_request, 8);
OFFSET_CHECK(struct rogue_fwif_slcflushinvaldata, context_fw_addr, 0);
OFFSET_CHECK(struct rogue_fwif_slcflushinvaldata, inval, 4);
OFFSET_CHECK(struct rogue_fwif_slcflushinvaldata, dm_context, 8);
OFFSET_CHECK(struct rogue_fwif_slcflushinvaldata, address, 16);
OFFSET_CHECK(struct rogue_fwif_slcflushinvaldata, size, 24);
SIZE_CHECK(struct rogue_fwif_slcflushinvaldata, 32);
OFFSET_CHECK(struct rogue_fwif_hwperf_ctrl, opcode, 0);
OFFSET_CHECK(struct rogue_fwif_hwperf_ctrl, mask, 8);
SIZE_CHECK(struct rogue_fwif_hwperf_ctrl, 16);
OFFSET_CHECK(struct rogue_fwif_hwperf_config_enable_blks, num_blocks, 0);
OFFSET_CHECK(struct rogue_fwif_hwperf_config_enable_blks, block_configs_fw_addr, 4);
SIZE_CHECK(struct rogue_fwif_hwperf_config_enable_blks, 8);
OFFSET_CHECK(struct rogue_fwif_hwperf_config_da_blks, num_blocks, 0);
OFFSET_CHECK(struct rogue_fwif_hwperf_config_da_blks, block_configs_fw_addr, 4);
SIZE_CHECK(struct rogue_fwif_hwperf_config_da_blks, 8);
OFFSET_CHECK(struct rogue_fwif_coreclkspeedchange_data, new_clock_speed, 0);
SIZE_CHECK(struct rogue_fwif_coreclkspeedchange_data, 4);
OFFSET_CHECK(struct rogue_fwif_hwperf_ctrl_blks, enable, 0);
OFFSET_CHECK(struct rogue_fwif_hwperf_ctrl_blks, num_blocks, 4);
OFFSET_CHECK(struct rogue_fwif_hwperf_ctrl_blks, block_ids, 8);
SIZE_CHECK(struct rogue_fwif_hwperf_ctrl_blks, 40);
OFFSET_CHECK(struct rogue_fwif_hwperf_select_custom_cntrs, custom_block, 0);
OFFSET_CHECK(struct rogue_fwif_hwperf_select_custom_cntrs, num_counters, 2);
OFFSET_CHECK(struct rogue_fwif_hwperf_select_custom_cntrs, custom_counter_ids_fw_addr, 4);
SIZE_CHECK(struct rogue_fwif_hwperf_select_custom_cntrs, 8);
OFFSET_CHECK(struct rogue_fwif_zsbuffer_backing_data, zs_buffer_fw_addr, 0);
OFFSET_CHECK(struct rogue_fwif_zsbuffer_backing_data, done, 4);
SIZE_CHECK(struct rogue_fwif_zsbuffer_backing_data, 8);
OFFSET_CHECK(struct rogue_fwif_freelist_gs_data, freelist_fw_addr, 0);
OFFSET_CHECK(struct rogue_fwif_freelist_gs_data, delta_pages, 4);
OFFSET_CHECK(struct rogue_fwif_freelist_gs_data, new_pages, 8);
OFFSET_CHECK(struct rogue_fwif_freelist_gs_data, ready_pages, 12);
SIZE_CHECK(struct rogue_fwif_freelist_gs_data, 16);
OFFSET_CHECK(struct rogue_fwif_freelists_reconstruction_data, freelist_count, 0);
OFFSET_CHECK(struct rogue_fwif_freelists_reconstruction_data, freelist_ids, 4);
SIZE_CHECK(struct rogue_fwif_freelists_reconstruction_data, 76);
OFFSET_CHECK(struct rogue_fwif_write_offset_update_data, context_fw_addr, 0);
SIZE_CHECK(struct rogue_fwif_write_offset_update_data, 8);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd, cmd_type, 0);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd, kccb_flags, 4);
OFFSET_CHECK(struct rogue_fwif_kccb_cmd, cmd_data, 8);
SIZE_CHECK(struct rogue_fwif_kccb_cmd, 88);
OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, server_common_context_id, 0);
OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, reset_reason, 4);
OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, dm, 8);
OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, reset_job_ref, 12);
OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, flags, 16);
OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, pc_address, 24);
OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, fault_address, 32);
SIZE_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, 40);
OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_fw_pagefault_data, fw_fault_addr, 0);
SIZE_CHECK(struct rogue_fwif_fwccb_cmd_fw_pagefault_data, 8);
OFFSET_CHECK(struct rogue_fwif_fwccb_cmd, cmd_type, 0);
OFFSET_CHECK(struct rogue_fwif_fwccb_cmd, fwccb_flags, 4);
OFFSET_CHECK(struct rogue_fwif_fwccb_cmd, cmd_data, 8);
SIZE_CHECK(struct rogue_fwif_fwccb_cmd, 88);
OFFSET_CHECK(struct rogue_fwif_ccb_cmd_header, cmd_type, 0);
OFFSET_CHECK(struct rogue_fwif_ccb_cmd_header, cmd_size, 4);
OFFSET_CHECK(struct rogue_fwif_ccb_cmd_header, ext_job_ref, 8);
OFFSET_CHECK(struct rogue_fwif_ccb_cmd_header, int_job_ref, 12);
OFFSET_CHECK(struct rogue_fwif_ccb_cmd_header, work_est_kick_data, 16);
SIZE_CHECK(struct rogue_fwif_ccb_cmd_header, 40);
OFFSET_CHECK(struct rogue_fwif_runtime_cfg, active_pm_latency_ms, 0);
OFFSET_CHECK(struct rogue_fwif_runtime_cfg, runtime_cfg_flags, 4);
OFFSET_CHECK(struct rogue_fwif_runtime_cfg, active_pm_latency_persistant, 8);
OFFSET_CHECK(struct rogue_fwif_runtime_cfg, core_clock_speed, 12);
OFFSET_CHECK(struct rogue_fwif_runtime_cfg, default_dusts_num_init, 16);
OFFSET_CHECK(struct rogue_fwif_runtime_cfg, phr_mode, 20);
OFFSET_CHECK(struct rogue_fwif_runtime_cfg, hcs_deadline_ms, 24);
OFFSET_CHECK(struct rogue_fwif_runtime_cfg, wdg_period_us, 28);
OFFSET_CHECK(struct rogue_fwif_runtime_cfg, osid_priority, 32);
OFFSET_CHECK(struct rogue_fwif_runtime_cfg, hwperf_buf_fw_addr, 64);
OFFSET_CHECK(struct rogue_fwif_runtime_cfg, padding, 68);
SIZE_CHECK(struct rogue_fwif_runtime_cfg, 72);
OFFSET_CHECK(struct rogue_fwif_connection_ctl, connection_fw_state, 0);
OFFSET_CHECK(struct rogue_fwif_connection_ctl, connection_os_state, 4);
OFFSET_CHECK(struct rogue_fwif_connection_ctl, alive_fw_token, 8);
OFFSET_CHECK(struct rogue_fwif_connection_ctl, alive_os_token, 12);
SIZE_CHECK(struct rogue_fwif_connection_ctl, 16);
OFFSET_CHECK(struct rogue_fwif_compchecks_bvnc, layout_version, 0);
OFFSET_CHECK(struct rogue_fwif_compchecks_bvnc, bvnc, 8);
SIZE_CHECK(struct rogue_fwif_compchecks_bvnc, 16);
OFFSET_CHECK(struct rogue_fwif_init_options, os_count_support, 0);
SIZE_CHECK(struct rogue_fwif_init_options, 8);
OFFSET_CHECK(struct rogue_fwif_compchecks, hw_bvnc, 0);
OFFSET_CHECK(struct rogue_fwif_compchecks, fw_bvnc, 16);
OFFSET_CHECK(struct rogue_fwif_compchecks, fw_processor_version, 32);
OFFSET_CHECK(struct rogue_fwif_compchecks, ddk_version, 36);
OFFSET_CHECK(struct rogue_fwif_compchecks, ddk_build, 40);
OFFSET_CHECK(struct rogue_fwif_compchecks, build_options, 44);
OFFSET_CHECK(struct rogue_fwif_compchecks, init_options, 48);
OFFSET_CHECK(struct rogue_fwif_compchecks, updated, 56);
SIZE_CHECK(struct rogue_fwif_compchecks, 64);
OFFSET_CHECK(struct rogue_fwif_osinit, kernel_ccbctl_fw_addr, 0);
OFFSET_CHECK(struct rogue_fwif_osinit, kernel_ccb_fw_addr, 4);
OFFSET_CHECK(struct rogue_fwif_osinit, kernel_ccb_rtn_slots_fw_addr, 8);
OFFSET_CHECK(struct rogue_fwif_osinit, firmware_ccbctl_fw_addr, 12);
OFFSET_CHECK(struct rogue_fwif_osinit, firmware_ccb_fw_addr, 16);
OFFSET_CHECK(struct rogue_fwif_osinit, work_est_firmware_ccbctl_fw_addr, 20);
OFFSET_CHECK(struct rogue_fwif_osinit, work_est_firmware_ccb_fw_addr, 24);
OFFSET_CHECK(struct rogue_fwif_osinit, rogue_fwif_hwr_info_buf_ctl_fw_addr, 28);
OFFSET_CHECK(struct rogue_fwif_osinit, hwr_debug_dump_limit, 32);
OFFSET_CHECK(struct rogue_fwif_osinit, fw_os_data_fw_addr, 36);
OFFSET_CHECK(struct rogue_fwif_osinit, rogue_comp_checks, 40);
SIZE_CHECK(struct rogue_fwif_osinit, 104);
OFFSET_CHECK(struct rogue_fwif_sigbuf_ctl, buffer_fw_addr, 0);
OFFSET_CHECK(struct rogue_fwif_sigbuf_ctl, left_size_in_regs, 4);
SIZE_CHECK(struct rogue_fwif_sigbuf_ctl, 8);
OFFSET_CHECK(struct pdvfs_opp, volt, 0);
OFFSET_CHECK(struct pdvfs_opp, freq, 4);
SIZE_CHECK(struct pdvfs_opp, 8);
OFFSET_CHECK(struct rogue_fwif_pdvfs_opp, opp_values, 0);
OFFSET_CHECK(struct rogue_fwif_pdvfs_opp, min_opp_point, 128);
OFFSET_CHECK(struct rogue_fwif_pdvfs_opp, max_opp_point, 132);
SIZE_CHECK(struct rogue_fwif_pdvfs_opp, 136);
OFFSET_CHECK(struct rogue_fwif_counter_dump_ctl, buffer_fw_addr, 0);
OFFSET_CHECK(struct rogue_fwif_counter_dump_ctl, size_in_dwords, 4);
SIZE_CHECK(struct rogue_fwif_counter_dump_ctl, 8);
OFFSET_CHECK(struct rogue_hwperf_bvnc, bvnc_string, 0);
OFFSET_CHECK(struct rogue_hwperf_bvnc, bvnc_km_feature_flags, 24);
OFFSET_CHECK(struct rogue_hwperf_bvnc, num_bvnc_blocks, 28);
OFFSET_CHECK(struct rogue_hwperf_bvnc, bvnc_gpu_cores, 30);
OFFSET_CHECK(struct rogue_hwperf_bvnc, bvnc_blocks, 32);
SIZE_CHECK(struct rogue_hwperf_bvnc, 160);
OFFSET_CHECK(struct rogue_fwif_sysinit, fault_phys_addr, 0);
OFFSET_CHECK(struct rogue_fwif_sysinit, pds_exec_base, 8);
OFFSET_CHECK(struct rogue_fwif_sysinit, usc_exec_base, 16);
OFFSET_CHECK(struct rogue_fwif_sysinit, fbcdc_state_table_base, 24);
OFFSET_CHECK(struct rogue_fwif_sysinit, fbcdc_large_state_table_base, 32);
OFFSET_CHECK(struct rogue_fwif_sysinit, texture_heap_base, 40);
OFFSET_CHECK(struct rogue_fwif_sysinit, hw_perf_filter, 48);
OFFSET_CHECK(struct rogue_fwif_sysinit, slc3_fence_dev_addr, 56);
OFFSET_CHECK(struct rogue_fwif_sysinit, tpu_trilinear_frac_mask, 64);
OFFSET_CHECK(struct rogue_fwif_sysinit, sigbuf_ctl, 80);
OFFSET_CHECK(struct rogue_fwif_sysinit, pdvfs_opp_info, 152);
OFFSET_CHECK(struct rogue_fwif_sysinit, coremem_data_store, 288);
OFFSET_CHECK(struct rogue_fwif_sysinit, counter_dump_ctl, 304);
OFFSET_CHECK(struct rogue_fwif_sysinit, filter_flags, 312);
OFFSET_CHECK(struct rogue_fwif_sysinit, runtime_cfg_fw_addr, 316);
OFFSET_CHECK(struct rogue_fwif_sysinit, trace_buf_ctl_fw_addr, 320);
OFFSET_CHECK(struct rogue_fwif_sysinit, fw_sys_data_fw_addr, 324);
OFFSET_CHECK(struct rogue_fwif_sysinit, gpu_util_fw_cb_ctl_fw_addr, 328);
OFFSET_CHECK(struct rogue_fwif_sysinit, reg_cfg_fw_addr, 332);
OFFSET_CHECK(struct rogue_fwif_sysinit, hwperf_ctl_fw_addr, 336);
OFFSET_CHECK(struct rogue_fwif_sysinit, align_checks, 340);
OFFSET_CHECK(struct rogue_fwif_sysinit, initial_core_clock_speed, 344);
OFFSET_CHECK(struct rogue_fwif_sysinit, active_pm_latency_ms, 348);
OFFSET_CHECK(struct rogue_fwif_sysinit, firmware_started, 352);
OFFSET_CHECK(struct rogue_fwif_sysinit, marker_val, 356);
OFFSET_CHECK(struct rogue_fwif_sysinit, firmware_started_timestamp, 360);
OFFSET_CHECK(struct rogue_fwif_sysinit, jones_disable_mask, 364);
OFFSET_CHECK(struct rogue_fwif_sysinit, firmware_perf, 368);
OFFSET_CHECK(struct rogue_fwif_sysinit, core_clock_rate_fw_addr, 372);
OFFSET_CHECK(struct rogue_fwif_sysinit, gpio_validation_mode, 376);
OFFSET_CHECK(struct rogue_fwif_sysinit, bvnc_km_feature_flags, 380);
OFFSET_CHECK(struct rogue_fwif_sysinit, tfbc_compression_control, 540);
SIZE_CHECK(struct rogue_fwif_sysinit, 544);
OFFSET_CHECK(struct rogue_fwif_gpu_util_fwcb, time_corr, 0);
OFFSET_CHECK(struct rogue_fwif_gpu_util_fwcb, time_corr_seq_count, 10240);
OFFSET_CHECK(struct rogue_fwif_gpu_util_fwcb, gpu_util_flags, 10244);
OFFSET_CHECK(struct rogue_fwif_gpu_util_fwcb, last_word, 10248);
OFFSET_CHECK(struct rogue_fwif_gpu_util_fwcb, stats_counters, 10256);
SIZE_CHECK(struct rogue_fwif_gpu_util_fwcb, 10280);
OFFSET_CHECK(struct rogue_fwif_rta_ctl, render_target_index, 0);
OFFSET_CHECK(struct rogue_fwif_rta_ctl, current_render_target, 4);
OFFSET_CHECK(struct rogue_fwif_rta_ctl, active_render_targets, 8);
OFFSET_CHECK(struct rogue_fwif_rta_ctl, cumul_active_render_targets, 12);
OFFSET_CHECK(struct rogue_fwif_rta_ctl, valid_render_targets_fw_addr, 16);
OFFSET_CHECK(struct rogue_fwif_rta_ctl, rta_num_partial_renders_fw_addr, 20);
OFFSET_CHECK(struct rogue_fwif_rta_ctl, max_rts, 24);
OFFSET_CHECK(struct rogue_fwif_rta_ctl, rta_ctl_flags, 28);
SIZE_CHECK(struct rogue_fwif_rta_ctl, 32);
OFFSET_CHECK(struct rogue_fwif_freelist, freelist_dev_addr, 0);
OFFSET_CHECK(struct rogue_fwif_freelist, current_dev_addr, 8);
OFFSET_CHECK(struct rogue_fwif_freelist, current_stack_top, 16);
OFFSET_CHECK(struct rogue_fwif_freelist, max_pages, 20);
OFFSET_CHECK(struct rogue_fwif_freelist, grow_pages, 24);
OFFSET_CHECK(struct rogue_fwif_freelist, current_pages, 28);
OFFSET_CHECK(struct rogue_fwif_freelist, allocated_page_count, 32);
OFFSET_CHECK(struct rogue_fwif_freelist, allocated_mmu_page_count, 36);
OFFSET_CHECK(struct rogue_fwif_freelist, freelist_id, 40);
OFFSET_CHECK(struct rogue_fwif_freelist, grow_pending, 44);
OFFSET_CHECK(struct rogue_fwif_freelist, ready_pages, 48);
OFFSET_CHECK(struct rogue_fwif_freelist, freelist_flags, 52);
OFFSET_CHECK(struct rogue_fwif_freelist, pm_global_pb, 56);
SIZE_CHECK(struct rogue_fwif_freelist, 64);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, geom_caches_need_zeroing, 0);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, screen_pixel_max, 4);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, multi_sample_ctl, 8);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, flipped_multi_sample_ctl, 16);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, tpc_stride, 24);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, tpc_size, 28);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, te_screen, 32);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, mtile_stride, 36);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, teaa, 40);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, te_mtile1, 44);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, te_mtile2, 48);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_merge_lower_x, 52);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_merge_lower_y, 56);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_merge_upper_x, 60);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_merge_upper_y, 64);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_merge_scale_x, 68);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_merge_scale_y, 72);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, rgn_header_size, 76);
OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_mtile_size, 80);
SIZE_CHECK(struct rogue_fwif_hwrtdata_common, 88);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, pm_mlist_dev_addr, 0);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, vce_cat_base, 8);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, vce_last_cat_base, 40);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, te_cat_base, 72);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, te_last_cat_base, 104);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, alist_cat_base, 136);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, alist_last_cat_base, 144);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, pm_alist_stack_pointer, 152);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, pm_mlist_stack_pointer, 160);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, hwrt_data_common_fw_addr, 164);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, hwrt_data_flags, 168);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, state, 172);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, freelists_fw_addr, 176);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, freelist_hwr_snapshot, 188);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, vheap_table_dev_addr, 200);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, rta_ctl, 208);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, tail_ptrs_dev_addr, 240);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, macrotile_array_dev_addr, 248);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, rgn_header_dev_addr, 256);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, rtc_dev_addr, 264);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, owner_geom_not_used_by_host, 272);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, geom_caches_need_zeroing, 276);
OFFSET_CHECK(struct rogue_fwif_hwrtdata, cleanup_state, 320);
SIZE_CHECK(struct rogue_fwif_hwrtdata, 384);
OFFSET_CHECK(struct rogue_fwif_sync_checkpoint, state, 0);
OFFSET_CHECK(struct rogue_fwif_sync_checkpoint, fw_ref_count, 4);
SIZE_CHECK(struct rogue_fwif_sync_checkpoint, 8);
#endif /* PVR_ROGUE_FWIF_CHECK_H */

View file

@ -0,0 +1,373 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_FWIF_CLIENT_H
#define PVR_ROGUE_FWIF_CLIENT_H
#include <linux/bits.h>
#include <linux/kernel.h>
#include <linux/sizes.h>
#include <linux/types.h>
#include "pvr_rogue_fwif_shared.h"
/*
* Page size used for Parameter Management.
*/
#define ROGUE_PM_PAGE_SIZE SZ_4K
/*
* Minimum/Maximum PB size.
*
* Base page size is dependent on core:
* S6/S6XT/S7 = 50 pages
* S8XE = 40 pages
* S8XE with BRN66011 fixed = 25 pages
*
* Minimum PB = Base Pages + (NUM_TE_PIPES-1)*16K + (NUM_VCE_PIPES-1)*64K +
* IF_PM_PREALLOC(NUM_TE_PIPES*16K + NUM_VCE_PIPES*16K)
*
* Maximum PB size must ensure that no PM address space can be fully used,
* because if the full address space was used it would wrap and corrupt itself.
* Since there are two freelists (local is always minimum sized) this can be
* described as following three conditions being met:
*
* (Minimum PB + Maximum PB) < ALIST PM address space size (16GB)
* (Minimum PB + Maximum PB) < TE PM address space size (16GB) / NUM_TE_PIPES
* (Minimum PB + Maximum PB) < VCE PM address space size (16GB) / NUM_VCE_PIPES
*
* Since the max of NUM_TE_PIPES and NUM_VCE_PIPES is 4, we have a hard limit
* of 4GB minus the Minimum PB. For convenience we take the smaller power-of-2
* value of 2GB. This is far more than any current applications use.
*/
#define ROGUE_PM_MAX_FREELIST_SIZE SZ_2G
/*
* Flags supported by the geometry DM command i.e. &struct rogue_fwif_cmd_geom.
*/
#define ROGUE_GEOM_FLAGS_FIRSTKICK BIT_MASK(0)
#define ROGUE_GEOM_FLAGS_LASTKICK BIT_MASK(1)
/* Use single core in a multi core setup. */
#define ROGUE_GEOM_FLAGS_SINGLE_CORE BIT_MASK(3)
/*
* Flags supported by the fragment DM command i.e. &struct rogue_fwif_cmd_frag.
*/
/* Use single core in a multi core setup. */
#define ROGUE_FRAG_FLAGS_SINGLE_CORE BIT_MASK(3)
/* Indicates whether this render produces visibility results. */
#define ROGUE_FRAG_FLAGS_GET_VIS_RESULTS BIT_MASK(5)
/* Indicates whether a depth buffer is present. */
#define ROGUE_FRAG_FLAGS_DEPTHBUFFER BIT_MASK(7)
/* Indicates whether a stencil buffer is present. */
#define ROGUE_FRAG_FLAGS_STENCILBUFFER BIT_MASK(8)
/* Disable pixel merging for this render. */
#define ROGUE_FRAG_FLAGS_DISABLE_PIXELMERGE BIT_MASK(15)
/* Indicates whether a scratch buffer is present. */
#define ROGUE_FRAG_FLAGS_SCRATCHBUFFER BIT_MASK(19)
/* Disallow compute overlapped with this render. */
#define ROGUE_FRAG_FLAGS_PREVENT_CDM_OVERLAP BIT_MASK(26)
/*
* Flags supported by the compute DM command i.e. &struct rogue_fwif_cmd_compute.
*/
#define ROGUE_COMPUTE_FLAG_PREVENT_ALL_OVERLAP BIT_MASK(2)
/*!< Use single core in a multi core setup. */
#define ROGUE_COMPUTE_FLAG_SINGLE_CORE BIT_MASK(5)
/*
* Flags supported by the transfer DM command i.e. &struct rogue_fwif_cmd_transfer.
*/
/*!< Use single core in a multi core setup. */
#define ROGUE_TRANSFER_FLAGS_SINGLE_CORE BIT_MASK(1)
/*
************************************************
* Parameter/HWRTData control structures.
************************************************
*/
/*
* Configuration registers which need to be loaded by the firmware before a geometry
* job can be started.
*/
struct rogue_fwif_geom_regs {
u64 vdm_ctrl_stream_base;
u64 tpu_border_colour_table;
/* Only used when feature VDM_DRAWINDIRECT present. */
u64 vdm_draw_indirect0;
/* Only used when feature VDM_DRAWINDIRECT present. */
u32 vdm_draw_indirect1;
u32 ppp_ctrl;
u32 te_psg;
/* Only used when BRN 49927 present. */
u32 tpu;
u32 vdm_context_resume_task0_size;
/* Only used when feature VDM_OBJECT_LEVEL_LLS present. */
u32 vdm_context_resume_task3_size;
/* Only used when BRN 56279 or BRN 67381 present. */
u32 pds_ctrl;
u32 view_idx;
/* Only used when feature TESSELLATION present */
u32 pds_coeff_free_prog;
u32 padding;
};
/* Only used when BRN 44455 or BRN 63027 present. */
struct rogue_fwif_dummy_rgnhdr_init_geom_regs {
u64 te_psgregion_addr;
};
/*
* Represents a geometry command that can be used to tile a whole scene's objects as
* per TA behavior.
*/
struct rogue_fwif_cmd_geom {
/*
* rogue_fwif_cmd_geom_frag_shared field must always be at the beginning of the
* struct.
*
* The command struct (rogue_fwif_cmd_geom) is shared between Client and
* Firmware. Kernel is unable to perform read/write operations on the
* command struct, the SHARED region is the only exception from this rule.
* This region must be the first member so that Kernel can easily access it.
* For more info, see rogue_fwif_cmd_geom_frag_shared definition.
*/
struct rogue_fwif_cmd_geom_frag_shared cmd_shared;
struct rogue_fwif_geom_regs regs __aligned(8);
u32 flags __aligned(8);
/*
* Holds the geometry/fragment fence value to allow the fragment partial render command
* to go through.
*/
struct rogue_fwif_ufo partial_render_geom_frag_fence;
/* Only used when BRN 44455 or BRN 63027 present. */
struct rogue_fwif_dummy_rgnhdr_init_geom_regs dummy_rgnhdr_init_geom_regs __aligned(8);
/* Only used when BRN 61484 or BRN 66333 present. */
u32 brn61484_66333_live_rt;
u32 padding;
};
/*
* Configuration registers which need to be loaded by the firmware before ISP
* can be started.
*/
struct rogue_fwif_frag_regs {
u32 usc_pixel_output_ctrl;
#define ROGUE_MAXIMUM_OUTPUT_REGISTERS_PER_PIXEL 8U
u32 usc_clear_register[ROGUE_MAXIMUM_OUTPUT_REGISTERS_PER_PIXEL];
u32 isp_bgobjdepth;
u32 isp_bgobjvals;
u32 isp_aa;
/* Only used when feature S7_TOP_INFRASTRUCTURE present. */
u32 isp_xtp_pipe_enable;
u32 isp_ctl;
/* Only used when BRN 49927 present. */
u32 tpu;
u32 event_pixel_pds_info;
/* Only used when feature CLUSTER_GROUPING present. */
u32 pixel_phantom;
u32 view_idx;
u32 event_pixel_pds_data;
/* Only used when BRN 65101 present. */
u32 brn65101_event_pixel_pds_data;
/* Only used when feature GPU_MULTICORE_SUPPORT or BRN 47217 present. */
u32 isp_oclqry_stride;
/* Only used when feature ZLS_SUBTILE present. */
u32 isp_zls_pixels;
/* Only used when feature ISP_ZLS_D24_S8_PACKING_OGL_MODE present. */
u32 rgx_cr_blackpearl_fix;
/* All values below the ALIGN(8) must be 64 bit. */
aligned_u64 isp_scissor_base;
u64 isp_dbias_base;
u64 isp_oclqry_base;
u64 isp_zlsctl;
u64 isp_zload_store_base;
u64 isp_stencil_load_store_base;
/*
* Only used when feature FBCDC_ALGORITHM present and value < 3 or feature
* FB_CDC_V4 present. Additionally, BRNs 48754, 60227, 72310 and 72311 must
* not be present.
*/
u64 fb_cdc_zls;
#define ROGUE_PBE_WORDS_REQUIRED_FOR_RENDERS 3U
u64 pbe_word[8U][ROGUE_PBE_WORDS_REQUIRED_FOR_RENDERS];
u64 tpu_border_colour_table;
u64 pds_bgnd[3U];
/* Only used when BRN 65101 present. */
u64 pds_bgnd_brn65101[3U];
u64 pds_pr_bgnd[3U];
/* Only used when BRN 62850 or 62865 present. */
u64 isp_dummy_stencil_store_base;
/* Only used when BRN 66193 present. */
u64 isp_dummy_depth_store_base;
/* Only used when BRN 67182 present. */
u32 rgnhdr_single_rt_size;
/* Only used when BRN 67182 present. */
u32 rgnhdr_scratch_offset;
};
struct rogue_fwif_cmd_frag {
struct rogue_fwif_cmd_geom_frag_shared cmd_shared __aligned(8);
struct rogue_fwif_frag_regs regs __aligned(8);
/* command control flags. */
u32 flags;
/* Stride IN BYTES for Z-Buffer in case of RTAs. */
u32 zls_stride;
/* Stride IN BYTES for S-Buffer in case of RTAs. */
u32 sls_stride;
/* Only used if feature GPU_MULTICORE_SUPPORT present. */
u32 execute_count;
};
/*
* Configuration registers which need to be loaded by the firmware before CDM
* can be started.
*/
struct rogue_fwif_compute_regs {
u64 tpu_border_colour_table;
/* Only used when feature CDM_USER_MODE_QUEUE present. */
u64 cdm_cb_queue;
/* Only used when feature CDM_USER_MODE_QUEUE present. */
u64 cdm_cb_base;
/* Only used when feature CDM_USER_MODE_QUEUE present. */
u64 cdm_cb;
/* Only used when feature CDM_USER_MODE_QUEUE is not present. */
u64 cdm_ctrl_stream_base;
u64 cdm_context_state_base_addr;
/* Only used when BRN 49927 is present. */
u32 tpu;
u32 cdm_resume_pds1;
/* Only used when feature COMPUTE_MORTON_CAPABLE present. */
u32 cdm_item;
/* Only used when feature CLUSTER_GROUPING present. */
u32 compute_cluster;
/* Only used when feature TPU_DM_GLOBAL_REGISTERS present. */
u32 tpu_tag_cdm_ctrl;
u32 padding;
};
struct rogue_fwif_cmd_compute {
/* Common command attributes */
struct rogue_fwif_cmd_common common __aligned(8);
/* CDM registers */
struct rogue_fwif_compute_regs regs;
/* Control flags */
u32 flags __aligned(8);
/* Only used when feature UNIFIED_STORE_VIRTUAL_PARTITIONING present. */
u32 num_temp_regions;
/* Only used when feature CDM_USER_MODE_QUEUE present. */
u32 stream_start_offset;
/* Only used when feature GPU_MULTICORE_SUPPORT present. */
u32 execute_count;
};
struct rogue_fwif_transfer_regs {
/*
* All 32 bit values should be added in the top section. This then requires only a
* single RGXFW_ALIGN to align all the 64 bit values in the second section.
*/
u32 isp_bgobjvals;
u32 usc_pixel_output_ctrl;
u32 usc_clear_register0;
u32 usc_clear_register1;
u32 usc_clear_register2;
u32 usc_clear_register3;
u32 isp_mtile_size;
u32 isp_render_origin;
u32 isp_ctl;
/* Only used when feature S7_TOP_INFRASTRUCTURE present. */
u32 isp_xtp_pipe_enable;
u32 isp_aa;
u32 event_pixel_pds_info;
u32 event_pixel_pds_code;
u32 event_pixel_pds_data;
u32 isp_render;
u32 isp_rgn;
/* Only used when feature GPU_MULTICORE_SUPPORT present. */
u32 frag_screen;
/* All values below the aligned_u64 must be 64 bit. */
aligned_u64 pds_bgnd0_base;
u64 pds_bgnd1_base;
u64 pds_bgnd3_sizeinfo;
u64 isp_mtile_base;
#define ROGUE_PBE_WORDS_REQUIRED_FOR_TQS 3
/* TQ_MAX_RENDER_TARGETS * PBE_STATE_SIZE */
u64 pbe_wordx_mrty[3U * ROGUE_PBE_WORDS_REQUIRED_FOR_TQS];
};
struct rogue_fwif_cmd_transfer {
/* Common command attributes */
struct rogue_fwif_cmd_common common __aligned(8);
struct rogue_fwif_transfer_regs regs __aligned(8);
u32 flags;
u32 padding;
};
#include "pvr_rogue_fwif_client_check.h"
#endif /* PVR_ROGUE_FWIF_CLIENT_H */

View file

@ -0,0 +1,133 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_FWIF_CLIENT_CHECK_H
#define PVR_ROGUE_FWIF_CLIENT_CHECK_H
#include <linux/build_bug.h>
#define OFFSET_CHECK(type, member, offset) \
static_assert(offsetof(type, member) == (offset), \
"offsetof(" #type ", " #member ") incorrect")
#define SIZE_CHECK(type, size) \
static_assert(sizeof(type) == (size), #type " is incorrect size")
OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_ctrl_stream_base, 0);
OFFSET_CHECK(struct rogue_fwif_geom_regs, tpu_border_colour_table, 8);
OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_draw_indirect0, 16);
OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_draw_indirect1, 24);
OFFSET_CHECK(struct rogue_fwif_geom_regs, ppp_ctrl, 28);
OFFSET_CHECK(struct rogue_fwif_geom_regs, te_psg, 32);
OFFSET_CHECK(struct rogue_fwif_geom_regs, tpu, 36);
OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_context_resume_task0_size, 40);
OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_context_resume_task3_size, 44);
OFFSET_CHECK(struct rogue_fwif_geom_regs, pds_ctrl, 48);
OFFSET_CHECK(struct rogue_fwif_geom_regs, view_idx, 52);
OFFSET_CHECK(struct rogue_fwif_geom_regs, pds_coeff_free_prog, 56);
SIZE_CHECK(struct rogue_fwif_geom_regs, 64);
OFFSET_CHECK(struct rogue_fwif_dummy_rgnhdr_init_geom_regs, te_psgregion_addr, 0);
SIZE_CHECK(struct rogue_fwif_dummy_rgnhdr_init_geom_regs, 8);
OFFSET_CHECK(struct rogue_fwif_cmd_geom, cmd_shared, 0);
OFFSET_CHECK(struct rogue_fwif_cmd_geom, regs, 16);
OFFSET_CHECK(struct rogue_fwif_cmd_geom, flags, 80);
OFFSET_CHECK(struct rogue_fwif_cmd_geom, partial_render_geom_frag_fence, 84);
OFFSET_CHECK(struct rogue_fwif_cmd_geom, dummy_rgnhdr_init_geom_regs, 96);
OFFSET_CHECK(struct rogue_fwif_cmd_geom, brn61484_66333_live_rt, 104);
SIZE_CHECK(struct rogue_fwif_cmd_geom, 112);
OFFSET_CHECK(struct rogue_fwif_frag_regs, usc_pixel_output_ctrl, 0);
OFFSET_CHECK(struct rogue_fwif_frag_regs, usc_clear_register, 4);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_bgobjdepth, 36);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_bgobjvals, 40);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_aa, 44);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_xtp_pipe_enable, 48);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_ctl, 52);
OFFSET_CHECK(struct rogue_fwif_frag_regs, tpu, 56);
OFFSET_CHECK(struct rogue_fwif_frag_regs, event_pixel_pds_info, 60);
OFFSET_CHECK(struct rogue_fwif_frag_regs, pixel_phantom, 64);
OFFSET_CHECK(struct rogue_fwif_frag_regs, view_idx, 68);
OFFSET_CHECK(struct rogue_fwif_frag_regs, event_pixel_pds_data, 72);
OFFSET_CHECK(struct rogue_fwif_frag_regs, brn65101_event_pixel_pds_data, 76);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_oclqry_stride, 80);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_zls_pixels, 84);
OFFSET_CHECK(struct rogue_fwif_frag_regs, rgx_cr_blackpearl_fix, 88);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_scissor_base, 96);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_dbias_base, 104);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_oclqry_base, 112);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_zlsctl, 120);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_zload_store_base, 128);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_stencil_load_store_base, 136);
OFFSET_CHECK(struct rogue_fwif_frag_regs, fb_cdc_zls, 144);
OFFSET_CHECK(struct rogue_fwif_frag_regs, pbe_word, 152);
OFFSET_CHECK(struct rogue_fwif_frag_regs, tpu_border_colour_table, 344);
OFFSET_CHECK(struct rogue_fwif_frag_regs, pds_bgnd, 352);
OFFSET_CHECK(struct rogue_fwif_frag_regs, pds_bgnd_brn65101, 376);
OFFSET_CHECK(struct rogue_fwif_frag_regs, pds_pr_bgnd, 400);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_dummy_stencil_store_base, 424);
OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_dummy_depth_store_base, 432);
OFFSET_CHECK(struct rogue_fwif_frag_regs, rgnhdr_single_rt_size, 440);
OFFSET_CHECK(struct rogue_fwif_frag_regs, rgnhdr_scratch_offset, 444);
SIZE_CHECK(struct rogue_fwif_frag_regs, 448);
OFFSET_CHECK(struct rogue_fwif_cmd_frag, cmd_shared, 0);
OFFSET_CHECK(struct rogue_fwif_cmd_frag, regs, 16);
OFFSET_CHECK(struct rogue_fwif_cmd_frag, flags, 464);
OFFSET_CHECK(struct rogue_fwif_cmd_frag, zls_stride, 468);
OFFSET_CHECK(struct rogue_fwif_cmd_frag, sls_stride, 472);
OFFSET_CHECK(struct rogue_fwif_cmd_frag, execute_count, 476);
SIZE_CHECK(struct rogue_fwif_cmd_frag, 480);
OFFSET_CHECK(struct rogue_fwif_compute_regs, tpu_border_colour_table, 0);
OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_cb_queue, 8);
OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_cb_base, 16);
OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_cb, 24);
OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_ctrl_stream_base, 32);
OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_context_state_base_addr, 40);
OFFSET_CHECK(struct rogue_fwif_compute_regs, tpu, 48);
OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_resume_pds1, 52);
OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_item, 56);
OFFSET_CHECK(struct rogue_fwif_compute_regs, compute_cluster, 60);
OFFSET_CHECK(struct rogue_fwif_compute_regs, tpu_tag_cdm_ctrl, 64);
SIZE_CHECK(struct rogue_fwif_compute_regs, 72);
OFFSET_CHECK(struct rogue_fwif_cmd_compute, common, 0);
OFFSET_CHECK(struct rogue_fwif_cmd_compute, regs, 8);
OFFSET_CHECK(struct rogue_fwif_cmd_compute, flags, 80);
OFFSET_CHECK(struct rogue_fwif_cmd_compute, num_temp_regions, 84);
OFFSET_CHECK(struct rogue_fwif_cmd_compute, stream_start_offset, 88);
OFFSET_CHECK(struct rogue_fwif_cmd_compute, execute_count, 92);
SIZE_CHECK(struct rogue_fwif_cmd_compute, 96);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_bgobjvals, 0);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, usc_pixel_output_ctrl, 4);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, usc_clear_register0, 8);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, usc_clear_register1, 12);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, usc_clear_register2, 16);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, usc_clear_register3, 20);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_mtile_size, 24);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_render_origin, 28);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_ctl, 32);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_xtp_pipe_enable, 36);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_aa, 40);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, event_pixel_pds_info, 44);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, event_pixel_pds_code, 48);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, event_pixel_pds_data, 52);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_render, 56);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_rgn, 60);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, frag_screen, 64);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, pds_bgnd0_base, 72);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, pds_bgnd1_base, 80);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, pds_bgnd3_sizeinfo, 88);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_mtile_base, 96);
OFFSET_CHECK(struct rogue_fwif_transfer_regs, pbe_wordx_mrty, 104);
SIZE_CHECK(struct rogue_fwif_transfer_regs, 176);
OFFSET_CHECK(struct rogue_fwif_cmd_transfer, common, 0);
OFFSET_CHECK(struct rogue_fwif_cmd_transfer, regs, 8);
OFFSET_CHECK(struct rogue_fwif_cmd_transfer, flags, 184);
SIZE_CHECK(struct rogue_fwif_cmd_transfer, 192);
#endif /* PVR_ROGUE_FWIF_CLIENT_CHECK_H */

View file

@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_FWIF_COMMON_H
#define PVR_ROGUE_FWIF_COMMON_H
#include <linux/build_bug.h>
/*
* This macro represents a mask of LSBs that must be zero on data structure
* sizes and offsets to ensure they are 8-byte granular on types shared between
* the FW and host driver.
*/
#define PVR_FW_ALIGNMENT_LSB 7U
/* Macro to test structure size alignment. */
#define PVR_FW_STRUCT_SIZE_ASSERT(_a) \
static_assert((sizeof(_a) & PVR_FW_ALIGNMENT_LSB) == 0U, \
"Size of " #_a " is not properly aligned")
/* The master definition for data masters known to the firmware. */
#define PVR_FWIF_DM_GP (0)
/* Either TDM or 2D DM is present. */
/* When the 'tla' feature is present in the hw (as per @pvr_device_features). */
#define PVR_FWIF_DM_2D (1)
/*
* When the 'fastrender_dm' feature is present in the hw (as per
* @pvr_device_features).
*/
#define PVR_FWIF_DM_TDM (1)
#define PVR_FWIF_DM_GEOM (2)
#define PVR_FWIF_DM_FRAG (3)
#define PVR_FWIF_DM_CDM (4)
#define PVR_FWIF_DM_RAY (5)
#define PVR_FWIF_DM_GEOM2 (6)
#define PVR_FWIF_DM_GEOM3 (7)
#define PVR_FWIF_DM_GEOM4 (8)
#define PVR_FWIF_DM_LAST PVR_FWIF_DM_GEOM4
/* Maximum number of DM in use: GP, 2D/TDM, GEOM, 3D, CDM, RAY, GEOM2, GEOM3, GEOM4 */
#define PVR_FWIF_DM_MAX (PVR_FWIF_DM_LAST + 1U)
/* GPU Utilisation states */
#define PVR_FWIF_GPU_UTIL_STATE_IDLE 0U
#define PVR_FWIF_GPU_UTIL_STATE_ACTIVE 1U
#define PVR_FWIF_GPU_UTIL_STATE_BLOCKED 2U
#define PVR_FWIF_GPU_UTIL_STATE_NUM 3U
#define PVR_FWIF_GPU_UTIL_STATE_MASK 0x3ULL
/*
* Maximum amount of register writes that can be done by the register
* programmer (FW or META DMA). This is not a HW limitation, it is only
* a protection against malformed inputs to the register programmer.
*/
#define PVR_MAX_NUM_REGISTER_PROGRAMMER_WRITES 128U
#endif /* PVR_ROGUE_FWIF_COMMON_H */

View file

@ -0,0 +1,113 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef __PVR_ROGUE_FWIF_DEV_INFO_H__
#define __PVR_ROGUE_FWIF_DEV_INFO_H__
enum {
PVR_FW_HAS_BRN_44079 = 0,
PVR_FW_HAS_BRN_47217,
PVR_FW_HAS_BRN_48492,
PVR_FW_HAS_BRN_48545,
PVR_FW_HAS_BRN_49927,
PVR_FW_HAS_BRN_50767,
PVR_FW_HAS_BRN_51764,
PVR_FW_HAS_BRN_62269,
PVR_FW_HAS_BRN_63142,
PVR_FW_HAS_BRN_63553,
PVR_FW_HAS_BRN_66011,
PVR_FW_HAS_BRN_71242,
PVR_FW_HAS_BRN_MAX
};
enum {
PVR_FW_HAS_ERN_35421 = 0,
PVR_FW_HAS_ERN_38020,
PVR_FW_HAS_ERN_38748,
PVR_FW_HAS_ERN_42064,
PVR_FW_HAS_ERN_42290,
PVR_FW_HAS_ERN_42606,
PVR_FW_HAS_ERN_47025,
PVR_FW_HAS_ERN_57596,
PVR_FW_HAS_ERN_MAX
};
enum {
PVR_FW_HAS_FEATURE_AXI_ACELITE = 0,
PVR_FW_HAS_FEATURE_CDM_CONTROL_STREAM_FORMAT,
PVR_FW_HAS_FEATURE_CLUSTER_GROUPING,
PVR_FW_HAS_FEATURE_COMMON_STORE_SIZE_IN_DWORDS,
PVR_FW_HAS_FEATURE_COMPUTE,
PVR_FW_HAS_FEATURE_COMPUTE_MORTON_CAPABLE,
PVR_FW_HAS_FEATURE_COMPUTE_OVERLAP,
PVR_FW_HAS_FEATURE_COREID_PER_OS,
PVR_FW_HAS_FEATURE_DYNAMIC_DUST_POWER,
PVR_FW_HAS_FEATURE_ECC_RAMS,
PVR_FW_HAS_FEATURE_FBCDC,
PVR_FW_HAS_FEATURE_FBCDC_ALGORITHM,
PVR_FW_HAS_FEATURE_FBCDC_ARCHITECTURE,
PVR_FW_HAS_FEATURE_FBC_MAX_DEFAULT_DESCRIPTORS,
PVR_FW_HAS_FEATURE_FBC_MAX_LARGE_DESCRIPTORS,
PVR_FW_HAS_FEATURE_FB_CDC_V4,
PVR_FW_HAS_FEATURE_GPU_MULTICORE_SUPPORT,
PVR_FW_HAS_FEATURE_GPU_VIRTUALISATION,
PVR_FW_HAS_FEATURE_GS_RTA_SUPPORT,
PVR_FW_HAS_FEATURE_IRQ_PER_OS,
PVR_FW_HAS_FEATURE_ISP_MAX_TILES_IN_FLIGHT,
PVR_FW_HAS_FEATURE_ISP_SAMPLES_PER_PIXEL,
PVR_FW_HAS_FEATURE_ISP_ZLS_D24_S8_PACKING_OGL_MODE,
PVR_FW_HAS_FEATURE_LAYOUT_MARS,
PVR_FW_HAS_FEATURE_MAX_PARTITIONS,
PVR_FW_HAS_FEATURE_META,
PVR_FW_HAS_FEATURE_META_COREMEM_SIZE,
PVR_FW_HAS_FEATURE_MIPS,
PVR_FW_HAS_FEATURE_NUM_CLUSTERS,
PVR_FW_HAS_FEATURE_NUM_ISP_IPP_PIPES,
PVR_FW_HAS_FEATURE_NUM_OSIDS,
PVR_FW_HAS_FEATURE_NUM_RASTER_PIPES,
PVR_FW_HAS_FEATURE_PBE2_IN_XE,
PVR_FW_HAS_FEATURE_PBVNC_COREID_REG,
PVR_FW_HAS_FEATURE_PERFBUS,
PVR_FW_HAS_FEATURE_PERF_COUNTER_BATCH,
PVR_FW_HAS_FEATURE_PHYS_BUS_WIDTH,
PVR_FW_HAS_FEATURE_RISCV_FW_PROCESSOR,
PVR_FW_HAS_FEATURE_ROGUEXE,
PVR_FW_HAS_FEATURE_S7_TOP_INFRASTRUCTURE,
PVR_FW_HAS_FEATURE_SIMPLE_INTERNAL_PARAMETER_FORMAT,
PVR_FW_HAS_FEATURE_SIMPLE_INTERNAL_PARAMETER_FORMAT_V2,
PVR_FW_HAS_FEATURE_SIMPLE_PARAMETER_FORMAT_VERSION,
PVR_FW_HAS_FEATURE_SLC_BANKS,
PVR_FW_HAS_FEATURE_SLC_CACHE_LINE_SIZE_BITS,
PVR_FW_HAS_FEATURE_SLC_SIZE_CONFIGURABLE,
PVR_FW_HAS_FEATURE_SLC_SIZE_IN_KILOBYTES,
PVR_FW_HAS_FEATURE_SOC_TIMER,
PVR_FW_HAS_FEATURE_SYS_BUS_SECURE_RESET,
PVR_FW_HAS_FEATURE_TESSELLATION,
PVR_FW_HAS_FEATURE_TILE_REGION_PROTECTION,
PVR_FW_HAS_FEATURE_TILE_SIZE_X,
PVR_FW_HAS_FEATURE_TILE_SIZE_Y,
PVR_FW_HAS_FEATURE_TLA,
PVR_FW_HAS_FEATURE_TPU_CEM_DATAMASTER_GLOBAL_REGISTERS,
PVR_FW_HAS_FEATURE_TPU_DM_GLOBAL_REGISTERS,
PVR_FW_HAS_FEATURE_TPU_FILTERING_MODE_CONTROL,
PVR_FW_HAS_FEATURE_USC_MIN_OUTPUT_REGISTERS_PER_PIX,
PVR_FW_HAS_FEATURE_VDM_DRAWINDIRECT,
PVR_FW_HAS_FEATURE_VDM_OBJECT_LEVEL_LLS,
PVR_FW_HAS_FEATURE_VIRTUAL_ADDRESS_SPACE_BITS,
PVR_FW_HAS_FEATURE_WATCHDOG_TIMER,
PVR_FW_HAS_FEATURE_WORKGROUP_PROTECTION,
PVR_FW_HAS_FEATURE_XE_ARCHITECTURE,
PVR_FW_HAS_FEATURE_XE_MEMORY_HIERARCHY,
PVR_FW_HAS_FEATURE_XE_TPU2,
PVR_FW_HAS_FEATURE_XPU_MAX_REGBANKS_ADDR_WIDTH,
PVR_FW_HAS_FEATURE_XPU_MAX_SLAVES,
PVR_FW_HAS_FEATURE_XPU_REGISTER_BROADCAST,
PVR_FW_HAS_FEATURE_XT_TOP_INFRASTRUCTURE,
PVR_FW_HAS_FEATURE_ZLS_SUBTILE,
PVR_FW_HAS_FEATURE_MAX
};
#endif /* __PVR_ROGUE_FWIF_DEV_INFO_H__ */

View file

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_FWIF_RESETFRAMEWORK_H
#define PVR_ROGUE_FWIF_RESETFRAMEWORK_H
#include <linux/bits.h>
#include <linux/types.h>
#include "pvr_rogue_fwif_shared.h"
struct rogue_fwif_rf_registers {
union {
u64 cdmreg_cdm_cb_base;
u64 cdmreg_cdm_ctrl_stream_base;
};
u64 cdmreg_cdm_cb_queue;
u64 cdmreg_cdm_cb;
};
struct rogue_fwif_rf_cmd {
/* THIS MUST BE THE LAST MEMBER OF THE CONTAINING STRUCTURE */
struct rogue_fwif_rf_registers fw_registers __aligned(8);
};
#define ROGUE_FWIF_RF_CMD_SIZE sizeof(struct rogue_fwif_rf_cmd)
#endif /* PVR_ROGUE_FWIF_RESETFRAMEWORK_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,258 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_FWIF_SHARED_H
#define PVR_ROGUE_FWIF_SHARED_H
#include <linux/compiler.h>
#include <linux/types.h>
#define ROGUE_FWIF_NUM_RTDATAS 2U
#define ROGUE_FWIF_NUM_GEOMDATAS 1U
#define ROGUE_FWIF_NUM_RTDATA_FREELISTS 2U
#define ROGUE_NUM_GEOM_CORES 1U
#define ROGUE_NUM_GEOM_CORES_SIZE 2U
/*
* Maximum number of UFOs in a CCB command.
* The number is based on having 32 sync prims (as originally), plus 32 sync
* checkpoints.
* Once the use of sync prims is no longer supported, we will retain
* the same total (64) as the number of sync checkpoints which may be
* supporting a fence is not visible to the client driver and has to
* allow for the number of different timelines involved in fence merges.
*/
#define ROGUE_FWIF_CCB_CMD_MAX_UFOS (32U + 32U)
/*
* This is a generic limit imposed on any DM (GEOMETRY,FRAGMENT,CDM,TDM,2D,TRANSFER)
* command passed through the bridge.
* Just across the bridge in the server, any incoming kick command size is
* checked against this maximum limit.
* In case the incoming command size is larger than the specified limit,
* the bridge call is retired with error.
*/
#define ROGUE_FWIF_DM_INDEPENDENT_KICK_CMD_SIZE (1024U)
#define ROGUE_FWIF_PRBUFFER_START (0)
#define ROGUE_FWIF_PRBUFFER_ZSBUFFER (0)
#define ROGUE_FWIF_PRBUFFER_MSAABUFFER (1)
#define ROGUE_FWIF_PRBUFFER_MAXSUPPORTED (2)
struct rogue_fwif_dma_addr {
aligned_u64 dev_addr;
u32 fw_addr;
u32 padding;
} __aligned(8);
struct rogue_fwif_ufo {
u32 addr;
u32 value;
};
#define ROGUE_FWIF_UFO_ADDR_IS_SYNC_CHECKPOINT (1)
struct rogue_fwif_sync_checkpoint {
u32 state;
u32 fw_ref_count;
};
struct rogue_fwif_cleanup_ctl {
/* Number of commands received by the FW */
u32 submitted_commands;
/* Number of commands executed by the FW */
u32 executed_commands;
} __aligned(8);
/*
* Used to share frame numbers across UM-KM-FW,
* frame number is set in UM,
* frame number is required in both KM for HTB and FW for FW trace.
*
* May be used to house Kick flags in the future.
*/
struct rogue_fwif_cmd_common {
/* associated frame number */
u32 frame_num;
};
/*
* Geometry and fragment commands require set of firmware addresses that are stored in the Kernel.
* Client has handle(s) to Kernel containers storing these addresses, instead of raw addresses. We
* have to patch/write these addresses in KM to prevent UM from controlling FW addresses directly.
* Typedefs for geometry and fragment commands are shared between Client and Firmware (both
* single-BVNC). Kernel is implemented in a multi-BVNC manner, so it can't use geometry|fragment
* CMD type definitions directly. Therefore we have a SHARED block that is shared between UM-KM-FW
* across all BVNC configurations.
*/
struct rogue_fwif_cmd_geom_frag_shared {
/* Common command attributes */
struct rogue_fwif_cmd_common cmn;
/*
* RTData associated with this command, this is used for context
* selection and for storing out HW-context, when TA is switched out for
* continuing later
*/
u32 hwrt_data_fw_addr;
/* Supported PR Buffers like Z/S/MSAA Scratch */
u32 pr_buffer_fw_addr[ROGUE_FWIF_PRBUFFER_MAXSUPPORTED];
};
/*
* Client Circular Command Buffer (CCCB) control structure.
* This is shared between the Server and the Firmware and holds byte offsets
* into the CCCB as well as the wrapping mask to aid wrap around. A given
* snapshot of this queue with Cmd 1 running on the GPU might be:
*
* Roff Doff Woff
* [..........|-1----------|=2===|=3===|=4===|~5~~~~|~6~~~~|~7~~~~|..........]
* < runnable commands >< !ready to run >
*
* Cmd 1 : Currently executing on the GPU data master.
* Cmd 2,3,4: Fence dependencies met, commands runnable.
* Cmd 5... : Fence dependency not met yet.
*/
struct rogue_fwif_cccb_ctl {
/* Host write offset into CCB. This must be aligned to 16 bytes. */
u32 write_offset;
/*
* Firmware read offset into CCB. Points to the command that is runnable
* on GPU, if R!=W
*/
u32 read_offset;
/*
* Firmware fence dependency offset. Points to commands not ready, i.e.
* fence dependencies are not met.
*/
u32 dep_offset;
/* Offset wrapping mask, total capacity in bytes of the CCB-1 */
u32 wrap_mask;
/* Only used if SUPPORT_AGP is present. */
u32 read_offset2;
/* Only used if SUPPORT_AGP4 is present. */
u32 read_offset3;
/* Only used if SUPPORT_AGP4 is present. */
u32 read_offset4;
u32 padding;
} __aligned(8);
#define ROGUE_FW_LOCAL_FREELIST (0)
#define ROGUE_FW_GLOBAL_FREELIST (1)
#define ROGUE_FW_FREELIST_TYPE_LAST ROGUE_FW_GLOBAL_FREELIST
#define ROGUE_FW_MAX_FREELISTS (ROGUE_FW_FREELIST_TYPE_LAST + 1U)
struct rogue_fwif_geom_registers_caswitch {
u64 geom_reg_vdm_context_state_base_addr;
u64 geom_reg_vdm_context_state_resume_addr;
u64 geom_reg_ta_context_state_base_addr;
struct {
u64 geom_reg_vdm_context_store_task0;
u64 geom_reg_vdm_context_store_task1;
u64 geom_reg_vdm_context_store_task2;
/* VDM resume state update controls */
u64 geom_reg_vdm_context_resume_task0;
u64 geom_reg_vdm_context_resume_task1;
u64 geom_reg_vdm_context_resume_task2;
u64 geom_reg_vdm_context_store_task3;
u64 geom_reg_vdm_context_store_task4;
u64 geom_reg_vdm_context_resume_task3;
u64 geom_reg_vdm_context_resume_task4;
} geom_state[2];
};
#define ROGUE_FWIF_GEOM_REGISTERS_CSWITCH_SIZE \
sizeof(struct rogue_fwif_geom_registers_caswitch)
struct rogue_fwif_cdm_registers_cswitch {
u64 cdmreg_cdm_context_pds0;
u64 cdmreg_cdm_context_pds1;
u64 cdmreg_cdm_terminate_pds;
u64 cdmreg_cdm_terminate_pds1;
/* CDM resume controls */
u64 cdmreg_cdm_resume_pds0;
u64 cdmreg_cdm_context_pds0_b;
u64 cdmreg_cdm_resume_pds0_b;
};
struct rogue_fwif_static_rendercontext_state {
/* Geom registers for ctx switch */
struct rogue_fwif_geom_registers_caswitch ctxswitch_regs[ROGUE_NUM_GEOM_CORES_SIZE]
__aligned(8);
};
#define ROGUE_FWIF_STATIC_RENDERCONTEXT_SIZE \
sizeof(struct rogue_fwif_static_rendercontext_state)
struct rogue_fwif_static_computecontext_state {
/* CDM registers for ctx switch */
struct rogue_fwif_cdm_registers_cswitch ctxswitch_regs __aligned(8);
};
#define ROGUE_FWIF_STATIC_COMPUTECONTEXT_SIZE \
sizeof(struct rogue_fwif_static_computecontext_state)
enum rogue_fwif_prbuffer_state {
ROGUE_FWIF_PRBUFFER_UNBACKED = 0,
ROGUE_FWIF_PRBUFFER_BACKED,
ROGUE_FWIF_PRBUFFER_BACKING_PENDING,
ROGUE_FWIF_PRBUFFER_UNBACKING_PENDING,
};
struct rogue_fwif_prbuffer {
/* Buffer ID*/
u32 buffer_id;
/* Needs On-demand Z/S/MSAA Buffer allocation */
bool on_demand __aligned(4);
/* Z/S/MSAA -Buffer state */
enum rogue_fwif_prbuffer_state state;
/* Cleanup state */
struct rogue_fwif_cleanup_ctl cleanup_sate;
/* Compatibility and other flags */
u32 prbuffer_flags;
} __aligned(8);
/* Last reset reason for a context. */
enum rogue_context_reset_reason {
/* No reset reason recorded */
ROGUE_CONTEXT_RESET_REASON_NONE = 0,
/* Caused a reset due to locking up */
ROGUE_CONTEXT_RESET_REASON_GUILTY_LOCKUP = 1,
/* Affected by another context locking up */
ROGUE_CONTEXT_RESET_REASON_INNOCENT_LOCKUP = 2,
/* Overran the global deadline */
ROGUE_CONTEXT_RESET_REASON_GUILTY_OVERRUNING = 3,
/* Affected by another context overrunning */
ROGUE_CONTEXT_RESET_REASON_INNOCENT_OVERRUNING = 4,
/* Forced reset to ensure scheduling requirements */
ROGUE_CONTEXT_RESET_REASON_HARD_CONTEXT_SWITCH = 5,
/* FW Safety watchdog triggered */
ROGUE_CONTEXT_RESET_REASON_FW_WATCHDOG = 12,
/* FW page fault (no HWR) */
ROGUE_CONTEXT_RESET_REASON_FW_PAGEFAULT = 13,
/* FW execution error (GPU reset requested) */
ROGUE_CONTEXT_RESET_REASON_FW_EXEC_ERR = 14,
/* Host watchdog detected FW error */
ROGUE_CONTEXT_RESET_REASON_HOST_WDG_FW_ERR = 15,
/* Geometry DM OOM event is not allowed */
ROGUE_CONTEXT_GEOM_OOM_DISABLED = 16,
};
struct rogue_context_reset_reason_data {
enum rogue_context_reset_reason reset_reason;
u32 reset_ext_job_ref;
};
#include "pvr_rogue_fwif_shared_check.h"
#endif /* PVR_ROGUE_FWIF_SHARED_H */

View file

@ -0,0 +1,108 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_FWIF_SHARED_CHECK_H
#define PVR_ROGUE_FWIF_SHARED_CHECK_H
#include <linux/build_bug.h>
#define OFFSET_CHECK(type, member, offset) \
static_assert(offsetof(type, member) == (offset), \
"offsetof(" #type ", " #member ") incorrect")
#define SIZE_CHECK(type, size) \
static_assert(sizeof(type) == (size), #type " is incorrect size")
OFFSET_CHECK(struct rogue_fwif_dma_addr, dev_addr, 0);
OFFSET_CHECK(struct rogue_fwif_dma_addr, fw_addr, 8);
SIZE_CHECK(struct rogue_fwif_dma_addr, 16);
OFFSET_CHECK(struct rogue_fwif_ufo, addr, 0);
OFFSET_CHECK(struct rogue_fwif_ufo, value, 4);
SIZE_CHECK(struct rogue_fwif_ufo, 8);
OFFSET_CHECK(struct rogue_fwif_cleanup_ctl, submitted_commands, 0);
OFFSET_CHECK(struct rogue_fwif_cleanup_ctl, executed_commands, 4);
SIZE_CHECK(struct rogue_fwif_cleanup_ctl, 8);
OFFSET_CHECK(struct rogue_fwif_cccb_ctl, write_offset, 0);
OFFSET_CHECK(struct rogue_fwif_cccb_ctl, read_offset, 4);
OFFSET_CHECK(struct rogue_fwif_cccb_ctl, dep_offset, 8);
OFFSET_CHECK(struct rogue_fwif_cccb_ctl, wrap_mask, 12);
OFFSET_CHECK(struct rogue_fwif_cccb_ctl, read_offset2, 16);
OFFSET_CHECK(struct rogue_fwif_cccb_ctl, read_offset3, 20);
OFFSET_CHECK(struct rogue_fwif_cccb_ctl, read_offset4, 24);
SIZE_CHECK(struct rogue_fwif_cccb_ctl, 32);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_reg_vdm_context_state_base_addr, 0);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_reg_vdm_context_state_resume_addr, 8);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_reg_ta_context_state_base_addr, 16);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_store_task0, 24);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_store_task1, 32);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_store_task2, 40);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_resume_task0, 48);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_resume_task1, 56);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_resume_task2, 64);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_store_task3, 72);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_store_task4, 80);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_resume_task3, 88);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_resume_task4, 96);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_store_task0, 104);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_store_task1, 112);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_store_task2, 120);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_resume_task0, 128);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_resume_task1, 136);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_resume_task2, 144);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_store_task3, 152);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_store_task4, 160);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_resume_task3, 168);
OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_resume_task4, 176);
SIZE_CHECK(struct rogue_fwif_geom_registers_caswitch, 184);
OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_context_pds0, 0);
OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_context_pds1, 8);
OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_terminate_pds, 16);
OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_terminate_pds1, 24);
OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_resume_pds0, 32);
OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_context_pds0_b, 40);
OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_resume_pds0_b, 48);
SIZE_CHECK(struct rogue_fwif_cdm_registers_cswitch, 56);
OFFSET_CHECK(struct rogue_fwif_static_rendercontext_state, ctxswitch_regs, 0);
SIZE_CHECK(struct rogue_fwif_static_rendercontext_state, 368);
OFFSET_CHECK(struct rogue_fwif_static_computecontext_state, ctxswitch_regs, 0);
SIZE_CHECK(struct rogue_fwif_static_computecontext_state, 56);
OFFSET_CHECK(struct rogue_fwif_cmd_common, frame_num, 0);
SIZE_CHECK(struct rogue_fwif_cmd_common, 4);
OFFSET_CHECK(struct rogue_fwif_cmd_geom_frag_shared, cmn, 0);
OFFSET_CHECK(struct rogue_fwif_cmd_geom_frag_shared, hwrt_data_fw_addr, 4);
OFFSET_CHECK(struct rogue_fwif_cmd_geom_frag_shared, pr_buffer_fw_addr, 8);
SIZE_CHECK(struct rogue_fwif_cmd_geom_frag_shared, 16);
#endif /* PVR_ROGUE_FWIF_SHARED_CHECK_H */

View file

@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_FWIF_STREAM_H
#define PVR_ROGUE_FWIF_STREAM_H
/**
* DOC: Streams
*
* Commands are submitted to the kernel driver in the form of streams.
*
* A command stream has the following layout :
* - A 64-bit header containing:
* * A u32 containing the length of the main stream inclusive of the length of the header.
* * A u32 for padding.
* - The main stream data.
* - The extension stream (optional), which is composed of:
* * One or more headers.
* * The extension stream data, corresponding to the extension headers.
*
* The main stream provides the base command data. This has a fixed layout based on the features
* supported by a given GPU.
*
* The extension stream provides the command parameters that are required for BRNs & ERNs for the
* current GPU. This stream is comprised of one or more headers, followed by data for each given
* BRN/ERN.
*
* Each header is a u32 containing a bitmask of quirks & enhancements in the extension stream, a
* "type" field determining the set of quirks & enhancements the bitmask represents, and a
* continuation bit determining whether any more headers are present. The headers are then followed
* by command data; this is specific to each quirk/enhancement. All unused / reserved bits in the
* header must be set to 0.
*
* All parameters and headers in the main and extension streams must be naturally aligned.
*
* If a parameter appears in both the main and extension streams, then the extension parameter is
* used.
*/
/*
* Stream extension header definition
*/
#define PVR_STREAM_EXTHDR_TYPE_SHIFT 29U
#define PVR_STREAM_EXTHDR_TYPE_MASK (7U << PVR_STREAM_EXTHDR_TYPE_SHIFT)
#define PVR_STREAM_EXTHDR_TYPE_MAX 8U
#define PVR_STREAM_EXTHDR_CONTINUATION BIT(28U)
#define PVR_STREAM_EXTHDR_DATA_MASK ~(PVR_STREAM_EXTHDR_TYPE_MASK | PVR_STREAM_EXTHDR_CONTINUATION)
/*
* Stream extension header - Geometry 0
*/
#define PVR_STREAM_EXTHDR_TYPE_GEOM0 0U
#define PVR_STREAM_EXTHDR_GEOM0_BRN49927 BIT(0U)
#define PVR_STREAM_EXTHDR_GEOM0_VALID PVR_STREAM_EXTHDR_GEOM0_BRN49927
/*
* Stream extension header - Fragment 0
*/
#define PVR_STREAM_EXTHDR_TYPE_FRAG0 0U
#define PVR_STREAM_EXTHDR_FRAG0_BRN47217 BIT(0U)
#define PVR_STREAM_EXTHDR_FRAG0_BRN49927 BIT(1U)
#define PVR_STREAM_EXTHDR_FRAG0_VALID PVR_STREAM_EXTHDR_FRAG0_BRN49927
/*
* Stream extension header - Compute 0
*/
#define PVR_STREAM_EXTHDR_TYPE_COMPUTE0 0U
#define PVR_STREAM_EXTHDR_COMPUTE0_BRN49927 BIT(0U)
#define PVR_STREAM_EXTHDR_COMPUTE0_VALID PVR_STREAM_EXTHDR_COMPUTE0_BRN49927
#endif /* PVR_ROGUE_FWIF_STREAM_H */

View file

@ -0,0 +1,113 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_HEAP_CONFIG_H
#define PVR_ROGUE_HEAP_CONFIG_H
#include <linux/sizes.h>
/*
* ROGUE Device Virtual Address Space Definitions
*
* This file defines the ROGUE virtual address heaps that are used in
* application memory contexts. It also shows where the Firmware memory heap
* fits into this, but the firmware heap is only ever created in the
* kernel driver and never exposed to userspace.
*
* ROGUE_PDSCODEDATA_HEAP_BASE and ROGUE_USCCODE_HEAP_BASE will be programmed,
* on a global basis, into ROGUE_CR_PDS_EXEC_BASE and ROGUE_CR_USC_CODE_BASE_*
* respectively. Therefore if client drivers use multiple configs they must
* still be consistent with their definitions for these heaps.
*
* Base addresses have to be a multiple of 4MiB.
* Heaps must not start at 0x0000000000, as this is reserved for internal
* use within the driver.
* Range comments, those starting in column 0 below are a section heading of
* sorts and are above the heaps in that range. Often this is the reserved
* size of the heap within the range.
*/
/* 0x00_0000_0000 ************************************************************/
/* 0x00_0000_0000 - 0x00_0040_0000 */
/* 0 MiB to 4 MiB, size of 4 MiB : RESERVED */
/* 0x00_0040_0000 - 0x7F_FFC0_0000 **/
/* 4 MiB to 512 GiB, size of 512 GiB less 4 MiB : RESERVED **/
/* 0x80_0000_0000 ************************************************************/
/* 0x80_0000_0000 - 0x9F_FFFF_FFFF **/
/* 512 GiB to 640 GiB, size of 128 GiB : GENERAL_HEAP **/
#define ROGUE_GENERAL_HEAP_BASE 0x8000000000ull
#define ROGUE_GENERAL_HEAP_SIZE SZ_128G
/* 0xA0_0000_0000 - 0xAF_FFFF_FFFF */
/* 640 GiB to 704 GiB, size of 64 GiB : FREE */
/* B0_0000_0000 - 0xB7_FFFF_FFFF */
/* 704 GiB to 736 GiB, size of 32 GiB : FREE */
/* 0xB8_0000_0000 - 0xBF_FFFF_FFFF */
/* 736 GiB to 768 GiB, size of 32 GiB : RESERVED */
/* 0xC0_0000_0000 ************************************************************/
/* 0xC0_0000_0000 - 0xD9_FFFF_FFFF */
/* 768 GiB to 872 GiB, size of 104 GiB : FREE */
/* 0xDA_0000_0000 - 0xDA_FFFF_FFFF */
/* 872 GiB to 876 GiB, size of 4 GiB : PDSCODEDATA_HEAP */
#define ROGUE_PDSCODEDATA_HEAP_BASE 0xDA00000000ull
#define ROGUE_PDSCODEDATA_HEAP_SIZE SZ_4G
/* 0xDB_0000_0000 - 0xDB_FFFF_FFFF */
/* 876 GiB to 880 GiB, size of 256 MiB (reserved 4GiB) : BRN **/
/*
* The BRN63142 quirk workaround requires Region Header memory to be at the top
* of a 16GiB aligned range. This is so when masked with 0x03FFFFFFFF the
* address will avoid aliasing PB addresses. Start at 879.75GiB. Size of 256MiB.
*/
#define ROGUE_RGNHDR_HEAP_BASE 0xDBF0000000ull
#define ROGUE_RGNHDR_HEAP_SIZE SZ_256M
/* 0xDC_0000_0000 - 0xDF_FFFF_FFFF */
/* 880 GiB to 896 GiB, size of 16 GiB : FREE */
/* 0xE0_0000_0000 - 0xE0_FFFF_FFFF */
/* 896 GiB to 900 GiB, size of 4 GiB : USCCODE_HEAP */
#define ROGUE_USCCODE_HEAP_BASE 0xE000000000ull
#define ROGUE_USCCODE_HEAP_SIZE SZ_4G
/* 0xE1_0000_0000 - 0xE1_BFFF_FFFF */
/* 900 GiB to 903 GiB, size of 3 GiB : RESERVED */
/* 0xE1_C000_000 - 0xE1_FFFF_FFFF */
/* 903 GiB to 904 GiB, reserved 1 GiB, : FIRMWARE_HEAP */
#define ROGUE_FW_HEAP_BASE 0xE1C0000000ull
/* 0xE2_0000_0000 - 0xE3_FFFF_FFFF */
/* 904 GiB to 912 GiB, size of 8 GiB : FREE */
/* 0xE4_0000_0000 - 0xE7_FFFF_FFFF */
/* 912 GiB to 968 GiB, size of 16 GiB : TRANSFER_FRAG */
#define ROGUE_TRANSFER_FRAG_HEAP_BASE 0xE400000000ull
#define ROGUE_TRANSFER_FRAG_HEAP_SIZE SZ_16G
/* 0xE8_0000_0000 - 0xF1_FFFF_FFFF */
/* 928 GiB to 968 GiB, size of 40 GiB : RESERVED */
/* 0xF2_0000_0000 - 0xF2_001F_FFFF **/
/* 968 GiB to 969 GiB, size of 2 MiB : VISTEST_HEAP */
#define ROGUE_VISTEST_HEAP_BASE 0xF200000000ull
#define ROGUE_VISTEST_HEAP_SIZE SZ_2M
/* 0xF2_4000_0000 - 0xF2_FFFF_FFFF */
/* 969 GiB to 972 GiB, size of 3 GiB : FREE */
/* 0xF3_0000_0000 - 0xFF_FFFF_FFFF */
/* 972 GiB to 1024 GiB, size of 52 GiB : FREE */
/* 0xFF_FFFF_FFFF ************************************************************/
#endif /* PVR_ROGUE_HEAP_CONFIG_H */

View file

@ -0,0 +1,356 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_META_H
#define PVR_ROGUE_META_H
/***** The META HW register definitions in the file are updated manually *****/
#include <linux/bits.h>
#include <linux/types.h>
/*
******************************************************************************
* META registers and MACROS
*****************************************************************************
*/
#define META_CR_CTRLREG_BASE(t) (0x04800000U + (0x1000U * (t)))
#define META_CR_TXPRIVEXT (0x048000E8)
#define META_CR_TXPRIVEXT_MINIM_EN BIT(7)
#define META_CR_SYSC_JTAG_THREAD (0x04830030)
#define META_CR_SYSC_JTAG_THREAD_PRIV_EN (0x00000004)
#define META_CR_PERF_COUNT0 (0x0480FFE0)
#define META_CR_PERF_COUNT1 (0x0480FFE8)
#define META_CR_PERF_COUNT_CTRL_SHIFT (28)
#define META_CR_PERF_COUNT_CTRL_MASK (0xF0000000)
#define META_CR_PERF_COUNT_CTRL_DCACHEHITS (8 << META_CR_PERF_COUNT_CTRL_SHIFT)
#define META_CR_PERF_COUNT_CTRL_ICACHEHITS (9 << META_CR_PERF_COUNT_CTRL_SHIFT)
#define META_CR_PERF_COUNT_CTRL_ICACHEMISS \
(0xA << META_CR_PERF_COUNT_CTRL_SHIFT)
#define META_CR_PERF_COUNT_CTRL_ICORE (0xD << META_CR_PERF_COUNT_CTRL_SHIFT)
#define META_CR_PERF_COUNT_THR_SHIFT (24)
#define META_CR_PERF_COUNT_THR_MASK (0x0F000000)
#define META_CR_PERF_COUNT_THR_0 (0x1 << META_CR_PERF_COUNT_THR_SHIFT)
#define META_CR_PERF_COUNT_THR_1 (0x2 << META_CR_PERF_COUNT_THR_1)
#define META_CR_TxVECINT_BHALT (0x04820500)
#define META_CR_PERF_ICORE0 (0x0480FFD0)
#define META_CR_PERF_ICORE1 (0x0480FFD8)
#define META_CR_PERF_ICORE_DCACHEMISS (0x8)
#define META_CR_PERF_COUNT(ctrl, thr) \
((META_CR_PERF_COUNT_CTRL_##ctrl << META_CR_PERF_COUNT_CTRL_SHIFT) | \
((thr) << META_CR_PERF_COUNT_THR_SHIFT))
#define META_CR_TXUXXRXDT_OFFSET (META_CR_CTRLREG_BASE(0U) + 0x0000FFF0U)
#define META_CR_TXUXXRXRQ_OFFSET (META_CR_CTRLREG_BASE(0U) + 0x0000FFF8U)
/* Poll for done. */
#define META_CR_TXUXXRXRQ_DREADY_BIT (0x80000000U)
/* Set for read. */
#define META_CR_TXUXXRXRQ_RDnWR_BIT (0x00010000U)
#define META_CR_TXUXXRXRQ_TX_S (12)
#define META_CR_TXUXXRXRQ_RX_S (4)
#define META_CR_TXUXXRXRQ_UXX_S (0)
/* Internal ctrl regs. */
#define META_CR_TXUIN_ID (0x0)
/* Data unit regs. */
#define META_CR_TXUD0_ID (0x1)
/* Data unit regs. */
#define META_CR_TXUD1_ID (0x2)
/* Address unit regs. */
#define META_CR_TXUA0_ID (0x3)
/* Address unit regs. */
#define META_CR_TXUA1_ID (0x4)
/* PC registers. */
#define META_CR_TXUPC_ID (0x5)
/* Macros to calculate register access values. */
#define META_CR_CORE_REG(thr, reg_num, unit) \
(((u32)(thr) << META_CR_TXUXXRXRQ_TX_S) | \
((u32)(reg_num) << META_CR_TXUXXRXRQ_RX_S) | \
((u32)(unit) << META_CR_TXUXXRXRQ_UXX_S))
#define META_CR_THR0_PC META_CR_CORE_REG(0, 0, META_CR_TXUPC_ID)
#define META_CR_THR0_PCX META_CR_CORE_REG(0, 1, META_CR_TXUPC_ID)
#define META_CR_THR0_SP META_CR_CORE_REG(0, 0, META_CR_TXUA0_ID)
#define META_CR_THR1_PC META_CR_CORE_REG(1, 0, META_CR_TXUPC_ID)
#define META_CR_THR1_PCX META_CR_CORE_REG(1, 1, META_CR_TXUPC_ID)
#define META_CR_THR1_SP META_CR_CORE_REG(1, 0, META_CR_TXUA0_ID)
#define SP_ACCESS(thread) META_CR_CORE_REG(thread, 0, META_CR_TXUA0_ID)
#define PC_ACCESS(thread) META_CR_CORE_REG(thread, 0, META_CR_TXUPC_ID)
#define META_CR_COREREG_ENABLE (0x0000000U)
#define META_CR_COREREG_STATUS (0x0000010U)
#define META_CR_COREREG_DEFR (0x00000A0U)
#define META_CR_COREREG_PRIVEXT (0x00000E8U)
#define META_CR_T0ENABLE_OFFSET \
(META_CR_CTRLREG_BASE(0U) + META_CR_COREREG_ENABLE)
#define META_CR_T0STATUS_OFFSET \
(META_CR_CTRLREG_BASE(0U) + META_CR_COREREG_STATUS)
#define META_CR_T0DEFR_OFFSET (META_CR_CTRLREG_BASE(0U) + META_CR_COREREG_DEFR)
#define META_CR_T0PRIVEXT_OFFSET \
(META_CR_CTRLREG_BASE(0U) + META_CR_COREREG_PRIVEXT)
#define META_CR_T1ENABLE_OFFSET \
(META_CR_CTRLREG_BASE(1U) + META_CR_COREREG_ENABLE)
#define META_CR_T1STATUS_OFFSET \
(META_CR_CTRLREG_BASE(1U) + META_CR_COREREG_STATUS)
#define META_CR_T1DEFR_OFFSET (META_CR_CTRLREG_BASE(1U) + META_CR_COREREG_DEFR)
#define META_CR_T1PRIVEXT_OFFSET \
(META_CR_CTRLREG_BASE(1U) + META_CR_COREREG_PRIVEXT)
#define META_CR_TXENABLE_ENABLE_BIT (0x00000001U) /* Set if running */
#define META_CR_TXSTATUS_PRIV (0x00020000U)
#define META_CR_TXPRIVEXT_MINIM (0x00000080U)
#define META_MEM_GLOBAL_RANGE_BIT (0x80000000U)
#define META_CR_TXCLKCTRL (0x048000B0)
#define META_CR_TXCLKCTRL_ALL_ON (0x55111111)
#define META_CR_TXCLKCTRL_ALL_AUTO (0xAA222222)
#define META_CR_MMCU_LOCAL_EBCTRL (0x04830600)
#define META_CR_MMCU_LOCAL_EBCTRL_ICWIN (0x3 << 14)
#define META_CR_MMCU_LOCAL_EBCTRL_DCWIN (0x3 << 6)
#define META_CR_SYSC_DCPART(n) (0x04830200 + (n) * 0x8)
#define META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE (0x1 << 31)
#define META_CR_SYSC_ICPART(n) (0x04830220 + (n) * 0x8)
#define META_CR_SYSC_XCPARTX_LOCAL_ADDR_OFFSET_TOP_HALF (0x8 << 16)
#define META_CR_SYSC_XCPARTX_LOCAL_ADDR_FULL_CACHE (0xF)
#define META_CR_SYSC_XCPARTX_LOCAL_ADDR_HALF_CACHE (0x7)
#define META_CR_MMCU_DCACHE_CTRL (0x04830018)
#define META_CR_MMCU_ICACHE_CTRL (0x04830020)
#define META_CR_MMCU_XCACHE_CTRL_CACHE_HITS_EN (0x1)
/*
******************************************************************************
* META LDR Format
******************************************************************************
*/
/* Block header structure. */
struct rogue_meta_ldr_block_hdr {
u32 dev_id;
u32 sl_code;
u32 sl_data;
u16 pc_ctrl;
u16 crc;
};
/* High level data stream block structure. */
struct rogue_meta_ldr_l1_data_blk {
u16 cmd;
u16 length;
u32 next;
u32 cmd_data[4];
};
/* High level data stream block structure. */
struct rogue_meta_ldr_l2_data_blk {
u16 tag;
u16 length;
u32 block_data[4];
};
/* Config command structure. */
struct rogue_meta_ldr_cfg_blk {
u32 type;
u32 block_data[4];
};
/* Block type definitions */
#define ROGUE_META_LDR_COMMENT_TYPE_MASK (0x0010U)
#define ROGUE_META_LDR_BLK_IS_COMMENT(x) (((x) & ROGUE_META_LDR_COMMENT_TYPE_MASK) != 0U)
/*
* Command definitions
* Value Name Description
* 0 LoadMem Load memory with binary data.
* 1 LoadCore Load a set of core registers.
* 2 LoadMMReg Load a set of memory mapped registers.
* 3 StartThreads Set each thread PC and SP, then enable threads.
* 4 ZeroMem Zeros a memory region.
* 5 Config Perform a configuration command.
*/
#define ROGUE_META_LDR_CMD_MASK (0x000FU)
#define ROGUE_META_LDR_CMD_LOADMEM (0x0000U)
#define ROGUE_META_LDR_CMD_LOADCORE (0x0001U)
#define ROGUE_META_LDR_CMD_LOADMMREG (0x0002U)
#define ROGUE_META_LDR_CMD_START_THREADS (0x0003U)
#define ROGUE_META_LDR_CMD_ZEROMEM (0x0004U)
#define ROGUE_META_LDR_CMD_CONFIG (0x0005U)
/*
* Config Command definitions
* Value Name Description
* 0 Pause Pause for x times 100 instructions
* 1 Read Read a value from register - No value return needed.
* Utilises effects of issuing reads to certain registers
* 2 Write Write to mem location
* 3 MemSet Set mem to value
* 4 MemCheck check mem for specific value.
*/
#define ROGUE_META_LDR_CFG_PAUSE (0x0000)
#define ROGUE_META_LDR_CFG_READ (0x0001)
#define ROGUE_META_LDR_CFG_WRITE (0x0002)
#define ROGUE_META_LDR_CFG_MEMSET (0x0003)
#define ROGUE_META_LDR_CFG_MEMCHECK (0x0004)
/*
******************************************************************************
* ROGUE FW segmented MMU definitions
******************************************************************************
*/
/* All threads can access the segment. */
#define ROGUE_FW_SEGMMU_ALLTHRS (0xf << 8U)
/* Writable. */
#define ROGUE_FW_SEGMMU_WRITEABLE (0x1U << 1U)
/* All threads can access and writable. */
#define ROGUE_FW_SEGMMU_ALLTHRS_WRITEABLE \
(ROGUE_FW_SEGMMU_ALLTHRS | ROGUE_FW_SEGMMU_WRITEABLE)
/* Direct map region 10 used for mapping GPU memory - max 8MB. */
#define ROGUE_FW_SEGMMU_DMAP_GPU_ID (10U)
#define ROGUE_FW_SEGMMU_DMAP_GPU_ADDR_START (0x07000000U)
#define ROGUE_FW_SEGMMU_DMAP_GPU_MAX_SIZE (0x00800000U)
/* Segment IDs. */
#define ROGUE_FW_SEGMMU_DATA_ID (1U)
#define ROGUE_FW_SEGMMU_BOOTLDR_ID (2U)
#define ROGUE_FW_SEGMMU_TEXT_ID (ROGUE_FW_SEGMMU_BOOTLDR_ID)
/*
* SLC caching strategy in S7 and volcanic is emitted through the segment MMU.
* All the segments configured through the macro ROGUE_FW_SEGMMU_OUTADDR_TOP are
* CACHED in the SLC.
* The interface has been kept the same to simplify the code changes.
* The bifdm argument is ignored (no longer relevant) in S7 and volcanic.
*/
#define ROGUE_FW_SEGMMU_OUTADDR_TOP_VIVT_SLC(pers, slc_policy, mmu_ctx) \
((((u64)((pers) & 0x3)) << 52) | (((u64)((mmu_ctx) & 0xFF)) << 44) | \
(((u64)((slc_policy) & 0x1)) << 40))
#define ROGUE_FW_SEGMMU_OUTADDR_TOP_VIVT_SLC_CACHED(mmu_ctx) \
ROGUE_FW_SEGMMU_OUTADDR_TOP_VIVT_SLC(0x3, 0x0, mmu_ctx)
#define ROGUE_FW_SEGMMU_OUTADDR_TOP_VIVT_SLC_UNCACHED(mmu_ctx) \
ROGUE_FW_SEGMMU_OUTADDR_TOP_VIVT_SLC(0x0, 0x1, mmu_ctx)
/*
* To configure the Page Catalog and BIF-DM fed into the BIF for Garten
* accesses through this segment.
*/
#define ROGUE_FW_SEGMMU_OUTADDR_TOP_SLC(pc, bifdm) \
(((u64)((u64)(pc) & 0xFU) << 44U) | ((u64)((u64)(bifdm) & 0xFU) << 40U))
#define ROGUE_FW_SEGMMU_META_BIFDM_ID (0x7U)
/* META segments have 4kB minimum size. */
#define ROGUE_FW_SEGMMU_ALIGN (0x1000U)
/* Segmented MMU registers (n = segment id). */
#define META_CR_MMCU_SEGMENT_N_BASE(n) (0x04850000U + ((n) * 0x10U))
#define META_CR_MMCU_SEGMENT_N_LIMIT(n) (0x04850004U + ((n) * 0x10U))
#define META_CR_MMCU_SEGMENT_N_OUTA0(n) (0x04850008U + ((n) * 0x10U))
#define META_CR_MMCU_SEGMENT_N_OUTA1(n) (0x0485000CU + ((n) * 0x10U))
/*
* The following defines must be recalculated if the Meta MMU segments used
* to access Host-FW data are changed
* Current combinations are:
* - SLC uncached, META cached, FW base address 0x70000000
* - SLC uncached, META uncached, FW base address 0xF0000000
* - SLC cached, META cached, FW base address 0x10000000
* - SLC cached, META uncached, FW base address 0x90000000
*/
#define ROGUE_FW_SEGMMU_DATA_BASE_ADDRESS (0x10000000U)
#define ROGUE_FW_SEGMMU_DATA_META_CACHED (0x0U)
#define ROGUE_FW_SEGMMU_DATA_META_UNCACHED (META_MEM_GLOBAL_RANGE_BIT)
#define ROGUE_FW_SEGMMU_DATA_META_CACHE_MASK (META_MEM_GLOBAL_RANGE_BIT)
/*
* For non-VIVT SLCs the cacheability of the FW data in the SLC is selected in
* the PTEs for the FW data, not in the Meta Segment MMU, which means these
* defines have no real effect in those cases.
*/
#define ROGUE_FW_SEGMMU_DATA_VIVT_SLC_CACHED (0x0U)
#define ROGUE_FW_SEGMMU_DATA_VIVT_SLC_UNCACHED (0x60000000U)
#define ROGUE_FW_SEGMMU_DATA_VIVT_SLC_CACHE_MASK (0x60000000U)
/*
******************************************************************************
* ROGUE FW Bootloader defaults
******************************************************************************
*/
#define ROGUE_FW_BOOTLDR_META_ADDR (0x40000000U)
#define ROGUE_FW_BOOTLDR_DEVV_ADDR_0 (0xC0000000U)
#define ROGUE_FW_BOOTLDR_DEVV_ADDR_1 (0x000000E1)
#define ROGUE_FW_BOOTLDR_DEVV_ADDR \
((((u64)ROGUE_FW_BOOTLDR_DEVV_ADDR_1) << 32) | \
ROGUE_FW_BOOTLDR_DEVV_ADDR_0)
#define ROGUE_FW_BOOTLDR_LIMIT (0x1FFFF000)
#define ROGUE_FW_MAX_BOOTLDR_OFFSET (0x1000)
/* Bootloader configuration offset is in dwords (512 bytes) */
#define ROGUE_FW_BOOTLDR_CONF_OFFSET (0x80)
/*
******************************************************************************
* ROGUE META Stack
******************************************************************************
*/
#define ROGUE_META_STACK_SIZE (0x1000U)
/*
******************************************************************************
* ROGUE META Core memory
******************************************************************************
*/
/* Code and data both map to the same physical memory. */
#define ROGUE_META_COREMEM_CODE_ADDR (0x80000000U)
#define ROGUE_META_COREMEM_DATA_ADDR (0x82000000U)
#define ROGUE_META_COREMEM_OFFSET_MASK (0x01ffffffU)
#define ROGUE_META_IS_COREMEM_CODE(a, b) \
({ \
u32 _a = (a), _b = (b); \
((_a) >= ROGUE_META_COREMEM_CODE_ADDR) && \
((_a) < (ROGUE_META_COREMEM_CODE_ADDR + (_b))); \
})
#define ROGUE_META_IS_COREMEM_DATA(a, b) \
({ \
u32 _a = (a), _b = (b); \
((_a) >= ROGUE_META_COREMEM_DATA_ADDR) && \
((_a) < (ROGUE_META_COREMEM_DATA_ADDR + (_b))); \
})
/*
******************************************************************************
* 2nd thread
******************************************************************************
*/
#define ROGUE_FW_THR1_PC (0x18930000)
#define ROGUE_FW_THR1_SP (0x78890000)
/*
******************************************************************************
* META compatibility
******************************************************************************
*/
#define META_CR_CORE_ID (0x04831000)
#define META_CR_CORE_ID_VER_SHIFT (16U)
#define META_CR_CORE_ID_VER_CLRMSK (0XFF00FFFFU)
#define ROGUE_CR_META_MTP218_CORE_ID_VALUE 0x19
#define ROGUE_CR_META_MTP219_CORE_ID_VALUE 0x1E
#define ROGUE_CR_META_LTP218_CORE_ID_VALUE 0x1C
#define ROGUE_CR_META_LTP217_CORE_ID_VALUE 0x1F
#define ROGUE_FW_PROCESSOR_META "META"
#endif /* PVR_ROGUE_META_H */

View file

@ -0,0 +1,335 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_MIPS_H
#define PVR_ROGUE_MIPS_H
#include <linux/bits.h>
#include <linux/types.h>
/* Utility defines for memory management. */
#define ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K (12)
#define ROGUE_MIPSFW_PAGE_SIZE_4K (0x1 << ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K)
#define ROGUE_MIPSFW_PAGE_MASK_4K (ROGUE_MIPSFW_PAGE_SIZE_4K - 1)
#define ROGUE_MIPSFW_LOG2_PAGE_SIZE_64K (16)
#define ROGUE_MIPSFW_PAGE_SIZE_64K (0x1 << ROGUE_MIPSFW_LOG2_PAGE_SIZE_64K)
#define ROGUE_MIPSFW_PAGE_MASK_64K (ROGUE_MIPSFW_PAGE_SIZE_64K - 1)
#define ROGUE_MIPSFW_LOG2_PAGE_SIZE_256K (18)
#define ROGUE_MIPSFW_PAGE_SIZE_256K (0x1 << ROGUE_MIPSFW_LOG2_PAGE_SIZE_256K)
#define ROGUE_MIPSFW_PAGE_MASK_256K (ROGUE_MIPSFW_PAGE_SIZE_256K - 1)
#define ROGUE_MIPSFW_LOG2_PAGE_SIZE_1MB (20)
#define ROGUE_MIPSFW_PAGE_SIZE_1MB (0x1 << ROGUE_MIPSFW_LOG2_PAGE_SIZE_1MB)
#define ROGUE_MIPSFW_PAGE_MASK_1MB (ROGUE_MIPSFW_PAGE_SIZE_1MB - 1)
#define ROGUE_MIPSFW_LOG2_PAGE_SIZE_4MB (22)
#define ROGUE_MIPSFW_PAGE_SIZE_4MB (0x1 << ROGUE_MIPSFW_LOG2_PAGE_SIZE_4MB)
#define ROGUE_MIPSFW_PAGE_MASK_4MB (ROGUE_MIPSFW_PAGE_SIZE_4MB - 1)
#define ROGUE_MIPSFW_LOG2_PTE_ENTRY_SIZE (2)
/* log2 page table sizes dependent on FW heap size and page size (for each OS). */
#define ROGUE_MIPSFW_LOG2_PAGETABLE_SIZE_4K(pvr_dev) ((pvr_dev)->fw_dev.fw_heap_info.log2_size - \
ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K + \
ROGUE_MIPSFW_LOG2_PTE_ENTRY_SIZE)
#define ROGUE_MIPSFW_LOG2_PAGETABLE_SIZE_64K(pvr_dev) ((pvr_dev)->fw_dev.fw_heap_info.log2_size - \
ROGUE_MIPSFW_LOG2_PAGE_SIZE_64K + \
ROGUE_MIPSFW_LOG2_PTE_ENTRY_SIZE)
/* Maximum number of page table pages (both Host and MIPS pages). */
#define ROGUE_MIPSFW_MAX_NUM_PAGETABLE_PAGES (4)
/* Total number of TLB entries. */
#define ROGUE_MIPSFW_NUMBER_OF_TLB_ENTRIES (16)
/* "Uncached" caching policy. */
#define ROGUE_MIPSFW_UNCACHED_CACHE_POLICY (2)
/* "Write-back write-allocate" caching policy. */
#define ROGUE_MIPSFW_WRITEBACK_CACHE_POLICY (3)
/* "Write-through no write-allocate" caching policy. */
#define ROGUE_MIPSFW_WRITETHROUGH_CACHE_POLICY (1)
/* Cached policy used by MIPS in case of physical bus on 32 bit. */
#define ROGUE_MIPSFW_CACHED_POLICY (ROGUE_MIPSFW_WRITEBACK_CACHE_POLICY)
/* Cached policy used by MIPS in case of physical bus on more than 32 bit. */
#define ROGUE_MIPSFW_CACHED_POLICY_ABOVE_32BIT (ROGUE_MIPSFW_WRITETHROUGH_CACHE_POLICY)
/* Total number of Remap entries. */
#define ROGUE_MIPSFW_NUMBER_OF_REMAP_ENTRIES (2 * ROGUE_MIPSFW_NUMBER_OF_TLB_ENTRIES)
/* MIPS EntryLo/PTE format. */
#define ROGUE_MIPSFW_ENTRYLO_READ_INHIBIT_SHIFT (31U)
#define ROGUE_MIPSFW_ENTRYLO_READ_INHIBIT_CLRMSK (0X7FFFFFFF)
#define ROGUE_MIPSFW_ENTRYLO_READ_INHIBIT_EN (0X80000000)
#define ROGUE_MIPSFW_ENTRYLO_EXEC_INHIBIT_SHIFT (30U)
#define ROGUE_MIPSFW_ENTRYLO_EXEC_INHIBIT_CLRMSK (0XBFFFFFFF)
#define ROGUE_MIPSFW_ENTRYLO_EXEC_INHIBIT_EN (0X40000000)
/* Page Frame Number */
#define ROGUE_MIPSFW_ENTRYLO_PFN_SHIFT (6)
#define ROGUE_MIPSFW_ENTRYLO_PFN_ALIGNSHIFT (12)
/* Mask used for the MIPS Page Table in case of physical bus on 32 bit. */
#define ROGUE_MIPSFW_ENTRYLO_PFN_MASK (0x03FFFFC0)
#define ROGUE_MIPSFW_ENTRYLO_PFN_SIZE (20)
/* Mask used for the MIPS Page Table in case of physical bus on more than 32 bit. */
#define ROGUE_MIPSFW_ENTRYLO_PFN_MASK_ABOVE_32BIT (0x3FFFFFC0)
#define ROGUE_MIPSFW_ENTRYLO_PFN_SIZE_ABOVE_32BIT (24)
#define ROGUE_MIPSFW_ADDR_TO_ENTRYLO_PFN_RSHIFT (ROGUE_MIPSFW_ENTRYLO_PFN_ALIGNSHIFT - \
ROGUE_MIPSFW_ENTRYLO_PFN_SHIFT)
#define ROGUE_MIPSFW_ENTRYLO_CACHE_POLICY_SHIFT (3U)
#define ROGUE_MIPSFW_ENTRYLO_CACHE_POLICY_CLRMSK (0XFFFFFFC7)
#define ROGUE_MIPSFW_ENTRYLO_DIRTY_SHIFT (2U)
#define ROGUE_MIPSFW_ENTRYLO_DIRTY_CLRMSK (0XFFFFFFFB)
#define ROGUE_MIPSFW_ENTRYLO_DIRTY_EN (0X00000004)
#define ROGUE_MIPSFW_ENTRYLO_VALID_SHIFT (1U)
#define ROGUE_MIPSFW_ENTRYLO_VALID_CLRMSK (0XFFFFFFFD)
#define ROGUE_MIPSFW_ENTRYLO_VALID_EN (0X00000002)
#define ROGUE_MIPSFW_ENTRYLO_GLOBAL_SHIFT (0U)
#define ROGUE_MIPSFW_ENTRYLO_GLOBAL_CLRMSK (0XFFFFFFFE)
#define ROGUE_MIPSFW_ENTRYLO_GLOBAL_EN (0X00000001)
#define ROGUE_MIPSFW_ENTRYLO_DVG (ROGUE_MIPSFW_ENTRYLO_DIRTY_EN | \
ROGUE_MIPSFW_ENTRYLO_VALID_EN | \
ROGUE_MIPSFW_ENTRYLO_GLOBAL_EN)
#define ROGUE_MIPSFW_ENTRYLO_UNCACHED (ROGUE_MIPSFW_UNCACHED_CACHE_POLICY << \
ROGUE_MIPSFW_ENTRYLO_CACHE_POLICY_SHIFT)
#define ROGUE_MIPSFW_ENTRYLO_DVG_UNCACHED (ROGUE_MIPSFW_ENTRYLO_DVG | \
ROGUE_MIPSFW_ENTRYLO_UNCACHED)
/* Remap Range Config Addr Out. */
/* These defines refer to the upper half of the Remap Range Config register. */
#define ROGUE_MIPSFW_REMAP_RANGE_ADDR_OUT_MASK (0x0FFFFFF0)
#define ROGUE_MIPSFW_REMAP_RANGE_ADDR_OUT_SHIFT (4) /* wrt upper half of the register. */
#define ROGUE_MIPSFW_REMAP_RANGE_ADDR_OUT_ALIGNSHIFT (12)
#define ROGUE_MIPSFW_ADDR_TO_RR_ADDR_OUT_RSHIFT (ROGUE_MIPSFW_REMAP_RANGE_ADDR_OUT_ALIGNSHIFT - \
ROGUE_MIPSFW_REMAP_RANGE_ADDR_OUT_SHIFT)
/*
* Pages to trampoline problematic physical addresses:
* - ROGUE_MIPSFW_BOOT_REMAP_PHYS_ADDR_IN : 0x1FC0_0000
* - ROGUE_MIPSFW_DATA_REMAP_PHYS_ADDR_IN : 0x1FC0_1000
* - ROGUE_MIPSFW_CODE_REMAP_PHYS_ADDR_IN : 0x1FC0_2000
* - (benign trampoline) : 0x1FC0_3000
* that would otherwise be erroneously remapped by the MIPS wrapper.
* (see "Firmware virtual layout and remap configuration" section below)
*/
#define ROGUE_MIPSFW_TRAMPOLINE_LOG2_NUMPAGES (2)
#define ROGUE_MIPSFW_TRAMPOLINE_NUMPAGES BIT(ROGUE_MIPSFW_TRAMPOLINE_LOG2_NUMPAGES)
#define ROGUE_MIPSFW_TRAMPOLINE_SIZE (ROGUE_MIPSFW_TRAMPOLINE_NUMPAGES << \
ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K)
#define ROGUE_MIPSFW_TRAMPOLINE_LOG2_SEGMENT_SIZE (ROGUE_MIPSFW_TRAMPOLINE_LOG2_NUMPAGES + \
ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K)
#define ROGUE_MIPSFW_TRAMPOLINE_TARGET_PHYS_ADDR (ROGUE_MIPSFW_BOOT_REMAP_PHYS_ADDR_IN)
#define ROGUE_MIPSFW_TRAMPOLINE_OFFSET(a) ((a) - ROGUE_MIPSFW_BOOT_REMAP_PHYS_ADDR_IN)
#define ROGUE_MIPSFW_SENSITIVE_ADDR(a) (ROGUE_MIPSFW_BOOT_REMAP_PHYS_ADDR_IN == \
(~((1 << ROGUE_MIPSFW_TRAMPOLINE_LOG2_SEGMENT_SIZE) - 1) \
& (a)))
/* Firmware virtual layout and remap configuration. */
/*
* For each remap region we define:
* - the virtual base used by the Firmware to access code/data through that region
* - the microAptivAP physical address correspondent to the virtual base address,
* used as input address and remapped to the actual physical address
* - log2 of size of the region remapped by the MIPS wrapper, i.e. number of bits from
* the bottom of the base input address that survive onto the output address
* (this defines both the alignment and the maximum size of the remapped region)
* - one or more code/data segments within the remapped region.
*/
/* Boot remap setup. */
#define ROGUE_MIPSFW_BOOT_REMAP_VIRTUAL_BASE (0xBFC00000)
#define ROGUE_MIPSFW_BOOT_REMAP_PHYS_ADDR_IN (0x1FC00000)
#define ROGUE_MIPSFW_BOOT_REMAP_LOG2_SEGMENT_SIZE (12)
#define ROGUE_MIPSFW_BOOT_NMI_CODE_VIRTUAL_BASE (ROGUE_MIPSFW_BOOT_REMAP_VIRTUAL_BASE)
/* Data remap setup. */
#define ROGUE_MIPSFW_DATA_REMAP_VIRTUAL_BASE (0xBFC01000)
#define ROGUE_MIPSFW_DATA_CACHED_REMAP_VIRTUAL_BASE (0x9FC01000)
#define ROGUE_MIPSFW_DATA_REMAP_PHYS_ADDR_IN (0x1FC01000)
#define ROGUE_MIPSFW_DATA_REMAP_LOG2_SEGMENT_SIZE (12)
#define ROGUE_MIPSFW_BOOT_NMI_DATA_VIRTUAL_BASE (ROGUE_MIPSFW_DATA_REMAP_VIRTUAL_BASE)
/* Code remap setup. */
#define ROGUE_MIPSFW_CODE_REMAP_VIRTUAL_BASE (0x9FC02000)
#define ROGUE_MIPSFW_CODE_REMAP_PHYS_ADDR_IN (0x1FC02000)
#define ROGUE_MIPSFW_CODE_REMAP_LOG2_SEGMENT_SIZE (12)
#define ROGUE_MIPSFW_EXCEPTIONS_VIRTUAL_BASE (ROGUE_MIPSFW_CODE_REMAP_VIRTUAL_BASE)
/* Permanent mappings setup. */
#define ROGUE_MIPSFW_PT_VIRTUAL_BASE (0xCF000000)
#define ROGUE_MIPSFW_REGISTERS_VIRTUAL_BASE (0xCF800000)
#define ROGUE_MIPSFW_STACK_VIRTUAL_BASE (0xCF600000)
/* Bootloader configuration data. */
/*
* Bootloader configuration offset (where ROGUE_MIPSFW_BOOT_DATA lives)
* within the bootloader/NMI data page.
*/
#define ROGUE_MIPSFW_BOOTLDR_CONF_OFFSET (0x0)
/* NMI shared data. */
/* Base address of the shared data within the bootloader/NMI data page. */
#define ROGUE_MIPSFW_NMI_SHARED_DATA_BASE (0x100)
/* Size used by Debug dump data. */
#define ROGUE_MIPSFW_NMI_SHARED_SIZE (0x2B0)
/* Offsets in the NMI shared area in 32-bit words. */
#define ROGUE_MIPSFW_NMI_SYNC_FLAG_OFFSET (0x0)
#define ROGUE_MIPSFW_NMI_STATE_OFFSET (0x1)
#define ROGUE_MIPSFW_NMI_ERROR_STATE_SET (0x1)
/* MIPS boot stage. */
#define ROGUE_MIPSFW_BOOT_STAGE_OFFSET (0x400)
/*
* MIPS private data in the bootloader data page.
* Memory below this offset is used by the FW only, no interface data allowed.
*/
#define ROGUE_MIPSFW_PRIVATE_DATA_OFFSET (0x800)
struct rogue_mipsfw_boot_data {
u64 stack_phys_addr;
u64 reg_base;
u64 pt_phys_addr[ROGUE_MIPSFW_MAX_NUM_PAGETABLE_PAGES];
u32 pt_log2_page_size;
u32 pt_num_pages;
u32 reserved1;
u32 reserved2;
};
#define ROGUE_MIPSFW_GET_OFFSET_IN_DWORDS(offset) ((offset) / sizeof(u32))
#define ROGUE_MIPSFW_GET_OFFSET_IN_QWORDS(offset) ((offset) / sizeof(u64))
/* Used for compatibility checks. */
#define ROGUE_MIPSFW_ARCHTYPE_VER_CLRMSK (0xFFFFE3FFU)
#define ROGUE_MIPSFW_ARCHTYPE_VER_SHIFT (10U)
#define ROGUE_MIPSFW_CORE_ID_VALUE (0x001U)
#define ROGUE_FW_PROCESSOR_MIPS "MIPS"
/* microAptivAP cache line size. */
#define ROGUE_MIPSFW_MICROAPTIVEAP_CACHELINE_SIZE (16U)
/*
* The SOCIF transactions are identified with the top 16 bits of the physical address emitted by
* the MIPS.
*/
#define ROGUE_MIPSFW_WRAPPER_CONFIG_REGBANK_ADDR_ALIGN (16U)
/* Values to put in the MIPS selectors for performance counters. */
/* Icache accesses in COUNTER0. */
#define ROGUE_MIPSFW_PERF_COUNT_CTRL_ICACHE_ACCESSES_C0 (9U)
/* Icache misses in COUNTER1. */
#define ROGUE_MIPSFW_PERF_COUNT_CTRL_ICACHE_MISSES_C1 (9U)
/* Dcache accesses in COUNTER0. */
#define ROGUE_MIPSFW_PERF_COUNT_CTRL_DCACHE_ACCESSES_C0 (10U)
/* Dcache misses in COUNTER1. */
#define ROGUE_MIPSFW_PERF_COUNT_CTRL_DCACHE_MISSES_C1 (11U)
/* ITLB instruction accesses in COUNTER0. */
#define ROGUE_MIPSFW_PERF_COUNT_CTRL_ITLB_INSTR_ACCESSES_C0 (5U)
/* JTLB instruction accesses misses in COUNTER1. */
#define ROGUE_MIPSFW_PERF_COUNT_CTRL_JTLB_INSTR_MISSES_C1 (7U)
/* Instructions completed in COUNTER0. */
#define ROGUE_MIPSFW_PERF_COUNT_CTRL_INSTR_COMPLETED_C0 (1U)
/* JTLB data misses in COUNTER1. */
#define ROGUE_MIPSFW_PERF_COUNT_CTRL_JTLB_DATA_MISSES_C1 (8U)
/* Shift for the Event field in the MIPS perf ctrl registers. */
#define ROGUE_MIPSFW_PERF_COUNT_CTRL_EVENT_SHIFT (5U)
/* Additional flags for performance counters. See MIPS manual for further reference. */
#define ROGUE_MIPSFW_PERF_COUNT_CTRL_COUNT_USER_MODE (8U)
#define ROGUE_MIPSFW_PERF_COUNT_CTRL_COUNT_KERNEL_MODE (2U)
#define ROGUE_MIPSFW_PERF_COUNT_CTRL_COUNT_EXL (1U)
#define ROGUE_MIPSFW_C0_NBHWIRQ 8
/* Macros to decode C0_Cause register. */
#define ROGUE_MIPSFW_C0_CAUSE_EXCCODE(cause) (((cause) & 0x7c) >> 2)
#define ROGUE_MIPSFW_C0_CAUSE_EXCCODE_FWERROR 9
/* Use only when Coprocessor Unusable exception. */
#define ROGUE_MIPSFW_C0_CAUSE_UNUSABLE_UNIT(cause) (((cause) >> 28) & 0x3)
#define ROGUE_MIPSFW_C0_CAUSE_PENDING_HWIRQ(cause) (((cause) & 0x3fc00) >> 10)
#define ROGUE_MIPSFW_C0_CAUSE_FDCIPENDING BIT(21)
#define ROGUE_MIPSFW_C0_CAUSE_IV BIT(23)
#define ROGUE_MIPSFW_C0_CAUSE_IC BIT(25)
#define ROGUE_MIPSFW_C0_CAUSE_PCIPENDING BIT(26)
#define ROGUE_MIPSFW_C0_CAUSE_TIPENDING BIT(30)
#define ROGUE_MIPSFW_C0_CAUSE_BRANCH_DELAY BIT(31)
/* Macros to decode C0_Debug register. */
#define ROGUE_MIPSFW_C0_DEBUG_EXCCODE(debug) (((debug) >> 10) & 0x1f)
#define ROGUE_MIPSFW_C0_DEBUG_DSS BIT(0)
#define ROGUE_MIPSFW_C0_DEBUG_DBP BIT(1)
#define ROGUE_MIPSFW_C0_DEBUG_DDBL BIT(2)
#define ROGUE_MIPSFW_C0_DEBUG_DDBS BIT(3)
#define ROGUE_MIPSFW_C0_DEBUG_DIB BIT(4)
#define ROGUE_MIPSFW_C0_DEBUG_DINT BIT(5)
#define ROGUE_MIPSFW_C0_DEBUG_DIBIMPR BIT(6)
#define ROGUE_MIPSFW_C0_DEBUG_DDBLIMPR BIT(18)
#define ROGUE_MIPSFW_C0_DEBUG_DDBSIMPR BIT(19)
#define ROGUE_MIPSFW_C0_DEBUG_IEXI BIT(20)
#define ROGUE_MIPSFW_C0_DEBUG_DBUSEP BIT(21)
#define ROGUE_MIPSFW_C0_DEBUG_CACHEEP BIT(22)
#define ROGUE_MIPSFW_C0_DEBUG_MCHECKP BIT(23)
#define ROGUE_MIPSFW_C0_DEBUG_IBUSEP BIT(24)
#define ROGUE_MIPSFW_C0_DEBUG_DM BIT(30)
#define ROGUE_MIPSFW_C0_DEBUG_DBD BIT(31)
/* Macros to decode TLB entries. */
#define ROGUE_MIPSFW_TLB_GET_MASK(page_mask) (((page_mask) >> 13) & 0XFFFFU)
/* Page size in KB. */
#define ROGUE_MIPSFW_TLB_GET_PAGE_SIZE(page_mask) ((((page_mask) | 0x1FFF) + 1) >> 11)
/* Page size in KB. */
#define ROGUE_MIPSFW_TLB_GET_PAGE_MASK(page_size) ((((page_size) << 11) - 1) & ~0x7FF)
#define ROGUE_MIPSFW_TLB_GET_VPN2(entry_hi) ((entry_hi) >> 13)
#define ROGUE_MIPSFW_TLB_GET_COHERENCY(entry_lo) (((entry_lo) >> 3) & 0x7U)
#define ROGUE_MIPSFW_TLB_GET_PFN(entry_lo) (((entry_lo) >> 6) & 0XFFFFFU)
/* GET_PA uses a non-standard PFN mask for 36 bit addresses. */
#define ROGUE_MIPSFW_TLB_GET_PA(entry_lo) (((u64)(entry_lo) & \
ROGUE_MIPSFW_ENTRYLO_PFN_MASK_ABOVE_32BIT) << 6)
#define ROGUE_MIPSFW_TLB_GET_INHIBIT(entry_lo) (((entry_lo) >> 30) & 0x3U)
#define ROGUE_MIPSFW_TLB_GET_DGV(entry_lo) ((entry_lo) & 0x7U)
#define ROGUE_MIPSFW_TLB_GLOBAL BIT(0)
#define ROGUE_MIPSFW_TLB_VALID BIT(1)
#define ROGUE_MIPSFW_TLB_DIRTY BIT(2)
#define ROGUE_MIPSFW_TLB_XI BIT(30)
#define ROGUE_MIPSFW_TLB_RI BIT(31)
#define ROGUE_MIPSFW_REMAP_GET_REGION_SIZE(region_size_encoding) (1 << (((region_size_encoding) \
+ 1) << 1))
struct rogue_mips_tlb_entry {
u32 tlb_page_mask;
u32 tlb_hi;
u32 tlb_lo0;
u32 tlb_lo1;
};
struct rogue_mips_remap_entry {
u32 remap_addr_in; /* Always 4k aligned. */
u32 remap_addr_out; /* Always 4k aligned. */
u32 remap_region_size;
};
struct rogue_mips_state {
u32 error_state; /* This must come first in the structure. */
u32 error_epc;
u32 status_register;
u32 cause_register;
u32 bad_register;
u32 epc;
u32 sp;
u32 debug;
u32 depc;
u32 bad_instr;
u32 unmapped_address;
struct rogue_mips_tlb_entry tlb[ROGUE_MIPSFW_NUMBER_OF_TLB_ENTRIES];
struct rogue_mips_remap_entry remap[ROGUE_MIPSFW_NUMBER_OF_REMAP_ENTRIES];
};
#include "pvr_rogue_mips_check.h"
#endif /* PVR_ROGUE_MIPS_H */

View file

@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_ROGUE_MIPS_CHECK_H
#define PVR_ROGUE_MIPS_CHECK_H
#include <linux/build_bug.h>
static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_page_mask) == 0,
"offsetof(struct rogue_mips_tlb_entry, tlb_page_mask) incorrect");
static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_hi) == 4,
"offsetof(struct rogue_mips_tlb_entry, tlb_hi) incorrect");
static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_lo0) == 8,
"offsetof(struct rogue_mips_tlb_entry, tlb_lo0) incorrect");
static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_lo1) == 12,
"offsetof(struct rogue_mips_tlb_entry, tlb_lo1) incorrect");
static_assert(sizeof(struct rogue_mips_tlb_entry) == 16,
"struct rogue_mips_tlb_entry is incorrect size");
static_assert(offsetof(struct rogue_mips_remap_entry, remap_addr_in) == 0,
"offsetof(struct rogue_mips_remap_entry, remap_addr_in) incorrect");
static_assert(offsetof(struct rogue_mips_remap_entry, remap_addr_out) == 4,
"offsetof(struct rogue_mips_remap_entry, remap_addr_out) incorrect");
static_assert(offsetof(struct rogue_mips_remap_entry, remap_region_size) == 8,
"offsetof(struct rogue_mips_remap_entry, remap_region_size) incorrect");
static_assert(sizeof(struct rogue_mips_remap_entry) == 12,
"struct rogue_mips_remap_entry is incorrect size");
static_assert(offsetof(struct rogue_mips_state, error_state) == 0,
"offsetof(struct rogue_mips_state, error_state) incorrect");
static_assert(offsetof(struct rogue_mips_state, error_epc) == 4,
"offsetof(struct rogue_mips_state, error_epc) incorrect");
static_assert(offsetof(struct rogue_mips_state, status_register) == 8,
"offsetof(struct rogue_mips_state, status_register) incorrect");
static_assert(offsetof(struct rogue_mips_state, cause_register) == 12,
"offsetof(struct rogue_mips_state, cause_register) incorrect");
static_assert(offsetof(struct rogue_mips_state, bad_register) == 16,
"offsetof(struct rogue_mips_state, bad_register) incorrect");
static_assert(offsetof(struct rogue_mips_state, epc) == 20,
"offsetof(struct rogue_mips_state, epc) incorrect");
static_assert(offsetof(struct rogue_mips_state, sp) == 24,
"offsetof(struct rogue_mips_state, sp) incorrect");
static_assert(offsetof(struct rogue_mips_state, debug) == 28,
"offsetof(struct rogue_mips_state, debug) incorrect");
static_assert(offsetof(struct rogue_mips_state, depc) == 32,
"offsetof(struct rogue_mips_state, depc) incorrect");
static_assert(offsetof(struct rogue_mips_state, bad_instr) == 36,
"offsetof(struct rogue_mips_state, bad_instr) incorrect");
static_assert(offsetof(struct rogue_mips_state, unmapped_address) == 40,
"offsetof(struct rogue_mips_state, unmapped_address) incorrect");
static_assert(offsetof(struct rogue_mips_state, tlb) == 44,
"offsetof(struct rogue_mips_state, tlb) incorrect");
static_assert(offsetof(struct rogue_mips_state, remap) == 300,
"offsetof(struct rogue_mips_state, remap) incorrect");
static_assert(sizeof(struct rogue_mips_state) == 684,
"struct rogue_mips_state is incorrect size");
#endif /* PVR_ROGUE_MIPS_CHECK_H */

View file

@ -0,0 +1,136 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
/* *** Autogenerated C -- do not edit *** */
#ifndef PVR_ROGUE_MMU_DEFS_H
#define PVR_ROGUE_MMU_DEFS_H
#define ROGUE_MMU_DEFS_REVISION 0
#define ROGUE_BIF_DM_ENCODING_VERTEX (0x00000000U)
#define ROGUE_BIF_DM_ENCODING_PIXEL (0x00000001U)
#define ROGUE_BIF_DM_ENCODING_COMPUTE (0x00000002U)
#define ROGUE_BIF_DM_ENCODING_TLA (0x00000003U)
#define ROGUE_BIF_DM_ENCODING_PB_VCE (0x00000004U)
#define ROGUE_BIF_DM_ENCODING_PB_TE (0x00000005U)
#define ROGUE_BIF_DM_ENCODING_META (0x00000007U)
#define ROGUE_BIF_DM_ENCODING_HOST (0x00000008U)
#define ROGUE_BIF_DM_ENCODING_PM_ALIST (0x00000009U)
#define ROGUE_MMUCTRL_VADDR_PC_INDEX_SHIFT (30U)
#define ROGUE_MMUCTRL_VADDR_PC_INDEX_CLRMSK (0xFFFFFF003FFFFFFFULL)
#define ROGUE_MMUCTRL_VADDR_PD_INDEX_SHIFT (21U)
#define ROGUE_MMUCTRL_VADDR_PD_INDEX_CLRMSK (0xFFFFFFFFC01FFFFFULL)
#define ROGUE_MMUCTRL_VADDR_PT_INDEX_SHIFT (12U)
#define ROGUE_MMUCTRL_VADDR_PT_INDEX_CLRMSK (0xFFFFFFFFFFE00FFFULL)
#define ROGUE_MMUCTRL_ENTRIES_PC_VALUE (0x00000400U)
#define ROGUE_MMUCTRL_ENTRIES_PD_VALUE (0x00000200U)
#define ROGUE_MMUCTRL_ENTRIES_PT_VALUE (0x00000200U)
#define ROGUE_MMUCTRL_ENTRY_SIZE_PC_VALUE (0x00000020U)
#define ROGUE_MMUCTRL_ENTRY_SIZE_PD_VALUE (0x00000040U)
#define ROGUE_MMUCTRL_ENTRY_SIZE_PT_VALUE (0x00000040U)
#define ROGUE_MMUCTRL_PAGE_SIZE_MASK (0x00000007U)
#define ROGUE_MMUCTRL_PAGE_SIZE_4KB (0x00000000U)
#define ROGUE_MMUCTRL_PAGE_SIZE_16KB (0x00000001U)
#define ROGUE_MMUCTRL_PAGE_SIZE_64KB (0x00000002U)
#define ROGUE_MMUCTRL_PAGE_SIZE_256KB (0x00000003U)
#define ROGUE_MMUCTRL_PAGE_SIZE_1MB (0x00000004U)
#define ROGUE_MMUCTRL_PAGE_SIZE_2MB (0x00000005U)
#define ROGUE_MMUCTRL_PAGE_4KB_RANGE_SHIFT (12U)
#define ROGUE_MMUCTRL_PAGE_4KB_RANGE_CLRMSK (0xFFFFFF0000000FFFULL)
#define ROGUE_MMUCTRL_PAGE_16KB_RANGE_SHIFT (14U)
#define ROGUE_MMUCTRL_PAGE_16KB_RANGE_CLRMSK (0xFFFFFF0000003FFFULL)
#define ROGUE_MMUCTRL_PAGE_64KB_RANGE_SHIFT (16U)
#define ROGUE_MMUCTRL_PAGE_64KB_RANGE_CLRMSK (0xFFFFFF000000FFFFULL)
#define ROGUE_MMUCTRL_PAGE_256KB_RANGE_SHIFT (18U)
#define ROGUE_MMUCTRL_PAGE_256KB_RANGE_CLRMSK (0xFFFFFF000003FFFFULL)
#define ROGUE_MMUCTRL_PAGE_1MB_RANGE_SHIFT (20U)
#define ROGUE_MMUCTRL_PAGE_1MB_RANGE_CLRMSK (0xFFFFFF00000FFFFFULL)
#define ROGUE_MMUCTRL_PAGE_2MB_RANGE_SHIFT (21U)
#define ROGUE_MMUCTRL_PAGE_2MB_RANGE_CLRMSK (0xFFFFFF00001FFFFFULL)
#define ROGUE_MMUCTRL_PT_BASE_4KB_RANGE_SHIFT (12U)
#define ROGUE_MMUCTRL_PT_BASE_4KB_RANGE_CLRMSK (0xFFFFFF0000000FFFULL)
#define ROGUE_MMUCTRL_PT_BASE_16KB_RANGE_SHIFT (10U)
#define ROGUE_MMUCTRL_PT_BASE_16KB_RANGE_CLRMSK (0xFFFFFF00000003FFULL)
#define ROGUE_MMUCTRL_PT_BASE_64KB_RANGE_SHIFT (8U)
#define ROGUE_MMUCTRL_PT_BASE_64KB_RANGE_CLRMSK (0xFFFFFF00000000FFULL)
#define ROGUE_MMUCTRL_PT_BASE_256KB_RANGE_SHIFT (6U)
#define ROGUE_MMUCTRL_PT_BASE_256KB_RANGE_CLRMSK (0xFFFFFF000000003FULL)
#define ROGUE_MMUCTRL_PT_BASE_1MB_RANGE_SHIFT (5U)
#define ROGUE_MMUCTRL_PT_BASE_1MB_RANGE_CLRMSK (0xFFFFFF000000001FULL)
#define ROGUE_MMUCTRL_PT_BASE_2MB_RANGE_SHIFT (5U)
#define ROGUE_MMUCTRL_PT_BASE_2MB_RANGE_CLRMSK (0xFFFFFF000000001FULL)
#define ROGUE_MMUCTRL_PT_DATA_PM_META_PROTECT_SHIFT (62U)
#define ROGUE_MMUCTRL_PT_DATA_PM_META_PROTECT_CLRMSK (0xBFFFFFFFFFFFFFFFULL)
#define ROGUE_MMUCTRL_PT_DATA_PM_META_PROTECT_EN (0x4000000000000000ULL)
#define ROGUE_MMUCTRL_PT_DATA_VP_PAGE_HI_SHIFT (40U)
#define ROGUE_MMUCTRL_PT_DATA_VP_PAGE_HI_CLRMSK (0xC00000FFFFFFFFFFULL)
#define ROGUE_MMUCTRL_PT_DATA_PAGE_SHIFT (12U)
#define ROGUE_MMUCTRL_PT_DATA_PAGE_CLRMSK (0xFFFFFF0000000FFFULL)
#define ROGUE_MMUCTRL_PT_DATA_VP_PAGE_LO_SHIFT (6U)
#define ROGUE_MMUCTRL_PT_DATA_VP_PAGE_LO_CLRMSK (0xFFFFFFFFFFFFF03FULL)
#define ROGUE_MMUCTRL_PT_DATA_ENTRY_PENDING_SHIFT (5U)
#define ROGUE_MMUCTRL_PT_DATA_ENTRY_PENDING_CLRMSK (0xFFFFFFFFFFFFFFDFULL)
#define ROGUE_MMUCTRL_PT_DATA_ENTRY_PENDING_EN (0x0000000000000020ULL)
#define ROGUE_MMUCTRL_PT_DATA_PM_SRC_SHIFT (4U)
#define ROGUE_MMUCTRL_PT_DATA_PM_SRC_CLRMSK (0xFFFFFFFFFFFFFFEFULL)
#define ROGUE_MMUCTRL_PT_DATA_PM_SRC_EN (0x0000000000000010ULL)
#define ROGUE_MMUCTRL_PT_DATA_SLC_BYPASS_CTRL_SHIFT (3U)
#define ROGUE_MMUCTRL_PT_DATA_SLC_BYPASS_CTRL_CLRMSK (0xFFFFFFFFFFFFFFF7ULL)
#define ROGUE_MMUCTRL_PT_DATA_SLC_BYPASS_CTRL_EN (0x0000000000000008ULL)
#define ROGUE_MMUCTRL_PT_DATA_CC_SHIFT (2U)
#define ROGUE_MMUCTRL_PT_DATA_CC_CLRMSK (0xFFFFFFFFFFFFFFFBULL)
#define ROGUE_MMUCTRL_PT_DATA_CC_EN (0x0000000000000004ULL)
#define ROGUE_MMUCTRL_PT_DATA_READ_ONLY_SHIFT (1U)
#define ROGUE_MMUCTRL_PT_DATA_READ_ONLY_CLRMSK (0xFFFFFFFFFFFFFFFDULL)
#define ROGUE_MMUCTRL_PT_DATA_READ_ONLY_EN (0x0000000000000002ULL)
#define ROGUE_MMUCTRL_PT_DATA_VALID_SHIFT (0U)
#define ROGUE_MMUCTRL_PT_DATA_VALID_CLRMSK (0xFFFFFFFFFFFFFFFEULL)
#define ROGUE_MMUCTRL_PT_DATA_VALID_EN (0x0000000000000001ULL)
#define ROGUE_MMUCTRL_PD_DATA_ENTRY_PENDING_SHIFT (40U)
#define ROGUE_MMUCTRL_PD_DATA_ENTRY_PENDING_CLRMSK (0xFFFFFEFFFFFFFFFFULL)
#define ROGUE_MMUCTRL_PD_DATA_ENTRY_PENDING_EN (0x0000010000000000ULL)
#define ROGUE_MMUCTRL_PD_DATA_PT_BASE_SHIFT (5U)
#define ROGUE_MMUCTRL_PD_DATA_PT_BASE_CLRMSK (0xFFFFFF000000001FULL)
#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_SHIFT (1U)
#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_CLRMSK (0xFFFFFFFFFFFFFFF1ULL)
#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_4KB (0x0000000000000000ULL)
#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_16KB (0x0000000000000002ULL)
#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_64KB (0x0000000000000004ULL)
#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_256KB (0x0000000000000006ULL)
#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_1MB (0x0000000000000008ULL)
#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_2MB (0x000000000000000aULL)
#define ROGUE_MMUCTRL_PD_DATA_VALID_SHIFT (0U)
#define ROGUE_MMUCTRL_PD_DATA_VALID_CLRMSK (0xFFFFFFFFFFFFFFFEULL)
#define ROGUE_MMUCTRL_PD_DATA_VALID_EN (0x0000000000000001ULL)
#define ROGUE_MMUCTRL_PC_DATA_PD_BASE_SHIFT (4U)
#define ROGUE_MMUCTRL_PC_DATA_PD_BASE_CLRMSK (0x0000000FU)
#define ROGUE_MMUCTRL_PC_DATA_PD_BASE_ALIGNSHIFT (12U)
#define ROGUE_MMUCTRL_PC_DATA_PD_BASE_ALIGNSIZE (4096U)
#define ROGUE_MMUCTRL_PC_DATA_ENTRY_PENDING_SHIFT (1U)
#define ROGUE_MMUCTRL_PC_DATA_ENTRY_PENDING_CLRMSK (0xFFFFFFFDU)
#define ROGUE_MMUCTRL_PC_DATA_ENTRY_PENDING_EN (0x00000002U)
#define ROGUE_MMUCTRL_PC_DATA_VALID_SHIFT (0U)
#define ROGUE_MMUCTRL_PC_DATA_VALID_CLRMSK (0xFFFFFFFEU)
#define ROGUE_MMUCTRL_PC_DATA_VALID_EN (0x00000001U)
#endif /* PVR_ROGUE_MMU_DEFS_H */

View file

@ -0,0 +1,285 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_device.h"
#include "pvr_rogue_fwif_stream.h"
#include "pvr_stream.h"
#include <linux/align.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <uapi/drm/pvr_drm.h>
static __always_inline bool
stream_def_is_supported(struct pvr_device *pvr_dev, const struct pvr_stream_def *stream_def)
{
if (stream_def->feature == PVR_FEATURE_NONE)
return true;
if (!(stream_def->feature & PVR_FEATURE_NOT) &&
pvr_device_has_feature(pvr_dev, stream_def->feature)) {
return true;
}
if ((stream_def->feature & PVR_FEATURE_NOT) &&
!pvr_device_has_feature(pvr_dev, stream_def->feature & ~PVR_FEATURE_NOT)) {
return true;
}
return false;
}
static int
pvr_stream_get_data(u8 *stream, u32 *stream_offset, u32 stream_size, u32 data_size, u32 align_size,
void *dest)
{
*stream_offset = ALIGN(*stream_offset, align_size);
if ((*stream_offset + data_size) > stream_size)
return -EINVAL;
memcpy(dest, stream + *stream_offset, data_size);
(*stream_offset) += data_size;
return 0;
}
/**
* pvr_stream_process_1() - Process a single stream and fill destination structure
* @pvr_dev: Device pointer.
* @stream_def: Stream definition.
* @nr_entries: Number of entries in &stream_def.
* @stream: Pointer to stream.
* @stream_offset: Starting offset within stream.
* @stream_size: Size of input stream, in bytes.
* @dest: Pointer to destination structure.
* @dest_size: Size of destination structure.
* @stream_offset_out: Pointer to variable to write updated stream offset to. May be NULL.
*
* Returns:
* * 0 on success, or
* * -%EINVAL on malformed stream.
*/
static int
pvr_stream_process_1(struct pvr_device *pvr_dev, const struct pvr_stream_def *stream_def,
u32 nr_entries, u8 *stream, u32 stream_offset, u32 stream_size,
u8 *dest, u32 dest_size, u32 *stream_offset_out)
{
int err = 0;
u32 i;
for (i = 0; i < nr_entries; i++) {
if (stream_def[i].offset >= dest_size) {
err = -EINVAL;
break;
}
if (!stream_def_is_supported(pvr_dev, &stream_def[i]))
continue;
switch (stream_def[i].size) {
case PVR_STREAM_SIZE_8:
err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u8),
sizeof(u8), dest + stream_def[i].offset);
if (err)
return err;
break;
case PVR_STREAM_SIZE_16:
err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u16),
sizeof(u16), dest + stream_def[i].offset);
if (err)
return err;
break;
case PVR_STREAM_SIZE_32:
err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u32),
sizeof(u32), dest + stream_def[i].offset);
if (err)
return err;
break;
case PVR_STREAM_SIZE_64:
err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u64),
sizeof(u64), dest + stream_def[i].offset);
if (err)
return err;
break;
case PVR_STREAM_SIZE_ARRAY:
err = pvr_stream_get_data(stream, &stream_offset, stream_size,
stream_def[i].array_size, sizeof(u64),
dest + stream_def[i].offset);
if (err)
return err;
break;
}
}
if (stream_offset_out)
*stream_offset_out = stream_offset;
return err;
}
static int
pvr_stream_process_ext_stream(struct pvr_device *pvr_dev,
const struct pvr_stream_cmd_defs *cmd_defs, void *ext_stream,
u32 stream_offset, u32 ext_stream_size, void *dest)
{
u32 musthave_masks[PVR_STREAM_EXTHDR_TYPE_MAX];
u32 ext_header;
int err = 0;
u32 i;
/* Copy "must have" mask from device. We clear this as we process the stream. */
memcpy(musthave_masks, pvr_dev->stream_musthave_quirks[cmd_defs->type],
sizeof(musthave_masks));
do {
const struct pvr_stream_ext_header *header;
u32 type;
u32 data;
err = pvr_stream_get_data(ext_stream, &stream_offset, ext_stream_size, sizeof(u32),
sizeof(ext_header), &ext_header);
if (err)
return err;
type = (ext_header & PVR_STREAM_EXTHDR_TYPE_MASK) >> PVR_STREAM_EXTHDR_TYPE_SHIFT;
data = ext_header & PVR_STREAM_EXTHDR_DATA_MASK;
if (type >= cmd_defs->ext_nr_headers)
return -EINVAL;
header = &cmd_defs->ext_headers[type];
if (data & ~header->valid_mask)
return -EINVAL;
musthave_masks[type] &= ~data;
for (i = 0; i < header->ext_streams_num; i++) {
const struct pvr_stream_ext_def *ext_def = &header->ext_streams[i];
if (!(ext_header & ext_def->header_mask))
continue;
if (!pvr_device_has_uapi_quirk(pvr_dev, ext_def->quirk))
return -EINVAL;
err = pvr_stream_process_1(pvr_dev, ext_def->stream, ext_def->stream_len,
ext_stream, stream_offset,
ext_stream_size, dest,
cmd_defs->dest_size, &stream_offset);
if (err)
return err;
}
} while (ext_header & PVR_STREAM_EXTHDR_CONTINUATION);
/*
* Verify that "must have" mask is now zero. If it isn't then one of the "must have" quirks
* for this command was not present.
*/
for (i = 0; i < cmd_defs->ext_nr_headers; i++) {
if (musthave_masks[i])
return -EINVAL;
}
return 0;
}
/**
* pvr_stream_process() - Build FW structure from stream
* @pvr_dev: Device pointer.
* @cmd_defs: Stream definition.
* @stream: Pointer to command stream.
* @stream_size: Size of command stream, in bytes.
* @dest_out: Pointer to destination buffer.
*
* Caller is responsible for freeing the output structure.
*
* Returns:
* * 0 on success,
* * -%ENOMEM on out of memory, or
* * -%EINVAL on malformed stream.
*/
int
pvr_stream_process(struct pvr_device *pvr_dev, const struct pvr_stream_cmd_defs *cmd_defs,
void *stream, u32 stream_size, void *dest_out)
{
u32 stream_offset = 0;
u32 main_stream_len;
u32 padding;
int err;
if (!stream || !stream_size)
return -EINVAL;
err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u32),
sizeof(u32), &main_stream_len);
if (err)
return err;
/*
* u32 after stream length is padding to ensure u64 alignment, but may be used for expansion
* in the future. Verify it's zero.
*/
err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u32),
sizeof(u32), &padding);
if (err)
return err;
if (main_stream_len < stream_offset || main_stream_len > stream_size || padding)
return -EINVAL;
err = pvr_stream_process_1(pvr_dev, cmd_defs->main_stream, cmd_defs->main_stream_len,
stream, stream_offset, main_stream_len, dest_out,
cmd_defs->dest_size, &stream_offset);
if (err)
return err;
if (stream_offset < stream_size) {
err = pvr_stream_process_ext_stream(pvr_dev, cmd_defs, stream, stream_offset,
stream_size, dest_out);
if (err)
return err;
} else {
u32 i;
/*
* If we don't have an extension stream then there must not be any "must have"
* quirks for this command.
*/
for (i = 0; i < cmd_defs->ext_nr_headers; i++) {
if (pvr_dev->stream_musthave_quirks[cmd_defs->type][i])
return -EINVAL;
}
}
return 0;
}
/**
* pvr_stream_create_musthave_masks() - Create "must have" masks for streams based on current device
* quirks
* @pvr_dev: Device pointer.
*/
void
pvr_stream_create_musthave_masks(struct pvr_device *pvr_dev)
{
memset(pvr_dev->stream_musthave_quirks, 0, sizeof(pvr_dev->stream_musthave_quirks));
if (pvr_device_has_uapi_quirk(pvr_dev, 47217))
pvr_dev->stream_musthave_quirks[PVR_STREAM_TYPE_FRAG][0] |=
PVR_STREAM_EXTHDR_FRAG0_BRN47217;
if (pvr_device_has_uapi_quirk(pvr_dev, 49927)) {
pvr_dev->stream_musthave_quirks[PVR_STREAM_TYPE_GEOM][0] |=
PVR_STREAM_EXTHDR_GEOM0_BRN49927;
pvr_dev->stream_musthave_quirks[PVR_STREAM_TYPE_FRAG][0] |=
PVR_STREAM_EXTHDR_FRAG0_BRN49927;
pvr_dev->stream_musthave_quirks[PVR_STREAM_TYPE_COMPUTE][0] |=
PVR_STREAM_EXTHDR_COMPUTE0_BRN49927;
}
}

View file

@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_STREAM_H
#define PVR_STREAM_H
#include <linux/bits.h>
#include <linux/limits.h>
#include <linux/types.h>
struct pvr_device;
struct pvr_job;
enum pvr_stream_type {
PVR_STREAM_TYPE_GEOM = 0,
PVR_STREAM_TYPE_FRAG,
PVR_STREAM_TYPE_COMPUTE,
PVR_STREAM_TYPE_TRANSFER,
PVR_STREAM_TYPE_STATIC_RENDER_CONTEXT,
PVR_STREAM_TYPE_STATIC_COMPUTE_CONTEXT,
PVR_STREAM_TYPE_MAX
};
enum pvr_stream_size {
PVR_STREAM_SIZE_8 = 0,
PVR_STREAM_SIZE_16,
PVR_STREAM_SIZE_32,
PVR_STREAM_SIZE_64,
PVR_STREAM_SIZE_ARRAY,
};
#define PVR_FEATURE_NOT BIT(31)
#define PVR_FEATURE_NONE U32_MAX
struct pvr_stream_def {
u32 offset;
enum pvr_stream_size size;
u32 array_size;
u32 feature;
};
struct pvr_stream_ext_def {
const struct pvr_stream_def *stream;
u32 stream_len;
u32 header_mask;
u32 quirk;
};
struct pvr_stream_ext_header {
const struct pvr_stream_ext_def *ext_streams;
u32 ext_streams_num;
u32 valid_mask;
};
struct pvr_stream_cmd_defs {
enum pvr_stream_type type;
const struct pvr_stream_def *main_stream;
u32 main_stream_len;
u32 ext_nr_headers;
const struct pvr_stream_ext_header *ext_headers;
size_t dest_size;
};
int
pvr_stream_process(struct pvr_device *pvr_dev, const struct pvr_stream_cmd_defs *cmd_defs,
void *stream, u32 stream_size, void *dest_out);
void
pvr_stream_create_musthave_masks(struct pvr_device *pvr_dev);
#endif /* PVR_STREAM_H */

View file

@ -0,0 +1,351 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_device_info.h"
#include "pvr_rogue_fwif_client.h"
#include "pvr_rogue_fwif_stream.h"
#include "pvr_stream.h"
#include "pvr_stream_defs.h"
#include <linux/stddef.h>
#include <uapi/drm/pvr_drm.h>
#define PVR_STREAM_DEF_SET(owner, member, _size, _array_size, _feature) \
{ .offset = offsetof(struct owner, member), \
.size = (_size), \
.array_size = (_array_size), \
.feature = (_feature) }
#define PVR_STREAM_DEF(owner, member, member_size) \
PVR_STREAM_DEF_SET(owner, member, PVR_STREAM_SIZE_ ## member_size, 0, PVR_FEATURE_NONE)
#define PVR_STREAM_DEF_FEATURE(owner, member, member_size, feature) \
PVR_STREAM_DEF_SET(owner, member, PVR_STREAM_SIZE_ ## member_size, 0, feature)
#define PVR_STREAM_DEF_NOT_FEATURE(owner, member, member_size, feature) \
PVR_STREAM_DEF_SET(owner, member, PVR_STREAM_SIZE_ ## member_size, 0, \
(feature) | PVR_FEATURE_NOT)
#define PVR_STREAM_DEF_ARRAY(owner, member) \
PVR_STREAM_DEF_SET(owner, member, PVR_STREAM_SIZE_ARRAY, \
sizeof(((struct owner *)0)->member), PVR_FEATURE_NONE)
#define PVR_STREAM_DEF_ARRAY_FEATURE(owner, member, feature) \
PVR_STREAM_DEF_SET(owner, member, PVR_STREAM_SIZE_ARRAY, \
sizeof(((struct owner *)0)->member), feature)
#define PVR_STREAM_DEF_ARRAY_NOT_FEATURE(owner, member, feature) \
PVR_STREAM_DEF_SET(owner, member, PVR_STREAM_SIZE_ARRAY, \
sizeof(((struct owner *)0)->member), (feature) | PVR_FEATURE_NOT)
/*
* When adding new parameters to the stream definition, the new parameters must go after the
* existing parameters, to preserve order. As parameters are naturally aligned, care must be taken
* with respect to implicit padding in the stream; padding should be minimised as much as possible.
*/
static const struct pvr_stream_def rogue_fwif_cmd_geom_stream[] = {
PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.vdm_ctrl_stream_base, 64),
PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.tpu_border_colour_table, 64),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_geom, regs.vdm_draw_indirect0, 64,
PVR_FEATURE_VDM_DRAWINDIRECT),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_geom, regs.vdm_draw_indirect1, 32,
PVR_FEATURE_VDM_DRAWINDIRECT),
PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.ppp_ctrl, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.te_psg, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.vdm_context_resume_task0_size, 32),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_geom, regs.vdm_context_resume_task3_size, 32,
PVR_FEATURE_VDM_OBJECT_LEVEL_LLS),
PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.view_idx, 32),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_geom, regs.pds_coeff_free_prog, 32,
PVR_FEATURE_TESSELLATION),
};
static const struct pvr_stream_def rogue_fwif_cmd_geom_stream_brn49927[] = {
PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.tpu, 32),
};
static const struct pvr_stream_ext_def cmd_geom_ext_streams_0[] = {
{
.stream = rogue_fwif_cmd_geom_stream_brn49927,
.stream_len = ARRAY_SIZE(rogue_fwif_cmd_geom_stream_brn49927),
.header_mask = PVR_STREAM_EXTHDR_GEOM0_BRN49927,
.quirk = 49927,
},
};
static const struct pvr_stream_ext_header cmd_geom_ext_headers[] = {
{
.ext_streams = cmd_geom_ext_streams_0,
.ext_streams_num = ARRAY_SIZE(cmd_geom_ext_streams_0),
.valid_mask = PVR_STREAM_EXTHDR_GEOM0_VALID,
},
};
const struct pvr_stream_cmd_defs pvr_cmd_geom_stream = {
.type = PVR_STREAM_TYPE_GEOM,
.main_stream = rogue_fwif_cmd_geom_stream,
.main_stream_len = ARRAY_SIZE(rogue_fwif_cmd_geom_stream),
.ext_nr_headers = ARRAY_SIZE(cmd_geom_ext_headers),
.ext_headers = cmd_geom_ext_headers,
.dest_size = sizeof(struct rogue_fwif_cmd_geom),
};
static const struct pvr_stream_def rogue_fwif_cmd_frag_stream[] = {
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_scissor_base, 64),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_dbias_base, 64),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_oclqry_base, 64),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_zlsctl, 64),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_zload_store_base, 64),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_stencil_load_store_base, 64),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, regs.fb_cdc_zls, 64,
PVR_FEATURE_REQUIRES_FB_CDC_ZLS_SETUP),
PVR_STREAM_DEF_ARRAY(rogue_fwif_cmd_frag, regs.pbe_word),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.tpu_border_colour_table, 64),
PVR_STREAM_DEF_ARRAY(rogue_fwif_cmd_frag, regs.pds_bgnd),
PVR_STREAM_DEF_ARRAY(rogue_fwif_cmd_frag, regs.pds_pr_bgnd),
PVR_STREAM_DEF_ARRAY(rogue_fwif_cmd_frag, regs.usc_clear_register),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.usc_pixel_output_ctrl, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_bgobjdepth, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_bgobjvals, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_aa, 32),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, regs.isp_xtp_pipe_enable, 32,
PVR_FEATURE_S7_TOP_INFRASTRUCTURE),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_ctl, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.event_pixel_pds_info, 32),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, regs.pixel_phantom, 32,
PVR_FEATURE_CLUSTER_GROUPING),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.view_idx, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.event_pixel_pds_data, 32),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, regs.isp_oclqry_stride, 32,
PVR_FEATURE_GPU_MULTICORE_SUPPORT),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, regs.isp_zls_pixels, 32,
PVR_FEATURE_ZLS_SUBTILE),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, regs.rgx_cr_blackpearl_fix, 32,
PVR_FEATURE_ISP_ZLS_D24_S8_PACKING_OGL_MODE),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, zls_stride, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_frag, sls_stride, 32),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, execute_count, 32,
PVR_FEATURE_GPU_MULTICORE_SUPPORT),
};
static const struct pvr_stream_def rogue_fwif_cmd_frag_stream_brn47217[] = {
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_oclqry_stride, 32),
};
static const struct pvr_stream_def rogue_fwif_cmd_frag_stream_brn49927[] = {
PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.tpu, 32),
};
static const struct pvr_stream_ext_def cmd_frag_ext_streams_0[] = {
{
.stream = rogue_fwif_cmd_frag_stream_brn47217,
.stream_len = ARRAY_SIZE(rogue_fwif_cmd_frag_stream_brn47217),
.header_mask = PVR_STREAM_EXTHDR_FRAG0_BRN47217,
.quirk = 47217,
},
{
.stream = rogue_fwif_cmd_frag_stream_brn49927,
.stream_len = ARRAY_SIZE(rogue_fwif_cmd_frag_stream_brn49927),
.header_mask = PVR_STREAM_EXTHDR_FRAG0_BRN49927,
.quirk = 49927,
},
};
static const struct pvr_stream_ext_header cmd_frag_ext_headers[] = {
{
.ext_streams = cmd_frag_ext_streams_0,
.ext_streams_num = ARRAY_SIZE(cmd_frag_ext_streams_0),
.valid_mask = PVR_STREAM_EXTHDR_FRAG0_VALID,
},
};
const struct pvr_stream_cmd_defs pvr_cmd_frag_stream = {
.type = PVR_STREAM_TYPE_FRAG,
.main_stream = rogue_fwif_cmd_frag_stream,
.main_stream_len = ARRAY_SIZE(rogue_fwif_cmd_frag_stream),
.ext_nr_headers = ARRAY_SIZE(cmd_frag_ext_headers),
.ext_headers = cmd_frag_ext_headers,
.dest_size = sizeof(struct rogue_fwif_cmd_frag),
};
static const struct pvr_stream_def rogue_fwif_cmd_compute_stream[] = {
PVR_STREAM_DEF(rogue_fwif_cmd_compute, regs.tpu_border_colour_table, 64),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, regs.cdm_cb_queue, 64,
PVR_FEATURE_CDM_USER_MODE_QUEUE),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, regs.cdm_cb_base, 64,
PVR_FEATURE_CDM_USER_MODE_QUEUE),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, regs.cdm_cb, 64,
PVR_FEATURE_CDM_USER_MODE_QUEUE),
PVR_STREAM_DEF_NOT_FEATURE(rogue_fwif_cmd_compute, regs.cdm_ctrl_stream_base, 64,
PVR_FEATURE_CDM_USER_MODE_QUEUE),
PVR_STREAM_DEF(rogue_fwif_cmd_compute, regs.cdm_context_state_base_addr, 64),
PVR_STREAM_DEF(rogue_fwif_cmd_compute, regs.cdm_resume_pds1, 32),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, regs.cdm_item, 32,
PVR_FEATURE_COMPUTE_MORTON_CAPABLE),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, regs.compute_cluster, 32,
PVR_FEATURE_CLUSTER_GROUPING),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, regs.tpu_tag_cdm_ctrl, 32,
PVR_FEATURE_TPU_DM_GLOBAL_REGISTERS),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, stream_start_offset, 32,
PVR_FEATURE_CDM_USER_MODE_QUEUE),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, execute_count, 32,
PVR_FEATURE_GPU_MULTICORE_SUPPORT),
};
static const struct pvr_stream_def rogue_fwif_cmd_compute_stream_brn49927[] = {
PVR_STREAM_DEF(rogue_fwif_cmd_compute, regs.tpu, 32),
};
static const struct pvr_stream_ext_def cmd_compute_ext_streams_0[] = {
{
.stream = rogue_fwif_cmd_compute_stream_brn49927,
.stream_len = ARRAY_SIZE(rogue_fwif_cmd_compute_stream_brn49927),
.header_mask = PVR_STREAM_EXTHDR_COMPUTE0_BRN49927,
.quirk = 49927,
},
};
static const struct pvr_stream_ext_header cmd_compute_ext_headers[] = {
{
.ext_streams = cmd_compute_ext_streams_0,
.ext_streams_num = ARRAY_SIZE(cmd_compute_ext_streams_0),
.valid_mask = PVR_STREAM_EXTHDR_COMPUTE0_VALID,
},
};
const struct pvr_stream_cmd_defs pvr_cmd_compute_stream = {
.type = PVR_STREAM_TYPE_COMPUTE,
.main_stream = rogue_fwif_cmd_compute_stream,
.main_stream_len = ARRAY_SIZE(rogue_fwif_cmd_compute_stream),
.ext_nr_headers = ARRAY_SIZE(cmd_compute_ext_headers),
.ext_headers = cmd_compute_ext_headers,
.dest_size = sizeof(struct rogue_fwif_cmd_compute),
};
static const struct pvr_stream_def rogue_fwif_cmd_transfer_stream[] = {
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.pds_bgnd0_base, 64),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.pds_bgnd1_base, 64),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.pds_bgnd3_sizeinfo, 64),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_mtile_base, 64),
PVR_STREAM_DEF_ARRAY(rogue_fwif_cmd_transfer, regs.pbe_wordx_mrty),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_bgobjvals, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.usc_pixel_output_ctrl, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.usc_clear_register0, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.usc_clear_register1, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.usc_clear_register2, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.usc_clear_register3, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_mtile_size, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_render_origin, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_ctl, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_aa, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.event_pixel_pds_info, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.event_pixel_pds_code, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.event_pixel_pds_data, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_render, 32),
PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_rgn, 32),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_transfer, regs.isp_xtp_pipe_enable, 32,
PVR_FEATURE_S7_TOP_INFRASTRUCTURE),
PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_transfer, regs.frag_screen, 32,
PVR_FEATURE_GPU_MULTICORE_SUPPORT),
};
const struct pvr_stream_cmd_defs pvr_cmd_transfer_stream = {
.type = PVR_STREAM_TYPE_TRANSFER,
.main_stream = rogue_fwif_cmd_transfer_stream,
.main_stream_len = ARRAY_SIZE(rogue_fwif_cmd_transfer_stream),
.ext_nr_headers = 0,
.dest_size = sizeof(struct rogue_fwif_cmd_transfer),
};
static const struct pvr_stream_def rogue_fwif_static_render_context_state_stream[] = {
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_reg_vdm_context_state_base_addr, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_reg_vdm_context_state_resume_addr, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_reg_ta_context_state_base_addr, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_store_task0, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_store_task1, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_store_task2, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_store_task3, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_store_task4, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_resume_task0, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_resume_task1, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_resume_task2, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_resume_task3, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[0].geom_reg_vdm_context_resume_task4, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_store_task0, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_store_task1, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_store_task2, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_store_task3, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_store_task4, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_resume_task0, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_resume_task1, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_resume_task2, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_resume_task3, 64),
PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch,
geom_state[1].geom_reg_vdm_context_resume_task4, 64),
};
const struct pvr_stream_cmd_defs pvr_static_render_context_state_stream = {
.type = PVR_STREAM_TYPE_STATIC_RENDER_CONTEXT,
.main_stream = rogue_fwif_static_render_context_state_stream,
.main_stream_len = ARRAY_SIZE(rogue_fwif_static_render_context_state_stream),
.ext_nr_headers = 0,
.dest_size = sizeof(struct rogue_fwif_geom_registers_caswitch),
};
static const struct pvr_stream_def rogue_fwif_static_compute_context_state_stream[] = {
PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_context_pds0, 64),
PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_context_pds1, 64),
PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_terminate_pds, 64),
PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_terminate_pds1, 64),
PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_resume_pds0, 64),
PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_context_pds0_b, 64),
PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_resume_pds0_b, 64),
};
const struct pvr_stream_cmd_defs pvr_static_compute_context_state_stream = {
.type = PVR_STREAM_TYPE_STATIC_COMPUTE_CONTEXT,
.main_stream = rogue_fwif_static_compute_context_state_stream,
.main_stream_len = ARRAY_SIZE(rogue_fwif_static_compute_context_state_stream),
.ext_nr_headers = 0,
.dest_size = sizeof(struct rogue_fwif_cdm_registers_cswitch),
};

View file

@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_STREAM_DEFS_H
#define PVR_STREAM_DEFS_H
#include "pvr_stream.h"
extern const struct pvr_stream_cmd_defs pvr_cmd_geom_stream;
extern const struct pvr_stream_cmd_defs pvr_cmd_frag_stream;
extern const struct pvr_stream_cmd_defs pvr_cmd_compute_stream;
extern const struct pvr_stream_cmd_defs pvr_cmd_transfer_stream;
extern const struct pvr_stream_cmd_defs pvr_static_render_context_state_stream;
extern const struct pvr_stream_cmd_defs pvr_static_compute_context_state_stream;
#endif /* PVR_STREAM_DEFS_H */

View file

@ -0,0 +1,289 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include <uapi/drm/pvr_drm.h>
#include <drm/drm_syncobj.h>
#include <drm/gpu_scheduler.h>
#include <linux/xarray.h>
#include <linux/dma-fence-unwrap.h>
#include "pvr_device.h"
#include "pvr_queue.h"
#include "pvr_sync.h"
static int
pvr_check_sync_op(const struct drm_pvr_sync_op *sync_op)
{
u8 handle_type;
if (sync_op->flags & ~DRM_PVR_SYNC_OP_FLAGS_MASK)
return -EINVAL;
handle_type = sync_op->flags & DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_MASK;
if (handle_type != DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_SYNCOBJ &&
handle_type != DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_TIMELINE_SYNCOBJ)
return -EINVAL;
if (handle_type == DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_SYNCOBJ &&
sync_op->value != 0)
return -EINVAL;
return 0;
}
static void
pvr_sync_signal_free(struct pvr_sync_signal *sig_sync)
{
if (!sig_sync)
return;
drm_syncobj_put(sig_sync->syncobj);
dma_fence_chain_free(sig_sync->chain);
dma_fence_put(sig_sync->fence);
kfree(sig_sync);
}
void
pvr_sync_signal_array_cleanup(struct xarray *array)
{
struct pvr_sync_signal *sig_sync;
unsigned long i;
xa_for_each(array, i, sig_sync)
pvr_sync_signal_free(sig_sync);
xa_destroy(array);
}
static struct pvr_sync_signal *
pvr_sync_signal_array_add(struct xarray *array, struct drm_file *file, u32 handle, u64 point)
{
struct pvr_sync_signal *sig_sync;
struct dma_fence *cur_fence;
int err;
u32 id;
sig_sync = kzalloc(sizeof(*sig_sync), GFP_KERNEL);
if (!sig_sync)
return ERR_PTR(-ENOMEM);
sig_sync->handle = handle;
sig_sync->point = point;
if (point > 0) {
sig_sync->chain = dma_fence_chain_alloc();
if (!sig_sync->chain) {
err = -ENOMEM;
goto err_free_sig_sync;
}
}
sig_sync->syncobj = drm_syncobj_find(file, handle);
if (!sig_sync->syncobj) {
err = -EINVAL;
goto err_free_sig_sync;
}
/* Retrieve the current fence attached to that point. It's
* perfectly fine to get a NULL fence here, it just means there's
* no fence attached to that point yet.
*/
if (!drm_syncobj_find_fence(file, handle, point, 0, &cur_fence))
sig_sync->fence = cur_fence;
err = xa_alloc(array, &id, sig_sync, xa_limit_32b, GFP_KERNEL);
if (err)
goto err_free_sig_sync;
return sig_sync;
err_free_sig_sync:
pvr_sync_signal_free(sig_sync);
return ERR_PTR(err);
}
static struct pvr_sync_signal *
pvr_sync_signal_array_search(struct xarray *array, u32 handle, u64 point)
{
struct pvr_sync_signal *sig_sync;
unsigned long i;
xa_for_each(array, i, sig_sync) {
if (handle == sig_sync->handle && point == sig_sync->point)
return sig_sync;
}
return NULL;
}
static struct pvr_sync_signal *
pvr_sync_signal_array_get(struct xarray *array, struct drm_file *file, u32 handle, u64 point)
{
struct pvr_sync_signal *sig_sync;
sig_sync = pvr_sync_signal_array_search(array, handle, point);
if (sig_sync)
return sig_sync;
return pvr_sync_signal_array_add(array, file, handle, point);
}
int
pvr_sync_signal_array_collect_ops(struct xarray *array,
struct drm_file *file,
u32 sync_op_count,
const struct drm_pvr_sync_op *sync_ops)
{
for (u32 i = 0; i < sync_op_count; i++) {
struct pvr_sync_signal *sig_sync;
int ret;
if (!(sync_ops[i].flags & DRM_PVR_SYNC_OP_FLAG_SIGNAL))
continue;
ret = pvr_check_sync_op(&sync_ops[i]);
if (ret)
return ret;
sig_sync = pvr_sync_signal_array_get(array, file,
sync_ops[i].handle,
sync_ops[i].value);
if (IS_ERR(sig_sync))
return PTR_ERR(sig_sync);
}
return 0;
}
int
pvr_sync_signal_array_update_fences(struct xarray *array,
u32 sync_op_count,
const struct drm_pvr_sync_op *sync_ops,
struct dma_fence *done_fence)
{
for (u32 i = 0; i < sync_op_count; i++) {
struct dma_fence *old_fence;
struct pvr_sync_signal *sig_sync;
if (!(sync_ops[i].flags & DRM_PVR_SYNC_OP_FLAG_SIGNAL))
continue;
sig_sync = pvr_sync_signal_array_search(array, sync_ops[i].handle,
sync_ops[i].value);
if (WARN_ON(!sig_sync))
return -EINVAL;
old_fence = sig_sync->fence;
sig_sync->fence = dma_fence_get(done_fence);
dma_fence_put(old_fence);
if (WARN_ON(!sig_sync->fence))
return -EINVAL;
}
return 0;
}
void
pvr_sync_signal_array_push_fences(struct xarray *array)
{
struct pvr_sync_signal *sig_sync;
unsigned long i;
xa_for_each(array, i, sig_sync) {
if (sig_sync->chain) {
drm_syncobj_add_point(sig_sync->syncobj, sig_sync->chain,
sig_sync->fence, sig_sync->point);
sig_sync->chain = NULL;
} else {
drm_syncobj_replace_fence(sig_sync->syncobj, sig_sync->fence);
}
}
}
static int
pvr_sync_add_dep_to_job(struct drm_sched_job *job, struct dma_fence *f)
{
struct dma_fence_unwrap iter;
u32 native_fence_count = 0;
struct dma_fence *uf;
int err = 0;
dma_fence_unwrap_for_each(uf, &iter, f) {
if (pvr_queue_fence_is_ufo_backed(uf))
native_fence_count++;
}
/* No need to unwrap the fence if it's fully non-native. */
if (!native_fence_count)
return drm_sched_job_add_dependency(job, f);
dma_fence_unwrap_for_each(uf, &iter, f) {
/* There's no dma_fence_unwrap_stop() helper cleaning up the refs
* owned by dma_fence_unwrap(), so let's just iterate over all
* entries without doing anything when something failed.
*/
if (err)
continue;
if (pvr_queue_fence_is_ufo_backed(uf)) {
struct drm_sched_fence *s_fence = to_drm_sched_fence(uf);
/* If this is a native dependency, we wait for the scheduled fence,
* and we will let pvr_queue_run_job() issue FW waits.
*/
err = drm_sched_job_add_dependency(job,
dma_fence_get(&s_fence->scheduled));
} else {
err = drm_sched_job_add_dependency(job, dma_fence_get(uf));
}
}
dma_fence_put(f);
return err;
}
int
pvr_sync_add_deps_to_job(struct pvr_file *pvr_file, struct drm_sched_job *job,
u32 sync_op_count,
const struct drm_pvr_sync_op *sync_ops,
struct xarray *signal_array)
{
int err = 0;
if (!sync_op_count)
return 0;
for (u32 i = 0; i < sync_op_count; i++) {
struct pvr_sync_signal *sig_sync;
struct dma_fence *fence;
if (sync_ops[i].flags & DRM_PVR_SYNC_OP_FLAG_SIGNAL)
continue;
err = pvr_check_sync_op(&sync_ops[i]);
if (err)
return err;
sig_sync = pvr_sync_signal_array_search(signal_array, sync_ops[i].handle,
sync_ops[i].value);
if (sig_sync) {
if (WARN_ON(!sig_sync->fence))
return -EINVAL;
fence = dma_fence_get(sig_sync->fence);
} else {
err = drm_syncobj_find_fence(from_pvr_file(pvr_file), sync_ops[i].handle,
sync_ops[i].value, 0, &fence);
if (err)
return err;
}
err = pvr_sync_add_dep_to_job(job, fence);
if (err)
return err;
}
return 0;
}

View file

@ -0,0 +1,84 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_SYNC_H
#define PVR_SYNC_H
#include <uapi/drm/pvr_drm.h>
/* Forward declaration from <linux/xarray.h>. */
struct xarray;
/* Forward declaration from <drm/drm_file.h>. */
struct drm_file;
/* Forward declaration from <drm/gpu_scheduler.h>. */
struct drm_sched_job;
/* Forward declaration from "pvr_device.h". */
struct pvr_file;
/**
* struct pvr_sync_signal - Object encoding a syncobj signal operation
*
* The job submission logic collects all signal operations in an array of
* pvr_sync_signal objects. This array also serves as a cache to get the
* latest dma_fence when multiple jobs are submitted at once, and one job
* signals a syncobj point that's later waited on by a subsequent job.
*/
struct pvr_sync_signal {
/** @handle: Handle of the syncobj to signal. */
u32 handle;
/**
* @point: Point to signal in the syncobj.
*
* Only relevant for timeline syncobjs.
*/
u64 point;
/** @syncobj: Syncobj retrieved from the handle. */
struct drm_syncobj *syncobj;
/**
* @chain: Chain object used to link the new fence with the
* existing timeline syncobj.
*
* Should be zero when manipulating a regular syncobj.
*/
struct dma_fence_chain *chain;
/**
* @fence: New fence object to attach to the syncobj.
*
* This pointer starts with the current fence bound to
* the <handle,point> pair.
*/
struct dma_fence *fence;
};
void
pvr_sync_signal_array_cleanup(struct xarray *array);
int
pvr_sync_signal_array_collect_ops(struct xarray *array,
struct drm_file *file,
u32 sync_op_count,
const struct drm_pvr_sync_op *sync_ops);
int
pvr_sync_signal_array_update_fences(struct xarray *array,
u32 sync_op_count,
const struct drm_pvr_sync_op *sync_ops,
struct dma_fence *done_fence);
void
pvr_sync_signal_array_push_fences(struct xarray *array);
int
pvr_sync_add_deps_to_job(struct pvr_file *pvr_file, struct drm_sched_job *job,
u32 sync_op_count,
const struct drm_pvr_sync_op *sync_ops,
struct xarray *signal_array);
#endif /* PVR_SYNC_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_VM_H
#define PVR_VM_H
#include "pvr_rogue_mmu_defs.h"
#include <uapi/drm/pvr_drm.h>
#include <linux/types.h>
/* Forward declaration from "pvr_device.h" */
struct pvr_device;
struct pvr_file;
/* Forward declaration from "pvr_gem.h" */
struct pvr_gem_object;
/* Forward declaration from "pvr_vm.c" */
struct pvr_vm_context;
/* Forward declaration from <uapi/drm/pvr_drm.h> */
struct drm_pvr_ioctl_get_heap_info_args;
/* Forward declaration from <drm/drm_exec.h> */
struct drm_exec;
/* Functions defined in pvr_vm.c */
bool pvr_device_addr_is_valid(u64 device_addr);
bool pvr_device_addr_and_size_are_valid(u64 device_addr, u64 size);
struct pvr_vm_context *pvr_vm_create_context(struct pvr_device *pvr_dev,
bool is_userspace_context);
int pvr_vm_map(struct pvr_vm_context *vm_ctx,
struct pvr_gem_object *pvr_obj, u64 pvr_obj_offset,
u64 device_addr, u64 size);
int pvr_vm_unmap(struct pvr_vm_context *vm_ctx, u64 device_addr, u64 size);
dma_addr_t pvr_vm_get_page_table_root_addr(struct pvr_vm_context *vm_ctx);
struct dma_resv *pvr_vm_get_dma_resv(struct pvr_vm_context *vm_ctx);
int pvr_static_data_areas_get(const struct pvr_device *pvr_dev,
struct drm_pvr_ioctl_dev_query_args *args);
int pvr_heap_info_get(const struct pvr_device *pvr_dev,
struct drm_pvr_ioctl_dev_query_args *args);
const struct drm_pvr_heap *pvr_find_heap_containing(struct pvr_device *pvr_dev,
u64 addr, u64 size);
struct pvr_gem_object *pvr_vm_find_gem_object(struct pvr_vm_context *vm_ctx,
u64 device_addr,
u64 *mapped_offset_out,
u64 *mapped_size_out);
struct pvr_fw_object *
pvr_vm_get_fw_mem_context(struct pvr_vm_context *vm_ctx);
struct pvr_vm_context *pvr_vm_context_lookup(struct pvr_file *pvr_file, u32 handle);
struct pvr_vm_context *pvr_vm_context_get(struct pvr_vm_context *vm_ctx);
bool pvr_vm_context_put(struct pvr_vm_context *vm_ctx);
void pvr_destroy_vm_contexts_for_file(struct pvr_file *pvr_file);
#endif /* PVR_VM_H */

View file

@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_device.h"
#include "pvr_fw_mips.h"
#include "pvr_gem.h"
#include "pvr_mmu.h"
#include "pvr_rogue_mips.h"
#include "pvr_vm.h"
#include "pvr_vm_mips.h"
#include <drm/drm_managed.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/types.h>
/**
* pvr_vm_mips_init() - Initialise MIPS FW pagetable
* @pvr_dev: Target PowerVR device.
*
* Returns:
* * 0 on success,
* * -%EINVAL,
* * Any error returned by pvr_gem_object_create(), or
* * And error returned by pvr_gem_object_vmap().
*/
int
pvr_vm_mips_init(struct pvr_device *pvr_dev)
{
u32 pt_size = 1 << ROGUE_MIPSFW_LOG2_PAGETABLE_SIZE_4K(pvr_dev);
struct device *dev = from_pvr_device(pvr_dev)->dev;
struct pvr_fw_mips_data *mips_data;
u32 phys_bus_width;
int page_nr;
int err;
/* Page table size must be at most ROGUE_MIPSFW_MAX_NUM_PAGETABLE_PAGES * 4k pages. */
if (pt_size > ROGUE_MIPSFW_MAX_NUM_PAGETABLE_PAGES * SZ_4K)
return -EINVAL;
if (PVR_FEATURE_VALUE(pvr_dev, phys_bus_width, &phys_bus_width))
return -EINVAL;
mips_data = drmm_kzalloc(from_pvr_device(pvr_dev), sizeof(*mips_data), GFP_KERNEL);
if (!mips_data)
return -ENOMEM;
for (page_nr = 0; page_nr < ARRAY_SIZE(mips_data->pt_pages); page_nr++) {
mips_data->pt_pages[page_nr] = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!mips_data->pt_pages[page_nr]) {
err = -ENOMEM;
goto err_free_pages;
}
mips_data->pt_dma_addr[page_nr] = dma_map_page(dev, mips_data->pt_pages[page_nr], 0,
PAGE_SIZE, DMA_TO_DEVICE);
if (dma_mapping_error(dev, mips_data->pt_dma_addr[page_nr])) {
err = -ENOMEM;
goto err_free_pages;
}
}
mips_data->pt = vmap(mips_data->pt_pages, pt_size >> PAGE_SHIFT, VM_MAP,
pgprot_writecombine(PAGE_KERNEL));
if (!mips_data->pt) {
err = -ENOMEM;
goto err_free_pages;
}
mips_data->pfn_mask = (phys_bus_width > 32) ? ROGUE_MIPSFW_ENTRYLO_PFN_MASK_ABOVE_32BIT :
ROGUE_MIPSFW_ENTRYLO_PFN_MASK;
mips_data->cache_policy = (phys_bus_width > 32) ? ROGUE_MIPSFW_CACHED_POLICY_ABOVE_32BIT :
ROGUE_MIPSFW_CACHED_POLICY;
pvr_dev->fw_dev.processor_data.mips_data = mips_data;
return 0;
err_free_pages:
for (; page_nr >= 0; page_nr--) {
if (mips_data->pt_dma_addr[page_nr])
dma_unmap_page(from_pvr_device(pvr_dev)->dev,
mips_data->pt_dma_addr[page_nr], PAGE_SIZE, DMA_TO_DEVICE);
if (mips_data->pt_pages[page_nr])
__free_page(mips_data->pt_pages[page_nr]);
}
return err;
}
/**
* pvr_vm_mips_fini() - Release MIPS FW pagetable
* @pvr_dev: Target PowerVR device.
*/
void
pvr_vm_mips_fini(struct pvr_device *pvr_dev)
{
struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
struct pvr_fw_mips_data *mips_data = fw_dev->processor_data.mips_data;
int page_nr;
vunmap(mips_data->pt);
for (page_nr = ARRAY_SIZE(mips_data->pt_pages) - 1; page_nr >= 0; page_nr--) {
dma_unmap_page(from_pvr_device(pvr_dev)->dev,
mips_data->pt_dma_addr[page_nr], PAGE_SIZE, DMA_TO_DEVICE);
__free_page(mips_data->pt_pages[page_nr]);
}
fw_dev->processor_data.mips_data = NULL;
}
static u32
get_mips_pte_flags(bool read, bool write, u32 cache_policy)
{
u32 flags = 0;
if (read && write) /* Read/write. */
flags |= ROGUE_MIPSFW_ENTRYLO_DIRTY_EN;
else if (write) /* Write only. */
flags |= ROGUE_MIPSFW_ENTRYLO_READ_INHIBIT_EN;
else
WARN_ON(!read);
flags |= cache_policy << ROGUE_MIPSFW_ENTRYLO_CACHE_POLICY_SHIFT;
flags |= ROGUE_MIPSFW_ENTRYLO_VALID_EN | ROGUE_MIPSFW_ENTRYLO_GLOBAL_EN;
return flags;
}
/**
* pvr_vm_mips_map() - Map a FW object into MIPS address space
* @pvr_dev: Target PowerVR device.
* @fw_obj: FW object to map.
*
* Returns:
* * 0 on success,
* * -%EINVAL if object does not reside within FW address space, or
* * Any error returned by pvr_fw_object_get_dma_addr().
*/
int
pvr_vm_mips_map(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj)
{
struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
struct pvr_fw_mips_data *mips_data = fw_dev->processor_data.mips_data;
struct pvr_gem_object *pvr_obj = fw_obj->gem;
const u64 start = fw_obj->fw_mm_node.start;
const u64 size = fw_obj->fw_mm_node.size;
u64 end;
u32 cache_policy;
u32 pte_flags;
u32 start_pfn;
u32 end_pfn;
s32 pfn;
int err;
if (check_add_overflow(start, size - 1, &end))
return -EINVAL;
if (start < ROGUE_FW_HEAP_BASE ||
start >= ROGUE_FW_HEAP_BASE + fw_dev->fw_heap_info.raw_size ||
end < ROGUE_FW_HEAP_BASE ||
end >= ROGUE_FW_HEAP_BASE + fw_dev->fw_heap_info.raw_size ||
(start & ROGUE_MIPSFW_PAGE_MASK_4K) ||
((end + 1) & ROGUE_MIPSFW_PAGE_MASK_4K))
return -EINVAL;
start_pfn = (start & fw_dev->fw_heap_info.offset_mask) >> ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K;
end_pfn = (end & fw_dev->fw_heap_info.offset_mask) >> ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K;
if (pvr_obj->flags & PVR_BO_FW_FLAGS_DEVICE_UNCACHED)
cache_policy = ROGUE_MIPSFW_UNCACHED_CACHE_POLICY;
else
cache_policy = mips_data->cache_policy;
pte_flags = get_mips_pte_flags(true, true, cache_policy);
for (pfn = start_pfn; pfn <= end_pfn; pfn++) {
dma_addr_t dma_addr;
u32 pte;
err = pvr_fw_object_get_dma_addr(fw_obj,
(pfn - start_pfn) <<
ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K,
&dma_addr);
if (err)
goto err_unmap_pages;
pte = ((dma_addr >> ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K)
<< ROGUE_MIPSFW_ENTRYLO_PFN_SHIFT) & mips_data->pfn_mask;
pte |= pte_flags;
WRITE_ONCE(mips_data->pt[pfn], pte);
}
pvr_mmu_flush_request_all(pvr_dev);
return 0;
err_unmap_pages:
for (; pfn >= start_pfn; pfn--)
WRITE_ONCE(mips_data->pt[pfn], 0);
pvr_mmu_flush_request_all(pvr_dev);
WARN_ON(pvr_mmu_flush_exec(pvr_dev, true));
return err;
}
/**
* pvr_vm_mips_unmap() - Unmap a FW object into MIPS address space
* @pvr_dev: Target PowerVR device.
* @fw_obj: FW object to unmap.
*/
void
pvr_vm_mips_unmap(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj)
{
struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
struct pvr_fw_mips_data *mips_data = fw_dev->processor_data.mips_data;
const u64 start = fw_obj->fw_mm_node.start;
const u64 size = fw_obj->fw_mm_node.size;
const u64 end = start + size;
const u32 start_pfn = (start & fw_dev->fw_heap_info.offset_mask) >>
ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K;
const u32 end_pfn = (end & fw_dev->fw_heap_info.offset_mask) >>
ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K;
for (u32 pfn = start_pfn; pfn < end_pfn; pfn++)
WRITE_ONCE(mips_data->pt[pfn], 0);
pvr_mmu_flush_request_all(pvr_dev);
WARN_ON(pvr_mmu_flush_exec(pvr_dev, true));
}

View file

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#ifndef PVR_VM_MIPS_H
#define PVR_VM_MIPS_H
/* Forward declaration from pvr_device.h. */
struct pvr_device;
/* Forward declaration from pvr_gem.h. */
struct pvr_fw_object;
int
pvr_vm_mips_init(struct pvr_device *pvr_dev);
void
pvr_vm_mips_fini(struct pvr_device *pvr_dev);
int
pvr_vm_mips_map(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj);
void
pvr_vm_mips_unmap(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj);
#endif /* PVR_VM_MIPS_H */

View file

@ -80,7 +80,7 @@ static int dcss_drv_platform_probe(struct platform_device *pdev)
return err;
}
static int dcss_drv_platform_remove(struct platform_device *pdev)
static void dcss_drv_platform_remove(struct platform_device *pdev)
{
struct dcss_drv *mdrv = dev_get_drvdata(&pdev->dev);
@ -88,8 +88,6 @@ static int dcss_drv_platform_remove(struct platform_device *pdev)
dcss_dev_destroy(mdrv->dcss);
kfree(mdrv);
return 0;
}
static void dcss_drv_platform_shutdown(struct platform_device *pdev)
@ -120,7 +118,7 @@ MODULE_DEVICE_TABLE(of, dcss_of_match);
static struct platform_driver dcss_platform_driver = {
.probe = dcss_drv_platform_probe,
.remove = dcss_drv_platform_remove,
.remove_new = dcss_drv_platform_remove,
.shutdown = dcss_drv_platform_shutdown,
.driver = {
.name = "imx-dcss",

View file

@ -515,14 +515,12 @@ static int imx_lcdc_probe(struct platform_device *pdev)
return 0;
}
static int imx_lcdc_remove(struct platform_device *pdev)
static void imx_lcdc_remove(struct platform_device *pdev)
{
struct drm_device *drm = platform_get_drvdata(pdev);
drm_dev_unregister(drm);
drm_atomic_helper_shutdown(drm);
return 0;
}
static void imx_lcdc_shutdown(struct platform_device *pdev)
@ -536,7 +534,7 @@ static struct platform_driver imx_lcdc_driver = {
.of_match_table = imx_lcdc_of_dev_id,
},
.probe = imx_lcdc_probe,
.remove = imx_lcdc_remove,
.remove_new = imx_lcdc_remove,
.shutdown = imx_lcdc_shutdown,
};
module_platform_driver(imx_lcdc_driver);

View file

@ -448,7 +448,7 @@ static const struct drm_driver kmb_driver = {
.minor = DRIVER_MINOR,
};
static int kmb_remove(struct platform_device *pdev)
static void kmb_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct drm_device *drm = dev_get_drvdata(dev);
@ -473,7 +473,6 @@ static int kmb_remove(struct platform_device *pdev)
/* Unregister DSI host */
kmb_dsi_host_unregister(kmb->kmb_dsi);
drm_atomic_helper_shutdown(drm);
return 0;
}
static int kmb_probe(struct platform_device *pdev)
@ -621,7 +620,7 @@ static SIMPLE_DEV_PM_OPS(kmb_pm_ops, kmb_pm_suspend, kmb_pm_resume);
static struct platform_driver kmb_platform_driver = {
.probe = kmb_probe,
.remove = kmb_remove,
.remove_new = kmb_remove,
.driver = {
.name = "kmb-drm",
.pm = &kmb_pm_ops,

View file

@ -531,16 +531,15 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev)
return ret;
}
static int mtk_disp_ovl_adaptor_remove(struct platform_device *pdev)
static void mtk_disp_ovl_adaptor_remove(struct platform_device *pdev)
{
component_master_del(&pdev->dev, &mtk_disp_ovl_adaptor_master_ops);
pm_runtime_disable(&pdev->dev);
return 0;
}
struct platform_driver mtk_disp_ovl_adaptor_driver = {
.probe = mtk_disp_ovl_adaptor_probe,
.remove = mtk_disp_ovl_adaptor_remove,
.remove_new = mtk_disp_ovl_adaptor_remove,
.driver = {
.name = "mediatek-disp-ovl-adaptor",
.owner = THIS_MODULE,

View file

@ -346,10 +346,9 @@ static int mtk_ethdr_probe(struct platform_device *pdev)
return ret;
}
static int mtk_ethdr_remove(struct platform_device *pdev)
static void mtk_ethdr_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &mtk_ethdr_component_ops);
return 0;
}
static const struct of_device_id mtk_ethdr_driver_dt_match[] = {
@ -361,7 +360,7 @@ MODULE_DEVICE_TABLE(of, mtk_ethdr_driver_dt_match);
struct platform_driver mtk_ethdr_driver = {
.probe = mtk_ethdr_probe,
.remove = mtk_ethdr_remove,
.remove_new = mtk_ethdr_remove,
.driver = {
.name = "mediatek-disp-ethdr",
.owner = THIS_MODULE,

View file

@ -323,13 +323,11 @@ static int meson_dw_mipi_dsi_probe(struct platform_device *pdev)
return 0;
}
static int meson_dw_mipi_dsi_remove(struct platform_device *pdev)
static void meson_dw_mipi_dsi_remove(struct platform_device *pdev)
{
struct meson_dw_mipi_dsi *mipi_dsi = platform_get_drvdata(pdev);
dw_mipi_dsi_remove(mipi_dsi->dmd);
return 0;
}
static const struct of_device_id meson_dw_mipi_dsi_of_table[] = {
@ -340,7 +338,7 @@ MODULE_DEVICE_TABLE(of, meson_dw_mipi_dsi_of_table);
static struct platform_driver meson_dw_mipi_dsi_platform_driver = {
.probe = meson_dw_mipi_dsi_probe,
.remove = meson_dw_mipi_dsi_remove,
.remove_new = meson_dw_mipi_dsi_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = meson_dw_mipi_dsi_of_table,

View file

@ -43,11 +43,10 @@ static int nouveau_platform_probe(struct platform_device *pdev)
return 0;
}
static int nouveau_platform_remove(struct platform_device *pdev)
static void nouveau_platform_remove(struct platform_device *pdev)
{
struct drm_device *dev = platform_get_drvdata(pdev);
nouveau_drm_device_remove(dev);
return 0;
}
#if IS_ENABLED(CONFIG_OF)
@ -93,5 +92,5 @@ struct platform_driver nouveau_platform_driver = {
.of_match_table = of_match_ptr(nouveau_platform_match),
},
.probe = nouveau_platform_probe,
.remove = nouveau_platform_remove,
.remove_new = nouveau_platform_remove,
};

View file

@ -39,7 +39,7 @@ struct nv04_fence_priv {
static int
nv04_fence_emit(struct nouveau_fence *fence)
{
struct nvif_push *push = fence->channel->chan.push;
struct nvif_push *push = unrcu_pointer(fence->channel)->chan.push;
int ret = PUSH_WAIT(push, 2);
if (ret == 0) {
PUSH_NVSQ(push, NV_SW, 0x0150, fence->base.seqno);

View file

@ -24,7 +24,6 @@
#include "chan.h"
#include "chid.h"
#include "cgrp.h"
#include "chid.h"
#include "runl.h"
#include "priv.h"

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Elida kd35t133 5.5" MIPI-DSI panel driver
* Elida kd35t133 3.5" MIPI-DSI panel driver
* Copyright (C) 2020 Theobroma Systems Design und Consulting GmbH
*
* based on
@ -43,7 +43,6 @@ struct kd35t133 {
struct regulator *vdd;
struct regulator *iovcc;
enum drm_panel_orientation orientation;
bool prepared;
};
static inline struct kd35t133 *panel_to_kd35t133(struct drm_panel *panel)
@ -91,9 +90,6 @@ static int kd35t133_unprepare(struct drm_panel *panel)
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
int ret;
if (!ctx->prepared)
return 0;
ret = mipi_dsi_dcs_set_display_off(dsi);
if (ret < 0)
dev_err(ctx->dev, "failed to set display off: %d\n", ret);
@ -104,11 +100,11 @@ static int kd35t133_unprepare(struct drm_panel *panel)
return ret;
}
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
regulator_disable(ctx->iovcc);
regulator_disable(ctx->vdd);
ctx->prepared = false;
return 0;
}
@ -118,9 +114,6 @@ static int kd35t133_prepare(struct drm_panel *panel)
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
int ret;
if (ctx->prepared)
return 0;
dev_dbg(ctx->dev, "Resetting the panel\n");
ret = regulator_enable(ctx->vdd);
if (ret < 0) {
@ -164,8 +157,6 @@ static int kd35t133_prepare(struct drm_panel *panel)
msleep(50);
ctx->prepared = true;
return 0;
disable_iovcc:
@ -209,11 +200,6 @@ static int kd35t133_get_modes(struct drm_panel *panel,
connector->display_info.width_mm = mode->width_mm;
connector->display_info.height_mm = mode->height_mm;
drm_mode_probed_add(connector, mode);
/*
* TODO: Remove once all drm drivers call
* drm_connector_set_orientation_from_panel()
*/
drm_connector_set_panel_orientation(connector, ctx->orientation);
return 1;
}
@ -299,27 +285,11 @@ static int kd35t133_probe(struct mipi_dsi_device *dsi)
return 0;
}
static void kd35t133_shutdown(struct mipi_dsi_device *dsi)
{
struct kd35t133 *ctx = mipi_dsi_get_drvdata(dsi);
int ret;
ret = drm_panel_unprepare(&ctx->panel);
if (ret < 0)
dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
ret = drm_panel_disable(&ctx->panel);
if (ret < 0)
dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
}
static void kd35t133_remove(struct mipi_dsi_device *dsi)
{
struct kd35t133 *ctx = mipi_dsi_get_drvdata(dsi);
int ret;
kd35t133_shutdown(dsi);
ret = mipi_dsi_detach(dsi);
if (ret < 0)
dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
@ -340,7 +310,6 @@ static struct mipi_dsi_driver kd35t133_driver = {
},
.probe = kd35t133_probe,
.remove = kd35t133_remove,
.shutdown = kd35t133_shutdown,
};
module_mipi_dsi_driver(kd35t133_driver);

Some files were not shown because too many files have changed in this diff Show more