media: rockchip: vpss: support proc debug info

Signed-off-by: Mingwei Yan <mingwei.yan@rock-chips.com>
Change-Id: Ib831dffa6af60a83c460aafb58f3aa4763074911
This commit is contained in:
Mingwei Yan
2024-03-29 10:57:34 +08:00
committed by Tao Huang
parent a9d424a35c
commit bdfed6ef9c
7 changed files with 339 additions and 3 deletions

View File

@@ -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 */

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;
};