summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/msm/msm_gpu_devfreq.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2021-07-30 16:05:58 +1000
committerDave Airlie <airlied@redhat.com>2021-07-30 16:24:01 +1000
commitf1b7996551a40a4ebb551130c83077a0cabcb935 (patch)
tree5f4341566519775e2d6a3a6c0c3654e5b2e57982 /drivers/gpu/drm/msm/msm_gpu_devfreq.c
parentcfeeb0b5e09c28bd7eb1e5c514200595e15967aa (diff)
parent4541e4f2225c30b0e9442be9eb2fb8b7086cdd1f (diff)
Merge tag 'drm-msm-next-2021-07-28' of https://gitlab.freedesktop.org/drm/msm into drm-next
An early pull for v5.15 (there'll be more coming in a week or two), consisting of the drm/scheduler conversion and a couple other small series that one was based one. Mostly sending this now because IIUC danvet wanted it in drm-next so he could rebase on it. (Daniel, if you disagree then speak up, and I'll instead include this in the main pull request once that is ready.) This also has a core patch to drop drm_gem_object_put_locked() now that the last use of it is removed. [airlied: add NULL to drm_sched_init] Signed-off-by: Dave Airlie <airlied@redhat.com> From: Rob Clark <robdclark@gmail.com> Link: https://patchwork.freedesktop.org/patch/msgid/CAF6AEGumRk7H88bqV=H9Fb1SM0zPBo5B7NsCU3jFFKBYxf5k+Q@mail.gmail.com
Diffstat (limited to 'drivers/gpu/drm/msm/msm_gpu_devfreq.c')
-rw-r--r--drivers/gpu/drm/msm/msm_gpu_devfreq.c203
1 files changed, 203 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/msm_gpu_devfreq.c b/drivers/gpu/drm/msm/msm_gpu_devfreq.c
new file mode 100644
index 000000000000..0a1ee20296a2
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_gpu_devfreq.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ */
+
+#include "msm_gpu.h"
+#include "msm_gpu_trace.h"
+
+#include <linux/devfreq.h>
+#include <linux/devfreq_cooling.h>
+
+/*
+ * Power Management:
+ */
+
+static int msm_devfreq_target(struct device *dev, unsigned long *freq,
+ u32 flags)
+{
+ struct msm_gpu *gpu = dev_to_gpu(dev);
+ struct dev_pm_opp *opp;
+
+ opp = devfreq_recommended_opp(dev, freq, flags);
+
+ /*
+ * If the GPU is idle, devfreq is not aware, so just ignore
+ * it's requests
+ */
+ if (gpu->devfreq.idle_freq) {
+ gpu->devfreq.idle_freq = *freq;
+ return 0;
+ }
+
+ if (IS_ERR(opp))
+ return PTR_ERR(opp);
+
+ trace_msm_gpu_freq_change(dev_pm_opp_get_freq(opp));
+
+ if (gpu->funcs->gpu_set_freq)
+ gpu->funcs->gpu_set_freq(gpu, opp);
+ else
+ clk_set_rate(gpu->core_clk, *freq);
+
+ dev_pm_opp_put(opp);
+
+ return 0;
+}
+
+static unsigned long get_freq(struct msm_gpu *gpu)
+{
+ if (gpu->devfreq.idle_freq)
+ return gpu->devfreq.idle_freq;
+
+ if (gpu->funcs->gpu_get_freq)
+ return gpu->funcs->gpu_get_freq(gpu);
+
+ return clk_get_rate(gpu->core_clk);
+}
+
+static int msm_devfreq_get_dev_status(struct device *dev,
+ struct devfreq_dev_status *status)
+{
+ struct msm_gpu *gpu = dev_to_gpu(dev);
+ ktime_t time;
+
+ status->current_frequency = get_freq(gpu);
+ status->busy_time = gpu->funcs->gpu_busy(gpu);
+
+ time = ktime_get();
+ status->total_time = ktime_us_delta(time, gpu->devfreq.time);
+ gpu->devfreq.time = time;
+
+ return 0;
+}
+
+static int msm_devfreq_get_cur_freq(struct device *dev, unsigned long *freq)
+{
+ *freq = get_freq(dev_to_gpu(dev));
+
+ return 0;
+}
+
+static struct devfreq_dev_profile msm_devfreq_profile = {
+ .timer = DEVFREQ_TIMER_DELAYED,
+ .polling_ms = 50,
+ .target = msm_devfreq_target,
+ .get_dev_status = msm_devfreq_get_dev_status,
+ .get_cur_freq = msm_devfreq_get_cur_freq,
+};
+
+void msm_devfreq_init(struct msm_gpu *gpu)
+{
+ /* We need target support to do devfreq */
+ if (!gpu->funcs->gpu_busy)
+ return;
+
+ msm_devfreq_profile.initial_freq = gpu->fast_rate;
+
+ /*
+ * Don't set the freq_table or max_state and let devfreq build the table
+ * from OPP
+ * After a deferred probe, these may have be left to non-zero values,
+ * so set them back to zero before creating the devfreq device
+ */
+ msm_devfreq_profile.freq_table = NULL;
+ msm_devfreq_profile.max_state = 0;
+
+ gpu->devfreq.devfreq = devm_devfreq_add_device(&gpu->pdev->dev,
+ &msm_devfreq_profile, DEVFREQ_GOV_SIMPLE_ONDEMAND,
+ NULL);
+
+ if (IS_ERR(gpu->devfreq.devfreq)) {
+ DRM_DEV_ERROR(&gpu->pdev->dev, "Couldn't initialize GPU devfreq\n");
+ gpu->devfreq.devfreq = NULL;
+ return;
+ }
+
+ devfreq_suspend_device(gpu->devfreq.devfreq);
+
+ gpu->cooling = of_devfreq_cooling_register(gpu->pdev->dev.of_node,
+ gpu->devfreq.devfreq);
+ if (IS_ERR(gpu->cooling)) {
+ DRM_DEV_ERROR(&gpu->pdev->dev,
+ "Couldn't register GPU cooling device\n");
+ gpu->cooling = NULL;
+ }
+}
+
+void msm_devfreq_cleanup(struct msm_gpu *gpu)
+{
+ devfreq_cooling_unregister(gpu->cooling);
+}
+
+void msm_devfreq_resume(struct msm_gpu *gpu)
+{
+ gpu->devfreq.busy_cycles = 0;
+ gpu->devfreq.time = ktime_get();
+
+ devfreq_resume_device(gpu->devfreq.devfreq);
+}
+
+void msm_devfreq_suspend(struct msm_gpu *gpu)
+{
+ devfreq_suspend_device(gpu->devfreq.devfreq);
+}
+
+void msm_devfreq_active(struct msm_gpu *gpu)
+{
+ struct msm_gpu_devfreq *df = &gpu->devfreq;
+ struct devfreq_dev_status status;
+ unsigned int idle_time;
+ unsigned long target_freq = df->idle_freq;
+
+ /*
+ * Hold devfreq lock to synchronize with get_dev_status()/
+ * target() callbacks
+ */
+ mutex_lock(&df->devfreq->lock);
+
+ idle_time = ktime_to_ms(ktime_sub(ktime_get(), df->idle_time));
+
+ /*
+ * If we've been idle for a significant fraction of a polling
+ * interval, then we won't meet the threshold of busyness for
+ * the governor to ramp up the freq.. so give some boost
+ */
+ if (idle_time > msm_devfreq_profile.polling_ms/2) {
+ target_freq *= 2;
+ }
+
+ df->idle_freq = 0;
+
+ msm_devfreq_target(&gpu->pdev->dev, &target_freq, 0);
+
+ /*
+ * Reset the polling interval so we aren't inconsistent
+ * about freq vs busy/total cycles
+ */
+ msm_devfreq_get_dev_status(&gpu->pdev->dev, &status);
+
+ mutex_unlock(&df->devfreq->lock);
+}
+
+void msm_devfreq_idle(struct msm_gpu *gpu)
+{
+ struct msm_gpu_devfreq *df = &gpu->devfreq;
+ unsigned long idle_freq, target_freq = 0;
+
+ /*
+ * Hold devfreq lock to synchronize with get_dev_status()/
+ * target() callbacks
+ */
+ mutex_lock(&df->devfreq->lock);
+
+ idle_freq = get_freq(gpu);
+
+ msm_devfreq_target(&gpu->pdev->dev, &target_freq, 0);
+
+ df->idle_time = ktime_get();
+ df->idle_freq = idle_freq;
+
+ mutex_unlock(&df->devfreq->lock);
+}