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 			&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 
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