summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/v3d/v3d_perfmon.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2021-07-23 11:31:50 +1000
committerDave Airlie <airlied@redhat.com>2021-07-23 11:32:43 +1000
commit8da49a33dda7294c1af508f8aa81cd638d0afd62 (patch)
tree03327c42b220f70e102d73d7990e04f3f2bb47b1 /drivers/gpu/drm/v3d/v3d_perfmon.c
parent588b3eee528873d73bf777f329d35b2e65e24777 (diff)
parent474596fc749ca8c87520fbd3529ff89464a94430 (diff)
Merge tag 'drm-misc-next-2021-07-22' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for v5.15-rc1: UAPI Changes: - Remove sysfs stats for dma-buf attachments, as it causes a performance regression. Previous merge is not in a rc kernel yet, so no userspace regression possible. Cross-subsystem Changes: - Sanitize user input in kyro's viewport ioctl. - Use refcount_t in fb_info->count - Assorted fixes to dma-buf. - Extend x86 efifb handling to all archs. - Fix neofb divide by 0. - Document corpro,gm7123 bridge dt bindings. Core Changes: - Slightly rework drm master handling. - Cleanup vgaarb handling. - Assorted fixes. Driver Changes: - Add support for ws2401 panel. - Assorted fixes to stm, ast, bochs. - Demidlayer ingenic irq. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/2d0d2fe8-01fc-e216-c3fd-38db9e69944e@linux.intel.com
Diffstat (limited to 'drivers/gpu/drm/v3d/v3d_perfmon.c')
-rw-r--r--drivers/gpu/drm/v3d/v3d_perfmon.c213
1 files changed, 213 insertions, 0 deletions
diff --git a/drivers/gpu/drm/v3d/v3d_perfmon.c b/drivers/gpu/drm/v3d/v3d_perfmon.c
new file mode 100644
index 000000000000..0288ef063513
--- /dev/null
+++ b/drivers/gpu/drm/v3d/v3d_perfmon.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Raspberry Pi
+ */
+
+#include "v3d_drv.h"
+#include "v3d_regs.h"
+
+#define V3D_PERFMONID_MIN 1
+#define V3D_PERFMONID_MAX U32_MAX
+
+void v3d_perfmon_get(struct v3d_perfmon *perfmon)
+{
+ if (perfmon)
+ refcount_inc(&perfmon->refcnt);
+}
+
+void v3d_perfmon_put(struct v3d_perfmon *perfmon)
+{
+ if (perfmon && refcount_dec_and_test(&perfmon->refcnt))
+ kfree(perfmon);
+}
+
+void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
+{
+ unsigned int i;
+ u32 mask;
+ u8 ncounters = perfmon->ncounters;
+
+ if (WARN_ON_ONCE(!perfmon || v3d->active_perfmon))
+ return;
+
+ mask = GENMASK(ncounters - 1, 0);
+
+ for (i = 0; i < ncounters; i++) {
+ u32 source = i / 4;
+ u32 channel = V3D_SET_FIELD(perfmon->counters[i], V3D_PCTR_S0);
+
+ i++;
+ channel |= V3D_SET_FIELD(i < ncounters ? perfmon->counters[i] : 0,
+ V3D_PCTR_S1);
+ i++;
+ channel |= V3D_SET_FIELD(i < ncounters ? perfmon->counters[i] : 0,
+ V3D_PCTR_S2);
+ i++;
+ channel |= V3D_SET_FIELD(i < ncounters ? perfmon->counters[i] : 0,
+ V3D_PCTR_S3);
+ V3D_CORE_WRITE(0, V3D_V4_PCTR_0_SRC_X(source), channel);
+ }
+
+ V3D_CORE_WRITE(0, V3D_V4_PCTR_0_CLR, mask);
+ V3D_CORE_WRITE(0, V3D_PCTR_0_OVERFLOW, mask);
+ V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, mask);
+
+ v3d->active_perfmon = perfmon;
+}
+
+void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
+ bool capture)
+{
+ unsigned int i;
+
+ if (!perfmon || !v3d->active_perfmon)
+ return;
+
+ mutex_lock(&perfmon->lock);
+ if (perfmon != v3d->active_perfmon) {
+ mutex_unlock(&perfmon->lock);
+ return;
+ }
+
+ if (capture)
+ for (i = 0; i < perfmon->ncounters; i++)
+ perfmon->values[i] += V3D_CORE_READ(0, V3D_PCTR_0_PCTRX(i));
+
+ V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, 0);
+
+ v3d->active_perfmon = NULL;
+ mutex_unlock(&perfmon->lock);
+}
+
+struct v3d_perfmon *v3d_perfmon_find(struct v3d_file_priv *v3d_priv, int id)
+{
+ struct v3d_perfmon *perfmon;
+
+ mutex_lock(&v3d_priv->perfmon.lock);
+ perfmon = idr_find(&v3d_priv->perfmon.idr, id);
+ v3d_perfmon_get(perfmon);
+ mutex_unlock(&v3d_priv->perfmon.lock);
+
+ return perfmon;
+}
+
+void v3d_perfmon_open_file(struct v3d_file_priv *v3d_priv)
+{
+ mutex_init(&v3d_priv->perfmon.lock);
+ idr_init(&v3d_priv->perfmon.idr);
+}
+
+static int v3d_perfmon_idr_del(int id, void *elem, void *data)
+{
+ struct v3d_perfmon *perfmon = elem;
+
+ v3d_perfmon_put(perfmon);
+
+ return 0;
+}
+
+void v3d_perfmon_close_file(struct v3d_file_priv *v3d_priv)
+{
+ mutex_lock(&v3d_priv->perfmon.lock);
+ idr_for_each(&v3d_priv->perfmon.idr, v3d_perfmon_idr_del, NULL);
+ idr_destroy(&v3d_priv->perfmon.idr);
+ mutex_unlock(&v3d_priv->perfmon.lock);
+}
+
+int v3d_perfmon_create_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
+ struct drm_v3d_perfmon_create *req = data;
+ struct v3d_perfmon *perfmon;
+ unsigned int i;
+ int ret;
+
+ /* Number of monitored counters cannot exceed HW limits. */
+ if (req->ncounters > DRM_V3D_MAX_PERF_COUNTERS ||
+ !req->ncounters)
+ return -EINVAL;
+
+ /* Make sure all counters are valid. */
+ for (i = 0; i < req->ncounters; i++) {
+ if (req->counters[i] >= V3D_PERFCNT_NUM)
+ return -EINVAL;
+ }
+
+ perfmon = kzalloc(struct_size(perfmon, values, req->ncounters),
+ GFP_KERNEL);
+ if (!perfmon)
+ return -ENOMEM;
+
+ for (i = 0; i < req->ncounters; i++)
+ perfmon->counters[i] = req->counters[i];
+
+ perfmon->ncounters = req->ncounters;
+
+ refcount_set(&perfmon->refcnt, 1);
+ mutex_init(&perfmon->lock);
+
+ mutex_lock(&v3d_priv->perfmon.lock);
+ ret = idr_alloc(&v3d_priv->perfmon.idr, perfmon, V3D_PERFMONID_MIN,
+ V3D_PERFMONID_MAX, GFP_KERNEL);
+ mutex_unlock(&v3d_priv->perfmon.lock);
+
+ if (ret < 0) {
+ kfree(perfmon);
+ return ret;
+ }
+
+ req->id = ret;
+
+ return 0;
+}
+
+int v3d_perfmon_destroy_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
+ struct drm_v3d_perfmon_destroy *req = data;
+ struct v3d_perfmon *perfmon;
+
+ mutex_lock(&v3d_priv->perfmon.lock);
+ perfmon = idr_remove(&v3d_priv->perfmon.idr, req->id);
+ mutex_unlock(&v3d_priv->perfmon.lock);
+
+ if (!perfmon)
+ return -EINVAL;
+
+ v3d_perfmon_put(perfmon);
+
+ return 0;
+}
+
+int v3d_perfmon_get_values_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct v3d_dev *v3d = to_v3d_dev(dev);
+ struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
+ struct drm_v3d_perfmon_get_values *req = data;
+ struct v3d_perfmon *perfmon;
+ int ret = 0;
+
+ if (req->pad != 0)
+ return -EINVAL;
+
+ mutex_lock(&v3d_priv->perfmon.lock);
+ perfmon = idr_find(&v3d_priv->perfmon.idr, req->id);
+ v3d_perfmon_get(perfmon);
+ mutex_unlock(&v3d_priv->perfmon.lock);
+
+ if (!perfmon)
+ return -EINVAL;
+
+ v3d_perfmon_stop(v3d, perfmon, true);
+
+ if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->values,
+ perfmon->ncounters * sizeof(u64)))
+ ret = -EFAULT;
+
+ v3d_perfmon_put(perfmon);
+
+ return ret;
+}