2 * Copyright 2016 Advanced Micro Devices, Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
26 #include "mod_stats.h"
27 #include "dm_services.h"
29 #include "core_types.h"
31 #define DAL_STATS_ENABLE_REGKEY "DalStatsEnable"
32 #define DAL_STATS_ENABLE_REGKEY_DEFAULT 0x00000001
33 #define DAL_STATS_ENABLE_REGKEY_ENABLED 0x00000001
35 #define DAL_STATS_ENTRIES_REGKEY "DalStatsEntries"
36 #define DAL_STATS_ENTRIES_REGKEY_DEFAULT 0x00350000
37 #define DAL_STATS_ENTRIES_REGKEY_MAX 0x01000000
39 #define DAL_STATS_EVENT_ENTRIES_DEFAULT 0x00000100
41 #define MOD_STATS_NUM_VSYNCS 5
42 #define MOD_STATS_EVENT_STRING_MAX 512
44 struct stats_time_cache
{
45 unsigned int entry_id
;
47 unsigned long flip_timestamp_in_ns
;
48 unsigned long vupdate_timestamp_in_ns
;
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
;
55 unsigned int flip_to_vsync_time_in_us
;
56 unsigned int vsync_to_flip_time_in_us
;
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
;
64 unsigned int lfc_mid_point_in_us
;
65 unsigned int num_frames_inserted
;
66 unsigned int inserted_duration_in_us
;
71 struct stats_event_cache
{
72 unsigned int entry_id
;
73 char event_string
[MOD_STATS_EVENT_STRING_MAX
];
77 struct mod_stats
public;
82 unsigned int event_entries
;
83 unsigned int entry_id
;
85 struct stats_time_cache
*time
;
88 struct stats_event_cache
*events
;
89 unsigned int event_index
;
93 #define MOD_STATS_TO_CORE(mod_stats)\
94 container_of(mod_stats, struct core_stats, public)
96 bool mod_stats_init(struct mod_stats
*mod_stats
)
99 struct core_stats
*core_stats
= NULL
;
100 struct dc
*dc
= NULL
;
102 if (mod_stats
== NULL
)
105 core_stats
= MOD_STATS_TO_CORE(mod_stats
);
111 struct mod_stats
*mod_stats_create(struct dc
*dc
)
113 struct core_stats
*core_stats
= NULL
;
114 struct persistent_data_flag flag
;
115 unsigned int reg_data
;
121 core_stats
= kzalloc(sizeof(struct core_stats
), GFP_KERNEL
);
123 if (core_stats
== NULL
)
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 ®_data
, sizeof(unsigned int), &flag
))
132 core_stats
->enabled
= reg_data
;
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 ®_data
, sizeof(unsigned int), &flag
)) {
139 if (reg_data
> DAL_STATS_ENTRIES_REGKEY_MAX
)
140 core_stats
->entries
= DAL_STATS_ENTRIES_REGKEY_MAX
;
142 core_stats
->entries
= reg_data
;
144 core_stats
->time
= kcalloc(core_stats
->entries
,
145 sizeof(struct stats_time_cache
),
148 if (core_stats
->time
== NULL
)
149 goto fail_construct_time
;
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
),
156 if (core_stats
->events
== NULL
)
157 goto fail_construct_events
;
160 core_stats
->entries
= 0;
163 /* Purposely leave index 0 unused so we don't need special logic to
164 * handle calculation cases that depend on previous flip data.
166 core_stats
->index
= 1;
167 core_stats
->event_index
= 0;
169 // Keeps track of ordering within the different stats structures
170 core_stats
->entry_id
= 0;
172 return &core_stats
->public;
174 fail_construct_events
:
175 kfree(core_stats
->time
);
184 void mod_stats_destroy(struct mod_stats
*mod_stats
)
186 if (mod_stats
!= NULL
) {
187 struct core_stats
*core_stats
= MOD_STATS_TO_CORE(mod_stats
);
189 if (core_stats
->time
!= NULL
)
190 kfree(core_stats
->time
);
192 if (core_stats
->events
!= NULL
)
193 kfree(core_stats
->events
);
199 void mod_stats_dump(struct mod_stats
*mod_stats
)
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
;
211 if (mod_stats
== NULL
)
214 core_stats
= MOD_STATS_TO_CORE(mod_stats
);
216 logger
= dc
->ctx
->logger
;
217 time
= core_stats
->time
;
218 events
= core_stats
->events
;
220 DISPLAY_STATS_BEGIN(log_entry
);
222 DISPLAY_STATS("==Display Caps==\n");
224 DISPLAY_STATS("==Display Stats==\n");
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");
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("%s\n", events
[event_index
].event_string
);
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
);
273 DISPLAY_STATS_END(log_entry
);
276 void mod_stats_reset_data(struct mod_stats
*mod_stats
)
278 struct core_stats
*core_stats
= NULL
;
279 struct stats_time_cache
*time
= NULL
;
280 unsigned int index
= 0;
282 if (mod_stats
== NULL
)
285 core_stats
= MOD_STATS_TO_CORE(mod_stats
);
287 memset(core_stats
->time
, 0,
288 sizeof(struct stats_time_cache
) * core_stats
->entries
);
290 memset(core_stats
->events
, 0,
291 sizeof(struct stats_event_cache
) * core_stats
->event_entries
);
293 core_stats
->index
= 1;
294 core_stats
->event_index
= 0;
296 // Keeps track of ordering within the different stats structures
297 core_stats
->entry_id
= 0;
300 void mod_stats_update_event(struct mod_stats
*mod_stats
,
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;
309 if (mod_stats
== NULL
)
312 core_stats
= MOD_STATS_TO_CORE(mod_stats
);
314 if (core_stats
->event_index
>= core_stats
->event_entries
)
317 events
= core_stats
->events
;
318 index
= core_stats
->event_index
;
320 copy_length
= length
;
321 if (length
> MOD_STATS_EVENT_STRING_MAX
)
322 copy_length
= MOD_STATS_EVENT_STRING_MAX
;
324 memcpy(&events
[index
].event_string
, event_string
, copy_length
);
325 events
[index
].event_string
[copy_length
- 1] = '\0';
327 events
[index
].entry_id
= core_stats
->entry_id
;
328 core_stats
->event_index
++;
329 core_stats
->entry_id
++;
332 void mod_stats_update_flip(struct mod_stats
*mod_stats
,
333 unsigned long timestamp_in_ns
)
335 struct core_stats
*core_stats
= NULL
;
336 struct stats_time_cache
*time
= NULL
;
337 unsigned int index
= 0;
339 if (mod_stats
== NULL
)
342 core_stats
= MOD_STATS_TO_CORE(mod_stats
);
344 if (core_stats
->index
>= core_stats
->entries
)
347 time
= core_stats
->time
;
348 index
= core_stats
->index
;
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;
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;
361 if (time
[index
].num_vsync_between_flips
> 0)
362 time
[index
].vsync_to_flip_time_in_us
=
364 time
[index
].vupdate_timestamp_in_ns
) / 1000;
366 time
[index
].vsync_to_flip_time_in_us
=
368 time
[index
- 1].vupdate_timestamp_in_ns
) / 1000;
370 time
[index
].entry_id
= core_stats
->entry_id
;
372 core_stats
->entry_id
++;
375 void mod_stats_update_vupdate(struct mod_stats
*mod_stats
,
376 unsigned long timestamp_in_ns
)
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;
384 if (mod_stats
== NULL
)
387 core_stats
= MOD_STATS_TO_CORE(mod_stats
);
389 if (core_stats
->index
>= core_stats
->entries
)
392 time
= core_stats
->time
;
393 index
= core_stats
->index
;
394 num_vsyncs
= time
[index
].num_vsync_between_flips
;
396 if (num_vsyncs
< MOD_STATS_NUM_VSYNCS
) {
397 if (num_vsyncs
== 0) {
399 time
[index
- 1].vupdate_timestamp_in_ns
;
401 time
[index
].flip_to_vsync_time_in_us
=
403 time
[index
- 1].flip_timestamp_in_ns
) /
407 time
[index
].vupdate_timestamp_in_ns
;
410 time
[index
].v_sync_time_in_us
[num_vsyncs
] =
411 (timestamp_in_ns
- prev_vsync_in_ns
) / 1000;
414 time
[index
].vupdate_timestamp_in_ns
= timestamp_in_ns
;
415 time
[index
].num_vsync_between_flips
++;
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
)
428 struct core_stats
*core_stats
= NULL
;
429 struct stats_time_cache
*time
= NULL
;
430 unsigned int index
= 0;
432 if (mod_stats
== NULL
)
435 core_stats
= MOD_STATS_TO_CORE(mod_stats
);
437 if (core_stats
->index
>= core_stats
->entries
)
440 time
= core_stats
->time
;
441 index
= core_stats
->index
;
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
;