// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2015 * Authors: Yannick Fertre * Hugues Fruchet */ #include #include "hva.h" #include "hva-hw.h" static void format_ctx(struct seq_file *s, struct hva_ctx *ctx) { struct hva_streaminfo *stream = &ctx->streaminfo; struct hva_frameinfo *frame = &ctx->frameinfo; struct hva_controls *ctrls = &ctx->ctrls; struct hva_ctx_dbg *dbg = &ctx->dbg; u32 bitrate_mode, aspect, entropy, vui_sar, sei_fp; seq_printf(s, "|-%s\n |\n", ctx->name); seq_printf(s, " |-[%sframe info]\n", ctx->flags & HVA_FLAG_FRAMEINFO ? "" : "default "); seq_printf(s, " | |- pixel format=%4.4s\n" " | |- wxh=%dx%d\n" " | |- wxh (w/ encoder alignment constraint)=%dx%d\n" " |\n", (char *)&frame->pixelformat, frame->width, frame->height, frame->aligned_width, frame->aligned_height); seq_printf(s, " |-[%sstream info]\n", ctx->flags & HVA_FLAG_STREAMINFO ? "" : "default "); seq_printf(s, " | |- stream format=%4.4s\n" " | |- wxh=%dx%d\n" " | |- %s\n" " | |- %s\n" " |\n", (char *)&stream->streamformat, stream->width, stream->height, stream->profile, stream->level); bitrate_mode = V4L2_CID_MPEG_VIDEO_BITRATE_MODE; aspect = V4L2_CID_MPEG_VIDEO_ASPECT; seq_puts(s, " |-[parameters]\n"); seq_printf(s, " | |- %s\n" " | |- bitrate=%d bps\n" " | |- GOP size=%d\n" " | |- video aspect=%s\n" " | |- framerate=%d/%d\n", v4l2_ctrl_get_menu(bitrate_mode)[ctrls->bitrate_mode], ctrls->bitrate, ctrls->gop_size, v4l2_ctrl_get_menu(aspect)[ctrls->aspect], ctrls->time_per_frame.denominator, ctrls->time_per_frame.numerator); entropy = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE; vui_sar = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC; sei_fp = V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE; if (stream->streamformat == V4L2_PIX_FMT_H264) { seq_printf(s, " | |- %s entropy mode\n" " | |- CPB size=%d kB\n" " | |- DCT8x8 enable=%s\n" " | |- qpmin=%d\n" " | |- qpmax=%d\n" " | |- PAR enable=%s\n" " | |- PAR id=%s\n" " | |- SEI frame packing enable=%s\n" " | |- SEI frame packing type=%s\n", v4l2_ctrl_get_menu(entropy)[ctrls->entropy_mode], ctrls->cpb_size, ctrls->dct8x8 ? "true" : "false", ctrls->qpmin, ctrls->qpmax, ctrls->vui_sar ? "true" : "false", v4l2_ctrl_get_menu(vui_sar)[ctrls->vui_sar_idc], ctrls->sei_fp ? "true" : "false", v4l2_ctrl_get_menu(sei_fp)[ctrls->sei_fp_type]); } if (ctx->sys_errors || ctx->encode_errors || ctx->frame_errors) { seq_puts(s, " |\n |-[errors]\n"); seq_printf(s, " | |- system=%d\n" " | |- encoding=%d\n" " | |- frame=%d\n", ctx->sys_errors, ctx->encode_errors, ctx->frame_errors); } seq_puts(s, " |\n |-[performances]\n"); seq_printf(s, " | |- frames encoded=%d\n" " | |- avg HW processing duration (0.1ms)=%d [min=%d, max=%d]\n" " | |- avg encoding period (0.1ms)=%d [min=%d, max=%d]\n" " | |- avg fps (0.1Hz)=%d\n" " | |- max reachable fps (0.1Hz)=%d\n" " | |- avg bitrate (kbps)=%d [min=%d, max=%d]\n" " | |- last bitrate (kbps)=%d\n", dbg->cnt_duration, dbg->avg_duration, dbg->min_duration, dbg->max_duration, dbg->avg_period, dbg->min_period, dbg->max_period, dbg->avg_fps, dbg->max_fps, dbg->avg_bitrate, dbg->min_bitrate, dbg->max_bitrate, dbg->last_bitrate); } /* * performance debug info */ void hva_dbg_perf_begin(struct hva_ctx *ctx) { u64 div; u32 period; u32 bitrate; struct hva_ctx_dbg *dbg = &ctx->dbg; ktime_t prev = dbg->begin; dbg->begin = ktime_get(); if (dbg->is_valid_period) { /* encoding period */ div = (u64)ktime_us_delta(dbg->begin, prev); do_div(div, 100); period = (u32)div; dbg->min_period = min(period, dbg->min_period); dbg->max_period = max(period, dbg->max_period); dbg->total_period += period; dbg->cnt_period++; /* * minimum and maximum bitrates are based on the * encoding period values upon a window of 32 samples */ dbg->window_duration += period; dbg->cnt_window++; if (dbg->cnt_window >= 32) { /* * bitrate in kbps = (size * 8 / 1000) / * (duration / 10000) * = size * 80 / duration */ if (dbg->window_duration > 0) { div = (u64)dbg->window_stream_size * 80; do_div(div, dbg->window_duration); bitrate = (u32)div; dbg->last_bitrate = bitrate; dbg->min_bitrate = min(bitrate, dbg->min_bitrate); dbg->max_bitrate = max(bitrate, dbg->max_bitrate); } dbg->window_stream_size = 0; dbg->window_duration = 0; dbg->cnt_window = 0; } } /* * filter sequences valid for performance: * - begin/begin (no stream available) is an invalid sequence * - begin/end is a valid sequence */ dbg->is_valid_period = false; } void hva_dbg_perf_end(struct hva_ctx *ctx, struct hva_stream *stream) { struct device *dev = ctx_to_dev(ctx); u64 div; u32 duration; u32 bytesused; u32 timestamp; struct hva_ctx_dbg *dbg = &ctx->dbg; ktime_t end = ktime_get(); /* stream bytesused and timestamp in us */ bytesused = vb2_get_plane_payload(&stream->vbuf.vb2_buf, 0); div = stream->vbuf.vb2_buf.timestamp; do_div(div, 1000); timestamp = (u32)div; /* encoding duration */ div = (u64)ktime_us_delta(end, dbg->begin); dev_dbg(dev, "%s perf stream[%d] dts=%d encoded using %d bytes in %d us", ctx->name, stream->vbuf.sequence, timestamp, bytesused, (u32)div); do_div(div, 100); duration = (u32)div; dbg->min_duration = min(duration, dbg->min_duration); dbg->max_duration = max(duration, dbg->max_duration); dbg->total_duration += duration; dbg->cnt_duration++; /* * the average bitrate is based on the total stream size * and the total encoding periods */ dbg->total_stream_size += bytesused; dbg->window_stream_size += bytesused; dbg->is_valid_period = true; } static void hva_dbg_perf_compute(struct hva_ctx *ctx) { u64 div; struct hva_ctx_dbg *dbg = &ctx->dbg; if (dbg->cnt_duration > 0) { div = (u64)dbg->total_duration; do_div(div, dbg->cnt_duration); dbg->avg_duration = (u32)div; } else { dbg->avg_duration = 0; } if (dbg->total_duration > 0) { div = (u64)dbg->cnt_duration * 100000; do_div(div, dbg->total_duration); dbg->max_fps = (u32)div; } else { dbg->max_fps = 0; } if (dbg->cnt_period > 0) { div = (u64)dbg->total_period; do_div(div, dbg->cnt_period); dbg->avg_period = (u32)div; } else { dbg->avg_period = 0; } if (dbg->total_period > 0) { div = (u64)dbg->cnt_period * 100000; do_div(div, dbg->total_period); dbg->avg_fps = (u32)div; } else { dbg->avg_fps = 0; } if (dbg->total_period > 0) { /* * bitrate in kbps = (video size * 8 / 1000) / * (video duration / 10000) * = video size * 80 / video duration */ div = (u64)dbg->total_stream_size * 80; do_div(div, dbg->total_period); dbg->avg_bitrate = (u32)div; } else { dbg->avg_bitrate = 0; } } /* * device debug info */ static int device_show(struct seq_file *s, void *data) { struct hva_dev *hva = s->private; seq_printf(s, "[%s]\n", hva->v4l2_dev.name); seq_printf(s, "registered as /dev/video%d\n", hva->vdev->num); return 0; } static int encoders_show(struct seq_file *s, void *data) { struct hva_dev *hva = s->private; unsigned int i = 0; seq_printf(s, "[encoders]\n|- %d registered encoders:\n", hva->nb_of_encoders); while (hva->encoders[i]) { seq_printf(s, "|- %s: %4.4s => %4.4s\n", hva->encoders[i]->name, (char *)&hva->encoders[i]->pixelformat, (char *)&hva->encoders[i]->streamformat); i++; } return 0; } static int last_show(struct seq_file *s, void *data) { struct hva_dev *hva = s->private; struct hva_ctx *last_ctx = &hva->dbg.last_ctx; if (last_ctx->flags & HVA_FLAG_STREAMINFO) { seq_puts(s, "[last encoding]\n"); hva_dbg_perf_compute(last_ctx); format_ctx(s, last_ctx); } else { seq_puts(s, "[no information recorded about last encoding]\n"); } return 0; } static int regs_show(struct seq_file *s, void *data) { struct hva_dev *hva = s->private; hva_hw_dump_regs(hva, s); return 0; } #define hva_dbg_create_entry(name) \ debugfs_create_file(#name, 0444, hva->dbg.debugfs_entry, hva, \ &name##_fops) DEFINE_SHOW_ATTRIBUTE(device); DEFINE_SHOW_ATTRIBUTE(encoders); DEFINE_SHOW_ATTRIBUTE(last); DEFINE_SHOW_ATTRIBUTE(regs); void hva_debugfs_create(struct hva_dev *hva) { hva->dbg.debugfs_entry = debugfs_create_dir(HVA_NAME, NULL); hva_dbg_create_entry(device); hva_dbg_create_entry(encoders); hva_dbg_create_entry(last); hva_dbg_create_entry(regs); } void hva_debugfs_remove(struct hva_dev *hva) { debugfs_remove_recursive(hva->dbg.debugfs_entry); hva->dbg.debugfs_entry = NULL; } /* * context (instance) debug info */ static int ctx_show(struct seq_file *s, void *data) { struct hva_ctx *ctx = s->private; seq_printf(s, "[running encoding %d]\n", ctx->id); hva_dbg_perf_compute(ctx); format_ctx(s, ctx); return 0; } DEFINE_SHOW_ATTRIBUTE(ctx); void hva_dbg_ctx_create(struct hva_ctx *ctx) { struct hva_dev *hva = ctx->hva_dev; char name[4] = ""; ctx->dbg.min_duration = UINT_MAX; ctx->dbg.min_period = UINT_MAX; ctx->dbg.min_bitrate = UINT_MAX; snprintf(name, sizeof(name), "%d", hva->instance_id); ctx->dbg.debugfs_entry = debugfs_create_file(name, 0444, hva->dbg.debugfs_entry, ctx, &ctx_fops); } void hva_dbg_ctx_remove(struct hva_ctx *ctx) { struct hva_dev *hva = ctx->hva_dev; if (ctx->flags & HVA_FLAG_STREAMINFO) /* save context before removing */ memcpy(&hva->dbg.last_ctx, ctx, sizeof(*ctx)); debugfs_remove(ctx->dbg.debugfs_entry); }