@@ -621,3 +621,29 @@ pvr_device_has_feature(struct pvr_device *pvr_dev, u32 feature)
return false;
}
}
+
+/**
+ * pvr_device_overrides_validate() - Ensure the overrides specified are compatible with a device.
+ * @pvr_dev: Target PowerVR device.
+ * @overrides: Device overrides to validate.
+ *
+ * Return:
+ * * %true if every override specified in @overrides is compatible with the current device, or
+ * * %false otherwise; as many incompatibilities as possible will be reported in the kernel log.
+ */
+bool
+pvr_device_overrides_validate(struct pvr_device *pvr_dev,
+ const struct pvr_device_overrides *overrides)
+{
+ bool ret = true;
+
+ /*
+ * Where possible, avoid early returns in this function. This allows for as
+ * many errors to be reported at once as possible.
+ *
+ * Note that this function may be called early during device initialization
+ * so it should not be assumed that @pvr_dev is ready for normal use yet.
+ */
+
+ return ret;
+}
@@ -16,6 +16,7 @@
#include <drm/drm_mm.h>
#include <linux/bits.h>
+#include <linux/compiler.h>
#include <linux/compiler_attributes.h>
#include <linux/compiler_types.h>
#include <linux/device.h>
@@ -56,6 +57,13 @@ struct pvr_fw_version {
u16 major, minor;
};
+/**
+ * struct pvr_device_overrides - Hardware-level overrides loaded from
+ * MODULE_DEVICE_TABLE() or similar.
+ */
+struct pvr_device_overrides {
+};
+
/**
* struct pvr_device - powervr-specific wrapper for &struct drm_device
*/
@@ -94,6 +102,13 @@ struct pvr_device {
*/
struct pvr_device_enhancements enhancements;
+ /**
+ * @overrides: Platform-specific overrides required for this device.
+ *
+ * Do not access this member directly, instead use PVR_HAS_OVERRIDE().
+ */
+ struct pvr_device_overrides overrides;
+
/** @fw_version: Firmware version detected at runtime. */
struct pvr_fw_version fw_version;
@@ -436,6 +451,13 @@ struct pvr_file {
*/
#define PVR_HAS_ENHANCEMENT(pvr_dev, enhancement) ((pvr_dev)->enhancements.has_ern##enhancement)
+/**
+ * PVR_HAS_OVERRIDE() - Tests whether a physical device requires a given override
+ * @pvr_dev: [IN] Target PowerVR device.
+ * @override: [IN] Override name.
+ */
+#define PVR_HAS_OVERRIDE(pvr_dev, override) unlikely((pvr_dev)->overrides.override)
+
#define from_pvr_device(pvr_dev) (&(pvr_dev)->base)
#define to_pvr_device(drm_dev) container_of_const(drm_dev, struct pvr_device, base)
@@ -516,6 +538,10 @@ pvr_device_has_uapi_enhancement(struct pvr_device *pvr_dev, u32 enhancement);
bool
pvr_device_has_feature(struct pvr_device *pvr_dev, u32 feature);
+bool
+pvr_device_overrides_validate(struct pvr_device *pvr_dev,
+ const struct pvr_device_overrides *overrides);
+
/**
* PVR_CR_FIELD_GET() - Extract a single field from a PowerVR control register
* @val: Value of the target register.
@@ -31,6 +31,7 @@
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
@@ -1396,11 +1397,13 @@ static struct drm_driver pvr_drm_driver = {
static int
pvr_probe(struct platform_device *plat_dev)
{
+ const struct pvr_device_overrides *overrides;
+ struct device *dev = &plat_dev->dev;
struct pvr_device *pvr_dev;
struct drm_device *drm_dev;
int err;
- pvr_dev = devm_drm_dev_alloc(&plat_dev->dev, &pvr_drm_driver,
+ pvr_dev = devm_drm_dev_alloc(dev, &pvr_drm_driver,
struct pvr_device, base);
if (IS_ERR(pvr_dev))
return PTR_ERR(pvr_dev);
@@ -1409,6 +1412,15 @@ pvr_probe(struct platform_device *plat_dev)
platform_set_drvdata(plat_dev, drm_dev);
+ overrides = of_device_get_match_data(dev);
+ if (!overrides)
+ return -EINVAL;
+
+ if (!pvr_device_overrides_validate(pvr_dev, overrides))
+ return -EINVAL;
+
+ pvr_dev->overrides = *overrides;
+
err = pvr_power_domains_init(pvr_dev);
if (err)
goto err_context_fini;
@@ -1421,11 +1433,11 @@ pvr_probe(struct platform_device *plat_dev)
if (err)
goto err_power_domains_fini;
- devm_pm_runtime_enable(&plat_dev->dev);
- pm_runtime_mark_last_busy(&plat_dev->dev);
+ devm_pm_runtime_enable(dev);
+ pm_runtime_mark_last_busy(dev);
- pm_runtime_set_autosuspend_delay(&plat_dev->dev, 50);
- pm_runtime_use_autosuspend(&plat_dev->dev);
+ pm_runtime_set_autosuspend_delay(dev, 50);
+ pm_runtime_use_autosuspend(dev);
pvr_watchdog_init(pvr_dev);
err = pvr_device_init(pvr_dev);
@@ -1478,18 +1490,24 @@ static void pvr_remove(struct platform_device *plat_dev)
pvr_power_domains_fini(pvr_dev);
}
+static const struct pvr_device_overrides pvr_device_overrides_default = {};
+
+/*
+ * Always specify &pvr_device_overrides_default instead of %NULL for &struct of_device_id->data so
+ * that we know of_device_get_match_data() returning %NULL is an error.
+ */
static const struct of_device_id dt_match[] = {
- { .compatible = "img,img-rogue", .data = NULL },
+ { .compatible = "img,img-rogue", .data = &pvr_device_overrides_default },
/* All supported GPU models */
- { .compatible = "img,img-axe-1-16m", .data = NULL },
+ { .compatible = "img,img-axe-1-16m", .data = &pvr_device_overrides_default },
/*
* This legacy compatible string was introduced early on before the more specific GPU
* identifiers were used. Keep it around here for compatibility, but never use
* "img,img-axe" in new devicetrees.
*/
- { .compatible = "img,img-axe", .data = NULL },
+ { .compatible = "img,img-axe", .data = &pvr_device_overrides_default },
{}
};
MODULE_DEVICE_TABLE(of, dt_match);
This infrastructure will be used in cases where a specific GPU integration or implementation requires some special handling in the driver. The first use case is the device cached memory override added in the next patch. The infrastructure is built out in this separate commit to make it clear which specific changes refer to the workaround added there. Signed-off-by: Matt Coster <matt.coster@imgtec.com> --- drivers/gpu/drm/imagination/pvr_device.c | 26 ++++++++++++++++++++++++ drivers/gpu/drm/imagination/pvr_device.h | 26 ++++++++++++++++++++++++ drivers/gpu/drm/imagination/pvr_drv.c | 34 ++++++++++++++++++++++++-------- 3 files changed, 78 insertions(+), 8 deletions(-)