Commit | Line | Data |
---|---|---|
a3e1737e AK |
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 0x00000001 | |
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 | ||
6474b282 AK |
39 | #define DAL_STATS_EVENT_ENTRIES_DEFAULT 0x00000100 |
40 | ||
a3e1737e | 41 | #define MOD_STATS_NUM_VSYNCS 5 |
6474b282 | 42 | #define MOD_STATS_EVENT_STRING_MAX 512 |
a3e1737e AK |
43 | |
44 | struct stats_time_cache { | |
6474b282 AK |
45 | unsigned int entry_id; |
46 | ||
a3e1737e AK |
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 | ||
6474b282 AK |
71 | struct stats_event_cache { |
72 | unsigned int entry_id; | |
73 | char event_string[MOD_STATS_EVENT_STRING_MAX]; | |
74 | }; | |
75 | ||
a3e1737e AK |
76 | struct core_stats { |
77 | struct mod_stats public; | |
78 | struct dc *dc; | |
79 | ||
6474b282 AK |
80 | bool enabled; |
81 | unsigned int entries; | |
82 | unsigned int event_entries; | |
83 | unsigned int entry_id; | |
84 | ||
a3e1737e AK |
85 | struct stats_time_cache *time; |
86 | unsigned int index; | |
87 | ||
6474b282 AK |
88 | struct stats_event_cache *events; |
89 | unsigned int event_index; | |
90 | ||
a3e1737e AK |
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 | ||
9fcab85c AK |
118 | if (dc == NULL) |
119 | goto fail_construct; | |
120 | ||
a3e1737e AK |
121 | core_stats = kzalloc(sizeof(struct core_stats), GFP_KERNEL); |
122 | ||
123 | if (core_stats == NULL) | |
a3e1737e AK |
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 | ®_data, sizeof(unsigned int), &flag)) | |
132 | core_stats->enabled = reg_data; | |
133 | ||
c4b0faae AK |
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; | |
141 | else | |
142 | core_stats->entries = reg_data; | |
143 | } | |
6396bb22 KC |
144 | core_stats->time = kcalloc(core_stats->entries, |
145 | sizeof(struct stats_time_cache), | |
6474b282 | 146 | GFP_KERNEL); |
c4b0faae | 147 | |
9fcab85c AK |
148 | if (core_stats->time == NULL) |
149 | goto fail_construct_time; | |
6474b282 AK |
150 | |
151 | core_stats->event_entries = DAL_STATS_EVENT_ENTRIES_DEFAULT; | |
6396bb22 KC |
152 | core_stats->events = kcalloc(core_stats->event_entries, |
153 | sizeof(struct stats_event_cache), | |
154 | GFP_KERNEL); | |
6474b282 | 155 | |
9fcab85c AK |
156 | if (core_stats->events == NULL) |
157 | goto fail_construct_events; | |
158 | ||
c4b0faae AK |
159 | } else { |
160 | core_stats->entries = 0; | |
a3e1737e AK |
161 | } |
162 | ||
a3e1737e AK |
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; | |
6474b282 AK |
167 | core_stats->event_index = 0; |
168 | ||
169 | // Keeps track of ordering within the different stats structures | |
170 | core_stats->entry_id = 0; | |
a3e1737e AK |
171 | |
172 | return &core_stats->public; | |
173 | ||
9fcab85c AK |
174 | fail_construct_events: |
175 | kfree(core_stats->time); | |
176 | ||
177 | fail_construct_time: | |
a3e1737e AK |
178 | kfree(core_stats); |
179 | ||
9fcab85c | 180 | fail_construct: |
a3e1737e AK |
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 | ||
9fcab85c AK |
192 | if (core_stats->events != NULL) |
193 | kfree(core_stats->events); | |
194 | ||
a3e1737e AK |
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; | |
6474b282 AK |
205 | struct stats_event_cache *events = NULL; |
206 | unsigned int time_index = 1; | |
207 | unsigned int event_index = 0; | |
a3e1737e | 208 | unsigned int index = 0; |
66dec27a | 209 | struct log_entry log_entry; |
a3e1737e AK |
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; | |
6474b282 | 218 | events = core_stats->events; |
a3e1737e | 219 | |
5103c568 | 220 | DISPLAY_STATS_BEGIN(log_entry); |
e8838df1 | 221 | |
5103c568 | 222 | DISPLAY_STATS("==Display Caps==\n"); |
e8838df1 | 223 | |
5103c568 | 224 | DISPLAY_STATS("==Display Stats==\n"); |
66dec27a | 225 | |
5103c568 | 226 | DISPLAY_STATS("%10s %10s %10s %10s %10s" |
e8838df1 AK |
227 | " %11s %11s %17s %10s %14s" |
228 | " %10s %10s %10s %10s %10s" | |
66dec27a | 229 | " %10s %10s %10s %10s\n", |
e8838df1 AK |
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 | ||
6474b282 AK |
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); | |
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 | } | |
a3e1737e | 271 | } |
66dec27a | 272 | |
5103c568 | 273 | DISPLAY_STATS_END(log_entry); |
a3e1737e AK |
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 | ||
6474b282 AK |
290 | memset(core_stats->events, 0, |
291 | sizeof(struct stats_event_cache) * core_stats->event_entries); | |
292 | ||
f412e830 | 293 | core_stats->index = 1; |
6474b282 AK |
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 | ||
dab911d5 | 314 | if (core_stats->event_index >= core_stats->event_entries) |
6474b282 AK |
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++; | |
a3e1737e AK |
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 = | |
f412e830 | 352 | (timestamp_in_ns - time[index - 1].flip_timestamp_in_ns) / 1000; |
a3e1737e AK |
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 = | |
f412e830 AK |
363 | (timestamp_in_ns - |
364 | time[index].vupdate_timestamp_in_ns) / 1000; | |
a3e1737e AK |
365 | else |
366 | time[index].vsync_to_flip_time_in_us = | |
f412e830 AK |
367 | (timestamp_in_ns - |
368 | time[index - 1].vupdate_timestamp_in_ns) / 1000; | |
a3e1737e | 369 | |
6474b282 | 370 | time[index].entry_id = core_stats->entry_id; |
a3e1737e | 371 | core_stats->index++; |
6474b282 | 372 | core_stats->entry_id++; |
a3e1737e AK |
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; | |
f412e830 AK |
381 | unsigned int num_vsyncs = 0; |
382 | unsigned int prev_vsync_in_ns = 0; | |
a3e1737e AK |
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; | |
f412e830 AK |
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 | } | |
a3e1737e | 409 | |
f412e830 AK |
410 | time[index].v_sync_time_in_us[num_vsyncs] = |
411 | (timestamp_in_ns - prev_vsync_in_ns) / 1000; | |
412 | } | |
a3e1737e | 413 | |
f412e830 | 414 | time[index].vupdate_timestamp_in_ns = timestamp_in_ns; |
a3e1737e AK |
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 |