From bdfed6ef9c5c271b925096bb583de19314edc1be Mon Sep 17 00:00:00 2001 From: Mingwei Yan Date: Fri, 29 Mar 2024 10:57:34 +0800 Subject: [PATCH] media: rockchip: vpss: support proc debug info Signed-off-by: Mingwei Yan Change-Id: Ib831dffa6af60a83c460aafb58f3aa4763074911 --- drivers/media/platform/rockchip/vpss/procfs.c | 199 +++++++++++++++++- drivers/media/platform/rockchip/vpss/procfs.h | 5 + drivers/media/platform/rockchip/vpss/stream.c | 26 +++ drivers/media/platform/rockchip/vpss/stream.h | 10 + drivers/media/platform/rockchip/vpss/vpss.c | 1 + .../platform/rockchip/vpss/vpss_offline.c | 58 +++++ .../platform/rockchip/vpss/vpss_offline.h | 43 ++++ 7 files changed, 339 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/rockchip/vpss/procfs.c b/drivers/media/platform/rockchip/vpss/procfs.c index a103a88e6e12..ad6153dbdc13 100644 --- a/drivers/media/platform/rockchip/vpss/procfs.c +++ b/drivers/media/platform/rockchip/vpss/procfs.c @@ -12,10 +12,60 @@ #include "version.h" #ifdef CONFIG_PROC_FS + +static void show_hw(struct seq_file *p, struct rkvpss_hw_dev *hw) +{ + int i; + u32 val, mask; + + seq_printf(p, "\n%s\n", "HW INFO"); + val = rkvpss_hw_read(hw, RKVPSS_VPSS_CTRL); + seq_printf(p, "\tmirror:%s(0x%x)\n", (val & 0x10) ? "ON" : "OFF", val); + + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + seq_printf(p, "\toutput[%d]", i); + val = rkvpss_hw_read(hw, RKVPSS_CMSC_CTRL); + mask = RKVPSS_CMSC_CHN_EN(i); + seq_printf(p, "\tcmsc:%s(0x%x)", (val & mask & 1) ? "ON" : "OFF", val); + if (hw->is_ofl_ch[i]) { + val = rkvpss_hw_read(hw, RKVPSS_CROP0_CTRL); + mask = RKVPSS_CROP_CHN_EN(i); + seq_printf(p, "\tcrop:%s(0x%x)", (val & mask) ? "ON" : "OFF", val); + } else { + val = rkvpss_hw_read(hw, RKVPSS_CROP1_CTRL); + mask = RKVPSS_CROP_CHN_EN(i); + seq_printf(p, "\tcrop:%s(0x%x)", (val & mask) ? "ON" : "OFF", val); + } + switch (i) { + case 0: + val = rkvpss_hw_read(hw, RKVPSS_RATIO0_CTRL); + break; + case 1: + val = rkvpss_hw_read(hw, RKVPSS_RATIO1_CTRL); + break; + case 2: + val = rkvpss_hw_read(hw, RKVPSS_RATIO2_CTRL); + break; + case 3: + val = rkvpss_hw_read(hw, RKVPSS_RATIO3_CTRL); + break; + default: + break; + } + seq_printf(p, "\taspt:%s(0x%x)", (val & 1) ? "ON" : "OFF", val); + val = rkvpss_hw_read(hw, RKVPSS_MI_WR_VFLIP_CTRL); + mask = RKVPSS_MI_CHN_V_FLIP(i); + seq_printf(p, "\tflip:%s(0x%x)\n", (val & mask) ? "ON" : "OFF", val); + } +} static int vpss_show(struct seq_file *p, void *v) { struct rkvpss_device *dev = p->private; + struct rkvpss_hw_dev *hw = dev->hw_dev; + struct rkvpss_subdev *vpss_sdev = &dev->vpss_sdev; + struct rkvpss_stream *stream; enum rkvpss_state state = dev->vpss_sdev.state; + int i; u32 val; seq_printf(p, "%-10s Version:v%02x.%02x.%02x\n", @@ -23,14 +73,49 @@ static int vpss_show(struct seq_file *p, void *v) RKVPSS_DRIVER_VERSION >> 16, (RKVPSS_DRIVER_VERSION & 0xff00) >> 8, RKVPSS_DRIVER_VERSION & 0x00ff); - for (val = 0; val < dev->hw_dev->clks_num; val++) { + for (i = 0; i < dev->hw_dev->clks_num; i++) { seq_printf(p, "%-10s %ld\n", - dev->hw_dev->match_data->clks[val], - clk_get_rate(dev->hw_dev->clks[val])); + dev->hw_dev->match_data->clks[i], + clk_get_rate(dev->hw_dev->clks[i])); } if (state != VPSS_START) return 0; + seq_printf(p, "%-10s %dx%d\n", "Input size", + vpss_sdev->in_fmt.width, vpss_sdev->in_fmt.height); + seq_printf(p, "is_ofl_cmsc:%d\n", hw->is_ofl_cmsc); + + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + stream = &dev->stream_vdev.stream[i]; + if (hw->is_ofl_ch[i] || !stream->streaming) { + seq_printf(p, "is_ofl_ch[%d]:%d OFF\n", i, hw->is_ofl_ch[i]); + continue; + } else { + val = rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_CTRL + i * 0x100); + seq_printf(p, "is_ofl_ch[%d]:%d ON(0x%x)\n", i, hw->is_ofl_ch[i], val); + seq_printf(p, "\tFormat:%c%c%c%c crop_v_offs:%d crop_h_offs:%d crop_width:%d crop_height:%d scl_width:%d scl_height:%d\n", + stream->out_fmt.pixelformat, + stream->out_fmt.pixelformat >> 8, + stream->out_fmt.pixelformat >> 16, + stream->out_fmt.pixelformat >> 24, + stream->crop.top, + stream->crop.left, + stream->crop.width, + stream->crop.height, + stream->out_fmt.width, + stream->out_fmt.height); +; + seq_printf(p, "\tframe_cnt:%d rate:%dms delay:%dms frameloss:%d buf_cnt:%d\n", + stream->dbg.id, + stream->dbg.interval / 1000 / 1000, + stream->dbg.delay / 1000 / 1000, + stream->dbg.frameloss, + rkvpss_stream_buf_cnt(stream)); + } + } + + show_hw(p, hw); + seq_printf(p, "%-10s Cnt:%d ErrCnt:%d\n", "Interrupt", dev->isr_cnt, @@ -66,4 +151,112 @@ void rkvpss_proc_cleanup(struct rkvpss_device *dev) remove_proc_entry(dev->name, NULL); dev->procfs = NULL; } + +/************************offline************************/ + +static int offline_vpss_show(struct seq_file *p, void *v) +{ + struct rkvpss_offline_dev *ofl = p->private; + struct rkvpss_hw_dev *hw = ofl->hw; + struct rkvpss_ofl_cfginfo *cfginfo, *next; + int i; + + seq_printf(p, "%-10s Version:v%02x.%02x.%02x\n", + ofl->v4l2_dev.name, + RKVPSS_DRIVER_VERSION >> 16, + (RKVPSS_DRIVER_VERSION & 0xff00) >> 8, + RKVPSS_DRIVER_VERSION & 0x00ff); + for (i = 0; i < ofl->hw->clks_num; i++) { + seq_printf(p, "%-10s %ld\n", + ofl->hw->match_data->clks[i], + clk_get_rate(ofl->hw->clks[i])); + } + + seq_printf(p, "is_ofl_cmsc:%d\n", ofl->hw->is_ofl_cmsc); + + mutex_lock(&ofl->ofl_lock); + list_for_each_entry_safe(cfginfo, next, &ofl->cfginfo_list, list) { + seq_printf(p, "dev_id:%d sequence:%d\n", + cfginfo->dev_id, + cfginfo->sequence); + seq_printf(p, "%-10sbuf_fd:%d Format:%c%c%c%c width:%d height:%d\n", + "Input", + cfginfo->input.buf_fd, + cfginfo->input.format, + cfginfo->input.format >> 8, + cfginfo->input.format >> 16, + cfginfo->input.format >> 24, + cfginfo->input.width, + cfginfo->input.height); + + seq_printf(p, "%-10s\n", "Output"); + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + if (!ofl->hw->is_ofl_ch[i] || !cfginfo->output[i].enable) { + seq_printf(p, "\tch[%d] OFF is_ofl_ch[%d]:%d output[%d].enable:%d\n", + i, i, ofl->hw->is_ofl_ch[i], i, + cfginfo->output[i].enable); + } else { + seq_printf(p, "\tch[%d] ON buf_fd:%d Format:%c%c%c%c crop_v_offs:%d crop_h_offs:%d crop_width:%d crop_height:%d scl_width:%d scl_height:%d\n", + i, + cfginfo->output[i].buf_fd, cfginfo->output[i].format, + cfginfo->output[i].format >> 8, + cfginfo->output[i].format >> 16, + cfginfo->output[i].format >> 24, + cfginfo->output[i].crop_v_offs, + cfginfo->output[i].crop_h_offs, + cfginfo->output[i].crop_width, + cfginfo->output[i].crop_height, + cfginfo->output[i].scl_width, + cfginfo->output[i].scl_height); + } + } + } + mutex_unlock(&ofl->ofl_lock); + + seq_printf(p, "\n%s\n", "Rate"); + for (i = 0; i < DEV_NUM_MAX; i++) { + if (ofl->dev_rate[i].in_timestamp == 0) + continue; + seq_printf(p, "\tdev_id:%d in_rate:%dms out_rate:%dms sequence:%d delay:%dms\n", + i, + ofl->dev_rate[i].in_rate / 1000 / 1000, + ofl->dev_rate[i].out_rate / 1000 / 1000, + ofl->dev_rate[i].sequence, + ofl->dev_rate[i].delay / 1000 / 1000); + } + + show_hw(p, hw); + + return 0; +} + +static int offline_vpss_open(struct inode *inode, struct file *file) +{ + struct rkvpss_offline_dev *data = pde_data(inode); + + return single_open(file, offline_vpss_show, data); +} + +static const struct proc_ops offline_ops = { + .proc_open = offline_vpss_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +int rkvpss_offline_proc_init(struct rkvpss_offline_dev *dev) +{ + dev->procfs = proc_create_data(dev->v4l2_dev.name, 0, NULL, &offline_ops, dev); + if (!dev->procfs) + return -EINVAL; + return 0; +} + +void rkvpss_offline_proc_cleanup(struct rkvpss_offline_dev *dev) +{ + if (dev->procfs) + remove_proc_entry(dev->v4l2_dev.name, NULL); + dev->procfs = NULL; +} + #endif /* CONFIG_PROC_FS */ diff --git a/drivers/media/platform/rockchip/vpss/procfs.h b/drivers/media/platform/rockchip/vpss/procfs.h index 739eb9cabcbc..5383c101e4ed 100644 --- a/drivers/media/platform/rockchip/vpss/procfs.h +++ b/drivers/media/platform/rockchip/vpss/procfs.h @@ -7,9 +7,14 @@ #ifdef CONFIG_PROC_FS int rkvpss_proc_init(struct rkvpss_device *dev); void rkvpss_proc_cleanup(struct rkvpss_device *dev); +int rkvpss_offline_proc_init(struct rkvpss_offline_dev *dev); +void rkvpss_offline_proc_cleanup(struct rkvpss_offline_dev *dev); + #else static inline int rkvpss_proc_init(struct rkvpss_device *dev) { return 0; } static inline void rkvpss_proc_cleanup(struct rkvpss_device *dev) {} +static inline int rkvpss_offline_proc_init(struct rkvpss_offline_dev *dev) { return 0; } +static inline void rkvpss_offline_proc_cleanup(struct rkvpss_offline_dev *dev) {} #endif #endif diff --git a/drivers/media/platform/rockchip/vpss/stream.c b/drivers/media/platform/rockchip/vpss/stream.c index 5443bcc78ba7..65557a2a9757 100644 --- a/drivers/media/platform/rockchip/vpss/stream.c +++ b/drivers/media/platform/rockchip/vpss/stream.c @@ -458,6 +458,23 @@ static struct stream_config scl3_config = { }, }; +int rkvpss_stream_buf_cnt(struct rkvpss_stream *stream) +{ + unsigned long lock_flags = 0; + struct rkvpss_buffer *buf, *tmp; + int cnt = 0; + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + list_for_each_entry_safe(buf, tmp, &stream->buf_queue, queue) + cnt++; + if (stream->curr_buf) + cnt++; + if (stream->next_buf && stream->next_buf != stream->curr_buf) + cnt++; + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + return cnt; +} + static void stream_frame_start(struct rkvpss_stream *stream, u32 irq) { if (stream->is_crop_upd) { @@ -722,6 +739,13 @@ static void rkvpss_frame_end(struct rkvpss_stream *stream) ns = ktime_get_ns(); buf->vb.vb2_buf.timestamp = ns; buf->vb.sequence = sdev->frame_seq; + + ns = ktime_get_ns(); + stream->dbg.frameloss += buf->vb.sequence - stream->dbg.id - 1; + stream->dbg.id = buf->vb.sequence; + stream->dbg.delay = ns - buf->vb.vb2_buf.timestamp; + stream->dbg.interval = ns - stream->dbg.timestamp; + stream->dbg.timestamp = ns; rkvpss_stream_buf_done(stream, buf); } @@ -1156,6 +1180,7 @@ static int rkvpss_stream_start(struct rkvpss_stream *stream) stream->ops->config_mi(stream); stream->streaming = true; + stream->dbg.id = -1; return ret; } @@ -1899,6 +1924,7 @@ void rkvpss_isr(struct rkvpss_device *dev, u32 mis_val) { v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, "isr:0x%x\n", mis_val); + dev->isr_cnt++; if (mis_val & RKVPSS_ISP_ALL_FRM_END && dev->remote_sd) v4l2_subdev_call(dev->remote_sd, core, ioctl, RKISP_VPSS_CMD_EOF, NULL); } diff --git a/drivers/media/platform/rockchip/vpss/stream.h b/drivers/media/platform/rockchip/vpss/stream.h index fc40f4200db9..23b1e75a786e 100644 --- a/drivers/media/platform/rockchip/vpss/stream.h +++ b/drivers/media/platform/rockchip/vpss/stream.h @@ -112,6 +112,14 @@ struct streams_ops { struct v4l2_pix_format_mplane *try_fmt); }; +struct frame_debug_info { + u64 timestamp; + u32 interval; + u32 delay; + u32 id; + u32 frameloss; +}; + /* struct rkvpss_stream - VPSS stream video device * id: stream video identify * buf_queue: queued buffer list @@ -145,6 +153,7 @@ struct rkvpss_stream { struct v4l2_rect crop; struct v4l2_pix_format_mplane out_fmt; + struct frame_debug_info dbg; int id; bool streaming; @@ -169,4 +178,5 @@ void rkvpss_isr(struct rkvpss_device *dev, u32 mis_val); void rkvpss_mi_isr(struct rkvpss_device *dev, u32 mis_val); void rkvpss_unregister_stream_vdevs(struct rkvpss_device *dev); int rkvpss_register_stream_vdevs(struct rkvpss_device *dev); +int rkvpss_stream_buf_cnt(struct rkvpss_stream *stream); #endif diff --git a/drivers/media/platform/rockchip/vpss/vpss.c b/drivers/media/platform/rockchip/vpss/vpss.c index 0c9e3632cd06..e6561115fec7 100644 --- a/drivers/media/platform/rockchip/vpss/vpss.c +++ b/drivers/media/platform/rockchip/vpss/vpss.c @@ -157,6 +157,7 @@ static int rkvpss_sd_s_stream(struct v4l2_subdev *sd, int on) sdev->frame_seq = -1; sdev->frame_timestamp = 0; + dev->isr_cnt = 0; atomic_inc(&dev->hw_dev->refcnt); dev->cmsc_upd = true; rkvpss_cmsc_config(dev, true); diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.c b/drivers/media/platform/rockchip/vpss/vpss_offline.c index c022139970ad..5c1ce67a1180 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.c +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.c @@ -570,6 +570,44 @@ end: rkvpss_hw_write(hw, reg_base + 0x4, val); } +static void add_cfginfo(struct rkvpss_offline_dev *ofl, struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_ofl_cfginfo *cfginfo = NULL, *new_cfg = NULL, *first_cfg = NULL; + int i, count = 0; + + new_cfg = kzalloc(sizeof(struct rkvpss_ofl_cfginfo), GFP_KERNEL); + new_cfg->dev_id = cfg->dev_id; + new_cfg->sequence = cfg->sequence; + new_cfg->input.buf_fd = cfg->input.buf_fd; + new_cfg->input.format = cfg->input.format; + new_cfg->input.width = cfg->input.width; + new_cfg->input.height = cfg->input.height; + + for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + new_cfg->output[i].enable = cfg->output[i].enable; + new_cfg->output[i].buf_fd = cfg->output[i].buf_fd; + new_cfg->output[i].format = cfg->output[i].format; + new_cfg->output[i].crop_v_offs = cfg->output[i].crop_v_offs; + new_cfg->output[i].crop_h_offs = cfg->output[i].crop_h_offs; + new_cfg->output[i].crop_width = cfg->output[i].crop_width; + new_cfg->output[i].crop_height = cfg->output[i].crop_height; + new_cfg->output[i].scl_width = cfg->output[i].scl_width; + new_cfg->output[i].scl_height = cfg->output[i].scl_height; + } + + mutex_lock(&ofl->ofl_lock); + list_for_each_entry(cfginfo, &ofl->cfginfo_list, list) { + count++; + } + if (count >= 5) { + first_cfg = list_first_entry(&ofl->cfginfo_list, struct rkvpss_ofl_cfginfo, list); + list_del_init(&first_cfg->list); + list_add_tail(&new_cfg->list, &ofl->cfginfo_list); + } else { + list_add_tail(&new_cfg->list, &ofl->cfginfo_list); + } + mutex_unlock(&ofl->ofl_lock); +} static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) { struct rkvpss_offline_dev *ofl = video_drvdata(file); @@ -585,6 +623,7 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) s64 us = 0; int ret, i, tile_num = 0; bool wr_uv_swap = false; + u64 ns; if (rkvpss_debug >= 2) { v4l2_info(&ofl->v4l2_dev, @@ -607,6 +646,13 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) } t = ktime_get(); } + + add_cfginfo(ofl, cfg); + + ns = ktime_get_ns(); + ofl->dev_rate[cfg->dev_id].in_rate = ns - ofl->dev_rate[cfg->dev_id].in_timestamp; + ofl->dev_rate[cfg->dev_id].in_timestamp = ns; + init_completion(&ofl->cmpl); ofl->mode_sel_en = false; @@ -1154,6 +1200,13 @@ static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg) "%s end, time:%lldus\n", __func__, us); } + ns = ktime_get_ns(); + ofl->dev_rate[cfg->dev_id].out_rate = ns - ofl->dev_rate[cfg->dev_id].out_timestamp; + ofl->dev_rate[cfg->dev_id].out_timestamp = ns; + ofl->dev_rate[cfg->dev_id].sequence = cfg->sequence; + ofl->dev_rate[cfg->dev_id].delay = ofl->dev_rate[cfg->dev_id].out_timestamp - + ofl->dev_rate[cfg->dev_id].in_timestamp; + return ret; free_buf: for (i -= 1; i >= 0; i--) { @@ -1351,6 +1404,9 @@ int rkvpss_register_offline(struct rkvpss_hw_dev *hw) } video_set_drvdata(vfd, ofl); INIT_LIST_HEAD(&ofl->list); + INIT_LIST_HEAD(&ofl->cfginfo_list); + mutex_init(&ofl->ofl_lock); + rkvpss_offline_proc_init(ofl); return 0; unreg_v4l2: mutex_destroy(&ofl->apilock); @@ -1363,4 +1419,6 @@ void rkvpss_unregister_offline(struct rkvpss_hw_dev *hw) mutex_destroy(&hw->ofl_dev.apilock); video_unregister_device(&hw->ofl_dev.vfd); v4l2_device_unregister(&hw->ofl_dev.v4l2_dev); + mutex_destroy(&hw->ofl_dev.ofl_lock); + rkvpss_offline_proc_cleanup(&hw->ofl_dev); } diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.h b/drivers/media/platform/rockchip/vpss/vpss_offline.h index 4fff3612244c..bbb3b801ec0b 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.h +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.h @@ -3,9 +3,48 @@ #ifndef _RKVPSS_OFFLINE_H #define _RKVPSS_OFFLINE_H +#define DEV_NUM_MAX 10 #include "hw.h" +struct rkvpss_ofl_incfginfo { + int width; + int height; + int format; + int buf_fd; +}; + +struct rkvpss_ofl_outcfginfo { + int enable; + int format; + int buf_fd; + + int crop_h_offs; + int crop_v_offs; + int crop_width; + int crop_height; + + int scl_width; + int scl_height; +}; + +struct rkvpss_ofl_cfginfo { + int dev_id; + int sequence; + struct rkvpss_ofl_incfginfo input; + struct rkvpss_ofl_outcfginfo output[RKVPSS_OUTPUT_MAX]; + struct list_head list; +}; + +struct rkvpss_dev_rate { + u32 sequence; + u32 in_rate; + u64 in_timestamp; + u64 out_timestamp; + u32 out_rate; + u32 delay; +}; + struct rkvpss_offline_dev { struct rkvpss_hw_dev *hw; struct v4l2_device v4l2_dev; @@ -13,6 +52,10 @@ struct rkvpss_offline_dev { struct mutex apilock; struct completion cmpl; struct list_head list; + struct proc_dir_entry *procfs; + struct list_head cfginfo_list; + struct mutex ofl_lock; + struct rkvpss_dev_rate dev_rate[DEV_NUM_MAX]; bool mode_sel_en; };