Unverified Commit 727538a4 authored by Sarah Walker's avatar Sarah Walker Committed by Maxime Ripard
Browse files

drm/imagination: Implement power management



Add power management to the driver, using runtime pm. The power off
sequence depends on firmware commands which are not implemented in this
patch.

Changes since v8:
- Corrected license identifiers

Changes since v5:
- Use RUNTIME_PM_OPS() to declare PM callbacks
- Add Kconfig dependency on CONFIG_PM

Changes since v4:
- Suspend runtime PM before unplugging device on rmmod

Changes since v3:
- Don't power device when calling pvr_device_gpu_fini()
- Documentation for pvr_dev->lost has been improved
- pvr_power_init() renamed to pvr_watchdog_init()
- Use drm_dev_{enter,exit}

Changes since v2:
- Use runtime PM
- Implement watchdog

Signed-off-by: default avatarSarah Walker <sarah.walker@imgtec.com>
Signed-off-by: default avatarDonald Robson <donald.robson@imgtec.com>
Reviewed-by: default avatarMaxime Ripard <mripard@kernel.org>
Link: https://lore.kernel.org/r/e09af4ef1ff514e1d6d7f97c7c5032c643c56f9c.1700668843.git.donald.robson@imgtec.com


Signed-off-by: default avatarMaxime Ripard <mripard@kernel.org>
parent ff5f643d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ config DRM_POWERVR
	tristate "Imagination Technologies PowerVR (Series 6 and later) & IMG Graphics"
	depends on ARM64
	depends on DRM
	depends on PM
	select DRM_GEM_SHMEM_HELPER
	select DRM_SCHED
	select DRM_GPUVM
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ powervr-y := \
	pvr_fw.o \
	pvr_gem.o \
	pvr_mmu.o \
	pvr_power.o \
	pvr_vm.o

obj-$(CONFIG_DRM_POWERVR) += powervr.o
+21 −2
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#include "pvr_device_info.h"

#include "pvr_fw.h"
#include "pvr_power.h"
#include "pvr_rogue_cr_defs.h"
#include "pvr_vm.h"

@@ -361,6 +362,8 @@ pvr_device_gpu_fini(struct pvr_device *pvr_dev)
int
pvr_device_init(struct pvr_device *pvr_dev)
{
	struct drm_device *drm_dev = from_pvr_device(pvr_dev);
	struct device *dev = drm_dev->dev;
	int err;

	/* Enable and initialize clocks required for the device to operate. */
@@ -368,13 +371,29 @@ pvr_device_init(struct pvr_device *pvr_dev)
	if (err)
		return err;

	/* Explicitly power the GPU so we can access control registers before the FW is booted. */
	err = pm_runtime_resume_and_get(dev);
	if (err)
		return err;

	/* Map the control registers into memory. */
	err = pvr_device_reg_init(pvr_dev);
	if (err)
		return err;
		goto err_pm_runtime_put;

	/* Perform GPU-specific initialization steps. */
	return pvr_device_gpu_init(pvr_dev);
	err = pvr_device_gpu_init(pvr_dev);
	if (err)
		goto err_pm_runtime_put;

	pm_runtime_put(dev);

	return 0;

err_pm_runtime_put:
	pm_runtime_put_sync_suspend(dev);

	return err;
}

/**
+22 −0
Original line number Diff line number Diff line
@@ -141,6 +141,28 @@ struct pvr_device {
	 * before submitting the next job.
	 */
	atomic_t mmu_flush_cache_flags;

	struct {
		/** @work: Work item for watchdog callback. */
		struct delayed_work work;

		/** @old_kccb_cmds_executed: KCCB command execution count at last watchdog poll. */
		u32 old_kccb_cmds_executed;

		/** @kccb_stall_count: Number of watchdog polls KCCB has been stalled for. */
		u32 kccb_stall_count;
	} watchdog;

	/**
	 * @lost: %true if the device has been lost.
	 *
	 * This variable is set if the device has become irretrievably unavailable, e.g. if the
	 * firmware processor has stopped responding and can not be revived via a hard reset.
	 */
	bool lost;

	/** @sched_wq: Workqueue for schedulers. */
	struct workqueue_struct *sched_wq;
};

/**
+19 −1
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#include "pvr_drv.h"
#include "pvr_gem.h"
#include "pvr_mmu.h"
#include "pvr_power.h"
#include "pvr_rogue_defs.h"
#include "pvr_rogue_fwif_client.h"
#include "pvr_rogue_fwif_shared.h"
@@ -1265,9 +1266,16 @@ pvr_probe(struct platform_device *plat_dev)

	platform_set_drvdata(plat_dev, drm_dev);

	devm_pm_runtime_enable(&plat_dev->dev);
	pm_runtime_mark_last_busy(&plat_dev->dev);

	pm_runtime_set_autosuspend_delay(&plat_dev->dev, 50);
	pm_runtime_use_autosuspend(&plat_dev->dev);
	pvr_watchdog_init(pvr_dev);

	err = pvr_device_init(pvr_dev);
	if (err)
		return err;
		goto err_watchdog_fini;

	err = drm_dev_register(drm_dev, 0);
	if (err)
@@ -1278,6 +1286,9 @@ pvr_probe(struct platform_device *plat_dev)
err_device_fini:
	pvr_device_fini(pvr_dev);

err_watchdog_fini:
	pvr_watchdog_fini(pvr_dev);

	return err;
}

@@ -1287,8 +1298,10 @@ pvr_remove(struct platform_device *plat_dev)
	struct drm_device *drm_dev = platform_get_drvdata(plat_dev);
	struct pvr_device *pvr_dev = to_pvr_device(drm_dev);

	pm_runtime_suspend(drm_dev->dev);
	pvr_device_fini(pvr_dev);
	drm_dev_unplug(drm_dev);
	pvr_watchdog_fini(pvr_dev);

	return 0;
}
@@ -1299,11 +1312,16 @@ static const struct of_device_id dt_match[] = {
};
MODULE_DEVICE_TABLE(of, dt_match);

static const struct dev_pm_ops pvr_pm_ops = {
	RUNTIME_PM_OPS(pvr_power_device_suspend, pvr_power_device_resume, pvr_power_device_idle)
};

static struct platform_driver pvr_driver = {
	.probe = pvr_probe,
	.remove = pvr_remove,
	.driver = {
		.name = PVR_DRIVER_NAME,
		.pm = &pvr_pm_ops,
		.of_match_table = dt_match,
	},
};
Loading