0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include "vc4_drv.h"
0013 #include "vc4_regs.h"
0014
0015 #define VC4_PERFMONID_MIN 1
0016 #define VC4_PERFMONID_MAX U32_MAX
0017
0018 void vc4_perfmon_get(struct vc4_perfmon *perfmon)
0019 {
0020 struct vc4_dev *vc4;
0021
0022 if (!perfmon)
0023 return;
0024
0025 vc4 = perfmon->dev;
0026 if (WARN_ON_ONCE(vc4->is_vc5))
0027 return;
0028
0029 refcount_inc(&perfmon->refcnt);
0030 }
0031
0032 void vc4_perfmon_put(struct vc4_perfmon *perfmon)
0033 {
0034 struct vc4_dev *vc4;
0035
0036 if (!perfmon)
0037 return;
0038
0039 vc4 = perfmon->dev;
0040 if (WARN_ON_ONCE(vc4->is_vc5))
0041 return;
0042
0043 if (refcount_dec_and_test(&perfmon->refcnt))
0044 kfree(perfmon);
0045 }
0046
0047 void vc4_perfmon_start(struct vc4_dev *vc4, struct vc4_perfmon *perfmon)
0048 {
0049 unsigned int i;
0050 u32 mask;
0051
0052 if (WARN_ON_ONCE(vc4->is_vc5))
0053 return;
0054
0055 if (WARN_ON_ONCE(!perfmon || vc4->active_perfmon))
0056 return;
0057
0058 for (i = 0; i < perfmon->ncounters; i++)
0059 V3D_WRITE(V3D_PCTRS(i), perfmon->events[i]);
0060
0061 mask = GENMASK(perfmon->ncounters - 1, 0);
0062 V3D_WRITE(V3D_PCTRC, mask);
0063 V3D_WRITE(V3D_PCTRE, V3D_PCTRE_EN | mask);
0064 vc4->active_perfmon = perfmon;
0065 }
0066
0067 void vc4_perfmon_stop(struct vc4_dev *vc4, struct vc4_perfmon *perfmon,
0068 bool capture)
0069 {
0070 unsigned int i;
0071
0072 if (WARN_ON_ONCE(vc4->is_vc5))
0073 return;
0074
0075 if (WARN_ON_ONCE(!vc4->active_perfmon ||
0076 perfmon != vc4->active_perfmon))
0077 return;
0078
0079 if (capture) {
0080 for (i = 0; i < perfmon->ncounters; i++)
0081 perfmon->counters[i] += V3D_READ(V3D_PCTR(i));
0082 }
0083
0084 V3D_WRITE(V3D_PCTRE, 0);
0085 vc4->active_perfmon = NULL;
0086 }
0087
0088 struct vc4_perfmon *vc4_perfmon_find(struct vc4_file *vc4file, int id)
0089 {
0090 struct vc4_dev *vc4 = vc4file->dev;
0091 struct vc4_perfmon *perfmon;
0092
0093 if (WARN_ON_ONCE(vc4->is_vc5))
0094 return NULL;
0095
0096 mutex_lock(&vc4file->perfmon.lock);
0097 perfmon = idr_find(&vc4file->perfmon.idr, id);
0098 vc4_perfmon_get(perfmon);
0099 mutex_unlock(&vc4file->perfmon.lock);
0100
0101 return perfmon;
0102 }
0103
0104 void vc4_perfmon_open_file(struct vc4_file *vc4file)
0105 {
0106 struct vc4_dev *vc4 = vc4file->dev;
0107
0108 if (WARN_ON_ONCE(vc4->is_vc5))
0109 return;
0110
0111 mutex_init(&vc4file->perfmon.lock);
0112 idr_init_base(&vc4file->perfmon.idr, VC4_PERFMONID_MIN);
0113 vc4file->dev = vc4;
0114 }
0115
0116 static int vc4_perfmon_idr_del(int id, void *elem, void *data)
0117 {
0118 struct vc4_perfmon *perfmon = elem;
0119
0120 vc4_perfmon_put(perfmon);
0121
0122 return 0;
0123 }
0124
0125 void vc4_perfmon_close_file(struct vc4_file *vc4file)
0126 {
0127 struct vc4_dev *vc4 = vc4file->dev;
0128
0129 if (WARN_ON_ONCE(vc4->is_vc5))
0130 return;
0131
0132 mutex_lock(&vc4file->perfmon.lock);
0133 idr_for_each(&vc4file->perfmon.idr, vc4_perfmon_idr_del, NULL);
0134 idr_destroy(&vc4file->perfmon.idr);
0135 mutex_unlock(&vc4file->perfmon.lock);
0136 }
0137
0138 int vc4_perfmon_create_ioctl(struct drm_device *dev, void *data,
0139 struct drm_file *file_priv)
0140 {
0141 struct vc4_dev *vc4 = to_vc4_dev(dev);
0142 struct vc4_file *vc4file = file_priv->driver_priv;
0143 struct drm_vc4_perfmon_create *req = data;
0144 struct vc4_perfmon *perfmon;
0145 unsigned int i;
0146 int ret;
0147
0148 if (WARN_ON_ONCE(vc4->is_vc5))
0149 return -ENODEV;
0150
0151 if (!vc4->v3d) {
0152 DRM_DEBUG("Creating perfmon no VC4 V3D probed\n");
0153 return -ENODEV;
0154 }
0155
0156
0157 if (req->ncounters > DRM_VC4_MAX_PERF_COUNTERS ||
0158 !req->ncounters)
0159 return -EINVAL;
0160
0161
0162 for (i = 0; i < req->ncounters; i++) {
0163 if (req->events[i] >= VC4_PERFCNT_NUM_EVENTS)
0164 return -EINVAL;
0165 }
0166
0167 perfmon = kzalloc(struct_size(perfmon, counters, req->ncounters),
0168 GFP_KERNEL);
0169 if (!perfmon)
0170 return -ENOMEM;
0171 perfmon->dev = vc4;
0172
0173 for (i = 0; i < req->ncounters; i++)
0174 perfmon->events[i] = req->events[i];
0175
0176 perfmon->ncounters = req->ncounters;
0177
0178 refcount_set(&perfmon->refcnt, 1);
0179
0180 mutex_lock(&vc4file->perfmon.lock);
0181 ret = idr_alloc(&vc4file->perfmon.idr, perfmon, VC4_PERFMONID_MIN,
0182 VC4_PERFMONID_MAX, GFP_KERNEL);
0183 mutex_unlock(&vc4file->perfmon.lock);
0184
0185 if (ret < 0) {
0186 kfree(perfmon);
0187 return ret;
0188 }
0189
0190 req->id = ret;
0191 return 0;
0192 }
0193
0194 int vc4_perfmon_destroy_ioctl(struct drm_device *dev, void *data,
0195 struct drm_file *file_priv)
0196 {
0197 struct vc4_dev *vc4 = to_vc4_dev(dev);
0198 struct vc4_file *vc4file = file_priv->driver_priv;
0199 struct drm_vc4_perfmon_destroy *req = data;
0200 struct vc4_perfmon *perfmon;
0201
0202 if (WARN_ON_ONCE(vc4->is_vc5))
0203 return -ENODEV;
0204
0205 if (!vc4->v3d) {
0206 DRM_DEBUG("Destroying perfmon no VC4 V3D probed\n");
0207 return -ENODEV;
0208 }
0209
0210 mutex_lock(&vc4file->perfmon.lock);
0211 perfmon = idr_remove(&vc4file->perfmon.idr, req->id);
0212 mutex_unlock(&vc4file->perfmon.lock);
0213
0214 if (!perfmon)
0215 return -EINVAL;
0216
0217 vc4_perfmon_put(perfmon);
0218 return 0;
0219 }
0220
0221 int vc4_perfmon_get_values_ioctl(struct drm_device *dev, void *data,
0222 struct drm_file *file_priv)
0223 {
0224 struct vc4_dev *vc4 = to_vc4_dev(dev);
0225 struct vc4_file *vc4file = file_priv->driver_priv;
0226 struct drm_vc4_perfmon_get_values *req = data;
0227 struct vc4_perfmon *perfmon;
0228 int ret;
0229
0230 if (WARN_ON_ONCE(vc4->is_vc5))
0231 return -ENODEV;
0232
0233 if (!vc4->v3d) {
0234 DRM_DEBUG("Getting perfmon no VC4 V3D probed\n");
0235 return -ENODEV;
0236 }
0237
0238 mutex_lock(&vc4file->perfmon.lock);
0239 perfmon = idr_find(&vc4file->perfmon.idr, req->id);
0240 vc4_perfmon_get(perfmon);
0241 mutex_unlock(&vc4file->perfmon.lock);
0242
0243 if (!perfmon)
0244 return -EINVAL;
0245
0246 if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->counters,
0247 perfmon->ncounters * sizeof(u64)))
0248 ret = -EFAULT;
0249 else
0250 ret = 0;
0251
0252 vc4_perfmon_put(perfmon);
0253 return ret;
0254 }