mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
media: rockchip: fec: init driver for rv1126b
Signed-off-by: Mingwei Yan <mingwei.yan@rock-chips.com> Signed-off-by: Xu Hongfei <xuhf@rock-chips.com> Change-Id: I2fa7a5b68c56fadcd664299c987ee4c227f998c7
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
comment "Rockchip media platform drivers"
|
||||
|
||||
source "drivers/media/platform/rockchip/cif/Kconfig"
|
||||
source "drivers/media/platform/rockchip/fec/Kconfig"
|
||||
source "drivers/media/platform/rockchip/flexbus_cif/Kconfig"
|
||||
source "drivers/media/platform/rockchip/isp1/Kconfig"
|
||||
source "drivers/media/platform/rockchip/isp/Kconfig"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-y += cif/
|
||||
obj-y += fec/
|
||||
obj-y += flexbus_cif/
|
||||
obj-y += isp1/
|
||||
obj-y += isp/
|
||||
|
||||
15
drivers/media/platform/rockchip/fec/Kconfig
Normal file
15
drivers/media/platform/rockchip/fec/Kconfig
Normal file
@@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
config VIDEO_ROCKCHIP_FEC
|
||||
tristate "Rockchip Fisheye Correction Sub System driver"
|
||||
depends on V4L_PLATFORM_DRIVERS
|
||||
depends on VIDEO_DEV
|
||||
depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
select VIDEO_V4L2_SUBDEV_API
|
||||
select VIDEOBUF2_CMA_SG
|
||||
select VIDEOBUF2_VMALLOC
|
||||
default n
|
||||
help
|
||||
Support for FEC on the rockchip SoC.
|
||||
|
||||
If you are using a Rockchip SoC with an integrated FEC module
|
||||
and require FEC, say Y or M here.
|
||||
6
drivers/media/platform/rockchip/fec/Makefile
Normal file
6
drivers/media/platform/rockchip/fec/Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_VIDEO_ROCKCHIP_FEC) += video_rkfec.o
|
||||
|
||||
video_rkfec-objs += hw.o \
|
||||
fec_offline.o \
|
||||
procfs.o
|
||||
626
drivers/media/platform/rockchip/fec/fec_offline.c
Normal file
626
drivers/media/platform/rockchip/fec/fec_offline.c
Normal file
@@ -0,0 +1,626 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/rk-video-format.h>
|
||||
|
||||
#include "fec_offline.h"
|
||||
#include "hw.h"
|
||||
#include "procfs.h"
|
||||
#include "regs.h"
|
||||
|
||||
int rkfec_debug;
|
||||
module_param_named(debug, rkfec_debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "Debug level (0-6)");
|
||||
|
||||
#if IS_LINUX_VERSION_AT_LEAST_6_1
|
||||
#define GET_SG_TABLE(mem_ops, off_buf) mem_ops->cookie(&(off_buf)->vb, (off_buf)->mem)
|
||||
#else
|
||||
#define GET_SG_TABLE(mem_ops, off_buf) mem_ops->cookie((off_buf)->mem)
|
||||
#endif
|
||||
|
||||
static void buf_del(struct file *file, int fd, bool is_all);
|
||||
|
||||
#if IS_LINUX_VERSION_AT_LEAST_6_1
|
||||
static void init_vb2(struct rkfec_offline_dev *ofl,
|
||||
struct rkfec_offline_buf *buf)
|
||||
{
|
||||
struct rkfec_hw_dev *hw = ofl->hw;
|
||||
unsigned long attrs = DMA_ATTR_NO_KERNEL_MAPPING;
|
||||
|
||||
if (!buf)
|
||||
return;
|
||||
memset(&buf->vb, 0, sizeof(buf->vb));
|
||||
ofl->vb2_queue.gfp_flags = GFP_KERNEL | GFP_DMA32;
|
||||
ofl->vb2_queue.dma_dir = DMA_BIDIRECTIONAL;
|
||||
if (hw->is_dma_config)
|
||||
attrs |= DMA_ATTR_FORCE_CONTIGUOUS;
|
||||
ofl->vb2_queue.dma_attrs = attrs;
|
||||
buf->vb.vb2_queue = &ofl->vb2_queue;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int buf_alloc(struct file *file, struct rkfec_buf *info)
|
||||
{
|
||||
struct rkfec_offline_dev *ofl = video_drvdata(file);
|
||||
struct rkfec_hw_dev *hw = ofl->hw;
|
||||
const struct vb2_mem_ops *ops = hw->mem_ops;
|
||||
struct rkfec_offline_buf *buf;
|
||||
struct dma_buf *dbuf;
|
||||
int fd, size;
|
||||
void *mem;
|
||||
|
||||
info->buf_fd = -1;
|
||||
size = PAGE_ALIGN(info->size);
|
||||
if (!size)
|
||||
return -EINVAL;
|
||||
buf = kzalloc(sizeof(struct rkfec_offline_buf), GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
#if IS_LINUX_VERSION_AT_LEAST_6_1
|
||||
init_vb2(ofl, buf);
|
||||
mem = ops->alloc(&buf->vb, hw->dev, info->size);
|
||||
#else
|
||||
mem = ops->alloc(hw->dev, DMA_ATTR_NO_KERNEL_MAPPING, size,
|
||||
DMA_BIDIRECTIONAL, GFP_KERNEL | GFP_DMA32);
|
||||
#endif
|
||||
if (IS_ERR_OR_NULL(mem)) {
|
||||
v4l2_err(&ofl->v4l2_dev, "failed to alloc dmabuf\n");
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
#if IS_LINUX_VERSION_AT_LEAST_6_1
|
||||
dbuf = ops->get_dmabuf(&buf->vb, mem, O_RDWR);
|
||||
#else
|
||||
dbuf = ops->get_dmabuf(mem, O_RDWR);
|
||||
#endif
|
||||
if (IS_ERR_OR_NULL(dbuf)) {
|
||||
v4l2_err(&ofl->v4l2_dev, "failed to get dmabuf\n");
|
||||
goto err_dmabuf;
|
||||
}
|
||||
|
||||
fd = dma_buf_fd(dbuf, O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
v4l2_err(&ofl->v4l2_dev, "failed to get dmabuf fd\n");
|
||||
goto err_dmabuf_fd;
|
||||
}
|
||||
|
||||
get_dma_buf(dbuf);
|
||||
|
||||
info->buf_fd = fd;
|
||||
buf->fd = fd;
|
||||
buf->file = file;
|
||||
buf->dbuf = dbuf;
|
||||
buf->mem = mem;
|
||||
buf->memory = RKFEC_MEMORY_MMAP;
|
||||
ops->prepare(buf->mem);
|
||||
mutex_lock(&hw->dev_lock);
|
||||
list_add_tail(&buf->list, &ofl->list);
|
||||
mutex_unlock(&hw->dev_lock);
|
||||
v4l2_dbg(1, rkfec_debug, &ofl->v4l2_dev, "%s file:%p, fd:%d dbuf:%p size %d\n",
|
||||
__func__, file, fd, dbuf, info->size);
|
||||
return 0;
|
||||
|
||||
err_dmabuf_fd:
|
||||
dma_buf_put(dbuf);
|
||||
err_dmabuf:
|
||||
ops->put(mem);
|
||||
err_alloc:
|
||||
kfree(buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct rkfec_offline_buf *buf_add(struct file *file, int fd, int size)
|
||||
{
|
||||
struct rkfec_offline_dev *ofl = video_drvdata(file);
|
||||
struct rkfec_hw_dev *hw = ofl->hw;
|
||||
const struct vb2_mem_ops *ops = hw->mem_ops;
|
||||
struct rkfec_offline_buf *buf = NULL, *next = NULL;
|
||||
struct dma_buf *dbuf;
|
||||
void *mem = NULL;
|
||||
bool need_add = true;
|
||||
|
||||
dbuf = dma_buf_get(fd);
|
||||
if (IS_ERR_OR_NULL(dbuf)) {
|
||||
v4l2_err(&ofl->v4l2_dev, "invalid dmabuf fd:%d\n", fd);
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (size && dbuf->size < size) {
|
||||
v4l2_err(&ofl->v4l2_dev,
|
||||
"input fd:%d size error:%zu < %u\n",
|
||||
fd, dbuf->size, size);
|
||||
dma_buf_put(dbuf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
mutex_lock(&hw->dev_lock);
|
||||
list_for_each_entry_safe(buf, next, &ofl->list, list) {
|
||||
if (buf->fd == fd && buf->dbuf == dbuf) {
|
||||
need_add = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_add) {
|
||||
buf = kzalloc(sizeof(struct rkfec_offline_buf), GFP_KERNEL);
|
||||
if (!buf)
|
||||
goto err_kzalloc;
|
||||
#if IS_LINUX_VERSION_AT_LEAST_6_1
|
||||
init_vb2(ofl, buf);
|
||||
|
||||
mem = ops->attach_dmabuf(&buf->vb, hw->dev, dbuf, dbuf->size);
|
||||
#else
|
||||
mem = ops->attach_dmabuf(hw->dev, dbuf, dbuf->size,
|
||||
DMA_BIDIRECTIONAL);
|
||||
#endif
|
||||
if (IS_ERR(mem)) {
|
||||
v4l2_err(&ofl->v4l2_dev, "failed to attach dmabuf, fd:%d\n", fd);
|
||||
goto err_attach;
|
||||
}
|
||||
if (ops->map_dmabuf(mem)) {
|
||||
v4l2_err(&ofl->v4l2_dev, "failed to map, fd:%d\n", fd);
|
||||
ops->detach_dmabuf(mem);
|
||||
goto err_map;
|
||||
}
|
||||
buf->fd = fd;
|
||||
buf->file = file;
|
||||
buf->dbuf = dbuf;
|
||||
buf->mem = mem;
|
||||
buf->memory = RKFEC_MEMORY_DMABUF;
|
||||
list_add_tail(&buf->list, &ofl->list);
|
||||
v4l2_dbg(1, rkfec_debug, &ofl->v4l2_dev,
|
||||
"%s file:%p fd:%d dbuf:%p size:%d\n",
|
||||
__func__, file, fd, dbuf, size);
|
||||
} else {
|
||||
dma_buf_put(dbuf);
|
||||
}
|
||||
|
||||
mutex_unlock(&hw->dev_lock);
|
||||
return buf;
|
||||
|
||||
err_map:
|
||||
ops->detach_dmabuf(mem);
|
||||
err_attach:
|
||||
dma_buf_put(dbuf);
|
||||
kfree(buf);
|
||||
err_kzalloc:
|
||||
mutex_unlock(&hw->dev_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void buf_del(struct file *file, int fd, bool is_all)
|
||||
{
|
||||
struct rkfec_offline_dev *ofl = video_drvdata(file);
|
||||
struct rkfec_hw_dev *hw = ofl->hw;
|
||||
const struct vb2_mem_ops *ops = hw->mem_ops;
|
||||
struct rkfec_offline_buf *buf, *next;
|
||||
|
||||
mutex_lock(&hw->dev_lock);
|
||||
list_for_each_entry_safe(buf, next, &ofl->list, list) {
|
||||
if ((is_all || buf->fd == fd)) {
|
||||
v4l2_dbg(1, rkfec_debug, &ofl->v4l2_dev,
|
||||
"%s file:%p fd:%d dbuf:%p, memory:%d\n",
|
||||
__func__, file, buf->fd, buf->dbuf, buf->memory);
|
||||
if (buf->memory == RKFEC_MEMORY_DMABUF) {
|
||||
ops->unmap_dmabuf(buf->mem);
|
||||
ops->detach_dmabuf(buf->mem);
|
||||
} else {
|
||||
ops->put(buf->mem);
|
||||
}
|
||||
dma_buf_put(buf->dbuf);
|
||||
buf->file = NULL;
|
||||
buf->mem = NULL;
|
||||
buf->dbuf = NULL;
|
||||
buf->fd = -1;
|
||||
list_del(&buf->list);
|
||||
kfree(buf);
|
||||
if (!is_all)
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&hw->dev_lock);
|
||||
}
|
||||
|
||||
static int fec_running(struct file *file, struct rkfec_in_out *buf)
|
||||
{
|
||||
struct rkfec_offline_dev *ofl = video_drvdata(file);
|
||||
struct rkfec_hw_dev *hw = ofl->hw;
|
||||
const struct vb2_mem_ops *mem_ops = hw->mem_ops;
|
||||
struct sg_table *sg_talbe;
|
||||
struct rkfec_offline_buf *off_buf;
|
||||
u32 in_fmt, out_fmt;
|
||||
u32 rd_mode, wr_mode;
|
||||
u32 val;
|
||||
u32 in_w = buf->in_width, in_h = buf->in_height;
|
||||
u32 out_w = buf->out_width, out_h = buf->out_height;
|
||||
u32 in_stride, out_stride_y, out_stride_uv;
|
||||
u32 in_uv_offset, out_uv_offset;
|
||||
void __iomem *base = ofl->hw->base_addr;
|
||||
int ret = -EINVAL;
|
||||
ktime_t t = 0;
|
||||
s64 us = 0;
|
||||
|
||||
t = ktime_get();
|
||||
v4l2_dbg(3, rkfec_debug, &ofl->v4l2_dev,
|
||||
"%s enter %dx%d->%dx%d format(in:%c%c%c%c out:%c%c%c%c)\n",
|
||||
__func__, in_w, in_h, out_w, out_h,
|
||||
buf->in_fourcc, buf->in_fourcc >> 8,
|
||||
buf->in_fourcc >> 16, buf->in_fourcc >> 24,
|
||||
buf->out_fourcc, buf->out_fourcc >> 8,
|
||||
buf->out_fourcc >> 16, buf->out_fourcc >> 24);
|
||||
|
||||
|
||||
//clk tosee
|
||||
|
||||
init_completion(&ofl->cmpl);
|
||||
|
||||
switch (buf->in_fourcc) {
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
in_fmt = SW_FEC_RD_FMT(1);
|
||||
rd_mode = SW_FEC_RD_MODE(0);
|
||||
in_stride = (buf->buf_cfg.in_stride + 15) / 16 * 16;
|
||||
in_uv_offset = in_stride * in_h;
|
||||
break;
|
||||
case V4L2_PIX_FMT_TILE420:
|
||||
in_fmt = SW_FEC_RD_FMT(0);
|
||||
rd_mode = SW_FEC_RD_MODE(1);
|
||||
in_stride = (buf->buf_cfg.in_stride * 6 + 15) / 16 * 16;
|
||||
in_uv_offset = in_stride * in_h;
|
||||
break;
|
||||
default:
|
||||
v4l2_err(&ofl->v4l2_dev,
|
||||
"no support in format:%c%c%c%c\n",
|
||||
buf->in_fourcc, buf->in_fourcc >> 8,
|
||||
buf->in_fourcc >> 16, buf->in_fourcc >> 24);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (buf->out_fourcc) {
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
out_fmt = SW_FEC_WR_FMT(1);
|
||||
wr_mode = SW_FEC_WR_MODE(0);
|
||||
out_stride_y = (buf->buf_cfg.out_stride + 15) / 16 * 16;
|
||||
out_stride_uv = out_stride_y;
|
||||
out_uv_offset = out_stride_y * out_h;
|
||||
break;
|
||||
case V4L2_PIX_FMT_TILE420:
|
||||
out_fmt = SW_FEC_WR_FMT(0);
|
||||
wr_mode = SW_FEC_WR_MODE(1);
|
||||
out_stride_y = (buf->buf_cfg.out_stride * 6 + 15) / 16 * 16;
|
||||
out_stride_uv = out_stride_y;
|
||||
out_uv_offset = out_stride_y * out_h;
|
||||
break;
|
||||
case V4L2_PIX_FMT_FBC0:
|
||||
out_fmt = SW_FEC_WR_FMT(0);
|
||||
wr_mode = SW_FEC_WR_MODE(2);
|
||||
out_stride_y = (buf->buf_cfg.out_stride + 63) / 64 * 384;
|
||||
out_stride_uv = (buf->buf_cfg.out_stride + 63) / 64 * 16;
|
||||
// Head stride is c channel
|
||||
out_uv_offset = out_stride_uv * out_h / 4;
|
||||
break;
|
||||
case V4L2_PIX_FMT_QUAD:
|
||||
out_fmt = SW_FEC_WR_FMT(0);
|
||||
wr_mode = SW_FEC_WR_MODE(3);
|
||||
out_stride_y = (buf->buf_cfg.out_stride * 3 + 15) / 16 * 16;
|
||||
out_stride_uv = out_stride_y;
|
||||
out_uv_offset = out_stride_y * out_h;
|
||||
break;
|
||||
default:
|
||||
v4l2_err(&ofl->v4l2_dev, "no support out format:%c%c%c%c\n",
|
||||
buf->out_fourcc, buf->out_fourcc >> 8,
|
||||
buf->out_fourcc >> 16, buf->out_fourcc >> 24);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* input picture buf */
|
||||
off_buf = buf_add(file, buf->buf_cfg.in_pic_fd, buf->buf_cfg.in_size);
|
||||
if (!off_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
sg_talbe = GET_SG_TABLE(mem_ops, off_buf);
|
||||
if (!sg_talbe)
|
||||
goto free_buf;
|
||||
val = sg_dma_address(sg_talbe->sgl);
|
||||
val += buf->buf_cfg.in_offs;
|
||||
writel(val, base + RKFEC_RD_Y_BASE);
|
||||
val += in_uv_offset;
|
||||
writel(val, base + RKFEC_RD_C_BASE);
|
||||
|
||||
/* output picture buf */
|
||||
off_buf = buf_add(file, buf->buf_cfg.out_pic_fd, buf->buf_cfg.out_size);
|
||||
if (!off_buf)
|
||||
goto free_buf;
|
||||
|
||||
sg_talbe = GET_SG_TABLE(mem_ops, off_buf);
|
||||
if (!sg_talbe)
|
||||
goto free_buf;
|
||||
val = sg_dma_address(sg_talbe->sgl);
|
||||
if (buf->out_fourcc == V4L2_PIX_FMT_FBC0) {
|
||||
val += buf->buf_cfg.out_offs;
|
||||
writel(val, base + RKFEC_WR_C_BASE);
|
||||
val += out_uv_offset;
|
||||
writel(val, base + RKFEC_WR_Y_BASE);
|
||||
} else {
|
||||
val += buf->buf_cfg.out_offs;
|
||||
writel(val, base + RKFEC_WR_Y_BASE);
|
||||
val += out_uv_offset;
|
||||
writel(val, base + RKFEC_WR_C_BASE);
|
||||
}
|
||||
|
||||
/* lut buf */
|
||||
off_buf = buf_add(file, buf->buf_cfg.lut_fd, buf->buf_cfg.lut_size);
|
||||
if (!off_buf)
|
||||
goto free_buf;
|
||||
|
||||
sg_talbe = GET_SG_TABLE(mem_ops, off_buf);
|
||||
if (!sg_talbe)
|
||||
goto free_buf;
|
||||
val = sg_dma_address(sg_talbe->sgl);
|
||||
writel(val, base + RKFEC_LUT_BASE);
|
||||
|
||||
//fmt
|
||||
val = in_fmt | out_fmt | rd_mode | wr_mode;
|
||||
writel(val, base + RKFEC_CTRL);
|
||||
|
||||
//stride
|
||||
val = FEC_RD_VIR_STRIDE_Y(in_stride / 4) | FEC_RD_VIR_STRIDE_C(in_stride / 4);
|
||||
writel(val, base + RKFEC_RD_VIR_STRIDE);
|
||||
val = FEC_WR_VIR_STRIDE_Y(out_stride_y / 4) | FEC_WR_VIR_STRIDE_C(out_stride_uv / 4);
|
||||
writel(val, base + RKFEC_WR_VIR_STRIDE);
|
||||
//with height lut_size
|
||||
val = SW_FEC_SRC_WIDTH(buf->in_width) | Sw_FEC_SRC_HEIGHT(buf->in_height);
|
||||
writel(val, base + RKFEC_SRC_SIZE);
|
||||
val = SW_FEC_DST_WIDTH(buf->out_width) | Sw_FEC_DST_HEIGHT(buf->out_height);
|
||||
writel(val, base + RKFEC_DST_SIZE);
|
||||
val = SW_LUT_SIZE(buf->buf_cfg.lut_size);
|
||||
writel(val, base + RKFEC_LUT_SIZE);
|
||||
|
||||
//new bg val
|
||||
val = SW_BG_Y_VALUE(buf->bg_val.bg_y) |
|
||||
SW_BG_U_VALUE(buf->bg_val.bg_u) |
|
||||
SW_BG_Y_VALUE(buf->bg_val.bg_v);
|
||||
writel(val, base + RKFEC_BG_VALUE);
|
||||
|
||||
//core_ctrl
|
||||
val = SW_FEC_BIC_MODE(buf->core_ctrl.bic_mode) |
|
||||
SW_LUT_DENSITY(buf->core_ctrl.density) |
|
||||
SW_FEC_BORDER_MODE(buf->core_ctrl.border_mode) |
|
||||
SW_FEC_PBUF_CRS_DIS(buf->core_ctrl.pbuf_crs_dis) |
|
||||
SW_FEC_CRS_BUF_MODE(buf->core_ctrl.buf_mode) |
|
||||
SYS_FEC_ST;
|
||||
writel(val, base + RKFEC_CORE_CTRL);
|
||||
|
||||
writel(0, base + RKFEC_CLK_DIS);
|
||||
|
||||
writel(0x1c, base + RKFEC_CACHE_MAX_READS);
|
||||
writel(0x27, base + RKFEC_CACHE_CTRL);
|
||||
|
||||
//update
|
||||
writel(SYS_FEC_FORCE_UPD, base + RKFEC_UPD);
|
||||
|
||||
//start
|
||||
if (!hw->is_shutdown)
|
||||
writel(SYS_FEC_ST, base + RKFEC_STRT);
|
||||
|
||||
ofl->state = RKFEC_START;
|
||||
|
||||
// add info for procfs
|
||||
ofl->in_fmt.width = in_w;
|
||||
ofl->in_fmt.height = in_h;
|
||||
ofl->in_fmt.pixelformat = buf->in_fourcc;
|
||||
ofl->in_fmt.bytesperline = buf->buf_cfg.in_stride;
|
||||
ofl->in_fmt.sizeimage = buf->buf_cfg.in_size;
|
||||
|
||||
ofl->out_fmt.width = out_w;
|
||||
ofl->out_fmt.height = out_h;
|
||||
ofl->out_fmt.pixelformat = buf->out_fourcc;
|
||||
ofl->out_fmt.bytesperline = buf->buf_cfg.out_stride;
|
||||
ofl->out_fmt.sizeimage = buf->buf_cfg.out_size;
|
||||
|
||||
ofl->curr_frame.fs_seq++;
|
||||
ofl->curr_frame.fs_timestamp = ktime_get_ns();
|
||||
|
||||
ret = wait_for_completion_timeout(&ofl->cmpl, msecs_to_jiffies(300));
|
||||
if (!ret) {
|
||||
v4l2_err(&ofl->v4l2_dev, "fec working timeout\n");
|
||||
ret = -EAGAIN;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
us = ktime_us_delta(ktime_get(), t);
|
||||
v4l2_dbg(3, rkfec_debug, &ofl->v4l2_dev,
|
||||
"%s exit ret:%d, time:%lldus\n", __func__, ret, us);
|
||||
|
||||
ofl->debug.interval = us;
|
||||
|
||||
ofl->state = RKFEC_FRAME_END;
|
||||
if (!ret) {
|
||||
ofl->curr_frame.fe_seq++;
|
||||
ofl->curr_frame.fe_timestamp = ktime_get_ns();
|
||||
if (ofl->curr_frame.fe_seq > ofl->prev_frame.fe_seq &&
|
||||
ofl->curr_frame.fe_seq - ofl->prev_frame.fe_seq > 1)
|
||||
ofl->debug.frameloss += ofl->curr_frame.fe_seq - ofl->prev_frame.fe_seq - 1;
|
||||
} else {
|
||||
ofl->debug.frameloss++;
|
||||
}
|
||||
|
||||
ofl->prev_frame = ofl->curr_frame;
|
||||
|
||||
return ret;
|
||||
|
||||
free_buf:
|
||||
v4l2_dbg(3, rkfec_debug, &ofl->v4l2_dev,
|
||||
"%s sg_talbe error\n", __func__);
|
||||
buf_del(file, 0, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long rkfec_ofl_ioctl(struct file *file, void *fh,
|
||||
bool valid_prio,
|
||||
unsigned int cmd, void *arg)
|
||||
{
|
||||
struct rkfec_offline_dev *ofl = video_drvdata(file);
|
||||
struct rkfec_offline_buf *buf = NULL;
|
||||
long ret = 0;
|
||||
|
||||
ofl->pm_need_wait = true;
|
||||
|
||||
if (!arg) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case RKFEC_CMD_IN_OUT:
|
||||
ret = fec_running(file, arg);
|
||||
break;
|
||||
case RKFEC_CMD_BUF_ALLOC:
|
||||
buf_alloc(file, arg);
|
||||
break;
|
||||
case RKFEC_CMD_BUF_ADD:
|
||||
buf = buf_add(file, *(int *)arg, 0);
|
||||
if (!buf)
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
case RKFEC_CMD_BUF_DEL:
|
||||
buf_del(file, *(int *)arg, false);
|
||||
break;
|
||||
default:
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
out:
|
||||
/* notify hw suspend */
|
||||
if (ofl->hw->is_suspend)
|
||||
complete(&ofl->pm_cmpl);
|
||||
|
||||
ofl->pm_need_wait = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct v4l2_ioctl_ops offline_ioctl_ops = {
|
||||
.vidioc_default = rkfec_ofl_ioctl,
|
||||
};
|
||||
|
||||
static int ofl_open(struct file *file)
|
||||
{
|
||||
struct rkfec_offline_dev *ofl = video_drvdata(file);
|
||||
int ret;
|
||||
|
||||
ret = v4l2_fh_open(file);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
mutex_lock(&ofl->hw->dev_lock);
|
||||
ret = pm_runtime_get_sync(ofl->hw->dev);
|
||||
mutex_unlock(&ofl->hw->dev_lock);
|
||||
if (ret < 0)
|
||||
v4l2_fh_release(file);
|
||||
end:
|
||||
v4l2_dbg(1, rkfec_debug, &ofl->v4l2_dev, "%s ret:%d\n", __func__, ret);
|
||||
return (ret > 0) ? 0 : ret;
|
||||
}
|
||||
|
||||
static int ofl_release(struct file *file)
|
||||
{
|
||||
struct rkfec_offline_dev *ofl = video_drvdata(file);
|
||||
int ret;
|
||||
|
||||
v4l2_dbg(1, rkfec_debug, &ofl->v4l2_dev, "%s\n", __func__);
|
||||
|
||||
ret = v4l2_fh_release(file);
|
||||
if (!ret) {
|
||||
buf_del(file, 0, true);
|
||||
mutex_lock(&ofl->hw->dev_lock);
|
||||
pm_runtime_put_sync(ofl->hw->dev);
|
||||
mutex_unlock(&ofl->hw->dev_lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_file_operations offline_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ofl_open,
|
||||
.release = ofl_release,
|
||||
.unlocked_ioctl = video_ioctl2,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl32 = video_ioctl2,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct video_device offline_videodev = {
|
||||
.name = "rkfec_offline",
|
||||
.vfl_dir = VFL_DIR_RX,
|
||||
.fops = &offline_fops,
|
||||
.ioctl_ops = &offline_ioctl_ops,
|
||||
.minor = -1,
|
||||
.release = video_device_release_empty,
|
||||
};
|
||||
|
||||
void rkfec_offline_irq(struct rkfec_hw_dev *hw, u32 irq)
|
||||
{
|
||||
struct rkfec_offline_dev *ofl = &hw->ofl_dev;
|
||||
|
||||
v4l2_dbg(3, rkfec_debug, &ofl->v4l2_dev, "%s 0x%x\n", __func__, irq);
|
||||
|
||||
if (!completion_done(&ofl->cmpl))
|
||||
complete(&ofl->cmpl);
|
||||
}
|
||||
|
||||
int rkfec_register_offline(struct rkfec_hw_dev *hw)
|
||||
{
|
||||
struct rkfec_offline_dev *ofl = &hw->ofl_dev;
|
||||
struct v4l2_device *v4l2_dev;
|
||||
struct video_device *vfd;
|
||||
int ret;
|
||||
|
||||
ofl->hw = hw;
|
||||
v4l2_dev = &ofl->v4l2_dev;
|
||||
strscpy(v4l2_dev->name, offline_videodev.name, sizeof(v4l2_dev->name));
|
||||
ret = v4l2_device_register(hw->dev, v4l2_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&ofl->apilock);
|
||||
ofl->vfd = offline_videodev;
|
||||
vfd = &ofl->vfd;
|
||||
vfd->device_caps = V4L2_CAP_STREAMING;
|
||||
vfd->v4l2_dev = v4l2_dev;
|
||||
ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
|
||||
if (ret) {
|
||||
v4l2_err(v4l2_dev, "Failed to register video device\n");
|
||||
goto unreg_v4l2;
|
||||
}
|
||||
video_set_drvdata(vfd, ofl);
|
||||
INIT_LIST_HEAD(&ofl->list);
|
||||
rkfec_offline_proc_init(ofl);
|
||||
ofl->state = RKFEC_STOP;
|
||||
//todo
|
||||
init_completion(&ofl->pm_cmpl);
|
||||
|
||||
memset(&ofl->vb2_queue, 0, sizeof(ofl->vb2_queue));
|
||||
memset(&ofl->curr_frame, 0, sizeof(ofl->curr_frame));
|
||||
memset(&ofl->prev_frame, 0, sizeof(ofl->prev_frame));
|
||||
|
||||
v4l2_info(&ofl->v4l2_dev, "%s success\n", __func__);
|
||||
return 0;
|
||||
unreg_v4l2:
|
||||
mutex_destroy(&ofl->apilock);
|
||||
v4l2_device_unregister(v4l2_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rkfec_unregister_offline(struct rkfec_hw_dev *hw)
|
||||
{
|
||||
struct rkfec_offline_dev *ofl = &hw->ofl_dev;
|
||||
|
||||
rkfec_offline_proc_cleanup(&hw->ofl_dev);
|
||||
mutex_destroy(&ofl->apilock);
|
||||
video_unregister_device(&ofl->vfd);
|
||||
v4l2_device_unregister(&ofl->v4l2_dev);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(DMA_BUF);
|
||||
109
drivers/media/platform/rockchip/fec/fec_offline.h
Normal file
109
drivers/media/platform/rockchip/fec/fec_offline.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKFEC_FEC_OFFLINE_H
|
||||
#define _RKFEC_FEC_OFFLINE_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-fh.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/videobuf2-dma-contig.h>
|
||||
#include <media/videobuf2-v4l2.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <uapi/linux/rk-fec-config.h>
|
||||
#include <uapi/linux/rk-video-format.h>
|
||||
|
||||
|
||||
extern int rkfec_debug;
|
||||
|
||||
/**
|
||||
* enum rkfec_memory - type of memory model used to make the buffers visible
|
||||
* on userspace.
|
||||
*
|
||||
* @RKFEC_MEMORY_UNKNOWN: Buffer status is unknown or it is not used yet on
|
||||
* userspace.
|
||||
* @RKFEC_MEMORY_MMAP: The buffers are allocated by the Kernel and it is
|
||||
* memory mapped via mmap() ioctl.
|
||||
* @RKFEC_MEMORY_DMABUF: The buffers are passed to userspace via DMA buffer.
|
||||
*/
|
||||
enum rkfec_memory {
|
||||
RKFEC_MEMORY_UNKNOWN = 0,
|
||||
RKFEC_MEMORY_MMAP = 1,
|
||||
RKFEC_MEMORY_DMABUF = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* V I D E O I M A G E F O R M A T
|
||||
*/
|
||||
struct rkfec_pix_format {
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 pixelformat;
|
||||
u32 offset;
|
||||
u32 bytesperline;
|
||||
u32 sizeimage;
|
||||
};
|
||||
|
||||
enum rkfec_state {
|
||||
RKFEC_FRAME_END = BIT(1),
|
||||
|
||||
RKFEC_STOP = BIT(16),
|
||||
RKFEC_START = BIT(17),
|
||||
RKFEC_ERROR = BIT(18),
|
||||
};
|
||||
|
||||
struct rkfec_frame_info {
|
||||
u32 fs_seq;
|
||||
u32 fe_seq;
|
||||
u64 fs_timestamp;
|
||||
u64 fe_timestamp;
|
||||
};
|
||||
struct rkfec_debug_info {
|
||||
u32 interval;
|
||||
u32 frameloss;
|
||||
};
|
||||
|
||||
struct rkfec_offline_dev {
|
||||
struct rkfec_hw_dev *hw;
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct video_device vfd;
|
||||
struct mutex apilock;
|
||||
struct completion cmpl;
|
||||
struct completion pm_cmpl;
|
||||
struct list_head list;
|
||||
bool pm_need_wait;
|
||||
struct vb2_queue vb2_queue;
|
||||
struct proc_dir_entry *procfs;
|
||||
unsigned int isr_cnt;
|
||||
unsigned int err_cnt;
|
||||
unsigned int state;
|
||||
unsigned int in_seq;
|
||||
unsigned int out_seq;
|
||||
struct rkfec_frame_info prev_frame;
|
||||
struct rkfec_frame_info curr_frame;
|
||||
struct rkfec_debug_info debug;
|
||||
struct rkfec_pix_format in_fmt;
|
||||
struct rkfec_pix_format out_fmt;
|
||||
};
|
||||
|
||||
/*
|
||||
* rkfec_offline_buf
|
||||
* @memory: current memory type used
|
||||
*/
|
||||
struct rkfec_offline_buf {
|
||||
struct list_head list;
|
||||
struct vb2_buffer vb;
|
||||
struct file *file;
|
||||
struct dma_buf *dbuf;
|
||||
void *mem;
|
||||
int fd;
|
||||
unsigned int memory;
|
||||
};
|
||||
|
||||
int rkfec_register_offline(struct rkfec_hw_dev *hw);
|
||||
void rkfec_unregister_offline(struct rkfec_hw_dev *hw);
|
||||
void rkfec_offline_irq(struct rkfec_hw_dev *hw, u32 irq);
|
||||
|
||||
#endif
|
||||
395
drivers/media/platform/rockchip/fec/hw.c
Normal file
395
drivers/media/platform/rockchip/fec/hw.c
Normal file
@@ -0,0 +1,395 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#include "fec_offline.h"
|
||||
#include "hw.h"
|
||||
#include "regs.h"
|
||||
#include "version.h"
|
||||
|
||||
static const char * const rv1126b_fec_clks[] = {
|
||||
"clk_fec",
|
||||
"aclk_fec",
|
||||
"hclk_fec",
|
||||
};
|
||||
|
||||
static void rkfec_set_clk_rate(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
clk_set_rate(clk, rate);
|
||||
}
|
||||
|
||||
static void rkfec_soft_reset(struct rkfec_hw_dev *hw)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* reset */
|
||||
val = SYS_SOFT_RST_FBCE | SYS_SOFT_RST_ACLK;
|
||||
writel(val, hw->base_addr + RKFEC_CLK_DIS);
|
||||
udelay(10);
|
||||
writel(~val, hw->base_addr + RKFEC_CLK_DIS);
|
||||
|
||||
if (hw->reset) {
|
||||
reset_control_assert(hw->reset);
|
||||
udelay(20);
|
||||
reset_control_deassert(hw->reset);
|
||||
udelay(20);
|
||||
}
|
||||
|
||||
/* refresh iommu after reset */
|
||||
if (hw->is_mmu) {
|
||||
rockchip_iommu_disable(hw->dev);
|
||||
rockchip_iommu_enable(hw->dev);
|
||||
}
|
||||
|
||||
/* clk_dis */
|
||||
val = SYS_FEC_LGC_CKG_DIS | SYS_FEC_RAM_CKG_DIS;
|
||||
writel(val, hw->base_addr + RKFEC_CLK_DIS);
|
||||
|
||||
/* int en */
|
||||
val = FRM_END_P_FEC;
|
||||
writel(val, hw->base_addr + RKFEC_INT_EN);
|
||||
}
|
||||
|
||||
static inline bool is_iommu_enable(struct device *dev)
|
||||
{
|
||||
struct device_node *iommu;
|
||||
|
||||
iommu = of_parse_phandle(dev->of_node, "iommus", 0);
|
||||
if (!iommu) {
|
||||
dev_info(dev, "no iommu attached, using non-iommu buffers\n");
|
||||
return false;
|
||||
} else if (!of_device_is_available(iommu)) {
|
||||
dev_info(dev, "iommu is disable, using non-iommu buffers\n");
|
||||
of_node_put(iommu);
|
||||
return false;
|
||||
}
|
||||
of_node_put(iommu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void disable_sys_clk(struct rkfec_hw_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->clks_num; i++)
|
||||
clk_disable_unprepare(dev->clks[i]);
|
||||
}
|
||||
|
||||
static int enable_sys_clk(struct rkfec_hw_dev *dev)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < dev->clks_num; i++) {
|
||||
ret = clk_prepare_enable(dev->clks[i]);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
//tosee
|
||||
|
||||
rkfec_set_clk_rate(dev->clks[0],
|
||||
dev->clk_rate_tbl[dev->clk_rate_tbl_num - 1].clk_rate * 1000000);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
for (--i; i >= 0; --i)
|
||||
clk_disable_unprepare(dev->clks[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t rkfec_irq_hdl(int irq, void *ctx)
|
||||
{
|
||||
struct device *dev = ctx;
|
||||
|
||||
struct rkfec_hw_dev *hw_dev = dev_get_drvdata(dev);
|
||||
void __iomem *base = hw_dev->base_addr;
|
||||
unsigned int mis_val;
|
||||
|
||||
spin_lock(&hw_dev->irq_lock);
|
||||
mis_val = readl(base + RKFEC_INT_MSK);
|
||||
writel(mis_val, base + RKFEC_INT_CLR);
|
||||
spin_unlock(&hw_dev->irq_lock);
|
||||
|
||||
v4l2_dbg(3, rkfec_debug, &hw_dev->ofl_dev.v4l2_dev,
|
||||
"fec isr:0x%x\n", mis_val);
|
||||
|
||||
hw_dev->ofl_dev.isr_cnt++;
|
||||
|
||||
if (mis_val & FRM_END_P_FEC) {
|
||||
mis_val &= -FRM_END_P_FEC;
|
||||
rkfec_offline_irq(hw_dev, mis_val);
|
||||
}
|
||||
|
||||
if (mis_val & (BIT(2) | BIT(3) | BIT(4) | BIT(5)))
|
||||
hw_dev->ofl_dev.err_cnt++;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct fec_clk_info rv1126b_fec_clk_rate[] = {
|
||||
{
|
||||
.clk_rate = 300,
|
||||
.refer_data = 1920,
|
||||
}, {
|
||||
.clk_rate = 400,
|
||||
.refer_data = 2688,
|
||||
}, {
|
||||
.clk_rate = 500,
|
||||
.refer_data = 3072,
|
||||
}, {
|
||||
.clk_rate = 600,
|
||||
.refer_data = 3840,
|
||||
}, {
|
||||
.clk_rate = 702,
|
||||
.refer_data = 4672,
|
||||
}
|
||||
};
|
||||
|
||||
static struct irqs_data rv1126b_fec_irqs[] = {
|
||||
{"fec_irq", rkfec_irq_hdl},
|
||||
};
|
||||
|
||||
static const struct fec_match_data rv1126b_fec_match_data = {
|
||||
.fec_ver = RKFEC_V20,
|
||||
.clks = rv1126b_fec_clks,
|
||||
.clks_num = ARRAY_SIZE(rv1126b_fec_clks),
|
||||
.clk_rate_tbl = rv1126b_fec_clk_rate,
|
||||
.clk_rate_tbl_num = ARRAY_SIZE(rv1126b_fec_clk_rate),
|
||||
.irqs = rv1126b_fec_irqs,
|
||||
.num_irqs = ARRAY_SIZE(rv1126b_fec_irqs),
|
||||
};
|
||||
|
||||
static const struct of_device_id rkfec_hw_of_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,rv1126b-rkfec",
|
||||
.data = &rv1126b_fec_match_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int rkfec_hw_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct fec_match_data *match_data;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rkfec_hw_dev *hw_dev;
|
||||
struct resource *res;
|
||||
int i, ret, irq;
|
||||
bool is_mem_reserved = true;
|
||||
|
||||
dev_info(dev, "RK FEC Version: %d.%d.%d\n",
|
||||
(RKFEC_DRIVER_VERSION >> 16) & 0xFF,
|
||||
(RKFEC_DRIVER_VERSION >> 8) & 0xFF,
|
||||
RKFEC_DRIVER_VERSION & 0xFF);
|
||||
|
||||
match_data = device_get_match_data(&pdev->dev);
|
||||
if (!match_data) {
|
||||
dev_err(dev, "no of match data provided\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hw_dev = devm_kzalloc(dev, sizeof(*hw_dev), GFP_KERNEL);
|
||||
if (!hw_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, hw_dev);
|
||||
hw_dev->dev = dev;
|
||||
hw_dev->match_data = match_data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "get resource failed\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
hw_dev->base_addr = devm_ioremap_resource(dev, res);
|
||||
if (PTR_ERR(hw_dev->base_addr) == -EBUSY) {
|
||||
resource_size_t offset = res->start;
|
||||
resource_size_t size = resource_size(res);
|
||||
|
||||
hw_dev->base_addr = devm_ioremap(dev, offset, size);
|
||||
}
|
||||
if (IS_ERR(hw_dev->base_addr)) {
|
||||
dev_err(dev, "ioremap failed\n");
|
||||
ret = PTR_ERR(hw_dev->base_addr);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* three are irq names in dts */
|
||||
spin_lock_init(&hw_dev->irq_lock);
|
||||
for (i = 0; i < match_data->num_irqs; i++) {
|
||||
irq = platform_get_irq_byname(pdev,
|
||||
match_data->irqs[i].name);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "no irq %s in dts\n",
|
||||
match_data->irqs[i].name);
|
||||
ret = irq;
|
||||
goto err;
|
||||
}
|
||||
ret = devm_request_irq(dev, irq,
|
||||
match_data->irqs[i].irq_hdl,
|
||||
IRQF_SHARED,
|
||||
dev_driver_string(dev),
|
||||
dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "request %s failed: %d\n",
|
||||
match_data->irqs[i].name, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev_err(dev, "request %s : %d\n",
|
||||
match_data->irqs[i].name, irq);
|
||||
}
|
||||
|
||||
for (i = 0; i < match_data->clks_num; i++) {
|
||||
struct clk *clk = devm_clk_get(dev, match_data->clks[i]);
|
||||
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "failed to get %s\n",
|
||||
match_data->clks[i]);
|
||||
ret = PTR_ERR(clk);
|
||||
goto err;
|
||||
}
|
||||
hw_dev->clks[i] = clk;
|
||||
}
|
||||
hw_dev->clks_num = match_data->clks_num;
|
||||
hw_dev->clk_rate_tbl = match_data->clk_rate_tbl;
|
||||
hw_dev->clk_rate_tbl_num = match_data->clk_rate_tbl_num;
|
||||
|
||||
hw_dev->reset = devm_reset_control_array_get(dev, false, false);
|
||||
if (IS_ERR(hw_dev->reset)) {
|
||||
dev_info(dev, "failed to get cru reset\n");
|
||||
hw_dev->reset = NULL;
|
||||
}
|
||||
|
||||
mutex_init(&hw_dev->dev_lock);
|
||||
hw_dev->is_idle = true;
|
||||
hw_dev->is_dma_config = true;
|
||||
hw_dev->is_dma_sg_ops = true;
|
||||
hw_dev->is_shutdown = false;
|
||||
hw_dev->is_mmu = is_iommu_enable(dev);
|
||||
ret = of_reserved_mem_device_init(dev);
|
||||
if (ret) {
|
||||
is_mem_reserved = false;
|
||||
if (!hw_dev->is_mmu)
|
||||
dev_info(dev, "No reserved memory region. default cma area!\n");
|
||||
}
|
||||
if (hw_dev->is_mmu && !is_mem_reserved)
|
||||
hw_dev->is_dma_config = false;
|
||||
hw_dev->mem_ops = &vb2_cma_sg_memops;
|
||||
|
||||
rkfec_register_offline(hw_dev);
|
||||
|
||||
dev_info(dev, "%s success\n", __func__);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rkfec_hw_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rkfec_hw_dev *hw_dev = platform_get_drvdata(pdev);
|
||||
|
||||
rkfec_unregister_offline(hw_dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
mutex_destroy(&hw_dev->dev_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rkfec_hw_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct rkfec_hw_dev *hw_dev = platform_get_drvdata(pdev);
|
||||
u32 val;
|
||||
|
||||
hw_dev->is_shutdown = true;
|
||||
if (pm_runtime_active(&pdev->dev)) {
|
||||
writel(0, hw_dev->base_addr + RKFEC_INT_EN);
|
||||
|
||||
val = SYS_SOFT_RST_FBCE | SYS_SOFT_RST_ACLK;
|
||||
writel(val, hw_dev->base_addr + RKFEC_CLK_DIS);
|
||||
udelay(10);
|
||||
writel(~val, hw_dev->base_addr + RKFEC_CLK_DIS);
|
||||
}
|
||||
dev_info(&pdev->dev, "%s\n", __func__);
|
||||
}
|
||||
|
||||
static int __maybe_unused rkfec_hw_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct rkfec_hw_dev *hw_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (rkfec_debug >= 4)
|
||||
dev_info(dev, "%s enter\n", __func__);
|
||||
|
||||
if (dev->power.runtime_status)
|
||||
writel(0, hw_dev->base_addr + RKFEC_INT_EN);
|
||||
|
||||
disable_sys_clk(hw_dev);
|
||||
|
||||
if (rkfec_debug >= 4)
|
||||
dev_info(dev, "%s exit\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused rkfec_hw_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct rkfec_hw_dev *hw_dev = dev_get_drvdata(dev);
|
||||
|
||||
if (rkfec_debug >= 4)
|
||||
dev_info(dev, "%s enter\n", __func__);
|
||||
|
||||
enable_sys_clk(hw_dev);
|
||||
rkfec_soft_reset(hw_dev);
|
||||
|
||||
if (dev->power.runtime_status) {
|
||||
//toto
|
||||
} else {
|
||||
//toto
|
||||
}
|
||||
|
||||
hw_dev->is_idle = true;
|
||||
|
||||
if (rkfec_debug >= 4)
|
||||
dev_info(dev, "%s exit\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops rkfec_hw_pm_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(rkfec_hw_runtime_suspend,
|
||||
rkfec_hw_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver rkfec_hw_drv = {
|
||||
.driver = {
|
||||
.name = "rkfec_hw",
|
||||
.of_match_table = of_match_ptr(rkfec_hw_of_match),
|
||||
.pm = &rkfec_hw_pm_ops,
|
||||
},
|
||||
.probe = rkfec_hw_probe,
|
||||
.remove = rkfec_hw_remove,
|
||||
.shutdown = rkfec_hw_shutdown,
|
||||
};
|
||||
|
||||
static int __init rkfec_hw_drv_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&rkfec_hw_drv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit rkfec_hw_drv_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rkfec_hw_drv);
|
||||
}
|
||||
|
||||
module_init(rkfec_hw_drv_init);
|
||||
module_exit(rkfec_hw_drv_exit);
|
||||
79
drivers/media/platform/rockchip/fec/hw.h
Normal file
79
drivers/media/platform/rockchip/fec/hw.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKFEC_HW_H
|
||||
#define _RKFEC_HW_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <media/videobuf2-cma-sg.h>
|
||||
#include <media/videobuf2-dma-sg.h>
|
||||
#include <soc/rockchip/rockchip_iommu.h>
|
||||
|
||||
|
||||
#define FEC_MAX_BUS_CLK 4
|
||||
|
||||
enum rkfec_fec_ver {
|
||||
RKFEC_V10 = 0x00, /* Version 1.0 of the FEC */
|
||||
RKFEC_V20 = 0x20, /* Version 2.0 of the FEC */
|
||||
};
|
||||
|
||||
struct fec_clk_info {
|
||||
u32 clk_rate;
|
||||
u32 refer_data;
|
||||
};
|
||||
|
||||
struct irqs_data {
|
||||
const char *name;
|
||||
irqreturn_t (*irq_hdl)(int irq, void *ctx);
|
||||
};
|
||||
|
||||
struct fec_match_data {
|
||||
enum rkfec_fec_ver fec_ver;
|
||||
int clks_num;
|
||||
const char * const *clks;
|
||||
int clk_rate_tbl_num;
|
||||
const struct fec_clk_info *clk_rate_tbl;
|
||||
struct irqs_data *irqs;
|
||||
int num_irqs;
|
||||
};
|
||||
|
||||
struct rkfec_hw_dev {
|
||||
struct device *dev;
|
||||
void __iomem *base_addr;
|
||||
const struct fec_match_data *match_data;
|
||||
const struct fec_clk_info *clk_rate_tbl;
|
||||
struct reset_control *reset;
|
||||
struct clk *clks[FEC_MAX_BUS_CLK];
|
||||
struct rkfec_offline_dev ofl_dev;
|
||||
int clk_rate_tbl_num;
|
||||
int clks_num;
|
||||
/* lock for hw */
|
||||
struct mutex dev_lock;
|
||||
/* lock for irq */
|
||||
spinlock_t irq_lock;
|
||||
const struct vb2_mem_ops *mem_ops;
|
||||
bool is_mmu;
|
||||
bool is_idle;
|
||||
bool is_dma_config;
|
||||
bool is_dma_sg_ops;
|
||||
bool is_shutdown;
|
||||
bool is_suspend;
|
||||
};
|
||||
|
||||
#ifndef IS_LINUX_VERSION_AT_LEAST_6_1
|
||||
#define IS_LINUX_VERSION_AT_LEAST_6_1 (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
161
drivers/media/platform/rockchip/fec/procfs.c
Normal file
161
drivers/media/platform/rockchip/fec/procfs.c
Normal file
@@ -0,0 +1,161 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "fec_offline.h"
|
||||
#include "hw.h"
|
||||
#include "procfs.h"
|
||||
#include "regs.h"
|
||||
#include "version.h"
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
/************************offline************************/
|
||||
|
||||
static void offline_fec_show_hw(struct seq_file *p, struct rkfec_hw_dev *hw)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
static const char * const wr_mode[] = {
|
||||
"rast",
|
||||
"til4x4",
|
||||
"fbce",
|
||||
"quad"
|
||||
};
|
||||
|
||||
static const char * const bic_mode[] = {
|
||||
"precise",
|
||||
"spline",
|
||||
"catrom",
|
||||
"mitchell"
|
||||
};
|
||||
|
||||
static const char * const lut_density[] = {
|
||||
"16x8",
|
||||
"32x16",
|
||||
"4x4"
|
||||
};
|
||||
|
||||
if (hw->dev->power.usage_count.counter <= 0) {
|
||||
seq_printf(p, "\n%s\n", "HW close");
|
||||
return;
|
||||
}
|
||||
|
||||
val = readl(hw->base_addr + RKFEC_CTRL);
|
||||
seq_printf(p, "%-10s RD_fmt:%s RD_mode:%s WR_fmt:%s WR_mode:%s WR_fbce:%s (0x%x)\n", "CTRL",
|
||||
val & BIT(2) ? "semi" : "interleave",
|
||||
(val >> 4) & 0x3 ? "semi" : "rast",
|
||||
val & BIT(8) ? "semi" : "interleave", wr_mode[val >> 9],
|
||||
val & BIT(13) ? "on" : "off", val);
|
||||
|
||||
val = readl(hw->base_addr + RKFEC_CORE_CTRL);
|
||||
seq_printf(p, "%-10s Bic:%s Lut_density:%s, Border_fill:%s, Cross_fill:%s (0x%x)\n",
|
||||
"CORE_CTRL",
|
||||
bic_mode[(val >> 3) & 0x3], lut_density[(val >> 5) & 0x3],
|
||||
val & BIT(7) ? "nearest" : "bg", val & BIT(10) ? "nearest" : "bg", val);
|
||||
|
||||
val = readl(hw->base_addr + RKFEC_RD_VIR_STRIDE);
|
||||
seq_printf(p, "%-10s Y:%d C:%d\n", "RD_VIR", (val & 0x3FFF) * 4,
|
||||
((val >> 16) & 0x3FFF) * 4);
|
||||
|
||||
val = readl(hw->base_addr + RKFEC_WR_VIR_STRIDE);
|
||||
seq_printf(p, "%-10s Y:%d C:%d\n", "WR_VIR", (val & 0x3FFF) * 4,
|
||||
((val >> 16) & 0x3FFF) * 4);
|
||||
|
||||
val = readl(hw->base_addr + RKFEC_BG_VALUE);
|
||||
seq_printf(p, "%-10s Y:%d U:%d V:%d\n", "BG_VALUE", val & 0xFF,
|
||||
(val >> 10) & 0xFF, (val >> 20) & 0xFF);
|
||||
|
||||
val = readl(hw->base_addr + RKFEC_LUT_SIZE);
|
||||
seq_printf(p, "%-10s Size: %d\n", "LUT", val & 0x3FFFFF);
|
||||
|
||||
val = readl(hw->base_addr + RKFEC_STATUS0);
|
||||
seq_printf(p, "%-10s 0x%x\n", "STATUS0", val & 0x3FFFFF);
|
||||
|
||||
val = readl(hw->base_addr + RKFEC_STATUS1);
|
||||
seq_printf(p, "%-10s 0x%x\n", "STATUS1", val & 0x3FFFFF);
|
||||
}
|
||||
|
||||
static int offline_fec_show(struct seq_file *p, void *v)
|
||||
{
|
||||
struct rkfec_offline_dev *ofl = p->private;
|
||||
struct rkfec_hw_dev *hw = ofl->hw;
|
||||
int i;
|
||||
|
||||
seq_printf(p, "%-10s Version:v%02x.%02x.%02x\n", ofl->v4l2_dev.name,
|
||||
RKFEC_DRIVER_VERSION >> 16,
|
||||
(RKFEC_DRIVER_VERSION & 0xff00) >> 8,
|
||||
RKFEC_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, "%-10s Cnt:%d ErrCnt:%d\n", "Interrupt", ofl->isr_cnt,
|
||||
ofl->err_cnt);
|
||||
|
||||
seq_printf(p, "%-10s Format:%c%c%c%c Size:%dx%d Offset(%d) Sizeimage(%d)\n", "Input",
|
||||
ofl->in_fmt.pixelformat, ofl->in_fmt.pixelformat >> 8,
|
||||
ofl->in_fmt.pixelformat >> 16, ofl->in_fmt.pixelformat >> 24,
|
||||
ofl->in_fmt.width, ofl->in_fmt.height, ofl->in_fmt.offset,
|
||||
ofl->in_fmt.sizeimage);
|
||||
|
||||
seq_printf(p, "%-10s (frame:%d rate:%dms state:%s time:%dms frameloss:%d)\n",
|
||||
"Fec offline",
|
||||
ofl->curr_frame.fs_seq,
|
||||
(u32)(ofl->curr_frame.fe_timestamp - ofl->prev_frame.fe_timestamp) / 1000,
|
||||
(ofl->state & RKFEC_FRAME_END) ? "idle" : "working",
|
||||
ofl->debug.interval / 1000,
|
||||
ofl->debug.frameloss);
|
||||
|
||||
seq_printf(p, "%-10s Format:%c%c%c%c Size:%dx%d Sizeimage(%d) (frame:%d rate:%dms frameloss:%d\n",
|
||||
"Output",
|
||||
ofl->out_fmt.pixelformat,
|
||||
ofl->out_fmt.pixelformat >> 8,
|
||||
ofl->out_fmt.pixelformat >> 16,
|
||||
ofl->out_fmt.pixelformat >> 24,
|
||||
ofl->out_fmt.width,
|
||||
ofl->out_fmt.height,
|
||||
ofl->out_fmt.sizeimage,
|
||||
ofl->curr_frame.fe_seq,
|
||||
ofl->debug.interval / 1000, ofl->debug.frameloss);
|
||||
|
||||
offline_fec_show_hw(p, hw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int offline_fec_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
#if IS_LINUX_VERSION_AT_LEAST_6_1
|
||||
struct rkfec_offline_dev *data = pde_data(inode);
|
||||
#else
|
||||
struct rkfec_offline_dev *data = PDE_DATA(inode);
|
||||
#endif
|
||||
|
||||
return single_open(file, offline_fec_show, data);
|
||||
}
|
||||
|
||||
static const struct proc_ops offline_ops = {
|
||||
.proc_open = offline_fec_open,
|
||||
.proc_read = seq_read,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = single_release,
|
||||
};
|
||||
|
||||
int rkfec_offline_proc_init(struct rkfec_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 rkfec_offline_proc_cleanup(struct rkfec_offline_dev *dev)
|
||||
{
|
||||
if (dev->procfs)
|
||||
remove_proc_entry(dev->v4l2_dev.name, NULL);
|
||||
dev->procfs = NULL;
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
21
drivers/media/platform/rockchip/fec/procfs.h
Normal file
21
drivers/media/platform/rockchip/fec/procfs.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKFEC_PROCFS_H
|
||||
#define _RKFEC_PROCFS_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/sem.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <media/v4l2-common.h>
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
int rkfec_offline_proc_init(struct rkfec_offline_dev *dev);
|
||||
void rkfec_offline_proc_cleanup(struct rkfec_offline_dev *dev);
|
||||
#else
|
||||
static inline int rkfec_offline_proc_init(struct rkfec_offline_dev *dev) { return 0; }
|
||||
static inline void rkfec_offline_proc_cleanup(struct rkfec_offline_dev *dev) {}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
133
drivers/media/platform/rockchip/fec/regs.h
Normal file
133
drivers/media/platform/rockchip/fec/regs.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKFEC_REGS_H
|
||||
#define _RKFEC_REGS_H
|
||||
|
||||
#define RKFEC_BASE 0x0000
|
||||
#define RKFEC_SYS_AR_QOS (RKFEC_BASE + 0x0014)
|
||||
#define RKFEC_STRT (RKFEC_BASE + 0x0200)
|
||||
#define RKFEC_UPD (RKFEC_BASE + 0x0204)
|
||||
#define RKFEC_CLK_DIS (RKFEC_BASE + 0x0208)
|
||||
#define RKFEC_CTRL (RKFEC_BASE + 0x0210)
|
||||
#define RKFEC_RD_VIR_STRIDE (RKFEC_BASE + 0x0214)
|
||||
#define RKFEC_RD_Y_BASE (RKFEC_BASE + 0x0218)
|
||||
#define RKFEC_RD_C_BASE (RKFEC_BASE + 0x021c)
|
||||
#define RKFEC_LUT_BASE (RKFEC_BASE + 0x0220)
|
||||
#define RKFEC_WR_Y_BASE (RKFEC_BASE + 0x0234)
|
||||
#define RKFEC_WR_C_BASE (RKFEC_BASE + 0x0238)
|
||||
#define RKFEC_WR_FBCE_HEAD_OFFSET (RKFEC_BASE + 0x023c)
|
||||
#define RKFEC_RD_Y_BASE_SHD (RKFEC_BASE + 0x0240)
|
||||
#define RKFEC_RD_C_BASE_SHD (RKFEC_BASE + 0x0244)
|
||||
#define RKFEC_LUT_BASE_SHD (RKFEC_BASE + 0x0248)
|
||||
#define RKFEC_WR_FBCE_HEAD_OFFSET_SHD (RKFEC_BASE + 0x024c)
|
||||
#define RKFEC_CORE_CTRL (RKFEC_BASE + 0x0280)
|
||||
#define RKFEC_BG_VALUE (RKFEC_BASE + 0x0284)
|
||||
#define RKFEC_DST_SIZE (RKFEC_BASE + 0x0288)
|
||||
#define RKFEC_LUT_SIZE (RKFEC_BASE + 0x028c)
|
||||
#define RKFEC_STATUS0 (RKFEC_BASE + 0x0290)
|
||||
#define RKFEC_STATUS1 (RKFEC_BASE + 0x0294)
|
||||
#define RKFEC_SRC_SIZE (RKFEC_BASE + 0x0298)
|
||||
#define RKFEC_STOP_REG (RKFEC_BASE + 0x029c)
|
||||
#define RKFEC_WR_VIR_STRIDE (RKFEC_BASE + 0x02a0)
|
||||
#define RKFEC_INT_EN (RKFEC_BASE + 0x02b0)
|
||||
#define RKFEC_INT_RAW (RKFEC_BASE + 0x02b4)
|
||||
#define RKFEC_INT_MSK (RKFEC_BASE + 0x02b8)
|
||||
#define RKFEC_INT_CLR (RKFEC_BASE + 0x02bc)
|
||||
|
||||
#define RKFEC_CACHE_STATUS (RKFEC_BASE + 0x0e08)
|
||||
#define RKFEC_CACHE_COMMAND (RKFEC_BASE + 0x0e10)
|
||||
#define RKFEC_CACHE_CLEAR_PAGE (RKFEC_BASE + 0x0e14)
|
||||
#define RKFEC_CACHE_MAX_READS (RKFEC_BASE + 0x0e18)
|
||||
#define RKFEC_CACHE_CTRL (RKFEC_BASE + 0x0e1c)
|
||||
#define RKFEC_CACHE_PERFCNT_SRC0 (RKFEC_BASE + 0x0e20)
|
||||
#define RKFEC_CACHE_PERFCNT_VAL0 (RKFEC_BASE + 0x0e24)
|
||||
#define RKFEC_CACHE_PERFCNT_SRC1 (RKFEC_BASE + 0x0e28)
|
||||
#define RKFEC_CACHE_RERFCNT_VAL1 (RKFEC_BASE + 0x0e2c)
|
||||
|
||||
#define RKFEC_MMU_DTE_ADDR (RKFEC_BASE + 0x0f00)
|
||||
#define RKFEC_MMU_STATUS (RKFEC_BASE + 0x0f04)
|
||||
#define RKFEC_MMU_COMMAND (RKFEC_BASE + 0x0f08)
|
||||
#define RKFEC_MMU_PAGE_FAULT_ADDR (RKFEC_BASE + 0x0f0c)
|
||||
#define RKFEC_MMU_ZAP_ONE_LINE (RKFEC_BASE + 0x0f10)
|
||||
#define RKFEC_MMU_INT_RAWSTAT (RKFEC_BASE + 0x0f14)
|
||||
#define RKFEC_MMU_INT_CLEAR (RKFEC_BASE + 0x0f18)
|
||||
#define RKFEC_MMU_INT_MASK (RKFEC_BASE + 0x0f1c)
|
||||
#define RKFEC_MMU_INT_STATUS (RKFEC_BASE + 0x0f20)
|
||||
#define RKFEC_MMU_AUTO_GATING (RKFEC_BASE + 0x0f24)
|
||||
#define RKFEC_MMU_REG_LOAD_EN (RKFEC_BASE + 0x0f28)
|
||||
|
||||
/****************** BIT *******************/
|
||||
|
||||
/* FEC_STRT */
|
||||
#define SYS_FEC_ST BIT(0)
|
||||
|
||||
/* FEC_UPD */
|
||||
#define SYS_FEC_FORCE_UPD BIT(0)
|
||||
|
||||
/* FEC_CLK_DIS */
|
||||
#define SYS_FEC_LGC_CKG_DIS BIT(0)
|
||||
#define SYS_FEC_RAM_CKG_DIS BIT(1)
|
||||
#define SYS_RST_PROTECT_DIS BIT(12)
|
||||
#define SYS_SOFT_RST_FBCE BIT(13)
|
||||
#define SYS_SOFT_RST_ACLK BIT(14)
|
||||
#define ARST_SAFETY_FEC_DONE BIT(15)
|
||||
#define SW_FEC_HURRY_EN BIT(16)
|
||||
#define SW_FEC_HURRY_NUM(n) ((n & 0x7fff) << 17)
|
||||
|
||||
/* FEC_CTRL */
|
||||
#define SW_FEC_RD_PIC_FORMAT BIT(1)
|
||||
#define SW_FEC_RD_FORMAT BIT(2)
|
||||
#define SW_FEC_RD_MODE(x) ((x & 0x3) << 4)
|
||||
#define SW_FEC_WR_PIC_FORMAT BIT(7)
|
||||
#define SW_FEC_WR_FORMAT BIT(8)
|
||||
#define SW_FEC_WR_MODE(x) ((x & 0x3) << 9)
|
||||
#define SW_FEC_WR_FBCE_FORCE_UNC_EN BIT(13)
|
||||
|
||||
#define SW_FEC_RD_FMT(x) ((x & 0xf) << 2)
|
||||
#define SW_FEC_WR_FMT(x) ((x & 0xf) << 8)
|
||||
|
||||
/* FEC_CORE_CTRL */
|
||||
#define SW_FEC_EN BIT(0)
|
||||
#define SW_FEC_BIC_MODE(x) ((x & 0x3) << 3)
|
||||
#define SW_LUT_DENSITY(x) ((x & 0x3) << 5)
|
||||
#define SW_FEC_BORDER_MODE(x) ((x & 0x1) << 7)
|
||||
#define SW_FEC_PBUF_CRS_DIS(x) ((x & 0x1) << 8)
|
||||
#define SW_FEC_CRS_BUF_MODE(x) ((x & 0x1) << 10)
|
||||
#define SW_FEC_EN_SHD BIT(31)
|
||||
|
||||
/* FEC_RD_VIR_STRIDE */
|
||||
#define FEC_RD_VIR_STRIDE_Y(x) ((x) & 0x3fff)
|
||||
#define FEC_RD_VIR_STRIDE_C(x) (((x) & 0x3fff) << 16)
|
||||
|
||||
/* FEC_BG_VALUE */
|
||||
#define SW_BG_Y_VALUE(y) ((y & 0xff))
|
||||
#define SW_BG_U_VALUE(u) ((u & 0xff) << 10)
|
||||
#define SW_BG_V_VALUE(v) ((v & 0xff) << 20)
|
||||
|
||||
/* FEC_DST_SIZE */
|
||||
#define SW_FEC_DST_WIDTH(x) ((x) & 0x1fff)
|
||||
#define Sw_FEC_DST_HEIGHT(x) (((x) & 0x1fff) << 16)
|
||||
|
||||
/* FEC_SRC_SIZE */
|
||||
#define SW_FEC_SRC_WIDTH(x) ((x) & 0x1fff)
|
||||
#define Sw_FEC_SRC_HEIGHT(x) (((x) & 0x1fff) << 16)
|
||||
|
||||
/* FEC_WR_VIR_STRIDE */
|
||||
#define FEC_WR_VIR_STRIDE_Y(x) ((x) & 0x3fff)
|
||||
#define FEC_WR_VIR_STRIDE_C(x) (((x) & 0x3fff) << 16)
|
||||
|
||||
/* LUT SIZE */
|
||||
#define SW_LUT_SIZE(x) ((x) & 0x3fffff)
|
||||
|
||||
/* FEC_INT_EN */
|
||||
#define PBUF_BD_CRS_P BIT(0)
|
||||
#define FEC_STOP_IRQ BIT(1)
|
||||
#define MBUF_PP_FAIL BIT(2)
|
||||
#define PBUF_PP_FAIL BIT(3)
|
||||
#define AXI_MS_BRESP_ERR BIT(4)
|
||||
#define AXI_MS_RRESP_ERR BIT(5)
|
||||
#define FRM_END_P_FEC BIT(7)
|
||||
|
||||
#endif /* _RKISPP_REGS_H */
|
||||
|
||||
18
drivers/media/platform/rockchip/fec/version.h
Normal file
18
drivers/media/platform/rockchip/fec/version.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef _RKFEC_VERSION_H
|
||||
#define _RKFEC_VERSION_H
|
||||
#include <linux/version.h>
|
||||
#include <linux/rk-fec-config.h>
|
||||
|
||||
/*
|
||||
* RKFEC DRIVER VERSION NOTE
|
||||
*
|
||||
* v0.1.0:
|
||||
* 1. First version;
|
||||
*/
|
||||
|
||||
#define RKFEC_DRIVER_VERSION RKFEC_API_VERSION
|
||||
|
||||
#endif
|
||||
86
include/uapi/linux/rk-fec-config.h
Normal file
86
include/uapi/linux/rk-fec-config.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0+ WITH Linux-syscall-note) OR MIT
|
||||
*
|
||||
* Rockchip FEC
|
||||
* Copyright (C) 2025 Rockchip Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_RK_FEC_CONFIG_H
|
||||
#define _UAPI_RK_FEC_CONFIG_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/v4l2-controls.h>
|
||||
|
||||
#define RKFEC_API_VERSION KERNEL_VERSION(0, 1, 0)
|
||||
|
||||
#define FEC_BUF_CNT 3
|
||||
|
||||
/*************VIDIOC_PRIVATE*************/
|
||||
#define RKFEC_CMD_IN_OUT \
|
||||
_IOW('V', BASE_VIDIOC_PRIVATE + 10, struct rkfec_in_out)
|
||||
|
||||
#define RKFEC_CMD_BUF_ADD \
|
||||
_IOW('V', BASE_VIDIOC_PRIVATE + 1, int)
|
||||
|
||||
#define RKFEC_CMD_BUF_DEL \
|
||||
_IOW('V', BASE_VIDIOC_PRIVATE + 2, int)
|
||||
|
||||
#define RKFEC_CMD_BUF_ALLOC \
|
||||
_IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct rkfec_buf)
|
||||
|
||||
/* rkfec_buf_info
|
||||
* @in_offs: in_buf c addr offset
|
||||
* @out_offs: out_buf c addr offset
|
||||
*/
|
||||
struct rkfec_buf_cfg {
|
||||
int in_pic_fd;
|
||||
int out_pic_fd;
|
||||
int lut_fd;
|
||||
int in_stride;
|
||||
int out_stride;
|
||||
int in_size;
|
||||
int out_size;
|
||||
int lut_size;
|
||||
int in_offs;
|
||||
int out_offs;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* rkfec_core_ctrl
|
||||
* @ bic_mode: 0:precise 1:spline 2:catrom 3: mitchell
|
||||
* @ border_mode: 0:fill with bg_value 1:copy with the nearest pixel
|
||||
* @ buf_mode: 0:fill with bg_value 1:copy with the nearest pixel
|
||||
* @ pbuf_crs_dis
|
||||
* @ density: 0:16x8; 1:32x16; 2:4x4
|
||||
*/
|
||||
struct rkfec_core_ctrl {
|
||||
int bic_mode;
|
||||
int density;
|
||||
int border_mode;
|
||||
int pbuf_crs_dis;
|
||||
int buf_mode;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rkfec_bg_val {
|
||||
int bg_y;
|
||||
int bg_u;
|
||||
int bg_v;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rkfec_in_out {
|
||||
int in_width;
|
||||
int in_height;
|
||||
int out_width;
|
||||
int out_height;
|
||||
int in_fourcc;
|
||||
int out_fourcc;
|
||||
|
||||
struct rkfec_buf_cfg buf_cfg;
|
||||
struct rkfec_core_ctrl core_ctrl;
|
||||
struct rkfec_bg_val bg_val;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rkfec_buf {
|
||||
int size;
|
||||
int buf_fd;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif
|
||||
@@ -27,6 +27,8 @@
|
||||
#define V4L2_PIX_FMT_TILE420 v4l2_fourcc('T', 'I', 'L', '0')
|
||||
/* yuv422 tile */
|
||||
#define V4L2_PIX_FMT_TILE422 v4l2_fourcc('T', 'I', 'L', '2')
|
||||
/* yuv420 quad */
|
||||
#define V4L2_PIX_FMT_QUAD v4l2_fourcc('Q', 'U', 'A', 'D')
|
||||
|
||||
/* Vendor specific - used for Rockchip ISP1 camera sub-system */
|
||||
#define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 params */
|
||||
|
||||
Reference in New Issue
Block a user