GNU Linux-libre 4.19.264-gnu1
[releases.git] / drivers / gpu / drm / amd / display / modules / stats / stats.c
1 /*
2  * Copyright 2016 Advanced Micro Devices, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: AMD
23  *
24  */
25
26 #include "mod_stats.h"
27 #include "dm_services.h"
28 #include "dc.h"
29 #include "core_types.h"
30
31 #define DAL_STATS_ENABLE_REGKEY                 "DalStatsEnable"
32 #define DAL_STATS_ENABLE_REGKEY_DEFAULT         0x00000000
33 #define DAL_STATS_ENABLE_REGKEY_ENABLED         0x00000001
34
35 #define DAL_STATS_ENTRIES_REGKEY                "DalStatsEntries"
36 #define DAL_STATS_ENTRIES_REGKEY_DEFAULT        0x00350000
37 #define DAL_STATS_ENTRIES_REGKEY_MAX            0x01000000
38
39 #define DAL_STATS_EVENT_ENTRIES_DEFAULT         0x00000100
40
41 #define MOD_STATS_NUM_VSYNCS                    5
42 #define MOD_STATS_EVENT_STRING_MAX              512
43
44 struct stats_time_cache {
45         unsigned int entry_id;
46
47         unsigned long flip_timestamp_in_ns;
48         unsigned long vupdate_timestamp_in_ns;
49
50         unsigned int render_time_in_us;
51         unsigned int avg_render_time_in_us_last_ten;
52         unsigned int v_sync_time_in_us[MOD_STATS_NUM_VSYNCS];
53         unsigned int num_vsync_between_flips;
54
55         unsigned int flip_to_vsync_time_in_us;
56         unsigned int vsync_to_flip_time_in_us;
57
58         unsigned int min_window;
59         unsigned int max_window;
60         unsigned int v_total_min;
61         unsigned int v_total_max;
62         unsigned int event_triggers;
63
64         unsigned int lfc_mid_point_in_us;
65         unsigned int num_frames_inserted;
66         unsigned int inserted_duration_in_us;
67
68         unsigned int flags;
69 };
70
71 struct stats_event_cache {
72         unsigned int entry_id;
73         char event_string[MOD_STATS_EVENT_STRING_MAX];
74 };
75
76 struct core_stats {
77         struct mod_stats public;
78         struct dc *dc;
79
80         bool enabled;
81         unsigned int entries;
82         unsigned int event_entries;
83         unsigned int entry_id;
84
85         struct stats_time_cache *time;
86         unsigned int index;
87
88         struct stats_event_cache *events;
89         unsigned int event_index;
90
91 };
92
93 #define MOD_STATS_TO_CORE(mod_stats)\
94                 container_of(mod_stats, struct core_stats, public)
95
96 bool mod_stats_init(struct mod_stats *mod_stats)
97 {
98         bool result = false;
99         struct core_stats *core_stats = NULL;
100         struct dc *dc = NULL;
101
102         if (mod_stats == NULL)
103                 return false;
104
105         core_stats = MOD_STATS_TO_CORE(mod_stats);
106         dc = core_stats->dc;
107
108         return result;
109 }
110
111 struct mod_stats *mod_stats_create(struct dc *dc)
112 {
113         struct core_stats *core_stats = NULL;
114         struct persistent_data_flag flag;
115         unsigned int reg_data;
116         int i = 0;
117
118         if (dc == NULL)
119                 goto fail_construct;
120
121         core_stats = kzalloc(sizeof(struct core_stats), GFP_KERNEL);
122
123         if (core_stats == NULL)
124                 goto fail_construct;
125
126         core_stats->dc = dc;
127
128         core_stats->enabled = DAL_STATS_ENABLE_REGKEY_DEFAULT;
129         if (dm_read_persistent_data(dc->ctx, NULL, NULL,
130                         DAL_STATS_ENABLE_REGKEY,
131                         &reg_data, sizeof(unsigned int), &flag))
132                 core_stats->enabled = reg_data;
133
134         if (core_stats->enabled) {
135                 core_stats->entries = DAL_STATS_ENTRIES_REGKEY_DEFAULT;
136                 if (dm_read_persistent_data(dc->ctx, NULL, NULL,
137                                 DAL_STATS_ENTRIES_REGKEY,
138                                 &reg_data, sizeof(unsigned int), &flag)) {
139                         if (reg_data > DAL_STATS_ENTRIES_REGKEY_MAX)
140                                 core_stats->entries = DAL_STATS_ENTRIES_REGKEY_MAX;
141                         else
142                                 core_stats->entries = reg_data;
143                 }
144                 core_stats->time = kcalloc(core_stats->entries,
145                                                 sizeof(struct stats_time_cache),
146                                                 GFP_KERNEL);
147
148                 if (core_stats->time == NULL)
149                         goto fail_construct_time;
150
151                 core_stats->event_entries = DAL_STATS_EVENT_ENTRIES_DEFAULT;
152                 core_stats->events = kcalloc(core_stats->event_entries,
153                                              sizeof(struct stats_event_cache),
154                                              GFP_KERNEL);
155
156                 if (core_stats->events == NULL)
157                         goto fail_construct_events;
158
159         } else {
160                 core_stats->entries = 0;
161         }
162
163         /* Purposely leave index 0 unused so we don't need special logic to
164          * handle calculation cases that depend on previous flip data.
165          */
166         core_stats->index = 1;
167         core_stats->event_index = 0;
168
169         // Keeps track of ordering within the different stats structures
170         core_stats->entry_id = 0;
171
172         return &core_stats->public;
173
174 fail_construct_events:
175         kfree(core_stats->time);
176
177 fail_construct_time:
178         kfree(core_stats);
179
180 fail_construct:
181         return NULL;
182 }
183
184 void mod_stats_destroy(struct mod_stats *mod_stats)
185 {
186         if (mod_stats != NULL) {
187                 struct core_stats *core_stats = MOD_STATS_TO_CORE(mod_stats);
188
189                 if (core_stats->time != NULL)
190                         kfree(core_stats->time);
191
192                 if (core_stats->events != NULL)
193                         kfree(core_stats->events);
194
195                 kfree(core_stats);
196         }
197 }
198
199 void mod_stats_dump(struct mod_stats *mod_stats)
200 {
201         struct dc  *dc = NULL;
202         struct dal_logger *logger = NULL;
203         struct core_stats *core_stats = NULL;
204         struct stats_time_cache *time = NULL;
205         struct stats_event_cache *events = NULL;
206         unsigned int time_index = 1;
207         unsigned int event_index = 0;
208         unsigned int index = 0;
209         struct log_entry log_entry;
210
211         if (mod_stats == NULL)
212                 return;
213
214         core_stats = MOD_STATS_TO_CORE(mod_stats);
215         dc = core_stats->dc;
216         logger = dc->ctx->logger;
217         time = core_stats->time;
218         events = core_stats->events;
219
220         DISPLAY_STATS_BEGIN(log_entry);
221
222         DISPLAY_STATS("==Display Caps==\n");
223
224         DISPLAY_STATS("==Display Stats==\n");
225
226         DISPLAY_STATS("%10s %10s %10s %10s %10s"
227                         " %11s %11s %17s %10s %14s"
228                         " %10s %10s %10s %10s %10s"
229                         " %10s %10s %10s %10s\n",
230                 "render", "avgRender",
231                 "minWindow", "midPoint", "maxWindow",
232                 "vsyncToFlip", "flipToVsync", "vsyncsBetweenFlip",
233                 "numFrame", "insertDuration",
234                 "vTotalMin", "vTotalMax", "eventTrigs",
235                 "vSyncTime1", "vSyncTime2", "vSyncTime3",
236                 "vSyncTime4", "vSyncTime5", "flags");
237
238         for (int i = 0; i < core_stats->entry_id; i++) {
239                 if (event_index < core_stats->event_index &&
240                                 i == events[event_index].entry_id) {
241                         DISPLAY_STATS("==Event==%s\n", events[event_index].event_string);
242                         event_index++;
243                 } else if (time_index < core_stats->index &&
244                                 i == time[time_index].entry_id) {
245                         DISPLAY_STATS("%10u %10u %10u %10u %10u"
246                                         " %11u %11u %17u %10u %14u"
247                                         " %10u %10u %10u %10u %10u"
248                                         " %10u %10u %10u %10u\n",
249                                 time[time_index].render_time_in_us,
250                                 time[time_index].avg_render_time_in_us_last_ten,
251                                 time[time_index].min_window,
252                                 time[time_index].lfc_mid_point_in_us,
253                                 time[time_index].max_window,
254                                 time[time_index].vsync_to_flip_time_in_us,
255                                 time[time_index].flip_to_vsync_time_in_us,
256                                 time[time_index].num_vsync_between_flips,
257                                 time[time_index].num_frames_inserted,
258                                 time[time_index].inserted_duration_in_us,
259                                 time[time_index].v_total_min,
260                                 time[time_index].v_total_max,
261                                 time[time_index].event_triggers,
262                                 time[time_index].v_sync_time_in_us[0],
263                                 time[time_index].v_sync_time_in_us[1],
264                                 time[time_index].v_sync_time_in_us[2],
265                                 time[time_index].v_sync_time_in_us[3],
266                                 time[time_index].v_sync_time_in_us[4],
267                                 time[time_index].flags);
268
269                         time_index++;
270                 }
271         }
272
273         DISPLAY_STATS_END(log_entry);
274 }
275
276 void mod_stats_reset_data(struct mod_stats *mod_stats)
277 {
278         struct core_stats *core_stats = NULL;
279         struct stats_time_cache *time = NULL;
280         unsigned int index = 0;
281
282         if (mod_stats == NULL)
283                 return;
284
285         core_stats = MOD_STATS_TO_CORE(mod_stats);
286
287         memset(core_stats->time, 0,
288                 sizeof(struct stats_time_cache) * core_stats->entries);
289
290         memset(core_stats->events, 0,
291                 sizeof(struct stats_event_cache) * core_stats->event_entries);
292
293         core_stats->index = 1;
294         core_stats->event_index = 0;
295
296         // Keeps track of ordering within the different stats structures
297         core_stats->entry_id = 0;
298 }
299
300 void mod_stats_update_event(struct mod_stats *mod_stats,
301                 char *event_string,
302                 unsigned int length)
303 {
304         struct core_stats *core_stats = NULL;
305         struct stats_event_cache *events = NULL;
306         unsigned int index = 0;
307         unsigned int copy_length = 0;
308
309         if (mod_stats == NULL)
310                 return;
311
312         core_stats = MOD_STATS_TO_CORE(mod_stats);
313
314         if (core_stats->event_index >= core_stats->event_entries)
315                 return;
316
317         events = core_stats->events;
318         index = core_stats->event_index;
319
320         copy_length = length;
321         if (length > MOD_STATS_EVENT_STRING_MAX)
322                 copy_length = MOD_STATS_EVENT_STRING_MAX;
323
324         memcpy(&events[index].event_string, event_string, copy_length);
325         events[index].event_string[copy_length - 1] = '\0';
326
327         events[index].entry_id = core_stats->entry_id;
328         core_stats->event_index++;
329         core_stats->entry_id++;
330 }
331
332 void mod_stats_update_flip(struct mod_stats *mod_stats,
333                 unsigned long timestamp_in_ns)
334 {
335         struct core_stats *core_stats = NULL;
336         struct stats_time_cache *time = NULL;
337         unsigned int index = 0;
338
339         if (mod_stats == NULL)
340                 return;
341
342         core_stats = MOD_STATS_TO_CORE(mod_stats);
343
344         if (core_stats->index >= core_stats->entries)
345                 return;
346
347         time = core_stats->time;
348         index = core_stats->index;
349
350         time[index].flip_timestamp_in_ns = timestamp_in_ns;
351         time[index].render_time_in_us =
352                 (timestamp_in_ns - time[index - 1].flip_timestamp_in_ns) / 1000;
353
354         if (index >= 10) {
355                 for (unsigned int i = 0; i < 10; i++)
356                         time[index].avg_render_time_in_us_last_ten +=
357                                         time[index - i].render_time_in_us;
358                 time[index].avg_render_time_in_us_last_ten /= 10;
359         }
360
361         if (time[index].num_vsync_between_flips > 0)
362                 time[index].vsync_to_flip_time_in_us =
363                         (timestamp_in_ns -
364                                 time[index].vupdate_timestamp_in_ns) / 1000;
365         else
366                 time[index].vsync_to_flip_time_in_us =
367                         (timestamp_in_ns -
368                                 time[index - 1].vupdate_timestamp_in_ns) / 1000;
369
370         time[index].entry_id = core_stats->entry_id;
371         core_stats->index++;
372         core_stats->entry_id++;
373 }
374
375 void mod_stats_update_vupdate(struct mod_stats *mod_stats,
376                 unsigned long timestamp_in_ns)
377 {
378         struct core_stats *core_stats = NULL;
379         struct stats_time_cache *time = NULL;
380         unsigned int index = 0;
381         unsigned int num_vsyncs = 0;
382         unsigned int prev_vsync_in_ns = 0;
383
384         if (mod_stats == NULL)
385                 return;
386
387         core_stats = MOD_STATS_TO_CORE(mod_stats);
388
389         if (core_stats->index >= core_stats->entries)
390                 return;
391
392         time = core_stats->time;
393         index = core_stats->index;
394         num_vsyncs = time[index].num_vsync_between_flips;
395
396         if (num_vsyncs < MOD_STATS_NUM_VSYNCS) {
397                 if (num_vsyncs == 0) {
398                         prev_vsync_in_ns =
399                                 time[index - 1].vupdate_timestamp_in_ns;
400
401                         time[index].flip_to_vsync_time_in_us =
402                                 (timestamp_in_ns -
403                                         time[index - 1].flip_timestamp_in_ns) /
404                                         1000;
405                 } else {
406                         prev_vsync_in_ns =
407                                 time[index].vupdate_timestamp_in_ns;
408                 }
409
410                 time[index].v_sync_time_in_us[num_vsyncs] =
411                         (timestamp_in_ns - prev_vsync_in_ns) / 1000;
412         }
413
414         time[index].vupdate_timestamp_in_ns = timestamp_in_ns;
415         time[index].num_vsync_between_flips++;
416 }
417
418 void mod_stats_update_freesync(struct mod_stats *mod_stats,
419                 unsigned int v_total_min,
420                 unsigned int v_total_max,
421                 unsigned int event_triggers,
422                 unsigned int window_min,
423                 unsigned int window_max,
424                 unsigned int lfc_mid_point_in_us,
425                 unsigned int inserted_frames,
426                 unsigned int inserted_duration_in_us)
427 {
428         struct core_stats *core_stats = NULL;
429         struct stats_time_cache *time = NULL;
430         unsigned int index = 0;
431
432         if (mod_stats == NULL)
433                 return;
434
435         core_stats = MOD_STATS_TO_CORE(mod_stats);
436
437         if (core_stats->index >= core_stats->entries)
438                 return;
439
440         time = core_stats->time;
441         index = core_stats->index;
442
443         time[index].v_total_min = v_total_min;
444         time[index].v_total_max = v_total_max;
445         time[index].event_triggers = event_triggers;
446         time[index].min_window = window_min;
447         time[index].max_window = window_max;
448         time[index].lfc_mid_point_in_us = lfc_mid_point_in_us;
449         time[index].num_frames_inserted = inserted_frames;
450         time[index].inserted_duration_in_us = inserted_duration_in_us;
451 }
452