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