Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.9.4.
  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
 44struct 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
 71struct stats_event_cache {
 72	unsigned int entry_id;
 73	char event_string[MOD_STATS_EVENT_STRING_MAX];
 74};
 75
 76struct 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
 96bool 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
111struct 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
174fail_construct_events:
175	kfree(core_stats->time);
176
177fail_construct_time:
178	kfree(core_stats);
179
180fail_construct:
181	return NULL;
182}
183
184void 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		kfree(core_stats->time);
190		kfree(core_stats->events);
191		kfree(core_stats);
192	}
193}
194
195void mod_stats_dump(struct mod_stats *mod_stats)
196{
197	struct dc  *dc = NULL;
198	struct dal_logger *logger = NULL;
199	struct core_stats *core_stats = NULL;
200	struct stats_time_cache *time = NULL;
201	struct stats_event_cache *events = NULL;
202	unsigned int time_index = 1;
203	unsigned int event_index = 0;
204	unsigned int index = 0;
205	struct log_entry log_entry;
206
207	if (mod_stats == NULL)
208		return;
209
210	core_stats = MOD_STATS_TO_CORE(mod_stats);
211	dc = core_stats->dc;
212	logger = dc->ctx->logger;
213	time = core_stats->time;
214	events = core_stats->events;
215
216	DISPLAY_STATS_BEGIN(log_entry);
217
218	DISPLAY_STATS("==Display Caps==\n");
219
220	DISPLAY_STATS("==Display Stats==\n");
221
222	DISPLAY_STATS("%10s %10s %10s %10s %10s"
223			" %11s %11s %17s %10s %14s"
224			" %10s %10s %10s %10s %10s"
225			" %10s %10s %10s %10s\n",
226		"render", "avgRender",
227		"minWindow", "midPoint", "maxWindow",
228		"vsyncToFlip", "flipToVsync", "vsyncsBetweenFlip",
229		"numFrame", "insertDuration",
230		"vTotalMin", "vTotalMax", "eventTrigs",
231		"vSyncTime1", "vSyncTime2", "vSyncTime3",
232		"vSyncTime4", "vSyncTime5", "flags");
233
234	for (int i = 0; i < core_stats->entry_id; i++) {
235		if (event_index < core_stats->event_index &&
236				i == events[event_index].entry_id) {
237			DISPLAY_STATS("==Event==%s\n", events[event_index].event_string);
238			event_index++;
239		} else if (time_index < core_stats->index &&
240				i == time[time_index].entry_id) {
241			DISPLAY_STATS("%10u %10u %10u %10u %10u"
242					" %11u %11u %17u %10u %14u"
243					" %10u %10u %10u %10u %10u"
244					" %10u %10u %10u %10u\n",
245				time[time_index].render_time_in_us,
246				time[time_index].avg_render_time_in_us_last_ten,
247				time[time_index].min_window,
248				time[time_index].lfc_mid_point_in_us,
249				time[time_index].max_window,
250				time[time_index].vsync_to_flip_time_in_us,
251				time[time_index].flip_to_vsync_time_in_us,
252				time[time_index].num_vsync_between_flips,
253				time[time_index].num_frames_inserted,
254				time[time_index].inserted_duration_in_us,
255				time[time_index].v_total_min,
256				time[time_index].v_total_max,
257				time[time_index].event_triggers,
258				time[time_index].v_sync_time_in_us[0],
259				time[time_index].v_sync_time_in_us[1],
260				time[time_index].v_sync_time_in_us[2],
261				time[time_index].v_sync_time_in_us[3],
262				time[time_index].v_sync_time_in_us[4],
263				time[time_index].flags);
264
265			time_index++;
266		}
267	}
268
269	DISPLAY_STATS_END(log_entry);
270}
271
272void mod_stats_reset_data(struct mod_stats *mod_stats)
273{
274	struct core_stats *core_stats = NULL;
275	struct stats_time_cache *time = NULL;
276	unsigned int index = 0;
277
278	if (mod_stats == NULL)
279		return;
280
281	core_stats = MOD_STATS_TO_CORE(mod_stats);
282
283	memset(core_stats->time, 0,
284		sizeof(struct stats_time_cache) * core_stats->entries);
285
286	memset(core_stats->events, 0,
287		sizeof(struct stats_event_cache) * core_stats->event_entries);
288
289	core_stats->index = 1;
290	core_stats->event_index = 0;
291
292	// Keeps track of ordering within the different stats structures
293	core_stats->entry_id = 0;
294}
295
296void mod_stats_update_event(struct mod_stats *mod_stats,
297		char *event_string,
298		unsigned int length)
299{
300	struct core_stats *core_stats = NULL;
301	struct stats_event_cache *events = NULL;
302	unsigned int index = 0;
303	unsigned int copy_length = 0;
304
305	if (mod_stats == NULL)
306		return;
307
308	core_stats = MOD_STATS_TO_CORE(mod_stats);
309
310	if (core_stats->event_index >= core_stats->event_entries)
311		return;
312
313	events = core_stats->events;
314	index = core_stats->event_index;
315
316	copy_length = length;
317	if (length > MOD_STATS_EVENT_STRING_MAX)
318		copy_length = MOD_STATS_EVENT_STRING_MAX;
319
320	memcpy(&events[index].event_string, event_string, copy_length);
321	events[index].event_string[copy_length - 1] = '\0';
322
323	events[index].entry_id = core_stats->entry_id;
324	core_stats->event_index++;
325	core_stats->entry_id++;
326}
327
328void mod_stats_update_flip(struct mod_stats *mod_stats,
329		unsigned long timestamp_in_ns)
330{
331	struct core_stats *core_stats = NULL;
332	struct stats_time_cache *time = NULL;
333	unsigned int index = 0;
334
335	if (mod_stats == NULL)
336		return;
337
338	core_stats = MOD_STATS_TO_CORE(mod_stats);
339
340	if (core_stats->index >= core_stats->entries)
341		return;
342
343	time = core_stats->time;
344	index = core_stats->index;
345
346	time[index].flip_timestamp_in_ns = timestamp_in_ns;
347	time[index].render_time_in_us =
348		(timestamp_in_ns - time[index - 1].flip_timestamp_in_ns) / 1000;
349
350	if (index >= 10) {
351		for (unsigned int i = 0; i < 10; i++)
352			time[index].avg_render_time_in_us_last_ten +=
353					time[index - i].render_time_in_us;
354		time[index].avg_render_time_in_us_last_ten /= 10;
355	}
356
357	if (time[index].num_vsync_between_flips > 0)
358		time[index].vsync_to_flip_time_in_us =
359			(timestamp_in_ns -
360				time[index].vupdate_timestamp_in_ns) / 1000;
361	else
362		time[index].vsync_to_flip_time_in_us =
363			(timestamp_in_ns -
364				time[index - 1].vupdate_timestamp_in_ns) / 1000;
365
366	time[index].entry_id = core_stats->entry_id;
367	core_stats->index++;
368	core_stats->entry_id++;
369}
370
371void mod_stats_update_vupdate(struct mod_stats *mod_stats,
372		unsigned long timestamp_in_ns)
373{
374	struct core_stats *core_stats = NULL;
375	struct stats_time_cache *time = NULL;
376	unsigned int index = 0;
377	unsigned int num_vsyncs = 0;
378	unsigned int prev_vsync_in_ns = 0;
379
380	if (mod_stats == NULL)
381		return;
382
383	core_stats = MOD_STATS_TO_CORE(mod_stats);
384
385	if (core_stats->index >= core_stats->entries)
386		return;
387
388	time = core_stats->time;
389	index = core_stats->index;
390	num_vsyncs = time[index].num_vsync_between_flips;
391
392	if (num_vsyncs < MOD_STATS_NUM_VSYNCS) {
393		if (num_vsyncs == 0) {
394			prev_vsync_in_ns =
395				time[index - 1].vupdate_timestamp_in_ns;
396
397			time[index].flip_to_vsync_time_in_us =
398				(timestamp_in_ns -
399					time[index - 1].flip_timestamp_in_ns) /
400					1000;
401		} else {
402			prev_vsync_in_ns =
403				time[index].vupdate_timestamp_in_ns;
404		}
405
406		time[index].v_sync_time_in_us[num_vsyncs] =
407			(timestamp_in_ns - prev_vsync_in_ns) / 1000;
408	}
409
410	time[index].vupdate_timestamp_in_ns = timestamp_in_ns;
411	time[index].num_vsync_between_flips++;
412}
413
414void mod_stats_update_freesync(struct mod_stats *mod_stats,
415		unsigned int v_total_min,
416		unsigned int v_total_max,
417		unsigned int event_triggers,
418		unsigned int window_min,
419		unsigned int window_max,
420		unsigned int lfc_mid_point_in_us,
421		unsigned int inserted_frames,
422		unsigned int inserted_duration_in_us)
423{
424	struct core_stats *core_stats = NULL;
425	struct stats_time_cache *time = NULL;
426	unsigned int index = 0;
427
428	if (mod_stats == NULL)
429		return;
430
431	core_stats = MOD_STATS_TO_CORE(mod_stats);
432
433	if (core_stats->index >= core_stats->entries)
434		return;
435
436	time = core_stats->time;
437	index = core_stats->index;
438
439	time[index].v_total_min = v_total_min;
440	time[index].v_total_max = v_total_max;
441	time[index].event_triggers = event_triggers;
442	time[index].min_window = window_min;
443	time[index].max_window = window_max;
444	time[index].lfc_mid_point_in_us = lfc_mid_point_in_us;
445	time[index].num_frames_inserted = inserted_frames;
446	time[index].inserted_duration_in_us = inserted_duration_in_us;
447}
448