Re: [PATCH v3 07/17] drm/imagination: Add GPU ID parsing and firmware loading

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Jun 13, 2023 at 10:20 AM Sarah Walker <sarah.walker@xxxxxxxxxx> wrote:
>
> 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.

I have a Rogue 6250 variant, but the BVNC is returning a slightly
different revision than the firmware that's currently available, and
the older firmware for the vendor driver doesn't work with this new
code.

Linux responds with Unsupported BVNC: 4.45.2.58.  From what I can
tell, the closest available firmware is 4.40.2.51.

Will there be more firmware variants in the future or will there be
some options to build the firmware somehow?

adam




>
> 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.
>
> Signed-off-by: Sarah Walker <sarah.walker@xxxxxxxxxx>
> ---
>  drivers/gpu/drm/imagination/Makefile          |   1 +
>  drivers/gpu/drm/imagination/pvr_device.c      | 359 ++++++++++++
>  drivers/gpu/drm/imagination/pvr_device.h      | 221 +++++++
>  drivers/gpu/drm/imagination/pvr_device_info.c | 223 +++++++
>  drivers/gpu/drm/imagination/pvr_device_info.h | 133 +++++
>  drivers/gpu/drm/imagination/pvr_drv.c         | 553 +++++++++++++++++-
>  drivers/gpu/drm/imagination/pvr_drv.h         | 108 ++++
>  drivers/gpu/drm/imagination/pvr_fw.h          |  20 +
>  8 files changed, 1617 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/imagination/pvr_device_info.c
>  create mode 100644 drivers/gpu/drm/imagination/pvr_device_info.h
>  create mode 100644 drivers/gpu/drm/imagination/pvr_fw.h
>
> diff --git a/drivers/gpu/drm/imagination/Makefile b/drivers/gpu/drm/imagination/Makefile
> index 186f920d615b..d713b1280776 100644
> --- a/drivers/gpu/drm/imagination/Makefile
> +++ b/drivers/gpu/drm/imagination/Makefile
> @@ -5,6 +5,7 @@ subdir-ccflags-y := -I$(srctree)/$(src)
>
>  powervr-y := \
>         pvr_device.o \
> +       pvr_device_info.o \
>         pvr_drv.o \
>
>  obj-$(CONFIG_DRM_POWERVR) += powervr.o
> diff --git a/drivers/gpu/drm/imagination/pvr_device.c b/drivers/gpu/drm/imagination/pvr_device.c
> index 790c36cebec1..2e03763f2eb7 100644
> --- a/drivers/gpu/drm/imagination/pvr_device.c
> +++ b/drivers/gpu/drm/imagination/pvr_device.c
> @@ -2,20 +2,32 @@
>  /* Copyright (c) 2022 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/regulator/consumer.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
> @@ -205,6 +217,246 @@ pvr_device_regulator_init(struct pvr_device *pvr_dev)
>         return err;
>  }
>
> +/**
> + * pvr_device_clk_core_get_freq - Get current PowerVR device core clock frequency
> + * @pvr_dev: Target PowerVR device.
> + * @freq_out: Pointer to location to store core clock frequency in Hz.
> + *
> + * Returns:
> + *  * 0 on success, or
> + *  * -%EINVAL if frequency can not be determined.
> + */
> +int
> +pvr_device_clk_core_get_freq(struct pvr_device *pvr_dev, u32 *freq_out)
> +{
> +       u32 freq = clk_get_rate(pvr_dev->core_clk);
> +
> +       if (!freq)
> +               return -EINVAL;
> +
> +       *freq_out = freq;
> +       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);
> +}
> +
> +/**
> + * 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 0;
> +
> +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, 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, CORE_REVISION);
> +               u32 core_id = PVR_CR_READ32(pvr_dev, 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;
> +       }
> +
> +       /*
> +        * See the comment on &pvr_drm_driver.prime_fd_to_handle for an
> +        * explanation of the dma_set_mask function and dma_set_max_seg_size
> +        * calls below.
> +        */
> +       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_device_info_init(pvr_dev);
> +       if (err)
> +               goto err_out;
> +
> +       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 {
> +               err = -EINVAL;
> +               goto err_out;
> +       }
> +
> +       err = pvr_set_dma_info(pvr_dev);
> +       if (err)
> +               goto err_out;
> +
> +       err = pvr_request_firmware(pvr_dev);
> +       if (err)
> +               goto err_out;
> +
> +       return 0;
> +
> +err_out:
> +       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)
> +{
> +       release_firmware(pvr_dev->fw_dev.firmware);
> +}
> +
>  /**
>   * pvr_device_init() - Initialize a PowerVR device
>   * @pvr_dev: Target PowerVR device.
> @@ -240,6 +492,11 @@ pvr_device_init(struct pvr_device *pvr_dev)
>
>         /* Map the control registers into memory. */
>         err = pvr_device_reg_init(pvr_dev);
> +       if (err)
> +               goto err_device_clk_fini;
> +
> +       /* Perform GPU-specific initialization steps. */
> +       err = pvr_device_gpu_init(pvr_dev);
>         if (err)
>                 goto err_device_reg_fini;
>
> @@ -266,6 +523,108 @@ 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_gpu_fini(pvr_dev);
>         pvr_device_reg_fini(pvr_dev);
>         pvr_device_clk_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;
> +       }
> +}
> diff --git a/drivers/gpu/drm/imagination/pvr_device.h b/drivers/gpu/drm/imagination/pvr_device.h
> index c0dd0562844c..d9d0cf5a3820 100644
> --- a/drivers/gpu/drm/imagination/pvr_device.h
> +++ b/drivers/gpu/drm/imagination/pvr_device.h
> @@ -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>
> @@ -31,6 +34,26 @@ struct firmware;
>  /* Forward declaration from <linux/regulator/consumer.h>. */
>  struct regulator;
>
> +/**
> + * 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
>   */
> @@ -43,6 +66,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_resource: Resource representing device control registers. */
>         struct resource *regs_resource;
>
> @@ -65,6 +117,9 @@ struct pvr_device {
>
>         /** @regulator: Power regulator. */
>         struct regulator *regulator;
> +
> +       /** @fw_dev: Firmware related data. */
> +       struct pvr_fw_device fw_dev;
>  };
>
>  /**
> @@ -85,8 +140,79 @@ struct pvr_file {
>          *           to_pvr_device().
>          */
>         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)
> +
>  static __always_inline struct drm_device *
>  from_pvr_device(struct pvr_device *pvr_dev)
>  {
> @@ -111,12 +237,80 @@ to_pvr_file(struct drm_file *file)
>         return 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);
>
>  int
>  pvr_device_clk_core_get_freq(struct pvr_device *pvr_dev, u32 *freq_out);
>
> +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_READ32() - Read a 32-bit register from a PowerVR device
>   * @pvr_dev: Target PowerVR device.
> @@ -286,6 +480,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_in_bits = 0;
> +       u16 slc_cacheline_size_in_bytes;
> +
> +       WARN_ON(!PVR_HAS_FEATURE(pvr_dev, slc_cache_line_size_in_bits));
> +       PVR_FEATURE_VALUE(pvr_dev, slc_cache_line_size_in_bits,
> +                         &slc_cacheline_size_in_bits);
> +       slc_cacheline_size_in_bytes = slc_cacheline_size_in_bits / 8;
> +
> +       return round_up(size, slc_cacheline_size_in_bytes);
> +}
> +
>  /**
>   * DOC: IOCTL validation helpers
>   *
> @@ -380,4 +597,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 */
> diff --git a/drivers/gpu/drm/imagination/pvr_device_info.c b/drivers/gpu/drm/imagination/pvr_device_info.c
> new file mode 100644
> index 000000000000..660324e7c3a3
> --- /dev/null
> +++ b/drivers/gpu/drm/imagination/pvr_device_info.c
> @@ -0,0 +1,223 @@
> +// SPDX-License-Identifier: GPL-2.0 OR MIT
> +/* Copyright (c) 2022 Imagination Technologies Ltd. */
> +
> +#include "pvr_device.h"
> +#include "pvr_device_info.h"
> +
> +#include <drm/drm_print.h>
> +
> +#include <linux/types.h>
> +
> +static const struct pvr_device_features pvr_device_4_V_2_51 = {
> +       .has_cdm_control_stream_format = true,
> +       .has_cluster_grouping = true,
> +       .has_common_store_size_in_dwords = true,
> +       .has_compute = true,
> +       .has_compute_morton_capable = true,
> +       .has_compute_overlap = true,
> +       .has_fbcdc_algorithm = true,
> +       .has_isp_max_tiles_in_flight = true,
> +       .has_isp_samples_per_pixel = true,
> +       .has_max_partitions = true,
> +       .has_meta = true,
> +       .has_meta_coremem_size = true,
> +       .has_num_clusters = true,
> +       .has_num_isp_ipp_pipes = true,
> +       .has_num_raster_pipes = true,
> +       .has_phys_bus_width = true,
> +       .has_slc_cache_line_size_in_bits = true,
> +       .has_tile_size_x = true,
> +       .has_tile_size_y = true,
> +       .has_usc_min_output_registers_per_pix = true,
> +       .has_virtual_address_space_bits = true,
> +       .has_xt_top_infrastructure = true,
> +       .has_zls_subtile = true,
> +
> +       .cdm_control_stream_format = 1,
> +       .common_store_size_in_dwords = 1280U * 4U * 4U,
> +       .fbcdc_algorithm = 2,
> +       .isp_max_tiles_in_flight = 4,
> +       .isp_samples_per_pixel = 2,
> +       .max_partitions = 8,
> +       .meta = true,
> +       .meta_coremem_size = 32,
> +       .num_clusters = 2,
> +       .num_isp_ipp_pipes = 8,
> +       .num_raster_pipes = 1,
> +       .phys_bus_width = 40,
> +       .slc_cache_line_size_in_bits = 512,
> +       .tile_size_x = 32,
> +       .tile_size_y = 32,
> +       .usc_min_output_registers_per_pix = 2,
> +       .virtual_address_space_bits = 40,
> +};
> +
> +static const struct pvr_device_quirks pvr_device_quirks_4_40_2_51 = {
> +       .has_brn44079 = true,
> +       .has_brn48492 = true,
> +       .has_brn48545 = true,
> +       .has_brn49927 = true,
> +       .has_brn51764 = true,
> +       .has_brn52354 = true,
> +       .has_brn62269 = true,
> +       .has_brn63142 = true,
> +       .has_brn66011 = true,
> +};
> +
> +static const struct pvr_device_enhancements pvr_device_enhancements_4_40_2_51 = {
> +       .has_ern35421 = true,
> +       .has_ern38020 = true,
> +       .has_ern38748 = true,
> +       .has_ern42064 = true,
> +};
> +
> +static const struct pvr_device_features pvr_device_33_V_11_3 = {
> +       .has_cdm_control_stream_format = true,
> +       .has_common_store_size_in_dwords = true,
> +       .has_compute = true,
> +       .has_isp_max_tiles_in_flight = true,
> +       .has_isp_samples_per_pixel = true,
> +       .has_max_partitions = true,
> +       .has_mips = true,
> +       .has_num_clusters = true,
> +       .has_num_isp_ipp_pipes = true,
> +       .has_num_raster_pipes = true,
> +       .has_phys_bus_width = true,
> +       .has_roguexe = true,
> +       .has_simple_internal_parameter_format = true,
> +       .has_slc_cache_line_size_in_bits = true,
> +       .has_sys_bus_secure_reset = true,
> +       .has_tile_size_x = true,
> +       .has_tile_size_y = true,
> +       .has_usc_min_output_registers_per_pix = true,
> +       .has_virtual_address_space_bits = true,
> +       .has_xe_memory_hierarchy = true,
> +
> +       .cdm_control_stream_format = 1,
> +       .common_store_size_in_dwords = 512U * 4U * 4U,
> +       .isp_max_tiles_in_flight = 1,
> +       .isp_samples_per_pixel = 1,
> +       .max_partitions = 4,
> +       .mips = true,
> +       .num_clusters = 1,
> +       .num_isp_ipp_pipes = 1,
> +       .num_raster_pipes = 1,
> +       .phys_bus_width = 36,
> +       .simple_internal_parameter_format = 2,
> +       .slc_cache_line_size_in_bits = 512,
> +       .tile_size_x = 16,
> +       .tile_size_y = 16,
> +       .usc_min_output_registers_per_pix = 1,
> +       .virtual_address_space_bits = 40,
> +};
> +
> +static const struct pvr_device_quirks pvr_device_quirks_33_15_11_3 = {
> +       .has_brn63553 = true,
> +};
> +
> +static const struct pvr_device_enhancements pvr_device_enhancements_33_15_11_3 = {
> +       .has_ern35421 = true,
> +       .has_ern38748 = true,
> +};
> +
> +static const struct pvr_device_features pvr_device_36_V_104_796 = {
> +       .has_cdm_control_stream_format = true,
> +       .has_common_store_size_in_dwords = true,
> +       .has_compute = true,
> +       .has_compute_overlap = true,
> +       .has_fbcdc_algorithm = true,
> +       .has_gpu_multicore_support = true,
> +       .has_isp_max_tiles_in_flight = true,
> +       .has_isp_samples_per_pixel = true,
> +       .has_max_partitions = true,
> +       .has_num_clusters = true,
> +       .has_num_isp_ipp_pipes = true,
> +       .has_num_raster_pipes = true,
> +       .has_phys_bus_width = true,
> +       .has_riscv_fw_processor = true,
> +       .has_roguexe = true,
> +       .has_simple_internal_parameter_format = true,
> +       .has_slc_cache_line_size_in_bits = true,
> +       .has_sys_bus_secure_reset = true,
> +       .has_tile_size_x = true,
> +       .has_tile_size_y = true,
> +       .has_tpu_dm_global_registers = true,
> +       .has_usc_min_output_registers_per_pix = true,
> +       .has_virtual_address_space_bits = true,
> +       .has_xe_memory_hierarchy = true,
> +       .has_xpu_max_slaves = true,
> +
> +       .cdm_control_stream_format = 1,
> +       .common_store_size_in_dwords = 1344U * 4U * 4U,
> +       .fbcdc_algorithm = 50,
> +       .isp_max_tiles_in_flight = 6,
> +       .isp_samples_per_pixel = 4,
> +       .max_partitions = 16,
> +       .num_clusters = 1,
> +       .num_isp_ipp_pipes = 6,
> +       .num_raster_pipes = 1,
> +       .phys_bus_width = 36,
> +       .riscv_fw_processor = true,
> +       .simple_internal_parameter_format = 2,
> +       .slc_cache_line_size_in_bits = 512,
> +       .tile_size_x = 16,
> +       .tile_size_y = 16,
> +       .usc_min_output_registers_per_pix = 2,
> +       .virtual_address_space_bits = 40,
> +       .xpu_max_slaves = 3,
> +};
> +
> +static const struct pvr_device_quirks pvr_device_quirks_36_53_104_796 = {
> +       .has_brn44079 = true,
> +};
> +
> +static const struct pvr_device_enhancements pvr_device_enhancements_36_53_104_796 = {
> +       .has_ern35421 = true,
> +       .has_ern38748 = true,
> +};
> +
> +/**
> + * pvr_device_info_init() - Initialize a PowerVR device's hardware features and quirks
> + * @pvr_dev: Target PowerVR device.
> + *
> + * This function relies on &pvr_dev.gpu_id having already been initialized. If
> + * PowerVR device version is supported then sets &pvr_dev.features and &pvr_dev.quirks.
> + *
> + * Return:
> + *  * 0 on success, or
> + *  * -%ENODEV if the device is not supported.
> + */
> +int
> +pvr_device_info_init(struct pvr_device *pvr_dev)
> +{
> +       struct drm_device *drm_dev = from_pvr_device(pvr_dev);
> +       struct pvr_gpu_id *gpu_id = &pvr_dev->gpu_id;
> +       const u64 bvnc = pvr_gpu_id_to_packed_bvnc(gpu_id);
> +
> +       /*
> +        * This macro results in a "Macros with multiple statements should be
> +        * enclosed in a do - while loop" checkpatch error. However, following
> +        * this advice would make the macro look a bit odd and isn't necessary
> +        * in this particular case, as the macro has a very specific use and a
> +        * very limited lifetime. The error can therefore be ignored.
> +        */
> +#define CASE_PACKED_BVNC_DEVICE_INFO(b, v, n, c)                  \
> +       case PVR_PACKED_BVNC(b, v, n, c):                         \
> +               pvr_dev->features = pvr_device_##b##_V_##n##_##c; \
> +               pvr_dev->quirks = pvr_device_quirks_##b##_##v##_##n##_##c; \
> +               pvr_dev->enhancements = pvr_device_enhancements_##b##_##v##_##n##_##c; \
> +               return 0
> +
> +       switch (bvnc) {
> +               CASE_PACKED_BVNC_DEVICE_INFO(4, 40, 2, 51);
> +               CASE_PACKED_BVNC_DEVICE_INFO(33, 15, 11, 3);
> +               CASE_PACKED_BVNC_DEVICE_INFO(36, 53, 104, 796);
> +       }
> +
> +#undef CASE_PACKED_BVNC_DEVICE_INFO
> +
> +       drm_warn(drm_dev, "Unsupported BVNC: %u.%u.%u.%u\n", gpu_id->b,
> +                gpu_id->v, gpu_id->n, gpu_id->c);
> +
> +       return -ENODEV;
> +}
> diff --git a/drivers/gpu/drm/imagination/pvr_device_info.h b/drivers/gpu/drm/imagination/pvr_device_info.h
> new file mode 100644
> index 000000000000..36ad09dea420
> --- /dev/null
> +++ b/drivers/gpu/drm/imagination/pvr_device_info.h
> @@ -0,0 +1,133 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR MIT */
> +/* Copyright (c) 2022 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_cdm_control_stream_format : 1;
> +       bool has_cluster_grouping : 1;
> +       bool has_common_store_size_in_dwords : 1;
> +       bool has_compute : 1;
> +       bool has_compute_morton_capable : 1;
> +       bool has_compute_overlap : 1;
> +       bool has_fb_cdc_v4 : 1;
> +       bool has_fbcdc_algorithm : 1;
> +       bool has_gpu_multicore_support : 1;
> +       bool has_isp_max_tiles_in_flight : 1;
> +       bool has_isp_samples_per_pixel : 1;
> +       bool has_isp_zls_d24_s8_packing_ogl_mode : 1;
> +       bool has_max_partitions : 1;
> +       bool has_meta : 1;
> +       bool has_meta_coremem_size : 1;
> +       bool has_mips : 1;
> +       bool has_num_clusters : 1;
> +       bool has_num_isp_ipp_pipes : 1;
> +       bool has_num_raster_pipes : 1;
> +       bool has_phys_bus_width : 1;
> +       bool has_riscv_fw_processor : 1;
> +       bool has_roguexe : 1;
> +       bool has_s7_top_infrastructure : 1;
> +       bool has_simple_internal_parameter_format : 1;
> +       bool has_slc_cache_line_size_in_bits : 1;
> +       bool has_sys_bus_secure_reset : 1;
> +       bool has_tessellation : 1;
> +       bool has_tile_size_x : 1;
> +       bool has_tile_size_y : 1;
> +       bool has_tpu_dm_global_registers : 1;
> +       bool has_usc_min_output_registers_per_pix : 1;
> +       bool has_vdm_drawindirect : 1;
> +       bool has_vdm_object_level_lls : 1;
> +       bool has_virtual_address_space_bits : 1;
> +       bool has_xe_memory_hierarchy : 1;
> +       bool has_xpu_max_slaves : 1;
> +       bool has_xt_top_infrastructure : 1;
> +       bool has_zls_subtile : 1;
> +
> +       u8 cdm_control_stream_format;
> +       u32 common_store_size_in_dwords;
> +       u8 fbcdc_algorithm;
> +       u16 isp_max_tiles_in_flight;
> +       bool isp_samples_per_pixel;
> +       u16 max_partitions;
> +       bool meta;
> +       u32 meta_coremem_size;
> +       bool mips;
> +       u16 num_clusters;
> +       u8 num_isp_ipp_pipes;
> +       u8 num_raster_pipes;
> +       u16 phys_bus_width;
> +       bool riscv_fw_processor;
> +       u32 simple_internal_parameter_format;
> +       u16 slc_cache_line_size_in_bits;
> +       u16 tile_size_x;
> +       u16 tile_size_y;
> +       u16 usc_min_output_registers_per_pix;
> +       u16 virtual_address_space_bits;
> +       u8 xpu_max_slaves;
> +};
> +
> +/*
> + * struct pvr_device_quirks - Hardware quirk information
> + */
> +struct pvr_device_quirks {
> +       bool has_brn44079 : 1;
> +       bool has_brn47217 : 1;
> +       bool has_brn48492 : 1;
> +       bool has_brn48545 : 1;
> +       bool has_brn49927 : 1;
> +       bool has_brn51764 : 1;
> +       bool has_brn52354 : 1;
> +       bool has_brn62269 : 1;
> +       bool has_brn63142 : 1;
> +       bool has_brn63553 : 1;
> +       bool has_brn66011 : 1;
> +};
> +
> +/*
> + * struct pvr_device_enhancements - Hardware enhancement information
> + */
> +struct pvr_device_enhancements {
> +       bool has_ern35421 : 1;
> +       bool has_ern38020 : 1;
> +       bool has_ern38748 : 1;
> +       bool has_ern42064 : 1;
> +};
> +
> +int pvr_device_info_init(struct pvr_device *pvr_dev);
> +
> +/*
> + * 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 */
> diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c
> index 48a870715426..5ebdea49c3be 100644
> --- a/drivers/gpu/drm/imagination/pvr_drv.c
> +++ b/drivers/gpu/drm/imagination/pvr_drv.c
> @@ -3,6 +3,9 @@
>
>  #include "pvr_device.h"
>  #include "pvr_drv.h"
> +#include "pvr_rogue_defs.h"
> +#include "pvr_rogue_fwif_client.h"
> +#include "pvr_rogue_fwif_shared.h"
>
>  #include <uapi/drm/pvr_drm.h>
>
> @@ -88,6 +91,422 @@ pvr_ioctl_get_bo_mmap_offset(__always_unused struct drm_device *drm_dev,
>         return -ENOTTY;
>  }
>
> +static __always_inline u64
> +pvr_fw_version_packed(u32 major, u32 minor)
> +{
> +       return ((u64)major << 32) | minor;
> +}
> +
> +static u32
> +rogue_get_common_store_partition_space_size(struct pvr_device *pvr_dev)
> +{
> +       u32 max_partitions = 0;
> +       u32 tile_size_x = 0;
> +       u32 tile_size_y = 0;
> +
> +       PVR_FEATURE_VALUE(pvr_dev, tile_size_x, &tile_size_x);
> +       PVR_FEATURE_VALUE(pvr_dev, tile_size_y, &tile_size_y);
> +       PVR_FEATURE_VALUE(pvr_dev, max_partitions, &max_partitions);
> +
> +       if (tile_size_x == 16 && tile_size_y == 16) {
> +               u32 usc_min_output_registers_per_pix = 0;
> +
> +               PVR_FEATURE_VALUE(pvr_dev, usc_min_output_registers_per_pix,
> +                                 &usc_min_output_registers_per_pix);
> +
> +               return tile_size_x * tile_size_y * max_partitions *
> +                      usc_min_output_registers_per_pix;
> +       }
> +
> +       return max_partitions * 1024;
> +}
> +
> +static u32
> +rogue_get_common_store_alloc_region_size(struct pvr_device *pvr_dev)
> +{
> +       u32 common_store_size_in_dwords = 512 * 4 * 4;
> +       u32 alloc_region_size;
> +
> +       PVR_FEATURE_VALUE(pvr_dev, common_store_size_in_dwords, &common_store_size_in_dwords);
> +
> +       alloc_region_size = common_store_size_in_dwords - (256U * 4U) -
> +                           rogue_get_common_store_partition_space_size(pvr_dev);
> +
> +       if (PVR_HAS_QUIRK(pvr_dev, 44079)) {
> +               u32 common_store_split_point = (768U * 4U * 4U);
> +
> +               return min(common_store_split_point - (256U * 4U), alloc_region_size);
> +       }
> +
> +       return alloc_region_size;
> +}
> +
> +static inline u32
> +rogue_get_num_phantoms(struct pvr_device *pvr_dev)
> +{
> +       u32 num_clusters = 1;
> +
> +       PVR_FEATURE_VALUE(pvr_dev, num_clusters, &num_clusters);
> +
> +       return ROGUE_REQ_NUM_PHANTOMS(num_clusters);
> +}
> +
> +static inline u32
> +rogue_get_max_coeffs(struct pvr_device *pvr_dev)
> +{
> +       u32 max_coeff_additional_portion = ROGUE_MAX_VERTEX_SHARED_REGISTERS;
> +       u32 pending_allocation_shared_regs = 2U * 1024U;
> +       u32 pending_allocation_coeff_regs = 0U;
> +       u32 num_phantoms = rogue_get_num_phantoms(pvr_dev);
> +       u32 tiles_in_flight = 0;
> +       u32 max_coeff_pixel_portion;
> +
> +       PVR_FEATURE_VALUE(pvr_dev, isp_max_tiles_in_flight, &tiles_in_flight);
> +       max_coeff_pixel_portion = DIV_ROUND_UP(tiles_in_flight, num_phantoms);
> +       max_coeff_pixel_portion *= ROGUE_MAX_PIXEL_SHARED_REGISTERS;
> +
> +       /*
> +        * Compute tasks on cores with BRN48492 and without compute overlap may lock
> +        * up without two additional lines of coeffs.
> +        */
> +       if (PVR_HAS_QUIRK(pvr_dev, 48492) && !PVR_HAS_FEATURE(pvr_dev, compute_overlap))
> +               pending_allocation_coeff_regs = 2U * 1024U;
> +
> +       if (PVR_HAS_ENHANCEMENT(pvr_dev, 38748))
> +               pending_allocation_shared_regs = 0;
> +
> +       if (PVR_HAS_ENHANCEMENT(pvr_dev, 38020))
> +               max_coeff_additional_portion += ROGUE_MAX_COMPUTE_SHARED_REGISTERS;
> +
> +       return rogue_get_common_store_alloc_region_size(pvr_dev) + pending_allocation_coeff_regs -
> +               (max_coeff_pixel_portion + max_coeff_additional_portion +
> +                pending_allocation_shared_regs);
> +}
> +
> +static inline u32
> +rogue_get_cdm_max_local_mem_size_regs(struct pvr_device *pvr_dev)
> +{
> +       u32 available_coeffs_in_dwords = rogue_get_max_coeffs(pvr_dev);
> +
> +       if (PVR_HAS_QUIRK(pvr_dev, 48492) && PVR_HAS_FEATURE(pvr_dev, roguexe) &&
> +           !PVR_HAS_FEATURE(pvr_dev, compute_overlap)) {
> +               /* Driver must not use the 2 reserved lines. */
> +               available_coeffs_in_dwords -= ROGUE_CSRM_LINE_SIZE_IN_DWORDS * 2;
> +       }
> +
> +       /*
> +        * The maximum amount of local memory available to a kernel is the minimum
> +        * of the total number of coefficient registers available and the max common
> +        * store allocation size which can be made by the CDM.
> +        *
> +        * If any coeff lines are reserved for tessellation or pixel then we need to
> +        * subtract those too.
> +        */
> +       return min(available_coeffs_in_dwords, (u32)ROGUE_MAX_PER_KERNEL_LOCAL_MEM_SIZE_REGS);
> +}
> +
> +/**
> + * pvr_dev_query_gpu_info_get()
> + * @pvr_dev: Device pointer.
> + * @args: [IN] Device query arguments containing a pointer to a userspace
> + *        struct drm_pvr_dev_query_gpu_info.
> + *
> + * If the query object pointer is NULL, the size field is updated with the
> + * expected size of the query object.
> + *
> + * Returns:
> + *  * 0 on success, or if size is requested using a NULL pointer, or
> + *  * -%E2BIG if the indicated length of the allocation is less than is
> + *    required to contain the copied data, or
> + *  * -%EFAULT if local memory could not be copied to userspace.
> + */
> +static int
> +pvr_dev_query_gpu_info_get(struct pvr_device *pvr_dev,
> +                          struct drm_pvr_ioctl_dev_query_args *args)
> +{
> +       struct drm_pvr_dev_query_gpu_info gpu_info = {0};
> +       int err;
> +
> +       if (!args->pointer) {
> +               args->size = sizeof(struct drm_pvr_dev_query_gpu_info);
> +               return 0;
> +       }
> +
> +       gpu_info.gpu_id =
> +               pvr_gpu_id_to_packed_bvnc(&pvr_dev->gpu_id);
> +       gpu_info.num_phantoms = rogue_get_num_phantoms(pvr_dev);
> +
> +       err = PVR_UOBJ_SET(args->pointer, args->size, gpu_info);
> +       if (err < 0)
> +               return err;
> +
> +       if (args->size > sizeof(gpu_info))
> +               args->size = sizeof(gpu_info);
> +       return 0;
> +}
> +
> +/**
> + * pvr_dev_query_runtime_info_get()
> + * @pvr_dev: Device pointer.
> + * @args: [IN] Device query arguments containing a pointer to a userspace
> + *        struct drm_pvr_dev_query_runtime_info.
> + *
> + * If the query object pointer is NULL, the size field is updated with the
> + * expected size of the query object.
> + *
> + * Returns:
> + *  * 0 on success, or if size is requested using a NULL pointer, or
> + *  * -%E2BIG if the indicated length of the allocation is less than is
> + *    required to contain the copied data, or
> + *  * -%EFAULT if local memory could not be copied to userspace.
> + */
> +static int
> +pvr_dev_query_runtime_info_get(struct pvr_device *pvr_dev,
> +                              struct drm_pvr_ioctl_dev_query_args *args)
> +{
> +       struct drm_pvr_dev_query_runtime_info runtime_info = {0};
> +       int err;
> +
> +       if (!args->pointer) {
> +               args->size = sizeof(struct drm_pvr_dev_query_runtime_info);
> +               return 0;
> +       }
> +
> +       runtime_info.free_list_min_pages = 0; /* FIXME */
> +       runtime_info.free_list_max_pages =
> +               ROGUE_PM_MAX_FREELIST_SIZE / ROGUE_PM_PAGE_SIZE;
> +       runtime_info.common_store_alloc_region_size =
> +               rogue_get_common_store_alloc_region_size(pvr_dev);
> +       runtime_info.common_store_partition_space_size =
> +               rogue_get_common_store_partition_space_size(pvr_dev);
> +       runtime_info.max_coeffs = rogue_get_max_coeffs(pvr_dev);
> +       runtime_info.cdm_max_local_mem_size_regs =
> +               rogue_get_cdm_max_local_mem_size_regs(pvr_dev);
> +
> +       err = PVR_UOBJ_SET(args->pointer, args->size, runtime_info);
> +       if (err < 0)
> +               return err;
> +
> +       if (args->size > sizeof(runtime_info))
> +               args->size = sizeof(runtime_info);
> +       return 0;
> +}
> +
> +/**
> + * pvr_dev_query_hwrt_info_get()
> + * @pvr_dev: Device pointer.
> + * @args: [IN] Device query arguments containing a pointer to a userspace
> + *        struct drm_pvr_dev_query_hwrt_info.
> + *
> + * If the query object pointer is NULL, the size field is updated with the
> + * expected size of the query object.
> + *
> + * Returns:
> + *  * 0 on success, or if size is requested using a NULL pointer, or
> + *  * -%E2BIG if the indicated length of the allocation is less than is
> + *    required to contain the copied data, or
> + *  * -%EFAULT if local memory could not be copied to userspace.
> + */
> +static int
> +pvr_dev_query_hwrt_info_get(struct pvr_device *pvr_dev,
> +                           struct drm_pvr_ioctl_dev_query_args *args)
> +{
> +       struct drm_pvr_dev_query_hwrt_info hwrt_info = {0};
> +       int err;
> +
> +       if (!args->pointer) {
> +               args->size = sizeof(struct drm_pvr_dev_query_hwrt_info);
> +               return 0;
> +       }
> +
> +       hwrt_info.num_geomdatas = ROGUE_FWIF_NUM_GEOMDATAS;
> +       hwrt_info.num_rtdatas = ROGUE_FWIF_NUM_RTDATAS;
> +       hwrt_info.num_freelists = ROGUE_FWIF_NUM_RTDATA_FREELISTS;
> +
> +       err = PVR_UOBJ_SET(args->pointer, args->size, hwrt_info);
> +       if (err < 0)
> +               return err;
> +
> +       if (args->size > sizeof(hwrt_info))
> +               args->size = sizeof(hwrt_info);
> +       return 0;
> +}
> +
> +/**
> + * pvr_dev_query_quirks_get() - Unpack array of quirks at the address given
> + * in a struct drm_pvr_dev_query_quirks, or gets the amount of space required
> + * for it.
> + * @pvr_dev: Device pointer.
> + * @args: [IN] Device query arguments containing a pointer to a userspace
> + *        struct drm_pvr_dev_query_query_quirks.
> + *
> + * If the query object pointer is NULL, the size field is updated with the
> + * expected size of the query object.
> + * If the userspace pointer in the query object is NULL, or the count is
> + * short, no data is copied.
> + * The count field will be updated to that copied, or if either pointer is
> + * NULL, that which would have been copied.
> + * The size field in the query object will be updated to the size copied.
> + *
> + * Returns:
> + *  * 0 on success, or if size/count is requested using a NULL pointer, or
> + *  * -%EINVAL if args contained non-zero reserved fields, or
> + *  * -%E2BIG if the indicated length of the allocation is less than is
> + *    required to contain the copied data, or
> + *  * -%EFAULT if local memory could not be copied to userspace.
> + */
> +static int
> +pvr_dev_query_quirks_get(struct pvr_device *pvr_dev,
> +                        struct drm_pvr_ioctl_dev_query_args *args)
> +{
> +       /*
> +        * @FIXME - hardcoding of numbers here is intended as an
> +        * intermediate step so the UAPI can be fixed, but requires a
> +        * a refactor in the future to store them in a more appropriate
> +        * location
> +        */
> +       static const u32 umd_quirks_musthave[] = {
> +               47217,
> +               49927,
> +               62269,
> +       };
> +       static const u32 umd_quirks[] = {
> +               48545,
> +               51764,
> +       };
> +       struct drm_pvr_dev_query_quirks query;
> +       u32 out[ARRAY_SIZE(umd_quirks_musthave) + ARRAY_SIZE(umd_quirks)];
> +       size_t out_musthave_count = 0;
> +       size_t out_count = 0;
> +       int err;
> +
> +       if (!args->pointer) {
> +               args->size = sizeof(struct drm_pvr_dev_query_quirks);
> +               return 0;
> +       }
> +
> +       err = PVR_UOBJ_GET(query, args->size, args->pointer);
> +
> +       if (err < 0)
> +               return err;
> +       if (query._padding_c)
> +               return -EINVAL;
> +
> +       for (int i = 0; i < ARRAY_SIZE(umd_quirks_musthave); i++) {
> +               if (pvr_device_has_uapi_quirk(pvr_dev, umd_quirks_musthave[i])) {
> +                       out[out_count++] = umd_quirks_musthave[i];
> +                       out_musthave_count++;
> +               }
> +       }
> +
> +       for (int i = 0; i < ARRAY_SIZE(umd_quirks); i++) {
> +               if (pvr_device_has_uapi_quirk(pvr_dev, umd_quirks[i]))
> +                       out[out_count++] = umd_quirks[i];
> +       }
> +
> +       if (!query.quirks)
> +               goto copy_out;
> +       if (query.count < out_count)
> +               return -E2BIG;
> +
> +       if (copy_to_user(u64_to_user_ptr(query.quirks), out,
> +                        out_count * sizeof(u32))) {
> +               return -EFAULT;
> +       }
> +
> +       query.musthave_count = out_musthave_count;
> +
> +copy_out:
> +       query.count = out_count;
> +       err = PVR_UOBJ_SET(args->pointer, args->size, query);
> +       if (err < 0)
> +               return err;
> +
> +       args->size = sizeof(query);
> +       return 0;
> +}
> +
> +/**
> + * pvr_dev_query_enhancements_get() - Unpack array of enhancements at the
> + * address given in a struct drm_pvr_dev_query_enhancements, or gets the amount
> + * of space required for it.
> + * @pvr_dev: Device pointer.
> + * @args: [IN] Device query arguments containing a pointer to a userspace
> + *        struct drm_pvr_dev_query_enhancements.
> + *
> + * If the query object pointer is NULL, the size field is updated with the
> + * expected size of the query object.
> + * If the userspace pointer in the query object is NULL, or the count is
> + * short, no data is copied.
> + * The count field will be updated to that copied, or if either pointer is
> + * NULL, that which would have been copied.
> + * The size field in the query object will be updated to the size copied.
> + *
> + * Returns:
> + *  * 0 on success, or if size/count is requested using a NULL pointer, or
> + *  * -%EINVAL if args contained non-zero reserved fields, or
> + *  * -%E2BIG if the indicated length of the allocation is less than is
> + *    required to contain the copied data, or
> + *  * -%EFAULT if local memory could not be copied to userspace.
> + */
> +static int
> +pvr_dev_query_enhancements_get(struct pvr_device *pvr_dev,
> +                              struct drm_pvr_ioctl_dev_query_args *args)
> +{
> +       /*
> +        * @FIXME - hardcoding of numbers here is intended as an
> +        * intermediate step so the UAPI can be fixed, but requires a
> +        * a refactor in the future to store them in a more appropriate
> +        * location
> +        */
> +       const u32 umd_enhancements[] = {
> +               35421,
> +               42064,
> +       };
> +       struct drm_pvr_dev_query_enhancements query;
> +       u32 out[ARRAY_SIZE(umd_enhancements)];
> +       size_t out_idx = 0;
> +       int err;
> +
> +       if (!args->pointer) {
> +               args->size = sizeof(struct drm_pvr_dev_query_enhancements);
> +               return 0;
> +       }
> +
> +       err = PVR_UOBJ_GET(query, args->size, args->pointer);
> +
> +       if (err < 0)
> +               return err;
> +       if (query._padding_a)
> +               return -EINVAL;
> +       if (query._padding_c)
> +               return -EINVAL;
> +
> +       for (int i = 0; i < ARRAY_SIZE(umd_enhancements); i++) {
> +               if (pvr_device_has_uapi_enhancement(pvr_dev, umd_enhancements[i]))
> +                       out[out_idx++] = umd_enhancements[i];
> +       }
> +
> +       if (!query.enhancements)
> +               goto copy_out;
> +       if (query.count < out_idx)
> +               return -E2BIG;
> +
> +       if (copy_to_user(u64_to_user_ptr(query.enhancements), out,
> +                        out_idx * sizeof(u32))) {
> +               return -EFAULT;
> +       }
> +
> +copy_out:
> +       query.count = out_idx;
> +       err = PVR_UOBJ_SET(args->pointer, args->size, query);
> +       if (err < 0)
> +               return err;
> +
> +       args->size = sizeof(query);
> +       return 0;
> +}
> +
>  /**
>   * pvr_ioctl_dev_query() - IOCTL to copy information about a device
>   * @drm_dev: [IN] DRM device.
> @@ -112,7 +531,33 @@ static int
>  pvr_ioctl_dev_query(struct drm_device *drm_dev, void *raw_args,
>                     struct drm_file *file)
>  {
> -       return -ENOTTY;
> +       struct pvr_device *pvr_dev = to_pvr_device(drm_dev);
> +       struct drm_pvr_ioctl_dev_query_args *args = raw_args;
> +
> +       switch ((enum drm_pvr_dev_query)args->type) {
> +       case DRM_PVR_DEV_QUERY_GPU_INFO_GET:
> +               return pvr_dev_query_gpu_info_get(pvr_dev, args);
> +
> +       case DRM_PVR_DEV_QUERY_RUNTIME_INFO_GET:
> +               return pvr_dev_query_runtime_info_get(pvr_dev, args);
> +
> +       case DRM_PVR_DEV_QUERY_HWRT_INFO_GET:
> +               return pvr_dev_query_hwrt_info_get(pvr_dev, args);
> +
> +       case DRM_PVR_DEV_QUERY_QUIRKS_GET:
> +               return pvr_dev_query_quirks_get(pvr_dev, args);
> +
> +       case DRM_PVR_DEV_QUERY_ENHANCEMENTS_GET:
> +               return pvr_dev_query_enhancements_get(pvr_dev, args);
> +
> +       case DRM_PVR_DEV_QUERY_HEAP_INFO_GET:
> +               return -EINVAL;
> +
> +       case DRM_PVR_DEV_QUERY_STATIC_DATA_AREAS_GET:
> +               return -EINVAL;
> +       }
> +
> +       return -EINVAL;
>  }
>
>  /**
> @@ -350,6 +795,112 @@ pvr_ioctl_submit_jobs(struct drm_device *drm_dev, void *raw_args,
>         return -ENOTTY;
>  }
>
> +int
> +pvr_get_uobj(u64 usr_ptr, u32 usr_stride, u32 min_stride, u32 obj_size, void *out)
> +{
> +       if (usr_stride < min_stride)
> +               return -EINVAL;
> +
> +       return copy_struct_from_user(out, obj_size, u64_to_user_ptr(usr_ptr), usr_stride);
> +}
> +
> +int
> +pvr_set_uobj(u64 usr_ptr, u32 usr_stride, u32 min_stride, u32 obj_size, const void *in)
> +{
> +       if (usr_stride < min_stride)
> +               return -EINVAL;
> +
> +       if (copy_to_user(u64_to_user_ptr(usr_ptr), in, min_t(u32, usr_stride, obj_size)))
> +               return -EFAULT;
> +
> +       if (usr_stride > obj_size &&
> +           clear_user(u64_to_user_ptr(usr_ptr + obj_size), usr_stride - obj_size)) {
> +               return -EFAULT;
> +       }
> +
> +       return 0;
> +}
> +
> +int
> +pvr_get_uobj_array(const struct drm_pvr_obj_array *in, u32 min_stride, u32 obj_size, void **out)
> +{
> +       int ret = 0;
> +       void *out_alloc;
> +
> +       if (in->stride < min_stride)
> +               return -EINVAL;
> +
> +       if (!in->count)
> +               return 0;
> +
> +       out_alloc = kvmalloc_array(in->count, obj_size, GFP_KERNEL);
> +       if (!out_alloc)
> +               return -ENOMEM;
> +
> +       if (obj_size == in->stride) {
> +               if (copy_from_user(out_alloc, u64_to_user_ptr(in->array),
> +                                  (unsigned long)obj_size * in->count))
> +                       ret = -EFAULT;
> +       } else {
> +               void __user *in_ptr = u64_to_user_ptr(in->array);
> +               void *out_ptr = out_alloc;
> +
> +               for (u32 i = 0; i < in->count; i++) {
> +                       ret = copy_struct_from_user(out_ptr, obj_size, in_ptr, in->stride);
> +                       if (ret)
> +                               break;
> +
> +                       out_ptr += obj_size;
> +                       in_ptr += in->stride;
> +               }
> +       }
> +
> +       if (ret) {
> +               kvfree(out_alloc);
> +               return ret;
> +       }
> +
> +       *out = out_alloc;
> +       return 0;
> +}
> +
> +int
> +pvr_set_uobj_array(const struct drm_pvr_obj_array *out, u32 min_stride, u32 obj_size,
> +                  const void *in)
> +{
> +       if (out->stride < min_stride)
> +               return -EINVAL;
> +
> +       if (!out->count)
> +               return 0;
> +
> +       if (obj_size == out->stride) {
> +               if (copy_to_user(u64_to_user_ptr(out->array), in,
> +                                (unsigned long)obj_size * out->count))
> +                       return -EFAULT;
> +       } else {
> +               u32 cpy_elem_size = min_t(u32, out->stride, obj_size);
> +               void __user *out_ptr = u64_to_user_ptr(out->array);
> +               const void *in_ptr = in;
> +
> +               for (u32 i = 0; i < out->count; i++) {
> +                       if (copy_to_user(out_ptr, in_ptr, cpy_elem_size))
> +                               return -EFAULT;
> +
> +                       out_ptr += obj_size;
> +                       in_ptr += out->stride;
> +               }
> +
> +               if (out->stride > obj_size &&
> +                   clear_user(u64_to_user_ptr(out->array + obj_size),
> +                              out->stride - obj_size)) {
> +                       return -EFAULT;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
>  #define DRM_PVR_IOCTL(_name, _func, _flags) \
>         DRM_IOCTL_DEF_DRV(PVR_##_name, pvr_ioctl_##_func, _flags)
>
> diff --git a/drivers/gpu/drm/imagination/pvr_drv.h b/drivers/gpu/drm/imagination/pvr_drv.h
> index 8e6f4a4dde3f..febdf1f09571 100644
> --- a/drivers/gpu/drm/imagination/pvr_drv.h
> +++ b/drivers/gpu/drm/imagination/pvr_drv.h
> @@ -19,4 +19,112 @@
>  #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_hwrt_info, _padding_4) \
> +       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 */
> diff --git a/drivers/gpu/drm/imagination/pvr_fw.h b/drivers/gpu/drm/imagination/pvr_fw.h
> new file mode 100644
> index 000000000000..ba3bfd41867f
> --- /dev/null
> +++ b/drivers/gpu/drm/imagination/pvr_fw.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR MIT */
> +/* Copyright (c) 2022 Imagination Technologies Ltd. */
> +
> +#ifndef PVR_FW_H
> +#define PVR_FW_H
> +
> +#include <linux/firmware.h>
> +
> +struct pvr_fw_device {
> +       /** @firmware: Handle to the firmware loaded into the device. */
> +       const struct firmware *firmware;
> +
> +       /*
> +        * @processor_type: FW processor type for this device. Must be one of
> +        *                  %PVR_FW_PROCESSOR_TYPE_*.
> +        */
> +       u16 processor_type;
> +};
> +
> +#endif /* PVR_FW_H */
> --
> 2.40.1
>




[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux