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 ®_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 ®_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 453