Commit f99f5f3e authored by Sarah Walker's avatar Sarah Walker Committed by Maxime Ripard

drm/imagination: Add GPU ID parsing and firmware loading

Read the GPU ID register at probe time and select the correct
features/quirks/enhancements. Use the GPU ID to form the firmware
file name and load the firmware.

The features/quirks/enhancements arrays are currently hardcoded in
the driver for the supported GPUs. We are looking at moving this
information to the firmware image.

Changes since v8:
- Corrected license identifiers

Changes since v7:
- Fix kerneldoc for pvr_device_info_set_enhancements()

Changes since v5:
- Add BRN 71242 to device info

Changes since v4:
- Retrieve device information from firmware header
- Pull forward firmware header parsing from FW infrastructure patch
- Use devm_add_action_or_reset to release firmware

Changes since v3:
- Use drm_dev_{enter,exit}
Co-developed-by: default avatarFrank Binns <frank.binns@imgtec.com>
Signed-off-by: default avatarFrank Binns <frank.binns@imgtec.com>
Co-developed-by: default avatarMatt Coster <matt.coster@imgtec.com>
Signed-off-by: default avatarMatt Coster <matt.coster@imgtec.com>
Co-developed-by: default avatarDonald Robson <donald.robson@imgtec.com>
Signed-off-by: default avatarDonald Robson <donald.robson@imgtec.com>
Signed-off-by: default avatarSarah Walker <sarah.walker@imgtec.com>
Link: https://lore.kernel.org/r/1ff76f7a5b45c742279c78910f8491b8a5e7f6e6.1700668843.git.donald.robson@imgtec.comSigned-off-by: default avatarMaxime Ripard <mripard@kernel.org>
parent a26f067f
......@@ -5,6 +5,8 @@ subdir-ccflags-y := -I$(srctree)/$(src)
powervr-y := \
pvr_device.o \
pvr_device_info.o \
pvr_drv.o \
pvr_fw.o
obj-$(CONFIG_DRM_POWERVR) += powervr.o
......@@ -2,19 +2,31 @@
/* Copyright (c) 2023 Imagination Technologies Ltd. */
#include "pvr_device.h"
#include "pvr_device_info.h"
#include "pvr_fw.h"
#include "pvr_rogue_cr_defs.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
......@@ -100,6 +112,209 @@ static int pvr_device_clk_init(struct pvr_device *pvr_dev)
return 0;
}
/**
* 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;
return pvr_set_dma_info(pvr_dev);
}
/**
* pvr_device_init() - Initialize a PowerVR device
* @pvr_dev: Target PowerVR device.
......@@ -130,7 +345,12 @@ pvr_device_init(struct pvr_device *pvr_dev)
return err;
/* Map the control registers into memory. */
return pvr_device_reg_init(pvr_dev);
err = pvr_device_reg_init(pvr_dev);
if (err)
return err;
/* Perform GPU-specific initialization steps. */
return pvr_device_gpu_init(pvr_dev);
}
/**
......@@ -145,3 +365,104 @@ pvr_device_fini(struct pvr_device *pvr_dev)
* the initialization stages in pvr_device_init().
*/
}
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;
}
}
......@@ -4,6 +4,9 @@
#ifndef PVR_DEVICE_H
#define PVR_DEVICE_H
#include "pvr_device_info.h"
#include "pvr_fw.h"
#include <drm/drm_device.h>
#include <drm/drm_file.h>
#include <drm/drm_mm.h>
......@@ -28,6 +31,26 @@ 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
*/
......@@ -40,6 +63,35 @@ struct 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: Device control registers.
*
......@@ -70,6 +122,9 @@ struct pvr_device {
* Interface (MEMIF). If present, this needs to be enabled/disabled together with @core_clk.
*/
struct clk *mem_clk;
/** @fw_dev: Firmware related data. */
struct pvr_fw_device fw_dev;
};
/**
......@@ -92,6 +147,76 @@ struct pvr_file {
struct pvr_device *pvr_dev;
};
/**
* 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)
......@@ -100,9 +225,77 @@ struct pvr_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);
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.
......@@ -208,6 +401,29 @@ pvr_cr_poll_reg64(struct pvr_device *pvr_dev, u32 reg_addr, u64 reg_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
*
......@@ -302,4 +518,8 @@ pvr_ioctl_union_padding_check(void *instance, size_t 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 */
This diff is collapsed.
/* 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 */
This diff is collapsed.
......@@ -19,4 +19,111 @@
#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 */
// 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 <drm/drm_drv.h>
#include <linux/firmware.h>
#include <linux/sizes.h>
#define FW_MAX_SUPPORTED_MAJOR_VERSION 1
/**
* pvr_fw_validate() - Parse firmware header and check compatibility
* @pvr_dev: Device pointer.
*
* Returns:
* * 0 on success, or
* * -EINVAL if firmware is incompatible.
*/
static int
pvr_fw_validate(struct pvr_device *pvr_dev)
{
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
const struct firmware *firmware = pvr_dev->fw_dev.firmware;
const struct pvr_fw_layout_entry *layout_entries;
const struct pvr_fw_info_header *header;
const u8 *fw = firmware->data;
u32 fw_offset = firmware->size - SZ_4K;
u32 layout_table_size;
u32 entry;
if (firmware->size < SZ_4K || (firmware->size % FW_BLOCK_SIZE))
return -EINVAL;
header = (const struct pvr_fw_info_header *)&fw[fw_offset];
if (header->info_version != PVR_FW_INFO_VERSION) {
drm_err(drm_dev, "Unsupported fw info version %u\n",
header->info_version);
return -EINVAL;
}
if (header->header_len != sizeof(struct pvr_fw_info_header) ||
header->layout_entry_size != sizeof(struct pvr_fw_layout_entry) ||
header->layout_entry_num > PVR_FW_INFO_MAX_NUM_ENTRIES) {
drm_err(drm_dev, "FW info format mismatch\n");
return -EINVAL;
}
if (!(header->flags & PVR_FW_FLAGS_OPEN_SOURCE) ||
header->fw_version_major > FW_MAX_SUPPORTED_MAJOR_VERSION ||
header->fw_version_major == 0) {
drm_err(drm_dev, "Unsupported FW version %u.%u (build: %u%s)\n",
header->fw_version_major, header->fw_version_minor,
header->fw_version_build,
(header->flags & PVR_FW_FLAGS_OPEN_SOURCE) ? " OS" : "");
return -EINVAL;
}
if (pvr_gpu_id_to_packed_bvnc(&pvr_dev->gpu_id) != header->bvnc) {
struct pvr_gpu_id fw_gpu_id;
packed_bvnc_to_pvr_gpu_id(header->bvnc, &fw_gpu_id);
drm_err(drm_dev, "FW built for incorrect GPU ID %i.%i.%i.%i (expected %i.%i.%i.%i)\n",
fw_gpu_id.b, fw_gpu_id.v, fw_gpu_id.n, fw_gpu_id.c,
pvr_dev->gpu_id.b, pvr_dev->gpu_id.v, pvr_dev->gpu_id.n, pvr_dev->gpu_id.c);
return -EINVAL;
}
fw_offset += header->header_len;
layout_table_size =
header->layout_entry_size * header->layout_entry_num;
if ((fw_offset + layout_table_size) > firmware->size)
return -EINVAL;
layout_entries = (const struct pvr_fw_layout_entry *)&fw[fw_offset];
for (entry = 0; entry < header->layout_entry_num; entry++) {
u32 start_addr = layout_entries[entry].base_addr;
u32 end_addr = start_addr + layout_entries[entry].alloc_size;
if (start_addr >= end_addr)
return -EINVAL;
}
fw_offset = (firmware->size - SZ_4K) - header->device_info_size;
drm_info(drm_dev, "FW version v%u.%u (build %u OS)\n", header->fw_version_major,
header->fw_version_minor, header->fw_version_build);
pvr_dev->fw_version.major = header->fw_version_major;
pvr_dev->fw_version.minor = header->fw_version_minor;
pvr_dev->fw_dev.header = header;
pvr_dev->fw_dev.layout_entries = layout_entries;
return 0;
}
static int
pvr_fw_get_device_info(struct pvr_device *pvr_dev)
{
const struct firmware *firmware = pvr_dev->fw_dev.firmware;
struct pvr_fw_device_info_header *header;
const u8 *fw = firmware->data;
const u64 *dev_info;
u32 fw_offset;
fw_offset = (firmware->size - SZ_4K) - pvr_dev->fw_dev.header->device_info_size;
header = (struct pvr_fw_device_info_header *)&fw[fw_offset];
dev_info = (u64 *)(header + 1);
pvr_device_info_set_quirks(pvr_dev, dev_info, header->brn_mask_size);
dev_info += header->brn_mask_size;
pvr_device_info_set_enhancements(pvr_dev, dev_info, header->ern_mask_size);
dev_info += header->ern_mask_size;
return pvr_device_info_set_features(pvr_dev, dev_info, header->feature_mask_size,
header->feature_param_size);
}
/**
* pvr_fw_validate_init_device_info() - Validate firmware and initialise device information
* @pvr_dev: Target PowerVR device.
*
* This function must be called before querying device information.
*
* Returns:
* * 0 on success, or
* * -%EINVAL if firmware validation fails.
*/
int
pvr_fw_validate_init_device_info(struct pvr_device *pvr_dev)
{
int err;
err = pvr_fw_validate(pvr_dev);
if (err)
return err;
return pvr_fw_get_device_info(pvr_dev);
}
/* 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 <linux/types.h>
/* Forward declarations from "pvr_device.h". */
struct pvr_device;
struct pvr_file;
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;
/**
* @processor_type: FW processor type for this device. Must be one of
* %PVR_FW_PROCESSOR_TYPE_*.
*/
u16 processor_type;
};
int pvr_fw_validate_init_device_info(struct pvr_device *pvr_dev);
#endif /* PVR_FW_H */
/* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment