--- /dev/null
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Yannick Fertre <yannick.fertre@st.com>
+ *          Hugues Fruchet <hugues.fruchet@st.com>
+ * 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 (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 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, 0444, 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, 0444,
+                                                    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);
+}
 
 
 #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;
 #define to_hva_stream(vb) \
        container_of(vb, struct hva_stream, vbuf)
 
+#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
+/**
+ * struct hva_ctx_dbg - instance context debug info
+ *
+ * @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
+ */
+struct hva_ctx_dbg {
+       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;
+};
+#endif
+
 struct hva_dev;
 struct hva_enc;
 
  * @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...)
+ * @dbg:             context debug info
  */
 struct hva_ctx {
        struct hva_dev                  *hva_dev;
        u32                             sys_errors;
        u32                             encode_errors;
        u32                             frame_errors;
+#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
+       struct hva_ctx_dbg              dbg;
+#endif
 };
 
 #define HVA_FLAG_STREAMINFO    0x0001
 #define HVA_FLAG_FRAMEINFO     0x0002
 
+#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
+/**
+ * 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;
+};
+#endif
+
 #define HVA_MAX_INSTANCES      16
 #define HVA_MAX_ENCODERS       10
 #define HVA_MAX_FORMATS                HVA_MAX_ENCODERS
  * @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;
        u32                     lmi_err_reg;
        u32                     emi_err_reg;
        u32                     hec_mif_err_reg;
+#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
+       struct hva_dev_dbg      dbg;
+#endif
 };
 
 /**
                                  struct hva_stream *stream);
 };
 
+#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
+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);
+void hva_dbg_perf_begin(struct hva_ctx *ctx);
+void hva_dbg_perf_end(struct hva_ctx *ctx, struct hva_stream *stream);
+#endif
+
 #endif /* HVA_H */