forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
396 lines
9.9 KiB
396 lines
9.9 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright (C) STMicroelectronics SA 2015 |
|
* Authors: Yannick Fertre <[email protected]> |
|
* Hugues Fruchet <[email protected]> |
|
*/ |
|
|
|
#include <linux/debugfs.h> |
|
|
|
#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); |
|
}
|
|
|