[v1,2/2] st-hva: add debug file system

Message ID 1473696075-9190-3-git-send-email-jean-christophe.trotin@st.com (mailing list archive)
State Changes Requested, archived
Delegated to: Hans Verkuil
Headers

Commit Message

Jean-Christophe Trotin Sept. 12, 2016, 4:01 p.m. UTC
  This patch creates 4 static debugfs entries to dump:
- the device-related information ("st-hva/device")
- the list of registered encoders ("st-hva/encoders")
- the current values of the hva registers ("st-hva/regs")
- the information about the last closed instance ("st-hva/last")

It also creates dynamically a debugfs entry for each opened instance,
("st-hva/<instance identifier>") to dump:
- the information about the stream (profile, level, resolution,
  alignment...)
- the control parameters (bitrate mode, framerate, GOP size...)
- the potential (system, encoding...) errors
- the performance information about the encoding (HW processing
  duration, average bitrate, average framerate...)
Each time a running instance is closed, its context (including the
debug information) is saved to feed, on demand, the last closed
instance debugfs entry.

Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
---
 drivers/media/platform/sti/hva/hva-debug.c | 362 +++++++++++++++++++++++++++++
 drivers/media/platform/sti/hva/hva-hw.c    |  39 ++++
 drivers/media/platform/sti/hva/hva-hw.h    |   1 +
 drivers/media/platform/sti/hva/hva-v4l2.c  |  11 +-
 drivers/media/platform/sti/hva/hva.h       |  86 +++++--
 5 files changed, 482 insertions(+), 17 deletions(-)
  

Comments

Hans Verkuil Sept. 15, 2016, 10:30 a.m. UTC | #1
Same thing here: this should be enabled in a Kconfig option. That new Kconfig
option should depend on DEBUG_FS as well.

BTW (independent of these two patches): I think it would be easier to maintain
if you make a Kconfig file in the sti directory, rather than adding them to the
Kconfig in platform.

Regards,

	Hans

On 09/12/2016 06:01 PM, Jean-Christophe Trotin wrote:
> This patch creates 4 static debugfs entries to dump:
> - the device-related information ("st-hva/device")
> - the list of registered encoders ("st-hva/encoders")
> - the current values of the hva registers ("st-hva/regs")
> - the information about the last closed instance ("st-hva/last")
> 
> It also creates dynamically a debugfs entry for each opened instance,
> ("st-hva/<instance identifier>") to dump:
> - the information about the stream (profile, level, resolution,
>   alignment...)
> - the control parameters (bitrate mode, framerate, GOP size...)
> - the potential (system, encoding...) errors
> - the performance information about the encoding (HW processing
>   duration, average bitrate, average framerate...)
> Each time a running instance is closed, its context (including the
> debug information) is saved to feed, on demand, the last closed
> instance debugfs entry.
> 
> Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
> ---
>  drivers/media/platform/sti/hva/hva-debug.c | 362 +++++++++++++++++++++++++++++
>  drivers/media/platform/sti/hva/hva-hw.c    |  39 ++++
>  drivers/media/platform/sti/hva/hva-hw.h    |   1 +
>  drivers/media/platform/sti/hva/hva-v4l2.c  |  11 +-
>  drivers/media/platform/sti/hva/hva.h       |  86 +++++--
>  5 files changed, 482 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/media/platform/sti/hva/hva-debug.c b/drivers/media/platform/sti/hva/hva-debug.c
> index 71bbf32..433b1d4 100644
> --- a/drivers/media/platform/sti/hva/hva-debug.c
> +++ b/drivers/media/platform/sti/hva/hva-debug.c
> @@ -5,7 +5,113 @@
>   * License terms:  GNU General Public License (GPL), version 2
>   */
>  
> +#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 (dbg->sys_errors || dbg->encode_errors || dbg->frame_errors) {
> +		seq_puts(s, "  |\n  |-[errors]\n");
> +		seq_printf(s, "  | |- system=%d\n"
> +			      "  | |- encoding=%d\n"
> +			      "  | |- frame=%d\n",
> +			      dbg->sys_errors,
> +			      dbg->encode_errors,
> +			      dbg->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);
> +}
>  
>  /*
>   * encoding summary
> @@ -77,10 +183,52 @@ char *hva_dbg_summary(struct hva_ctx *ctx)
>  
>  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
> @@ -118,8 +266,222 @@ void hva_dbg_perf_end(struct hva_ctx *ctx, struct hva_stream *stream)
>  	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 hva_dbg_device(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 hva_dbg_encoders(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 hva_dbg_last(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 hva_dbg_regs(struct seq_file *s, void *data)
> +{
> +	struct hva_dev *hva = s->private;
> +
> +	hva_hw_dump_regs(hva, s);
> +
> +	return 0;
> +}
> +
> +#define hva_dbg_declare(name)						  \
> +	static int hva_dbg_##name##_open(struct inode *i, struct file *f) \
> +	{								  \
> +		return single_open(f, hva_dbg_##name, i->i_private);	  \
> +	}								  \
> +	static const struct file_operations hva_dbg_##name##_fops = {	  \
> +		.open		= hva_dbg_##name##_open,		  \
> +		.read		= seq_read,				  \
> +		.llseek		= seq_lseek,				  \
> +		.release	= single_release,			  \
> +	}
> +
> +#define hva_dbg_create_entry(name)					 \
> +	debugfs_create_file(#name, S_IRUGO, hva->dbg.debugfs_entry, hva, \
> +			    &hva_dbg_##name##_fops)
> +
> +hva_dbg_declare(device);
> +hva_dbg_declare(encoders);
> +hva_dbg_declare(last);
> +hva_dbg_declare(regs);
> +
> +void hva_debugfs_create(struct hva_dev *hva)
> +{
> +	hva->dbg.debugfs_entry = debugfs_create_dir(HVA_NAME, NULL);
> +	if (!hva->dbg.debugfs_entry)
> +		goto err;
> +
> +	if (!hva_dbg_create_entry(device))
> +		goto err;
> +
> +	if (!hva_dbg_create_entry(encoders))
> +		goto err;
> +
> +	if (!hva_dbg_create_entry(last))
> +		goto err;
> +
> +	if (!hva_dbg_create_entry(regs))
> +		goto err;
> +
> +	return;
> +
> +err:
> +	hva_debugfs_remove(hva);
> +}
> +
> +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 hva_dbg_ctx(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;
> +}
> +
> +hva_dbg_declare(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, S_IRUGO,
> +						     hva->dbg.debugfs_entry,
> +						     ctx, &hva_dbg_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);
> +}
> diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/sti/hva/hva-hw.c
> index c84b860..d395204 100644
> --- a/drivers/media/platform/sti/hva/hva-hw.c
> +++ b/drivers/media/platform/sti/hva/hva-hw.c
> @@ -9,6 +9,7 @@
>  #include <linux/interrupt.h>
>  #include <linux/platform_device.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/seq_file.h>
>  
>  #include "hva.h"
>  #include "hva-hw.h"
> @@ -541,3 +542,41 @@ out:
>  
>  	return ret;
>  }
> +
> +#define DUMP(reg) seq_printf(s, "%-30s: 0x%08X\n",\
> +			     #reg, readl_relaxed(hva->regs + reg))
> +
> +void hva_hw_dump_regs(struct hva_dev *hva, struct seq_file *s)
> +{
> +	struct device *dev = hva_to_dev(hva);
> +
> +	mutex_lock(&hva->protect_mutex);
> +
> +	if (pm_runtime_get_sync(dev) < 0) {
> +		seq_puts(s, "Cannot wake up IP\n");
> +		mutex_unlock(&hva->protect_mutex);
> +		return;
> +	}
> +
> +	seq_printf(s, "Registers:\nReg @ = 0x%p\n", hva->regs);
> +
> +	DUMP(HVA_HIF_REG_RST);
> +	DUMP(HVA_HIF_REG_RST_ACK);
> +	DUMP(HVA_HIF_REG_MIF_CFG);
> +	DUMP(HVA_HIF_REG_HEC_MIF_CFG);
> +	DUMP(HVA_HIF_REG_CFL);
> +	DUMP(HVA_HIF_REG_SFL);
> +	DUMP(HVA_HIF_REG_LMI_ERR);
> +	DUMP(HVA_HIF_REG_EMI_ERR);
> +	DUMP(HVA_HIF_REG_HEC_MIF_ERR);
> +	DUMP(HVA_HIF_REG_HEC_STS);
> +	DUMP(HVA_HIF_REG_HVC_STS);
> +	DUMP(HVA_HIF_REG_HJE_STS);
> +	DUMP(HVA_HIF_REG_CNT);
> +	DUMP(HVA_HIF_REG_HEC_CHKSYN_DIS);
> +	DUMP(HVA_HIF_REG_CLK_GATING);
> +	DUMP(HVA_HIF_REG_VERSION);
> +
> +	pm_runtime_put_autosuspend(dev);
> +	mutex_unlock(&hva->protect_mutex);
> +}
> diff --git a/drivers/media/platform/sti/hva/hva-hw.h b/drivers/media/platform/sti/hva/hva-hw.h
> index efb45b9..f8df405 100644
> --- a/drivers/media/platform/sti/hva/hva-hw.h
> +++ b/drivers/media/platform/sti/hva/hva-hw.h
> @@ -38,5 +38,6 @@ int hva_hw_runtime_suspend(struct device *dev);
>  int hva_hw_runtime_resume(struct device *dev);
>  int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd,
>  			struct hva_buffer *task);
> +void hva_hw_dump_regs(struct hva_dev *hva, struct seq_file *s);
>  
>  #endif /* HVA_HW_H */
> diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c
> index fb65816..71bb7b3 100644
> --- a/drivers/media/platform/sti/hva/hva-v4l2.c
> +++ b/drivers/media/platform/sti/hva/hva-v4l2.c
> @@ -15,8 +15,6 @@
>  #include "hva.h"
>  #include "hva-hw.h"
>  
> -#define HVA_NAME "st-hva"
> -
>  #define MIN_FRAMES	1
>  #define MIN_STREAMS	1
>  
> @@ -1181,6 +1179,8 @@ static int hva_open(struct file *file)
>  	/* default parameters for frame and stream */
>  	set_default_params(ctx);
>  
> +	hva_dbg_ctx_create(ctx);
> +
>  	dev_info(dev, "%s encoder instance created\n", ctx->name);
>  
>  	return 0;
> @@ -1223,6 +1223,8 @@ static int hva_release(struct file *file)
>  	v4l2_fh_del(&ctx->fh);
>  	v4l2_fh_exit(&ctx->fh);
>  
> +	hva_dbg_ctx_remove(ctx);
> +
>  	dev_info(dev, "%s encoder instance released\n", ctx->name);
>  
>  	kfree(ctx);
> @@ -1347,6 +1349,8 @@ static int hva_probe(struct platform_device *pdev)
>  		goto err_hw;
>  	}
>  
> +	hva_debugfs_create(hva);
> +
>  	hva->work_queue = create_workqueue(HVA_NAME);
>  	if (!hva->work_queue) {
>  		dev_err(dev, "%s %s failed to allocate work queue\n",
> @@ -1368,6 +1372,7 @@ static int hva_probe(struct platform_device *pdev)
>  err_work_queue:
>  	destroy_workqueue(hva->work_queue);
>  err_v4l2:
> +	hva_debugfs_remove(hva);
>  	v4l2_device_unregister(&hva->v4l2_dev);
>  err_hw:
>  	hva_hw_remove(hva);
> @@ -1386,6 +1391,8 @@ static int hva_remove(struct platform_device *pdev)
>  
>  	hva_hw_remove(hva);
>  
> +	hva_debugfs_remove(hva);
> +
>  	v4l2_device_unregister(&hva->v4l2_dev);
>  
>  	dev_info(dev, "%s %s removed\n", HVA_PREFIX, pdev->name);
> diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/sti/hva/hva.h
> index badc5f4..eef9769 100644
> --- a/drivers/media/platform/sti/hva/hva.h
> +++ b/drivers/media/platform/sti/hva/hva.h
> @@ -21,7 +21,8 @@
>  
>  #define ctx_to_hdev(c)  (c->hva_dev)
>  
> -#define HVA_PREFIX "[---:----]"
> +#define HVA_NAME	"st-hva"
> +#define HVA_PREFIX	"[---:----]"
>  
>  extern const struct hva_enc nv12h264enc;
>  extern const struct hva_enc nv21h264enc;
> @@ -156,22 +157,60 @@ struct hva_stream {
>  /**
>   * struct hva_ctx_dbg - instance context debug info
>   *
> - * @is_valid_period: true if the sequence is valid for performance
> - * @begin:           start time of last HW task
> - * @total_duration:  total HW processing durations in 0.1ms
> - * @cnt_duration:    number of HW processings
> - * @sys_errors:      number of system errors (memory, resource, pm..)
> - * @encode_errors:   number of encoding errors (hw/driver errors)
> - * @frame_errors:    number of frame errors (format, size, header...)
> + * @debugfs_entry:      debugfs entry
> + * @is_valid_period:    true if the sequence is valid for performance
> + * @begin:              start time of last HW task
> + * @total_duration:     total HW processing durations in 0.1ms
> + * @cnt_duration:       number of HW processings
> + * @min_duration:       minimum HW processing duration in 0.1ms
> + * @max_duration:       maximum HW processing duration in 0.1ms
> + * @avg_duration:       average HW processing duration in 0.1ms
> + * @max_fps:            maximum frames encoded per second (in 0.1Hz)
> + * @total_period:       total encoding periods in 0.1ms
> + * @cnt_period:         number of periods
> + * @min_period:         minimum encoding period in 0.1ms
> + * @max_period:         maximum encoding period in 0.1ms
> + * @avg_period:         average encoding period in 0.1ms
> + * @total_stream_size:  total number of encoded bytes
> + * @avg_fps:            average frames encoded per second (in 0.1Hz)
> + * @window_duration:    duration of the sampling window in 0.1ms
> + * @cnt_window:         number of samples in the window
> + * @window_stream_size: number of encoded bytes upon the sampling window
> + * @last_bitrate:       bitrate upon the last sampling window
> + * @min_bitrate:        minimum bitrate in kbps
> + * @max_bitrate:        maximum bitrate in kbps
> + * @avg_bitrate:        average bitrate in kbps
> + * @sys_errors:         number of system errors (memory, resource, pm..)
> + * @encode_errors:      number of encoding errors (hw/driver errors)
> + * @frame_errors:       number of frame errors (format, size, header...)
>   */
>  struct hva_ctx_dbg {
> -	bool	is_valid_period;
> -	ktime_t	begin;
> -	u32	total_duration;
> -	u32	cnt_duration;
> -	u32	sys_errors;
> -	u32	encode_errors;
> -	u32	frame_errors;
> +	struct dentry	*debugfs_entry;
> +	bool		is_valid_period;
> +	ktime_t		begin;
> +	u32		total_duration;
> +	u32		cnt_duration;
> +	u32		min_duration;
> +	u32		max_duration;
> +	u32		avg_duration;
> +	u32		max_fps;
> +	u32		total_period;
> +	u32		cnt_period;
> +	u32		min_period;
> +	u32		max_period;
> +	u32		avg_period;
> +	u32		total_stream_size;
> +	u32		avg_fps;
> +	u32		window_duration;
> +	u32		cnt_window;
> +	u32		window_stream_size;
> +	u32		last_bitrate;
> +	u32		min_bitrate;
> +	u32		max_bitrate;
> +	u32		avg_bitrate;
> +	u32		sys_errors;
> +	u32		encode_errors;
> +	u32		frame_errors;
>  };
>  
>  struct hva_dev;
> @@ -235,6 +274,17 @@ struct hva_ctx {
>  #define HVA_FLAG_STREAMINFO	0x0001
>  #define HVA_FLAG_FRAMEINFO	0x0002
>  
> +/**
> + * struct hva_dev_dbg - device debug info
> + *
> + * @debugfs_entry: debugfs entry
> + * @last_ctx:      debug information about last running instance context
> + */
> +struct hva_dev_dbg {
> +	struct dentry	*debugfs_entry;
> +	struct hva_ctx	last_ctx;
> +};
> +
>  #define HVA_MAX_INSTANCES	16
>  #define HVA_MAX_ENCODERS	10
>  #define HVA_MAX_FORMATS		HVA_MAX_ENCODERS
> @@ -273,6 +323,7 @@ struct hva_ctx {
>   * @lmi_err_reg:         local memory interface error register value
>   * @emi_err_reg:         external memory interface error register value
>   * @hec_mif_err_reg:     HEC memory interface error register value
> + * @dbg:             device debug info
>   */
>  struct hva_dev {
>  	struct v4l2_device	v4l2_dev;
> @@ -307,6 +358,7 @@ struct hva_dev {
>  	u32			lmi_err_reg;
>  	u32			emi_err_reg;
>  	u32			hec_mif_err_reg;
> +	struct hva_dev_dbg	dbg;
>  };
>  
>  /**
> @@ -335,6 +387,10 @@ struct hva_enc {
>  				  struct hva_stream *stream);
>  };
>  
> +void hva_debugfs_create(struct hva_dev *hva);
> +void hva_debugfs_remove(struct hva_dev *hva);
> +void hva_dbg_ctx_create(struct hva_ctx *ctx);
> +void hva_dbg_ctx_remove(struct hva_ctx *ctx);
>  char *hva_dbg_summary(struct hva_ctx *ctx);
>  void hva_dbg_perf_begin(struct hva_ctx *ctx);
>  void hva_dbg_perf_end(struct hva_ctx *ctx, struct hva_stream *stream);
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  
Jean-Christophe Trotin Sept. 20, 2016, 9:47 a.m. UTC | #2
On 09/15/2016 12:30 PM, Hans Verkuil wrote:
> Same thing here: this should be enabled in a Kconfig option. That new Kconfig
> option should depend on DEBUG_FS as well.
>
> BTW (independent of these two patches): I think it would be easier to maintain
> if you make a Kconfig file in the sti directory, rather than adding them to the
> Kconfig in platform.
>
> Regards,
>
>         Hans

Hi Hans,

Thank you for your remarks about the 2 debug patches for HVA.
I've just sent a version 2 with the following propositions:

- the encoding summary (first patch) doesn't include any longer information
about the encoding performance. There's no more computation in this patch
(hva_dbg_perf_begin and hva_dbg_perf_end removed). Thus, there's no potential
penalty on the encoding performance. So, I propose that the short summary (very
useful to easily get a status about the encoding operation) is always enabled
(dev_info).

- for the second patch, I propose that the computation of the performances
(hva_dbg_perf_begin and hva_dbg_perf_end) is conditioned by CONFIG_DEBUG_FS. It
avoids the definition of a new Kconfig option (mkt-vpu also uses
CONFIG_DEBUG_FS). As to the functions that manage the debugfs entries
(hva_*_create/remove), I propose that they are called even if DEBUG_FS is
disabled: in this case, thanks to the debugfs API, the entries will simply not
created.

Finally, I think that your proposition of having a Kconfig in the sti directory
makes sense. It might be part of a different patch series dealing with the "sti
coherency" (we discussed about that on IRC with Benjamin Gaignard few weeks ago).

Regards,
Jean-Christophe.

>
> On 09/12/2016 06:01 PM, Jean-Christophe Trotin wrote:
>> This patch creates 4 static debugfs entries to dump:
>> - the device-related information ("st-hva/device")
>> - the list of registered encoders ("st-hva/encoders")
>> - the current values of the hva registers ("st-hva/regs")
>> - the information about the last closed instance ("st-hva/last")
>>
>> It also creates dynamically a debugfs entry for each opened instance,
>> ("st-hva/<instance identifier>") to dump:
>> - the information about the stream (profile, level, resolution,
>>   alignment...)
>> - the control parameters (bitrate mode, framerate, GOP size...)
>> - the potential (system, encoding...) errors
>> - the performance information about the encoding (HW processing
>>   duration, average bitrate, average framerate...)
>> Each time a running instance is closed, its context (including the
>> debug information) is saved to feed, on demand, the last closed
>> instance debugfs entry.
>>
>> Signed-off-by: Yannick Fertre <yannick.fertre@st.com>
>> Signed-off-by: Jean-Christophe Trotin <jean-christophe.trotin@st.com>
>> ---
>>  drivers/media/platform/sti/hva/hva-debug.c | 362 +++++++++++++++++++++++++++++
>>  drivers/media/platform/sti/hva/hva-hw.c    |  39 ++++
>>  drivers/media/platform/sti/hva/hva-hw.h    |   1 +
>>  drivers/media/platform/sti/hva/hva-v4l2.c  |  11 +-
>>  drivers/media/platform/sti/hva/hva.h       |  86 +++++--
>>  5 files changed, 482 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/media/platform/sti/hva/hva-debug.c b/drivers/media/platform/sti/hva/hva-debug.c
>> index 71bbf32..433b1d4 100644
>> --- a/drivers/media/platform/sti/hva/hva-debug.c
>> +++ b/drivers/media/platform/sti/hva/hva-debug.c
>> @@ -5,7 +5,113 @@
>>   * License terms:  GNU General Public License (GPL), version 2
>>   */
>>
>> +#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 (dbg->sys_errors || dbg->encode_errors || dbg->frame_errors) {
>> +             seq_puts(s, "  |\n  |-[errors]\n");
>> +             seq_printf(s, "  | |- system=%d\n"
>> +                           "  | |- encoding=%d\n"
>> +                           "  | |- frame=%d\n",
>> +                           dbg->sys_errors,
>> +                           dbg->encode_errors,
>> +                           dbg->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);
>> +}
>>
>>  /*
>>   * encoding summary
>> @@ -77,10 +183,52 @@ char *hva_dbg_summary(struct hva_ctx *ctx)
>>
>>  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
>> @@ -118,8 +266,222 @@ void hva_dbg_perf_end(struct hva_ctx *ctx, struct hva_stream *stream)
>>       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 hva_dbg_device(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 hva_dbg_encoders(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 hva_dbg_last(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 hva_dbg_regs(struct seq_file *s, void *data)
>> +{
>> +     struct hva_dev *hva = s->private;
>> +
>> +     hva_hw_dump_regs(hva, s);
>> +
>> +     return 0;
>> +}
>> +
>> +#define hva_dbg_declare(name)                                                  \
>> +     static int hva_dbg_##name##_open(struct inode *i, struct file *f) \
>> +     {                                                                 \
>> +             return single_open(f, hva_dbg_##name, i->i_private);      \
>> +     }                                                                 \
>> +     static const struct file_operations hva_dbg_##name##_fops = {     \
>> +             .open           = hva_dbg_##name##_open,                  \
>> +             .read           = seq_read,                               \
>> +             .llseek         = seq_lseek,                              \
>> +             .release        = single_release,                         \
>> +     }
>> +
>> +#define hva_dbg_create_entry(name)                                    \
>> +     debugfs_create_file(#name, S_IRUGO, hva->dbg.debugfs_entry, hva, \
>> +                         &hva_dbg_##name##_fops)
>> +
>> +hva_dbg_declare(device);
>> +hva_dbg_declare(encoders);
>> +hva_dbg_declare(last);
>> +hva_dbg_declare(regs);
>> +
>> +void hva_debugfs_create(struct hva_dev *hva)
>> +{
>> +     hva->dbg.debugfs_entry = debugfs_create_dir(HVA_NAME, NULL);
>> +     if (!hva->dbg.debugfs_entry)
>> +             goto err;
>> +
>> +     if (!hva_dbg_create_entry(device))
>> +             goto err;
>> +
>> +     if (!hva_dbg_create_entry(encoders))
>> +             goto err;
>> +
>> +     if (!hva_dbg_create_entry(last))
>> +             goto err;
>> +
>> +     if (!hva_dbg_create_entry(regs))
>> +             goto err;
>> +
>> +     return;
>> +
>> +err:
>> +     hva_debugfs_remove(hva);
>> +}
>> +
>> +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 hva_dbg_ctx(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;
>> +}
>> +
>> +hva_dbg_declare(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, S_IRUGO,
>> +                                                  hva->dbg.debugfs_entry,
>> +                                                  ctx, &hva_dbg_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);
>> +}
>> diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/sti/hva/hva-hw.c
>> index c84b860..d395204 100644
>> --- a/drivers/media/platform/sti/hva/hva-hw.c
>> +++ b/drivers/media/platform/sti/hva/hva-hw.c
>> @@ -9,6 +9,7 @@
>>  #include <linux/interrupt.h>
>>  #include <linux/platform_device.h>
>>  #include <linux/pm_runtime.h>
>> +#include <linux/seq_file.h>
>>
>>  #include "hva.h"
>>  #include "hva-hw.h"
>> @@ -541,3 +542,41 @@ out:
>>
>>       return ret;
>>  }
>> +
>> +#define DUMP(reg) seq_printf(s, "%-30s: 0x%08X\n",\
>> +                          #reg, readl_relaxed(hva->regs + reg))
>> +
>> +void hva_hw_dump_regs(struct hva_dev *hva, struct seq_file *s)
>> +{
>> +     struct device *dev = hva_to_dev(hva);
>> +
>> +     mutex_lock(&hva->protect_mutex);
>> +
>> +     if (pm_runtime_get_sync(dev) < 0) {
>> +             seq_puts(s, "Cannot wake up IP\n");
>> +             mutex_unlock(&hva->protect_mutex);
>> +             return;
>> +     }
>> +
>> +     seq_printf(s, "Registers:\nReg @ = 0x%p\n", hva->regs);
>> +
>> +     DUMP(HVA_HIF_REG_RST);
>> +     DUMP(HVA_HIF_REG_RST_ACK);
>> +     DUMP(HVA_HIF_REG_MIF_CFG);
>> +     DUMP(HVA_HIF_REG_HEC_MIF_CFG);
>> +     DUMP(HVA_HIF_REG_CFL);
>> +     DUMP(HVA_HIF_REG_SFL);
>> +     DUMP(HVA_HIF_REG_LMI_ERR);
>> +     DUMP(HVA_HIF_REG_EMI_ERR);
>> +     DUMP(HVA_HIF_REG_HEC_MIF_ERR);
>> +     DUMP(HVA_HIF_REG_HEC_STS);
>> +     DUMP(HVA_HIF_REG_HVC_STS);
>> +     DUMP(HVA_HIF_REG_HJE_STS);
>> +     DUMP(HVA_HIF_REG_CNT);
>> +     DUMP(HVA_HIF_REG_HEC_CHKSYN_DIS);
>> +     DUMP(HVA_HIF_REG_CLK_GATING);
>> +     DUMP(HVA_HIF_REG_VERSION);
>> +
>> +     pm_runtime_put_autosuspend(dev);
>> +     mutex_unlock(&hva->protect_mutex);
>> +}
>> diff --git a/drivers/media/platform/sti/hva/hva-hw.h b/drivers/media/platform/sti/hva/hva-hw.h
>> index efb45b9..f8df405 100644
>> --- a/drivers/media/platform/sti/hva/hva-hw.h
>> +++ b/drivers/media/platform/sti/hva/hva-hw.h
>> @@ -38,5 +38,6 @@ int hva_hw_runtime_suspend(struct device *dev);
>>  int hva_hw_runtime_resume(struct device *dev);
>>  int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd,
>>                       struct hva_buffer *task);
>> +void hva_hw_dump_regs(struct hva_dev *hva, struct seq_file *s);
>>
>>  #endif /* HVA_HW_H */
>> diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c
>> index fb65816..71bb7b3 100644
>> --- a/drivers/media/platform/sti/hva/hva-v4l2.c
>> +++ b/drivers/media/platform/sti/hva/hva-v4l2.c
>> @@ -15,8 +15,6 @@
>>  #include "hva.h"
>>  #include "hva-hw.h"
>>
>> -#define HVA_NAME "st-hva"
>> -
>>  #define MIN_FRAMES   1
>>  #define MIN_STREAMS  1
>>
>> @@ -1181,6 +1179,8 @@ static int hva_open(struct file *file)
>>       /* default parameters for frame and stream */
>>       set_default_params(ctx);
>>
>> +     hva_dbg_ctx_create(ctx);
>> +
>>       dev_info(dev, "%s encoder instance created\n", ctx->name);
>>
>>       return 0;
>> @@ -1223,6 +1223,8 @@ static int hva_release(struct file *file)
>>       v4l2_fh_del(&ctx->fh);
>>       v4l2_fh_exit(&ctx->fh);
>>
>> +     hva_dbg_ctx_remove(ctx);
>> +
>>       dev_info(dev, "%s encoder instance released\n", ctx->name);
>>
>>       kfree(ctx);
>> @@ -1347,6 +1349,8 @@ static int hva_probe(struct platform_device *pdev)
>>               goto err_hw;
>>       }
>>
>> +     hva_debugfs_create(hva);
>> +
>>       hva->work_queue = create_workqueue(HVA_NAME);
>>       if (!hva->work_queue) {
>>               dev_err(dev, "%s %s failed to allocate work queue\n",
>> @@ -1368,6 +1372,7 @@ static int hva_probe(struct platform_device *pdev)
>>  err_work_queue:
>>       destroy_workqueue(hva->work_queue);
>>  err_v4l2:
>> +     hva_debugfs_remove(hva);
>>       v4l2_device_unregister(&hva->v4l2_dev);
>>  err_hw:
>>       hva_hw_remove(hva);
>> @@ -1386,6 +1391,8 @@ static int hva_remove(struct platform_device *pdev)
>>
>>       hva_hw_remove(hva);
>>
>> +     hva_debugfs_remove(hva);
>> +
>>       v4l2_device_unregister(&hva->v4l2_dev);
>>
>>       dev_info(dev, "%s %s removed\n", HVA_PREFIX, pdev->name);
>> diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/sti/hva/hva.h
>> index badc5f4..eef9769 100644
>> --- a/drivers/media/platform/sti/hva/hva.h
>> +++ b/drivers/media/platform/sti/hva/hva.h
>> @@ -21,7 +21,8 @@
>>
>>  #define ctx_to_hdev(c)  (c->hva_dev)
>>
>> -#define HVA_PREFIX "[---:----]"
>> +#define HVA_NAME     "st-hva"
>> +#define HVA_PREFIX   "[---:----]"
>>
>>  extern const struct hva_enc nv12h264enc;
>>  extern const struct hva_enc nv21h264enc;
>> @@ -156,22 +157,60 @@ struct hva_stream {
>>  /**
>>   * struct hva_ctx_dbg - instance context debug info
>>   *
>> - * @is_valid_period: true if the sequence is valid for performance
>> - * @begin:           start time of last HW task
>> - * @total_duration:  total HW processing durations in 0.1ms
>> - * @cnt_duration:    number of HW processings
>> - * @sys_errors:      number of system errors (memory, resource, pm..)
>> - * @encode_errors:   number of encoding errors (hw/driver errors)
>> - * @frame_errors:    number of frame errors (format, size, header...)
>> + * @debugfs_entry:      debugfs entry
>> + * @is_valid_period:    true if the sequence is valid for performance
>> + * @begin:              start time of last HW task
>> + * @total_duration:     total HW processing durations in 0.1ms
>> + * @cnt_duration:       number of HW processings
>> + * @min_duration:       minimum HW processing duration in 0.1ms
>> + * @max_duration:       maximum HW processing duration in 0.1ms
>> + * @avg_duration:       average HW processing duration in 0.1ms
>> + * @max_fps:            maximum frames encoded per second (in 0.1Hz)
>> + * @total_period:       total encoding periods in 0.1ms
>> + * @cnt_period:         number of periods
>> + * @min_period:         minimum encoding period in 0.1ms
>> + * @max_period:         maximum encoding period in 0.1ms
>> + * @avg_period:         average encoding period in 0.1ms
>> + * @total_stream_size:  total number of encoded bytes
>> + * @avg_fps:            average frames encoded per second (in 0.1Hz)
>> + * @window_duration:    duration of the sampling window in 0.1ms
>> + * @cnt_window:         number of samples in the window
>> + * @window_stream_size: number of encoded bytes upon the sampling window
>> + * @last_bitrate:       bitrate upon the last sampling window
>> + * @min_bitrate:        minimum bitrate in kbps
>> + * @max_bitrate:        maximum bitrate in kbps
>> + * @avg_bitrate:        average bitrate in kbps
>> + * @sys_errors:         number of system errors (memory, resource, pm..)
>> + * @encode_errors:      number of encoding errors (hw/driver errors)
>> + * @frame_errors:       number of frame errors (format, size, header...)
>>   */
>>  struct hva_ctx_dbg {
>> -     bool    is_valid_period;
>> -     ktime_t begin;
>> -     u32     total_duration;
>> -     u32     cnt_duration;
>> -     u32     sys_errors;
>> -     u32     encode_errors;
>> -     u32     frame_errors;
>> +     struct dentry   *debugfs_entry;
>> +     bool            is_valid_period;
>> +     ktime_t         begin;
>> +     u32             total_duration;
>> +     u32             cnt_duration;
>> +     u32             min_duration;
>> +     u32             max_duration;
>> +     u32             avg_duration;
>> +     u32             max_fps;
>> +     u32             total_period;
>> +     u32             cnt_period;
>> +     u32             min_period;
>> +     u32             max_period;
>> +     u32             avg_period;
>> +     u32             total_stream_size;
>> +     u32             avg_fps;
>> +     u32             window_duration;
>> +     u32             cnt_window;
>> +     u32             window_stream_size;
>> +     u32             last_bitrate;
>> +     u32             min_bitrate;
>> +     u32             max_bitrate;
>> +     u32             avg_bitrate;
>> +     u32             sys_errors;
>> +     u32             encode_errors;
>> +     u32             frame_errors;
>>  };
>>
>>  struct hva_dev;
>> @@ -235,6 +274,17 @@ struct hva_ctx {
>>  #define HVA_FLAG_STREAMINFO  0x0001
>>  #define HVA_FLAG_FRAMEINFO   0x0002
>>
>> +/**
>> + * struct hva_dev_dbg - device debug info
>> + *
>> + * @debugfs_entry: debugfs entry
>> + * @last_ctx:      debug information about last running instance context
>> + */
>> +struct hva_dev_dbg {
>> +     struct dentry   *debugfs_entry;
>> +     struct hva_ctx  last_ctx;
>> +};
>> +
>>  #define HVA_MAX_INSTANCES    16
>>  #define HVA_MAX_ENCODERS     10
>>  #define HVA_MAX_FORMATS              HVA_MAX_ENCODERS
>> @@ -273,6 +323,7 @@ struct hva_ctx {
>>   * @lmi_err_reg:         local memory interface error register value
>>   * @emi_err_reg:         external memory interface error register value
>>   * @hec_mif_err_reg:     HEC memory interface error register value
>> + * @dbg:             device debug info
>>   */
>>  struct hva_dev {
>>       struct v4l2_device      v4l2_dev;
>> @@ -307,6 +358,7 @@ struct hva_dev {
>>       u32                     lmi_err_reg;
>>       u32                     emi_err_reg;
>>       u32                     hec_mif_err_reg;
>> +     struct hva_dev_dbg      dbg;
>>  };
>>
>>  /**
>> @@ -335,6 +387,10 @@ struct hva_enc {
>>                                 struct hva_stream *stream);
>>  };
>>
>> +void hva_debugfs_create(struct hva_dev *hva);
>> +void hva_debugfs_remove(struct hva_dev *hva);
>> +void hva_dbg_ctx_create(struct hva_ctx *ctx);
>> +void hva_dbg_ctx_remove(struct hva_ctx *ctx);
>>  char *hva_dbg_summary(struct hva_ctx *ctx);
>>  void hva_dbg_perf_begin(struct hva_ctx *ctx);
>>  void hva_dbg_perf_end(struct hva_ctx *ctx, struct hva_stream *stream);
>>
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  

Patch

diff --git a/drivers/media/platform/sti/hva/hva-debug.c b/drivers/media/platform/sti/hva/hva-debug.c
index 71bbf32..433b1d4 100644
--- a/drivers/media/platform/sti/hva/hva-debug.c
+++ b/drivers/media/platform/sti/hva/hva-debug.c
@@ -5,7 +5,113 @@ 
  * License terms:  GNU General Public License (GPL), version 2
  */
 
+#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 (dbg->sys_errors || dbg->encode_errors || dbg->frame_errors) {
+		seq_puts(s, "  |\n  |-[errors]\n");
+		seq_printf(s, "  | |- system=%d\n"
+			      "  | |- encoding=%d\n"
+			      "  | |- frame=%d\n",
+			      dbg->sys_errors,
+			      dbg->encode_errors,
+			      dbg->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);
+}
 
 /*
  * encoding summary
@@ -77,10 +183,52 @@  char *hva_dbg_summary(struct hva_ctx *ctx)
 
 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
@@ -118,8 +266,222 @@  void hva_dbg_perf_end(struct hva_ctx *ctx, struct hva_stream *stream)
 	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 hva_dbg_device(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 hva_dbg_encoders(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 hva_dbg_last(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 hva_dbg_regs(struct seq_file *s, void *data)
+{
+	struct hva_dev *hva = s->private;
+
+	hva_hw_dump_regs(hva, s);
+
+	return 0;
+}
+
+#define hva_dbg_declare(name)						  \
+	static int hva_dbg_##name##_open(struct inode *i, struct file *f) \
+	{								  \
+		return single_open(f, hva_dbg_##name, i->i_private);	  \
+	}								  \
+	static const struct file_operations hva_dbg_##name##_fops = {	  \
+		.open		= hva_dbg_##name##_open,		  \
+		.read		= seq_read,				  \
+		.llseek		= seq_lseek,				  \
+		.release	= single_release,			  \
+	}
+
+#define hva_dbg_create_entry(name)					 \
+	debugfs_create_file(#name, S_IRUGO, hva->dbg.debugfs_entry, hva, \
+			    &hva_dbg_##name##_fops)
+
+hva_dbg_declare(device);
+hva_dbg_declare(encoders);
+hva_dbg_declare(last);
+hva_dbg_declare(regs);
+
+void hva_debugfs_create(struct hva_dev *hva)
+{
+	hva->dbg.debugfs_entry = debugfs_create_dir(HVA_NAME, NULL);
+	if (!hva->dbg.debugfs_entry)
+		goto err;
+
+	if (!hva_dbg_create_entry(device))
+		goto err;
+
+	if (!hva_dbg_create_entry(encoders))
+		goto err;
+
+	if (!hva_dbg_create_entry(last))
+		goto err;
+
+	if (!hva_dbg_create_entry(regs))
+		goto err;
+
+	return;
+
+err:
+	hva_debugfs_remove(hva);
+}
+
+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 hva_dbg_ctx(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;
+}
+
+hva_dbg_declare(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, S_IRUGO,
+						     hva->dbg.debugfs_entry,
+						     ctx, &hva_dbg_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);
+}
diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/sti/hva/hva-hw.c
index c84b860..d395204 100644
--- a/drivers/media/platform/sti/hva/hva-hw.c
+++ b/drivers/media/platform/sti/hva/hva-hw.c
@@ -9,6 +9,7 @@ 
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/seq_file.h>
 
 #include "hva.h"
 #include "hva-hw.h"
@@ -541,3 +542,41 @@  out:
 
 	return ret;
 }
+
+#define DUMP(reg) seq_printf(s, "%-30s: 0x%08X\n",\
+			     #reg, readl_relaxed(hva->regs + reg))
+
+void hva_hw_dump_regs(struct hva_dev *hva, struct seq_file *s)
+{
+	struct device *dev = hva_to_dev(hva);
+
+	mutex_lock(&hva->protect_mutex);
+
+	if (pm_runtime_get_sync(dev) < 0) {
+		seq_puts(s, "Cannot wake up IP\n");
+		mutex_unlock(&hva->protect_mutex);
+		return;
+	}
+
+	seq_printf(s, "Registers:\nReg @ = 0x%p\n", hva->regs);
+
+	DUMP(HVA_HIF_REG_RST);
+	DUMP(HVA_HIF_REG_RST_ACK);
+	DUMP(HVA_HIF_REG_MIF_CFG);
+	DUMP(HVA_HIF_REG_HEC_MIF_CFG);
+	DUMP(HVA_HIF_REG_CFL);
+	DUMP(HVA_HIF_REG_SFL);
+	DUMP(HVA_HIF_REG_LMI_ERR);
+	DUMP(HVA_HIF_REG_EMI_ERR);
+	DUMP(HVA_HIF_REG_HEC_MIF_ERR);
+	DUMP(HVA_HIF_REG_HEC_STS);
+	DUMP(HVA_HIF_REG_HVC_STS);
+	DUMP(HVA_HIF_REG_HJE_STS);
+	DUMP(HVA_HIF_REG_CNT);
+	DUMP(HVA_HIF_REG_HEC_CHKSYN_DIS);
+	DUMP(HVA_HIF_REG_CLK_GATING);
+	DUMP(HVA_HIF_REG_VERSION);
+
+	pm_runtime_put_autosuspend(dev);
+	mutex_unlock(&hva->protect_mutex);
+}
diff --git a/drivers/media/platform/sti/hva/hva-hw.h b/drivers/media/platform/sti/hva/hva-hw.h
index efb45b9..f8df405 100644
--- a/drivers/media/platform/sti/hva/hva-hw.h
+++ b/drivers/media/platform/sti/hva/hva-hw.h
@@ -38,5 +38,6 @@  int hva_hw_runtime_suspend(struct device *dev);
 int hva_hw_runtime_resume(struct device *dev);
 int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd,
 			struct hva_buffer *task);
+void hva_hw_dump_regs(struct hva_dev *hva, struct seq_file *s);
 
 #endif /* HVA_HW_H */
diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c
index fb65816..71bb7b3 100644
--- a/drivers/media/platform/sti/hva/hva-v4l2.c
+++ b/drivers/media/platform/sti/hva/hva-v4l2.c
@@ -15,8 +15,6 @@ 
 #include "hva.h"
 #include "hva-hw.h"
 
-#define HVA_NAME "st-hva"
-
 #define MIN_FRAMES	1
 #define MIN_STREAMS	1
 
@@ -1181,6 +1179,8 @@  static int hva_open(struct file *file)
 	/* default parameters for frame and stream */
 	set_default_params(ctx);
 
+	hva_dbg_ctx_create(ctx);
+
 	dev_info(dev, "%s encoder instance created\n", ctx->name);
 
 	return 0;
@@ -1223,6 +1223,8 @@  static int hva_release(struct file *file)
 	v4l2_fh_del(&ctx->fh);
 	v4l2_fh_exit(&ctx->fh);
 
+	hva_dbg_ctx_remove(ctx);
+
 	dev_info(dev, "%s encoder instance released\n", ctx->name);
 
 	kfree(ctx);
@@ -1347,6 +1349,8 @@  static int hva_probe(struct platform_device *pdev)
 		goto err_hw;
 	}
 
+	hva_debugfs_create(hva);
+
 	hva->work_queue = create_workqueue(HVA_NAME);
 	if (!hva->work_queue) {
 		dev_err(dev, "%s %s failed to allocate work queue\n",
@@ -1368,6 +1372,7 @@  static int hva_probe(struct platform_device *pdev)
 err_work_queue:
 	destroy_workqueue(hva->work_queue);
 err_v4l2:
+	hva_debugfs_remove(hva);
 	v4l2_device_unregister(&hva->v4l2_dev);
 err_hw:
 	hva_hw_remove(hva);
@@ -1386,6 +1391,8 @@  static int hva_remove(struct platform_device *pdev)
 
 	hva_hw_remove(hva);
 
+	hva_debugfs_remove(hva);
+
 	v4l2_device_unregister(&hva->v4l2_dev);
 
 	dev_info(dev, "%s %s removed\n", HVA_PREFIX, pdev->name);
diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/sti/hva/hva.h
index badc5f4..eef9769 100644
--- a/drivers/media/platform/sti/hva/hva.h
+++ b/drivers/media/platform/sti/hva/hva.h
@@ -21,7 +21,8 @@ 
 
 #define ctx_to_hdev(c)  (c->hva_dev)
 
-#define HVA_PREFIX "[---:----]"
+#define HVA_NAME	"st-hva"
+#define HVA_PREFIX	"[---:----]"
 
 extern const struct hva_enc nv12h264enc;
 extern const struct hva_enc nv21h264enc;
@@ -156,22 +157,60 @@  struct hva_stream {
 /**
  * struct hva_ctx_dbg - instance context debug info
  *
- * @is_valid_period: true if the sequence is valid for performance
- * @begin:           start time of last HW task
- * @total_duration:  total HW processing durations in 0.1ms
- * @cnt_duration:    number of HW processings
- * @sys_errors:      number of system errors (memory, resource, pm..)
- * @encode_errors:   number of encoding errors (hw/driver errors)
- * @frame_errors:    number of frame errors (format, size, header...)
+ * @debugfs_entry:      debugfs entry
+ * @is_valid_period:    true if the sequence is valid for performance
+ * @begin:              start time of last HW task
+ * @total_duration:     total HW processing durations in 0.1ms
+ * @cnt_duration:       number of HW processings
+ * @min_duration:       minimum HW processing duration in 0.1ms
+ * @max_duration:       maximum HW processing duration in 0.1ms
+ * @avg_duration:       average HW processing duration in 0.1ms
+ * @max_fps:            maximum frames encoded per second (in 0.1Hz)
+ * @total_period:       total encoding periods in 0.1ms
+ * @cnt_period:         number of periods
+ * @min_period:         minimum encoding period in 0.1ms
+ * @max_period:         maximum encoding period in 0.1ms
+ * @avg_period:         average encoding period in 0.1ms
+ * @total_stream_size:  total number of encoded bytes
+ * @avg_fps:            average frames encoded per second (in 0.1Hz)
+ * @window_duration:    duration of the sampling window in 0.1ms
+ * @cnt_window:         number of samples in the window
+ * @window_stream_size: number of encoded bytes upon the sampling window
+ * @last_bitrate:       bitrate upon the last sampling window
+ * @min_bitrate:        minimum bitrate in kbps
+ * @max_bitrate:        maximum bitrate in kbps
+ * @avg_bitrate:        average bitrate in kbps
+ * @sys_errors:         number of system errors (memory, resource, pm..)
+ * @encode_errors:      number of encoding errors (hw/driver errors)
+ * @frame_errors:       number of frame errors (format, size, header...)
  */
 struct hva_ctx_dbg {
-	bool	is_valid_period;
-	ktime_t	begin;
-	u32	total_duration;
-	u32	cnt_duration;
-	u32	sys_errors;
-	u32	encode_errors;
-	u32	frame_errors;
+	struct dentry	*debugfs_entry;
+	bool		is_valid_period;
+	ktime_t		begin;
+	u32		total_duration;
+	u32		cnt_duration;
+	u32		min_duration;
+	u32		max_duration;
+	u32		avg_duration;
+	u32		max_fps;
+	u32		total_period;
+	u32		cnt_period;
+	u32		min_period;
+	u32		max_period;
+	u32		avg_period;
+	u32		total_stream_size;
+	u32		avg_fps;
+	u32		window_duration;
+	u32		cnt_window;
+	u32		window_stream_size;
+	u32		last_bitrate;
+	u32		min_bitrate;
+	u32		max_bitrate;
+	u32		avg_bitrate;
+	u32		sys_errors;
+	u32		encode_errors;
+	u32		frame_errors;
 };
 
 struct hva_dev;
@@ -235,6 +274,17 @@  struct hva_ctx {
 #define HVA_FLAG_STREAMINFO	0x0001
 #define HVA_FLAG_FRAMEINFO	0x0002
 
+/**
+ * struct hva_dev_dbg - device debug info
+ *
+ * @debugfs_entry: debugfs entry
+ * @last_ctx:      debug information about last running instance context
+ */
+struct hva_dev_dbg {
+	struct dentry	*debugfs_entry;
+	struct hva_ctx	last_ctx;
+};
+
 #define HVA_MAX_INSTANCES	16
 #define HVA_MAX_ENCODERS	10
 #define HVA_MAX_FORMATS		HVA_MAX_ENCODERS
@@ -273,6 +323,7 @@  struct hva_ctx {
  * @lmi_err_reg:         local memory interface error register value
  * @emi_err_reg:         external memory interface error register value
  * @hec_mif_err_reg:     HEC memory interface error register value
+ * @dbg:             device debug info
  */
 struct hva_dev {
 	struct v4l2_device	v4l2_dev;
@@ -307,6 +358,7 @@  struct hva_dev {
 	u32			lmi_err_reg;
 	u32			emi_err_reg;
 	u32			hec_mif_err_reg;
+	struct hva_dev_dbg	dbg;
 };
 
 /**
@@ -335,6 +387,10 @@  struct hva_enc {
 				  struct hva_stream *stream);
 };
 
+void hva_debugfs_create(struct hva_dev *hva);
+void hva_debugfs_remove(struct hva_dev *hva);
+void hva_dbg_ctx_create(struct hva_ctx *ctx);
+void hva_dbg_ctx_remove(struct hva_ctx *ctx);
 char *hva_dbg_summary(struct hva_ctx *ctx);
 void hva_dbg_perf_begin(struct hva_ctx *ctx);
 void hva_dbg_perf_end(struct hva_ctx *ctx, struct hva_stream *stream);