Merge tag 'v5.15.57' into rpi-5.15.y
[platform/kernel/linux-rpi.git] / drivers / gpu / drm / vc4 / vc4_perfmon.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2018 Broadcom
4  */
5
6 /**
7  * DOC: VC4 V3D performance monitor module
8  *
9  * The V3D block provides 16 hardware counters which can count various events.
10  */
11
12 #include "vc4_drv.h"
13 #include "vc4_regs.h"
14
15 #define VC4_PERFMONID_MIN       1
16 #define VC4_PERFMONID_MAX       U32_MAX
17
18 void vc4_perfmon_get(struct vc4_perfmon *perfmon)
19 {
20         struct vc4_dev *vc4 = perfmon->dev;
21
22         if (WARN_ON_ONCE(vc4->is_vc5))
23                 return;
24
25         if (perfmon)
26                 refcount_inc(&perfmon->refcnt);
27 }
28
29 void vc4_perfmon_put(struct vc4_perfmon *perfmon)
30 {
31         struct vc4_dev *vc4;
32
33         if (!perfmon)
34                 return;
35
36         vc4 = perfmon->dev;
37         if (WARN_ON_ONCE(vc4->is_vc5))
38                 return;
39
40         if (refcount_dec_and_test(&perfmon->refcnt))
41                 kfree(perfmon);
42 }
43
44 void vc4_perfmon_start(struct vc4_dev *vc4, struct vc4_perfmon *perfmon)
45 {
46         unsigned int i;
47         u32 mask;
48
49         if (WARN_ON_ONCE(vc4->is_vc5))
50                 return;
51
52         if (WARN_ON_ONCE(!perfmon || vc4->active_perfmon))
53                 return;
54
55         for (i = 0; i < perfmon->ncounters; i++)
56                 V3D_WRITE(V3D_PCTRS(i), perfmon->events[i]);
57
58         mask = GENMASK(perfmon->ncounters - 1, 0);
59         V3D_WRITE(V3D_PCTRC, mask);
60         V3D_WRITE(V3D_PCTRE, V3D_PCTRE_EN | mask);
61         vc4->active_perfmon = perfmon;
62 }
63
64 void vc4_perfmon_stop(struct vc4_dev *vc4, struct vc4_perfmon *perfmon,
65                       bool capture)
66 {
67         unsigned int i;
68
69         if (WARN_ON_ONCE(vc4->is_vc5))
70                 return;
71
72         if (WARN_ON_ONCE(!vc4->active_perfmon ||
73                          perfmon != vc4->active_perfmon))
74                 return;
75
76         if (capture) {
77                 for (i = 0; i < perfmon->ncounters; i++)
78                         perfmon->counters[i] += V3D_READ(V3D_PCTR(i));
79         }
80
81         V3D_WRITE(V3D_PCTRE, 0);
82         vc4->active_perfmon = NULL;
83 }
84
85 struct vc4_perfmon *vc4_perfmon_find(struct vc4_file *vc4file, int id)
86 {
87         struct vc4_dev *vc4 = vc4file->dev;
88         struct vc4_perfmon *perfmon;
89
90         if (WARN_ON_ONCE(vc4->is_vc5))
91                 return NULL;
92
93         mutex_lock(&vc4file->perfmon.lock);
94         perfmon = idr_find(&vc4file->perfmon.idr, id);
95         vc4_perfmon_get(perfmon);
96         mutex_unlock(&vc4file->perfmon.lock);
97
98         return perfmon;
99 }
100
101 void vc4_perfmon_open_file(struct vc4_file *vc4file)
102 {
103         struct vc4_dev *vc4 = vc4file->dev;
104
105         if (WARN_ON_ONCE(vc4->is_vc5))
106                 return;
107
108         mutex_init(&vc4file->perfmon.lock);
109         idr_init_base(&vc4file->perfmon.idr, VC4_PERFMONID_MIN);
110         vc4file->dev = vc4;
111 }
112
113 static int vc4_perfmon_idr_del(int id, void *elem, void *data)
114 {
115         struct vc4_perfmon *perfmon = elem;
116
117         vc4_perfmon_put(perfmon);
118
119         return 0;
120 }
121
122 void vc4_perfmon_close_file(struct vc4_file *vc4file)
123 {
124         struct vc4_dev *vc4 = vc4file->dev;
125
126         if (WARN_ON_ONCE(vc4->is_vc5))
127                 return;
128
129         mutex_lock(&vc4file->perfmon.lock);
130         idr_for_each(&vc4file->perfmon.idr, vc4_perfmon_idr_del, NULL);
131         idr_destroy(&vc4file->perfmon.idr);
132         mutex_unlock(&vc4file->perfmon.lock);
133 }
134
135 int vc4_perfmon_create_ioctl(struct drm_device *dev, void *data,
136                              struct drm_file *file_priv)
137 {
138         struct vc4_dev *vc4 = to_vc4_dev(dev);
139         struct vc4_file *vc4file = file_priv->driver_priv;
140         struct drm_vc4_perfmon_create *req = data;
141         struct vc4_perfmon *perfmon;
142         unsigned int i;
143         int ret;
144
145         if (WARN_ON_ONCE(vc4->is_vc5))
146                 return -ENODEV;
147
148         if (!vc4->v3d) {
149                 DRM_DEBUG("Creating perfmon no VC4 V3D probed\n");
150                 return -ENODEV;
151         }
152
153         /* Number of monitored counters cannot exceed HW limits. */
154         if (req->ncounters > DRM_VC4_MAX_PERF_COUNTERS ||
155             !req->ncounters)
156                 return -EINVAL;
157
158         /* Make sure all events are valid. */
159         for (i = 0; i < req->ncounters; i++) {
160                 if (req->events[i] >= VC4_PERFCNT_NUM_EVENTS)
161                         return -EINVAL;
162         }
163
164         perfmon = kzalloc(struct_size(perfmon, counters, req->ncounters),
165                           GFP_KERNEL);
166         if (!perfmon)
167                 return -ENOMEM;
168         perfmon->dev = vc4;
169
170         for (i = 0; i < req->ncounters; i++)
171                 perfmon->events[i] = req->events[i];
172
173         perfmon->ncounters = req->ncounters;
174
175         refcount_set(&perfmon->refcnt, 1);
176
177         mutex_lock(&vc4file->perfmon.lock);
178         ret = idr_alloc(&vc4file->perfmon.idr, perfmon, VC4_PERFMONID_MIN,
179                         VC4_PERFMONID_MAX, GFP_KERNEL);
180         mutex_unlock(&vc4file->perfmon.lock);
181
182         if (ret < 0) {
183                 kfree(perfmon);
184                 return ret;
185         }
186
187         req->id = ret;
188         return 0;
189 }
190
191 int vc4_perfmon_destroy_ioctl(struct drm_device *dev, void *data,
192                               struct drm_file *file_priv)
193 {
194         struct vc4_dev *vc4 = to_vc4_dev(dev);
195         struct vc4_file *vc4file = file_priv->driver_priv;
196         struct drm_vc4_perfmon_destroy *req = data;
197         struct vc4_perfmon *perfmon;
198
199         if (WARN_ON_ONCE(vc4->is_vc5))
200                 return -ENODEV;
201
202         if (!vc4->v3d) {
203                 DRM_DEBUG("Destroying perfmon no VC4 V3D probed\n");
204                 return -ENODEV;
205         }
206
207         mutex_lock(&vc4file->perfmon.lock);
208         perfmon = idr_remove(&vc4file->perfmon.idr, req->id);
209         mutex_unlock(&vc4file->perfmon.lock);
210
211         if (!perfmon)
212                 return -EINVAL;
213
214         vc4_perfmon_put(perfmon);
215         return 0;
216 }
217
218 int vc4_perfmon_get_values_ioctl(struct drm_device *dev, void *data,
219                                  struct drm_file *file_priv)
220 {
221         struct vc4_dev *vc4 = to_vc4_dev(dev);
222         struct vc4_file *vc4file = file_priv->driver_priv;
223         struct drm_vc4_perfmon_get_values *req = data;
224         struct vc4_perfmon *perfmon;
225         int ret;
226
227         if (WARN_ON_ONCE(vc4->is_vc5))
228                 return -ENODEV;
229
230         if (!vc4->v3d) {
231                 DRM_DEBUG("Getting perfmon no VC4 V3D probed\n");
232                 return -ENODEV;
233         }
234
235         mutex_lock(&vc4file->perfmon.lock);
236         perfmon = idr_find(&vc4file->perfmon.idr, req->id);
237         vc4_perfmon_get(perfmon);
238         mutex_unlock(&vc4file->perfmon.lock);
239
240         if (!perfmon)
241                 return -EINVAL;
242
243         if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->counters,
244                          perfmon->ncounters * sizeof(u64)))
245                 ret = -EFAULT;
246         else
247                 ret = 0;
248
249         vc4_perfmon_put(perfmon);
250         return ret;
251 }