1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) STMicroelectronics SA 2015
4 * Authors: Yannick Fertre <yannick.fertre@st.com>
5 *          Hugues Fruchet <hugues.fruchet@st.com>
6 */
7
8#include <linux/debugfs.h>
9
10#include "hva.h"
11#include "hva-hw.h"
12
13static void format_ctx(struct seq_file *s, struct hva_ctx *ctx)
14{
15	struct hva_streaminfo *stream = &ctx->streaminfo;
16	struct hva_frameinfo *frame = &ctx->frameinfo;
17	struct hva_controls *ctrls = &ctx->ctrls;
18	struct hva_ctx_dbg *dbg = &ctx->dbg;
19	u32 bitrate_mode, aspect, entropy, vui_sar, sei_fp;
20
21	seq_printf(s, "|-%s\n  |\n", ctx->name);
22
23	seq_printf(s, "  |-[%sframe info]\n",
24		   ctx->flags & HVA_FLAG_FRAMEINFO ? "" : "default ");
25	seq_printf(s, "  | |- pixel format=%4.4s\n"
26		      "  | |- wxh=%dx%d\n"
27		      "  | |- wxh (w/ encoder alignment constraint)=%dx%d\n"
28		      "  |\n",
29		      (char *)&frame->pixelformat,
30		      frame->width, frame->height,
31		      frame->aligned_width, frame->aligned_height);
32
33	seq_printf(s, "  |-[%sstream info]\n",
34		   ctx->flags & HVA_FLAG_STREAMINFO ? "" : "default ");
35	seq_printf(s, "  | |- stream format=%4.4s\n"
36		      "  | |- wxh=%dx%d\n"
37		      "  | |- %s\n"
38		      "  | |- %s\n"
39		      "  |\n",
40		      (char *)&stream->streamformat,
41		      stream->width, stream->height,
42		      stream->profile, stream->level);
43
44	bitrate_mode = V4L2_CID_MPEG_VIDEO_BITRATE_MODE;
45	aspect = V4L2_CID_MPEG_VIDEO_ASPECT;
46	seq_puts(s, "  |-[parameters]\n");
47	seq_printf(s, "  | |- %s\n"
48		      "  | |- bitrate=%d bps\n"
49		      "  | |- GOP size=%d\n"
50		      "  | |- video aspect=%s\n"
51		      "  | |- framerate=%d/%d\n",
52		      v4l2_ctrl_get_menu(bitrate_mode)[ctrls->bitrate_mode],
53		      ctrls->bitrate,
54		      ctrls->gop_size,
55		      v4l2_ctrl_get_menu(aspect)[ctrls->aspect],
56		      ctrls->time_per_frame.denominator,
57		      ctrls->time_per_frame.numerator);
58
59	entropy = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE;
60	vui_sar = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC;
61	sei_fp =  V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE;
62	if (stream->streamformat == V4L2_PIX_FMT_H264) {
63		seq_printf(s, "  | |- %s entropy mode\n"
64			      "  | |- CPB size=%d kB\n"
65			      "  | |- DCT8x8 enable=%s\n"
66			      "  | |- qpmin=%d\n"
67			      "  | |- qpmax=%d\n"
68			      "  | |- PAR enable=%s\n"
69			      "  | |- PAR id=%s\n"
70			      "  | |- SEI frame packing enable=%s\n"
71			      "  | |- SEI frame packing type=%s\n",
72			      v4l2_ctrl_get_menu(entropy)[ctrls->entropy_mode],
73			      ctrls->cpb_size,
74			      ctrls->dct8x8 ? "true" : "false",
75			      ctrls->qpmin,
76			      ctrls->qpmax,
77			      ctrls->vui_sar ? "true" : "false",
78			      v4l2_ctrl_get_menu(vui_sar)[ctrls->vui_sar_idc],
79			      ctrls->sei_fp ? "true" : "false",
80			      v4l2_ctrl_get_menu(sei_fp)[ctrls->sei_fp_type]);
81	}
82
83	if (ctx->sys_errors || ctx->encode_errors || ctx->frame_errors) {
84		seq_puts(s, "  |\n  |-[errors]\n");
85		seq_printf(s, "  | |- system=%d\n"
86			      "  | |- encoding=%d\n"
87			      "  | |- frame=%d\n",
88			      ctx->sys_errors,
89			      ctx->encode_errors,
90			      ctx->frame_errors);
91	}
92
93	seq_puts(s, "  |\n  |-[performances]\n");
94	seq_printf(s, "  | |- frames encoded=%d\n"
95		      "  | |- avg HW processing duration (0.1ms)=%d [min=%d, max=%d]\n"
96		      "  | |- avg encoding period (0.1ms)=%d [min=%d, max=%d]\n"
97		      "  | |- avg fps (0.1Hz)=%d\n"
98		      "  | |- max reachable fps (0.1Hz)=%d\n"
99		      "  | |- avg bitrate (kbps)=%d [min=%d, max=%d]\n"
100		      "  | |- last bitrate (kbps)=%d\n",
101		      dbg->cnt_duration,
102		      dbg->avg_duration,
103		      dbg->min_duration,
104		      dbg->max_duration,
105		      dbg->avg_period,
106		      dbg->min_period,
107		      dbg->max_period,
108		      dbg->avg_fps,
109		      dbg->max_fps,
110		      dbg->avg_bitrate,
111		      dbg->min_bitrate,
112		      dbg->max_bitrate,
113		      dbg->last_bitrate);
114}
115
116/*
117 * performance debug info
118 */
119void hva_dbg_perf_begin(struct hva_ctx *ctx)
120{
121	u64 div;
122	u32 period;
123	u32 bitrate;
124	struct hva_ctx_dbg *dbg = &ctx->dbg;
125	ktime_t prev = dbg->begin;
126
127	dbg->begin = ktime_get();
128
129	if (dbg->is_valid_period) {
130		/* encoding period */
131		div = (u64)ktime_us_delta(dbg->begin, prev);
132		do_div(div, 100);
133		period = (u32)div;
134		dbg->min_period = min(period, dbg->min_period);
135		dbg->max_period = max(period, dbg->max_period);
136		dbg->total_period += period;
137		dbg->cnt_period++;
138
139		/*
140		 * minimum and maximum bitrates are based on the
141		 * encoding period values upon a window of 32 samples
142		 */
143		dbg->window_duration += period;
144		dbg->cnt_window++;
145		if (dbg->cnt_window >= 32) {
146			/*
147			 * bitrate in kbps = (size * 8 / 1000) /
148			 *                   (duration / 10000)
149			 *                 = size * 80 / duration
150			 */
151			if (dbg->window_duration > 0) {
152				div = (u64)dbg->window_stream_size * 80;
153				do_div(div, dbg->window_duration);
154				bitrate = (u32)div;
155				dbg->last_bitrate = bitrate;
156				dbg->min_bitrate = min(bitrate,
157						       dbg->min_bitrate);
158				dbg->max_bitrate = max(bitrate,
159						       dbg->max_bitrate);
160			}
161			dbg->window_stream_size = 0;
162			dbg->window_duration = 0;
163			dbg->cnt_window = 0;
164		}
165	}
166
167	/*
168	 * filter sequences valid for performance:
169	 * - begin/begin (no stream available) is an invalid sequence
170	 * - begin/end is a valid sequence
171	 */
172	dbg->is_valid_period = false;
173}
174
175void hva_dbg_perf_end(struct hva_ctx *ctx, struct hva_stream *stream)
176{
177	struct device *dev = ctx_to_dev(ctx);
178	u64 div;
179	u32 duration;
180	u32 bytesused;
181	u32 timestamp;
182	struct hva_ctx_dbg *dbg = &ctx->dbg;
183	ktime_t end = ktime_get();
184
185	/* stream bytesused and timestamp in us */
186	bytesused = vb2_get_plane_payload(&stream->vbuf.vb2_buf, 0);
187	div = stream->vbuf.vb2_buf.timestamp;
188	do_div(div, 1000);
189	timestamp = (u32)div;
190
191	/* encoding duration */
192	div = (u64)ktime_us_delta(end, dbg->begin);
193
194	dev_dbg(dev,
195		"%s perf stream[%d] dts=%d encoded using %d bytes in %d us",
196		ctx->name,
197		stream->vbuf.sequence,
198		timestamp,
199		bytesused, (u32)div);
200
201	do_div(div, 100);
202	duration = (u32)div;
203
204	dbg->min_duration = min(duration, dbg->min_duration);
205	dbg->max_duration = max(duration, dbg->max_duration);
206	dbg->total_duration += duration;
207	dbg->cnt_duration++;
208
209	/*
210	 * the average bitrate is based on the total stream size
211	 * and the total encoding periods
212	 */
213	dbg->total_stream_size += bytesused;
214	dbg->window_stream_size += bytesused;
215
216	dbg->is_valid_period = true;
217}
218
219static void hva_dbg_perf_compute(struct hva_ctx *ctx)
220{
221	u64 div;
222	struct hva_ctx_dbg *dbg = &ctx->dbg;
223
224	if (dbg->cnt_duration > 0) {
225		div = (u64)dbg->total_duration;
226		do_div(div, dbg->cnt_duration);
227		dbg->avg_duration = (u32)div;
228	} else {
229		dbg->avg_duration = 0;
230	}
231
232	if (dbg->total_duration > 0) {
233		div = (u64)dbg->cnt_duration * 100000;
234		do_div(div, dbg->total_duration);
235		dbg->max_fps = (u32)div;
236	} else {
237		dbg->max_fps = 0;
238	}
239
240	if (dbg->cnt_period > 0) {
241		div = (u64)dbg->total_period;
242		do_div(div, dbg->cnt_period);
243		dbg->avg_period = (u32)div;
244	} else {
245		dbg->avg_period = 0;
246	}
247
248	if (dbg->total_period > 0) {
249		div = (u64)dbg->cnt_period * 100000;
250		do_div(div, dbg->total_period);
251		dbg->avg_fps = (u32)div;
252	} else {
253		dbg->avg_fps = 0;
254	}
255
256	if (dbg->total_period > 0) {
257		/*
258		 * bitrate in kbps = (video size * 8 / 1000) /
259		 *                   (video duration / 10000)
260		 *                 = video size * 80 / video duration
261		 */
262		div = (u64)dbg->total_stream_size * 80;
263		do_div(div, dbg->total_period);
264		dbg->avg_bitrate = (u32)div;
265	} else {
266		dbg->avg_bitrate = 0;
267	}
268}
269
270/*
271 * device debug info
272 */
273
274static int device_show(struct seq_file *s, void *data)
275{
276	struct hva_dev *hva = s->private;
277
278	seq_printf(s, "[%s]\n", hva->v4l2_dev.name);
279	seq_printf(s, "registered as /dev/video%d\n", hva->vdev->num);
280
281	return 0;
282}
283
284static int encoders_show(struct seq_file *s, void *data)
285{
286	struct hva_dev *hva = s->private;
287	unsigned int i = 0;
288
289	seq_printf(s, "[encoders]\n|- %d registered encoders:\n",
290		   hva->nb_of_encoders);
291
292	while (hva->encoders[i]) {
293		seq_printf(s, "|- %s: %4.4s => %4.4s\n", hva->encoders[i]->name,
294			   (char *)&hva->encoders[i]->pixelformat,
295			   (char *)&hva->encoders[i]->streamformat);
296		i++;
297	}
298
299	return 0;
300}
301
302static int last_show(struct seq_file *s, void *data)
303{
304	struct hva_dev *hva = s->private;
305	struct hva_ctx *last_ctx = &hva->dbg.last_ctx;
306
307	if (last_ctx->flags & HVA_FLAG_STREAMINFO) {
308		seq_puts(s, "[last encoding]\n");
309
310		hva_dbg_perf_compute(last_ctx);
311		format_ctx(s, last_ctx);
312	} else {
313		seq_puts(s, "[no information recorded about last encoding]\n");
314	}
315
316	return 0;
317}
318
319static int regs_show(struct seq_file *s, void *data)
320{
321	struct hva_dev *hva = s->private;
322
323	hva_hw_dump_regs(hva, s);
324
325	return 0;
326}
327
328#define hva_dbg_create_entry(name)					 \
329	debugfs_create_file(#name, 0444, hva->dbg.debugfs_entry, hva, \
330			    &name##_fops)
331
332DEFINE_SHOW_ATTRIBUTE(device);
333DEFINE_SHOW_ATTRIBUTE(encoders);
334DEFINE_SHOW_ATTRIBUTE(last);
335DEFINE_SHOW_ATTRIBUTE(regs);
336
337void hva_debugfs_create(struct hva_dev *hva)
338{
339	hva->dbg.debugfs_entry = debugfs_create_dir(HVA_NAME, NULL);
340
341	hva_dbg_create_entry(device);
342	hva_dbg_create_entry(encoders);
343	hva_dbg_create_entry(last);
344	hva_dbg_create_entry(regs);
345}
346
347void hva_debugfs_remove(struct hva_dev *hva)
348{
349	debugfs_remove_recursive(hva->dbg.debugfs_entry);
350	hva->dbg.debugfs_entry = NULL;
351}
352
353/*
354 * context (instance) debug info
355 */
356
357static int ctx_show(struct seq_file *s, void *data)
358{
359	struct hva_ctx *ctx = s->private;
360
361	seq_printf(s, "[running encoding %d]\n", ctx->id);
362
363	hva_dbg_perf_compute(ctx);
364	format_ctx(s, ctx);
365
366	return 0;
367}
368
369DEFINE_SHOW_ATTRIBUTE(ctx);
370
371void hva_dbg_ctx_create(struct hva_ctx *ctx)
372{
373	struct hva_dev *hva = ctx->hva_dev;
374	char name[4] = "";
375
376	ctx->dbg.min_duration = UINT_MAX;
377	ctx->dbg.min_period = UINT_MAX;
378	ctx->dbg.min_bitrate = UINT_MAX;
379
380	snprintf(name, sizeof(name), "%d", hva->instance_id);
381
382	ctx->dbg.debugfs_entry = debugfs_create_file(name, 0444,
383						     hva->dbg.debugfs_entry,
384						     ctx, &ctx_fops);
385}
386
387void hva_dbg_ctx_remove(struct hva_ctx *ctx)
388{
389	struct hva_dev *hva = ctx->hva_dev;
390
391	if (ctx->flags & HVA_FLAG_STREAMINFO)
392		/* save context before removing */
393		memcpy(&hva->dbg.last_ctx, ctx, sizeof(*ctx));
394
395	debugfs_remove(ctx->dbg.debugfs_entry);
396}
397