xref: /dragonfly/sys/dev/drm/amd/display/modules/stats/stats.c (revision b843c749addef9340ee7d4e250b09fdd492602a1)
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 
mod_stats_init(struct mod_stats * mod_stats)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 
mod_stats_create(struct dc * dc)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 
mod_stats_destroy(struct mod_stats * mod_stats)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 
mod_stats_dump(struct mod_stats * mod_stats)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 
mod_stats_reset_data(struct mod_stats * mod_stats)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 
mod_stats_update_event(struct mod_stats * mod_stats,char * event_string,unsigned int length)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 
mod_stats_update_flip(struct mod_stats * mod_stats,unsigned long timestamp_in_ns)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 
mod_stats_update_vupdate(struct mod_stats * mod_stats,unsigned long timestamp_in_ns)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 
mod_stats_update_freesync(struct mod_stats * mod_stats,unsigned int v_total_min,unsigned int v_total_max,unsigned int event_triggers,unsigned int window_min,unsigned int window_max,unsigned int lfc_mid_point_in_us,unsigned int inserted_frames,unsigned int inserted_duration_in_us)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 
453