267 lines
7.5 KiB
C
267 lines
7.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) Rockchip Electronics Co., Ltd. */
|
|
#include <linux/clk.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/sem.h>
|
|
#include <linux/seq_file.h>
|
|
#include <media/v4l2-common.h>
|
|
|
|
#include "dev.h"
|
|
#include "procfs.h"
|
|
#include "regs.h"
|
|
#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;
|
|
|
|
if (hw->dev->power.usage_count.counter <= 0) {
|
|
seq_printf(p, "\n%s\n", "HW close");
|
|
return;
|
|
}
|
|
|
|
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",
|
|
dev->name,
|
|
RKVPSS_DRIVER_VERSION >> 16,
|
|
(RKVPSS_DRIVER_VERSION & 0xff00) >> 8,
|
|
RKVPSS_DRIVER_VERSION & 0x00ff);
|
|
for (i = 0; i < dev->hw_dev->clks_num; i++) {
|
|
seq_printf(p, "%-10s %ld\n",
|
|
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,
|
|
dev->isr_err_cnt);
|
|
return 0;
|
|
}
|
|
|
|
static int vpss_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct rkvpss_device *data = pde_data(inode);
|
|
|
|
return single_open(file, vpss_show, data);
|
|
}
|
|
|
|
static const struct proc_ops ops = {
|
|
.proc_open = vpss_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
};
|
|
|
|
int rkvpss_proc_init(struct rkvpss_device *dev)
|
|
{
|
|
dev->procfs = proc_create_data(dev->name, 0, NULL, &ops, dev);
|
|
if (!dev->procfs)
|
|
return -EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
void rkvpss_proc_cleanup(struct rkvpss_device *dev)
|
|
{
|
|
if (dev->procfs)
|
|
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 */
|