@@ -11,6 +11,7 @@ config DRM_POWERVR
select DRM_SCHED
select DRM_GPUVM
select FW_LOADER
+ select POWER_SEQUENCING
help
Choose this option if you have a system that has an Imagination
Technologies PowerVR (Series 6 or later) or IMG GPU.
@@ -25,6 +25,7 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/pwrseq/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/stddef.h>
@@ -631,10 +632,34 @@ pvr_device_init(struct pvr_device *pvr_dev)
if (err)
return err;
- /* Get the reset line for the GPU */
- err = pvr_device_reset_init(pvr_dev);
- if (err)
- return err;
+ /*
+ * Try to get a power sequencer. If successful, it will handle clocks
+ * and resets. Otherwise, we fall back to managing them ourselves.
+ */
+ pvr_dev->pwrseq = devm_pwrseq_get(dev, "gpu-power");
+ if (IS_ERR(pvr_dev->pwrseq)) {
+ int pwrseq_err = PTR_ERR(pvr_dev->pwrseq);
+
+ /*
+ * If the error is -EPROBE_DEFER, it's because the
+ * optional sequencer provider is not present
+ * and it's safe to fall back on manual power-up.
+ */
+ if (pwrseq_err == -EPROBE_DEFER)
+ pvr_dev->pwrseq = NULL;
+ else
+ return dev_err_probe(dev, pwrseq_err,
+ "Failed to get power sequencer\n");
+ }
+
+ /* Get the reset line for the GPU, but since it's exclusive only
+ * get it if the pwerseq is NULL.
+ */
+ if (!pvr_dev->pwrseq) {
+ err = pvr_device_reset_init(pvr_dev);
+ if (err)
+ return err;
+ }
/* Explicitly power the GPU so we can access control registers before the FW is booted. */
err = pm_runtime_resume_and_get(dev);
@@ -37,6 +37,9 @@ struct clk;
/* Forward declaration from <linux/firmware.h>. */
struct firmware;
+/* Forward declaration from <linux/pwrseq/consumer.h */
+struct pwrseq_desc;
+
/**
* struct pvr_gpu_id - Hardware GPU ID information for a PowerVR device
* @b: Branch ID.
@@ -148,6 +151,9 @@ struct pvr_device {
*/
struct reset_control *reset;
+ /** @pwrseq: Pointer to a power sequencer, if one is used. */
+ struct pwrseq_desc *pwrseq;
+
/** @irq: IRQ number. */
int irq;
@@ -21,6 +21,7 @@
#include <linux/reset.h>
#include <linux/timer.h>
#include <linux/types.h>
+#include <linux/pwrseq/consumer.h>
#include <linux/workqueue.h>
#define POWER_SYNC_TIMEOUT_US (1000000) /* 1s */
@@ -234,6 +235,19 @@ pvr_watchdog_init(struct pvr_device *pvr_dev)
return 0;
}
+static int pvr_power_off_sequence_manual(struct pvr_device *pvr_dev)
+{
+ int err;
+
+ err = reset_control_assert(pvr_dev->reset);
+
+ clk_disable_unprepare(pvr_dev->mem_clk);
+ clk_disable_unprepare(pvr_dev->sys_clk);
+ clk_disable_unprepare(pvr_dev->core_clk);
+
+ return err;
+}
+
int
pvr_power_device_suspend(struct device *dev)
{
@@ -252,11 +266,10 @@ pvr_power_device_suspend(struct device *dev)
goto err_drm_dev_exit;
}
- clk_disable_unprepare(pvr_dev->mem_clk);
- clk_disable_unprepare(pvr_dev->sys_clk);
- clk_disable_unprepare(pvr_dev->core_clk);
-
- err = reset_control_assert(pvr_dev->reset);
+ if (pvr_dev->pwrseq)
+ err = pwrseq_power_off(pvr_dev->pwrseq);
+ else
+ err = pvr_power_off_sequence_manual(pvr_dev);
err_drm_dev_exit:
drm_dev_exit(idx);
@@ -276,44 +289,55 @@ pvr_power_device_resume(struct device *dev)
if (!drm_dev_enter(drm_dev, &idx))
return -EIO;
- err = clk_prepare_enable(pvr_dev->core_clk);
- if (err)
- goto err_drm_dev_exit;
+ if (pvr_dev->pwrseq) {
+ err = pwrseq_power_on(pvr_dev->pwrseq);
+ if (err)
+ goto err_drm_dev_exit;
+ } else {
+ err = clk_prepare_enable(pvr_dev->core_clk);
+ if (err)
+ goto err_drm_dev_exit;
- err = clk_prepare_enable(pvr_dev->sys_clk);
- if (err)
- goto err_core_clk_disable;
+ err = clk_prepare_enable(pvr_dev->sys_clk);
+ if (err)
+ goto err_core_clk_disable;
- err = clk_prepare_enable(pvr_dev->mem_clk);
- if (err)
- goto err_sys_clk_disable;
+ err = clk_prepare_enable(pvr_dev->mem_clk);
+ if (err)
+ goto err_sys_clk_disable;
- /*
- * According to the hardware manual, a delay of at least 32 clock
- * cycles is required between de-asserting the clkgen reset and
- * de-asserting the GPU reset. Assuming a worst-case scenario with
- * a very high GPU clock frequency, a delay of 1 microsecond is
- * sufficient to ensure this requirement is met across all
- * feasible GPU clock speeds.
- */
- udelay(1);
+ /*
+ * According to the hardware manual, a delay of at least 32 clock
+ * cycles is required between de-asserting the clkgen reset and
+ * de-asserting the GPU reset. Assuming a worst-case scenario with
+ * a very high GPU clock frequency, a delay of 1 microsecond is
+ * sufficient to ensure this requirement is met across all
+ * feasible GPU clock speeds.
+ */
+ udelay(1);
- err = reset_control_deassert(pvr_dev->reset);
- if (err)
- goto err_mem_clk_disable;
+ err = reset_control_deassert(pvr_dev->reset);
+ if (err)
+ goto err_mem_clk_disable;
+ }
if (pvr_dev->fw_dev.booted) {
err = pvr_power_fw_enable(pvr_dev);
if (err)
- goto err_reset_assert;
+ goto err_power_off;
}
drm_dev_exit(idx);
return 0;
-err_reset_assert:
- reset_control_assert(pvr_dev->reset);
+err_power_off:
+ if (pvr_dev->pwrseq)
+ pwrseq_power_off(pvr_dev->pwrseq);
+ else
+ pvr_power_off_sequence_manual(pvr_dev);
+
+ goto err_drm_dev_exit;
err_mem_clk_disable:
clk_disable_unprepare(pvr_dev->mem_clk);
Update the Imagination PVR DRM driver to leverage the pwrseq framework for managing the power sequence of the GPU on the T-HEAD TH1520 SoC. In pvr_device_init(), the driver now attempts to get a handle to the "gpu-power" sequencer target using devm_pwrseq_get(). If successful, the responsibility for powering on and off the GPU's core clocks and resets is delegated to the power sequencer. Consequently, the GPU driver conditionally skips acquiring the GPU reset line if the pwrseq handle is obtained, as the sequencer's match function will acquire it. Clock handles are still acquired by the GPU driver for other purposes like devfreq. The runtime PM callbacks, pvr_power_device_resume() and pvr_power_device_suspend(), are modified to call pwrseq_power_on() and pwrseq_power_off() respectively when the sequencer is present. If no sequencer is found, the driver falls back to its existing manual clock and reset management. A helper function, pvr_power_off_sequence_manual(), is introduced to encapsulate the manual power-down logic. Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com> --- drivers/gpu/drm/imagination/Kconfig | 1 + drivers/gpu/drm/imagination/pvr_device.c | 33 +++++++++++-- drivers/gpu/drm/imagination/pvr_device.h | 6 +++ drivers/gpu/drm/imagination/pvr_power.c | 82 +++++++++++++++++++++----------- 4 files changed, 89 insertions(+), 33 deletions(-)