From 3f26cdc2d258b6f9c59187e902026474eda0eb57 Mon Sep 17 00:00:00 2001 From: Mingwei Yan Date: Tue, 25 Feb 2025 15:19:32 +0800 Subject: [PATCH] media: rockchip: vpss: refactor v_1 for rk3576 Signed-off-by: Mingwei Yan Change-Id: Iff2b7f690b9143807cb5135aa518804dbaf29614 --- drivers/media/platform/rockchip/vpss/Kconfig | 8 + drivers/media/platform/rockchip/vpss/Makefile | 4 + drivers/media/platform/rockchip/vpss/common.c | 13 +- drivers/media/platform/rockchip/vpss/common.h | 11 + drivers/media/platform/rockchip/vpss/dev.c | 71 +- drivers/media/platform/rockchip/vpss/dev.h | 23 +- drivers/media/platform/rockchip/vpss/hw.c | 25 +- drivers/media/platform/rockchip/vpss/hw.h | 18 +- drivers/media/platform/rockchip/vpss/procfs.c | 21 +- drivers/media/platform/rockchip/vpss/procfs.h | 6 + .../rockchip/vpss/{regs.h => regs_v1.h} | 0 drivers/media/platform/rockchip/vpss/stream.c | 2751 +--------------- drivers/media/platform/rockchip/vpss/stream.h | 17 +- .../media/platform/rockchip/vpss/stream_v1.c | 2823 +++++++++++++++++ .../media/platform/rockchip/vpss/stream_v1.h | 42 + .../media/platform/rockchip/vpss/version.h | 1 - drivers/media/platform/rockchip/vpss/vpss.c | 77 +- drivers/media/platform/rockchip/vpss/vpss.h | 26 +- .../platform/rockchip/vpss/vpss_offline.c | 2255 +------------ .../platform/rockchip/vpss/vpss_offline.h | 3 +- .../platform/rockchip/vpss/vpss_offline_v1.c | 2271 +++++++++++++ .../platform/rockchip/vpss/vpss_offline_v1.h | 22 + include/uapi/linux/rk-vpss-config.h | 2 + 23 files changed, 5455 insertions(+), 5035 deletions(-) rename drivers/media/platform/rockchip/vpss/{regs.h => regs_v1.h} (100%) create mode 100644 drivers/media/platform/rockchip/vpss/stream_v1.c create mode 100644 drivers/media/platform/rockchip/vpss/stream_v1.h create mode 100644 drivers/media/platform/rockchip/vpss/vpss_offline_v1.c create mode 100644 drivers/media/platform/rockchip/vpss/vpss_offline_v1.h diff --git a/drivers/media/platform/rockchip/vpss/Kconfig b/drivers/media/platform/rockchip/vpss/Kconfig index 282b627eb689..891b5b59fb5e 100644 --- a/drivers/media/platform/rockchip/vpss/Kconfig +++ b/drivers/media/platform/rockchip/vpss/Kconfig @@ -10,3 +10,11 @@ config VIDEO_ROCKCHIP_VPSS default n help Support for VPSS on the rockchip SoC. + +config VIDEO_ROCKCHIP_VPSS_V1 + bool "vpss1 for rk3576" + depends on CPU_RK3576 + depends on VIDEO_ROCKCHIP_VPSS + default y + help + Support for rk3576 diff --git a/drivers/media/platform/rockchip/vpss/Makefile b/drivers/media/platform/rockchip/vpss/Makefile index db2cba72f536..0bf19031f3af 100644 --- a/drivers/media/platform/rockchip/vpss/Makefile +++ b/drivers/media/platform/rockchip/vpss/Makefile @@ -8,3 +8,7 @@ video_rkvpss-objs += hw.o \ stream.o \ procfs.o \ vpss_offline.o + +video_rkvpss-$(CONFIG_VIDEO_ROCKCHIP_VPSS_V1) += \ + stream_v1.o \ + vpss_offline_v1.o diff --git a/drivers/media/platform/rockchip/vpss/common.c b/drivers/media/platform/rockchip/vpss/common.c index 33532b80b1e3..a5c66b8adf2d 100644 --- a/drivers/media/platform/rockchip/vpss/common.c +++ b/drivers/media/platform/rockchip/vpss/common.c @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2023 Rockchip Electronics Co., Ltd. */ -#include -#include -#include - +#include "vpss.h" #include "common.h" +#include "stream.h" #include "dev.h" -#include "regs.h" +#include "vpss_offline.h" +#include "hw.h" +#include "procfs.h" +#include "regs_v1.h" void rkvpss_idx_write(struct rkvpss_device *dev, u32 reg, u32 val, int idx) @@ -105,7 +106,7 @@ void rkvpss_update_regs(struct rkvpss_device *dev, u32 start, u32 end) if (i == RKVPSS_VPSS_ONLINE) { u32 mask = 0; - for (j = 0; j < RKVPSS_OUTPUT_MAX; j++) { + for (j = 0; j < vpss_outchn_max(hw->vpss_ver); j++) { if (!hw->is_ofl_ch[j]) continue; mask |= (RKVPSS_ISP2VPSS_CHN0_SEL(3) << j * 2); diff --git a/drivers/media/platform/rockchip/vpss/common.h b/drivers/media/platform/rockchip/vpss/common.h index 5d64bf0090df..0bad5eb6aa3f 100644 --- a/drivers/media/platform/rockchip/vpss/common.h +++ b/drivers/media/platform/rockchip/vpss/common.h @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include #include "../isp/isp_vpss.h" #include @@ -88,6 +91,14 @@ static inline struct vb2_queue *to_vb2_queue(struct file *file) return &vnode->buf_queue; } +static inline int vpss_outchn_max(int version) +{ + if (version == VPSS_V10) + return 4; + + return 0; +} + extern int rkvpss_debug; extern struct platform_driver rkvpss_plat_drv; extern int rkvpss_cfginfo_num; diff --git a/drivers/media/platform/rockchip/vpss/dev.c b/drivers/media/platform/rockchip/vpss/dev.c index 6a7798d87bc4..b78831c79c3d 100644 --- a/drivers/media/platform/rockchip/vpss/dev.c +++ b/drivers/media/platform/rockchip/vpss/dev.c @@ -1,23 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2023 Rockchip Electronics Co., Ltd. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "vpss.h" +#include "common.h" +#include "stream.h" #include "dev.h" -#include "regs.h" +#include "vpss_offline.h" +#include "hw.h" +#include "procfs.h" +#include "regs_v1.h" + #include "version.h" #define RKVPSS_VERNO_LEN 10 @@ -34,6 +26,14 @@ static char rkvpss_version[RKVPSS_VERNO_LEN]; module_param_string(version, rkvpss_version, RKVPSS_VERNO_LEN, 0444); MODULE_PARM_DESC(version, "version number"); +static unsigned int rkvpss_wrap_line; +module_param_named(wrap_line, rkvpss_wrap_line, uint, 0644); +MODULE_PARM_DESC(wrap_line, "rkvpss wrap line"); + +char rkvpss_regfile[RKVPSS_REGFILE_LEN]; +module_param_string(reg_file, rkvpss_regfile, RKVPSS_REGFILE_LEN, 0644); +MODULE_PARM_DESC(reg_file, "dump reg file"); + int rkvpss_cfginfo_num = 5; static int rkvpss_get_cfginfo_num(const char *val, const struct kernel_param *kp) @@ -72,7 +72,7 @@ void rkvpss_pipeline_default_fmt(struct rkvpss_device *dev) w = dev->vpss_sdev.out_fmt.width; h = dev->vpss_sdev.out_fmt.height; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) + for (i = 0; i < vpss_outchn_max(dev->hw_dev->vpss_ver); i++) rkvpss_stream_default_fmt(dev, i, w, h, V4L2_PIX_FMT_NV12); } @@ -134,7 +134,7 @@ static int rkvpss_create_links(struct rkvpss_device *dev) struct media_entity *source, *sink; struct rkvpss_stream *stream; unsigned int flags = 0; - int ret; + int ret, i; if (!dev->remote_sd) return -EINVAL; @@ -152,33 +152,14 @@ static int rkvpss_create_links(struct rkvpss_device *dev) flags = MEDIA_LNK_FL_ENABLED; source = &dev->vpss_sdev.sd.entity; - stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH0]; - stream->linked = flags; - sink = &stream->vnode.vdev.entity; - ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags); - if (ret < 0) - goto end; - - stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH1]; - stream->linked = flags; - sink = &stream->vnode.vdev.entity; - ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags); - if (ret < 0) - goto end; - - stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH2]; - stream->linked = flags; - sink = &stream->vnode.vdev.entity; - ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags); - if (ret < 0) - goto end; - - stream = &stream_vdev->stream[RKVPSS_OUTPUT_CH3]; - stream->linked = flags; - sink = &stream->vnode.vdev.entity; - ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags); - if (ret < 0) - goto end; + for (i = 0; i < vpss_outchn_max(dev->hw_dev->vpss_ver); i++) { + stream = &stream_vdev->stream[i]; + stream->linked = flags; + sink = &stream->vnode.vdev.entity; + ret = media_create_pad_link(source, RKVPSS_PAD_SOURCE, sink, 0, flags); + if (ret < 0) + goto end; + } end: return ret; diff --git a/drivers/media/platform/rockchip/vpss/dev.h b/drivers/media/platform/rockchip/vpss/dev.h index c75ae36e892f..eaab883d823e 100644 --- a/drivers/media/platform/rockchip/vpss/dev.h +++ b/drivers/media/platform/rockchip/vpss/dev.h @@ -4,12 +4,20 @@ #ifndef _RKVPSS_DEV_H #define _RKVPSS_DEV_H -#include - -#include "hw.h" -#include "procfs.h" -#include "stream.h" -#include "vpss.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #define DRIVER_NAME "rkvpss" #define S0_VDEV_NAME DRIVER_NAME "_scale0" @@ -17,6 +25,8 @@ #define S2_VDEV_NAME DRIVER_NAME "_scale2" #define S3_VDEV_NAME DRIVER_NAME "_scale3" +#define RKVPSS_REGFILE_LEN 50 + enum rkvpss_input { INP_INVAL = 0, INP_ISP, @@ -59,6 +69,7 @@ struct rkvpss_device { atomic_t pipe_stream_cnt; spinlock_t cmsc_lock; + spinlock_t idle_lock; struct rkvpss_cmsc_cfg cmsc_cfg; enum rkvpss_ver vpss_ver; diff --git a/drivers/media/platform/rockchip/vpss/hw.c b/drivers/media/platform/rockchip/vpss/hw.c index 9431bf238fb9..5cb2aff63525 100644 --- a/drivers/media/platform/rockchip/vpss/hw.c +++ b/drivers/media/platform/rockchip/vpss/hw.c @@ -1,27 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2023 Rockchip Electronics Co., Ltd. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "vpss.h" #include "common.h" +#include "stream.h" #include "dev.h" +#include "vpss_offline.h" #include "hw.h" -#include "regs.h" +#include "procfs.h" +#include "regs_v1.h" struct irqs_data { const char *name; @@ -895,7 +882,7 @@ static int rkvpss_hw_probe(struct platform_device *pdev) spin_lock_init(&hw_dev->reg_lock); atomic_set(&hw_dev->refcnt, 0); INIT_LIST_HEAD(&hw_dev->list); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) + for (i = 0; i < vpss_outchn_max(hw_dev->vpss_ver); i++) hw_dev->is_ofl_ch[i] = false; hw_dev->is_ofl_cmsc = false; hw_dev->is_single = true; diff --git a/drivers/media/platform/rockchip/vpss/hw.h b/drivers/media/platform/rockchip/vpss/hw.h index 8fb8bff15ad8..100778ab3dc6 100644 --- a/drivers/media/platform/rockchip/vpss/hw.h +++ b/drivers/media/platform/rockchip/vpss/hw.h @@ -4,8 +4,22 @@ #ifndef _RKVPSS_HW_H #define _RKVPSS_HW_H -#include "common.h" -#include "vpss_offline.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #define VPSS_MAX_BUS_CLK 4 #define VPSS_MAX_DEV 8 diff --git a/drivers/media/platform/rockchip/vpss/procfs.c b/drivers/media/platform/rockchip/vpss/procfs.c index b4fe978847ab..c82e1515a4ad 100644 --- a/drivers/media/platform/rockchip/vpss/procfs.c +++ b/drivers/media/platform/rockchip/vpss/procfs.c @@ -1,14 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) Rockchip Electronics Co., Ltd. */ -#include -#include -#include -#include -#include +#include "vpss.h" +#include "common.h" +#include "stream.h" #include "dev.h" +#include "vpss_offline.h" +#include "hw.h" #include "procfs.h" -#include "regs.h" +#include "regs_v1.h" + #include "version.h" #ifdef CONFIG_PROC_FS @@ -27,7 +28,7 @@ static void show_hw(struct seq_file *p, struct rkvpss_hw_dev *hw) 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++) { + for (i = 0; i < vpss_outchn_max(hw->vpss_ver); i++) { seq_printf(p, "\toutput[%d]", i); val = rkvpss_hw_read(hw, RKVPSS_CMSC_CTRL); mask = RKVPSS_CMSC_CHN_EN(i); @@ -90,7 +91,7 @@ static int vpss_show(struct seq_file *p, void *v) 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++) { + for (i = 0; i < vpss_outchn_max(hw->vpss_ver); 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]); @@ -109,7 +110,7 @@ static int vpss_show(struct seq_file *p, void *v) 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", + seq_printf(p, "\tsequence:%d rate:%dms delay:%dms frameloss:%d buf_cnt:%d\n", stream->dbg.id, stream->dbg.interval / 1000 / 1000, stream->dbg.delay / 1000 / 1000, @@ -194,7 +195,7 @@ static int offline_vpss_show(struct seq_file *p, void *v) cfginfo->input.height); seq_printf(p, "%-10s\n", "Output"); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + for (i = 0; i < vpss_outchn_max(hw->vpss_ver); 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, diff --git a/drivers/media/platform/rockchip/vpss/procfs.h b/drivers/media/platform/rockchip/vpss/procfs.h index 5383c101e4ed..584fa715fe66 100644 --- a/drivers/media/platform/rockchip/vpss/procfs.h +++ b/drivers/media/platform/rockchip/vpss/procfs.h @@ -4,6 +4,12 @@ #ifndef _RKVPSS_PROCFS_H #define _RKVPSS_PROCFS_H +#include +#include +#include +#include +#include + #ifdef CONFIG_PROC_FS int rkvpss_proc_init(struct rkvpss_device *dev); void rkvpss_proc_cleanup(struct rkvpss_device *dev); diff --git a/drivers/media/platform/rockchip/vpss/regs.h b/drivers/media/platform/rockchip/vpss/regs_v1.h similarity index 100% rename from drivers/media/platform/rockchip/vpss/regs.h rename to drivers/media/platform/rockchip/vpss/regs_v1.h diff --git a/drivers/media/platform/rockchip/vpss/stream.c b/drivers/media/platform/rockchip/vpss/stream.c index 98cf598f41dd..611b1018a317 100644 --- a/drivers/media/platform/rockchip/vpss/stream.c +++ b/drivers/media/platform/rockchip/vpss/stream.c @@ -1,2758 +1,65 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "vpss.h" +#include "common.h" +#include "stream.h" #include "dev.h" -#include "regs.h" +#include "vpss_offline.h" +#include "hw.h" +#include "procfs.h" +#include "regs_v1.h" -#define STREAM_OUT_REQ_BUFS_MIN 0 +#include "stream_v1.h" -static void rkvpss_frame_end(struct rkvpss_stream *stream); -static int rkvpss_stream_crop(struct rkvpss_stream *stream, bool on, bool sync); -static int rkvpss_stream_scale(struct rkvpss_stream *stream, bool on, bool sync); -static void rkvpss_stream_mf(struct rkvpss_stream *stream); - -static const struct capture_fmt scl0_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV16, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV12, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - .fmt_type = FMT_YUV, - .bpp = { 8 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .fmt_type = FMT_YUV, - .bpp = { 16 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_422P, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV61, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV21, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .fmt_type = FMT_YUV, - .bpp = { 16 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_422P, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_TILE420, - .fmt_type = FMT_YUV, - .bpp = { 24 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_TILE422, - .fmt_type = FMT_YUV, - .bpp = { 32 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - } -}; - -static const struct capture_fmt scl1_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV16, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV12, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - .fmt_type = FMT_YUV, - .bpp = { 8 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .fmt_type = FMT_YUV, - .bpp = { 16 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_422P, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, - .fmt_type = FMT_RGB, - .bpp = { 16 }, - .mplanes = 1, - .cplanes = 1, - .wr_fmt = 0, - .swap = RKVPSS_MI_CHN_WR_RB_SWAP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_RGB24, - .fmt_type = FMT_RGB, - .bpp = { 24 }, - .mplanes = 1, - .cplanes = 1, - .wr_fmt = 0, - .swap = RKVPSS_MI_CHN_WR_RB_SWAP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB888, - }, { - .fourcc = V4L2_PIX_FMT_XBGR32, - .fmt_type = FMT_RGB, - .bpp = { 32 }, - .mplanes = 1, - .cplanes = 1, - .wr_fmt = 0, - .swap = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_ARGB888, - }, { - .fourcc = V4L2_PIX_FMT_RGB565X, - .fmt_type = FMT_RGB, - .bpp = { 16 }, - .mplanes = 1, - .cplanes = 1, - .wr_fmt = 0, - .swap = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB565, - }, { - .fourcc = V4L2_PIX_FMT_BGR24, - .fmt_type = FMT_RGB, - .bpp = { 24 }, - .mplanes = 1, - .cplanes = 1, - .wr_fmt = 0, - .swap = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB888, - }, { - .fourcc = V4L2_PIX_FMT_XRGB32, - .fmt_type = FMT_RGB, - .bpp = { 32 }, - .mplanes = 1, - .cplanes = 1, - .wr_fmt = 0, - .swap = RKVPSS_MI_CHN_WR_RB_SWAP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_ARGB888, - }, { - .fourcc = V4L2_PIX_FMT_NV61, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV21, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .fmt_type = FMT_YUV, - .bpp = { 16 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_422P, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_TILE420, - .fmt_type = FMT_YUV, - .bpp = { 24 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_TILE422, - .fmt_type = FMT_YUV, - .bpp = { 32 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = 0, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - } -}; - -static const struct capture_fmt scl2_3_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV16, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV12, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_GREY, - .fmt_type = FMT_YUV, - .bpp = { 8 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .fmt_type = FMT_YUV, - .bpp = { 16 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_422P, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV61, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - }, { - .fourcc = V4L2_PIX_FMT_NV21, - .fmt_type = FMT_YUV, - .bpp = { 8, 16 }, - .cplanes = 2, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .fmt_type = FMT_YUV, - .bpp = { 16 }, - .cplanes = 1, - .mplanes = 1, - .swap = 0, - .wr_fmt = RKVPSS_MI_CHN_WR_422P, - .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, - } -}; - - -static struct stream_config scl0_config = { - .fmts = scl0_fmts, - .fmt_size = ARRAY_SIZE(scl0_fmts), - .frame_end_id = RKVPSS_MI_CHN0_FRM_END, - .crop = { - .ctrl = RKVPSS_CROP1_CTRL, - .update = RKVPSS_CROP1_UPDATE, - .h_offs = RKVPSS_CROP1_0_H_OFFS, - .v_offs = RKVPSS_CROP1_0_V_OFFS, - .h_size = RKVPSS_CROP1_0_H_SIZE, - .v_size = RKVPSS_CROP1_0_V_SIZE, - .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, - .h_offs_shd = RKVPSS_CROP1_0_H_OFFS_SHD, - .v_offs_shd = RKVPSS_CROP1_0_V_OFFS_SHD, - .h_size_shd = RKVPSS_CROP1_0_H_SIZE_SHD, - .v_size_shd = RKVPSS_CROP1_0_V_SIZE_SHD, - }, - .mi = { - .ctrl = RKVPSS_MI_CHN0_WR_CTRL, - .stride = RKVPSS_MI_CHN0_WR_Y_STRIDE, - .y_base = RKVPSS_MI_CHN0_WR_Y_BASE, - .uv_base = RKVPSS_MI_CHN0_WR_CB_BASE, - .y_size = RKVPSS_MI_CHN0_WR_Y_SIZE, - .uv_size = RKVPSS_MI_CHN0_WR_CB_SIZE, - .y_offs_cnt = RKVPSS_MI_CHN0_WR_Y_OFFS_CNT, - .uv_offs_cnt = RKVPSS_MI_CHN0_WR_CB_OFFS_CNT, - .y_pic_width = RKVPSS_MI_CHN0_WR_Y_PIC_WIDTH, - .y_pic_size = RKVPSS_MI_CHN0_WR_Y_PIC_SIZE, - - .ctrl_shd = RKVPSS_MI_CHN0_WR_CTRL_SHD, - .y_shd = RKVPSS_MI_CHN0_WR_Y_BASE_SHD, - .uv_shd = RKVPSS_MI_CHN0_WR_CB_BASE_SHD, - }, -}; - -static struct stream_config scl1_config = { - .fmts = scl1_fmts, - .fmt_size = ARRAY_SIZE(scl1_fmts), - .frame_end_id = RKVPSS_MI_CHN1_FRM_END, - .crop = { - .ctrl = RKVPSS_CROP1_CTRL, - .update = RKVPSS_CROP1_UPDATE, - .h_offs = RKVPSS_CROP1_1_H_OFFS, - .v_offs = RKVPSS_CROP1_1_V_OFFS, - .h_size = RKVPSS_CROP1_1_H_SIZE, - .v_size = RKVPSS_CROP1_1_V_SIZE, - .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, - .h_offs_shd = RKVPSS_CROP1_1_H_OFFS_SHD, - .v_offs_shd = RKVPSS_CROP1_1_V_OFFS_SHD, - .h_size_shd = RKVPSS_CROP1_1_H_SIZE_SHD, - .v_size_shd = RKVPSS_CROP1_1_V_SIZE_SHD, - }, - .scale = { - .ctrl = RKVPSS_SCALE1_CTRL, - .update = RKVPSS_SCALE1_UPDATE, - .src_size = RKVPSS_SCALE1_SRC_SIZE, - .dst_size = RKVPSS_SCALE1_DST_SIZE, - .hy_fac = RKVPSS_SCALE1_HY_FAC, - .hc_fac = RKVPSS_SCALE1_HC_FAC, - .vy_fac = RKVPSS_SCALE1_VY_FAC, - .vc_fac = RKVPSS_SCALE1_VC_FAC, - .hy_offs = RKVPSS_SCALE1_HY_OFFS, - .hc_offs = RKVPSS_SCALE1_HC_OFFS, - .vy_offs = RKVPSS_SCALE1_VY_OFFS, - .vc_offs = RKVPSS_SCALE1_VC_OFFS, - .hy_size = RKVPSS_SCALE1_HY_SIZE, - .hc_size = RKVPSS_SCALE1_HC_SIZE, - .hy_offs_mi = RKVPSS_SCALE1_HY_OFFS_MI, - .hc_offs_mi = RKVPSS_SCALE1_HC_OFFS_MI, - .in_crop_offs = RKVPSS_SCALE1_IN_CROP_OFFSET, - .ctrl_shd = RKVPSS_SCALE1_CTRL_SHD, - .src_size_shd = RKVPSS_SCALE1_SRC_SIZE_SHD, - .dst_size_shd = RKVPSS_SCALE1_DST_SIZE_SHD, - .hy_fac_shd = RKVPSS_SCALE1_HY_FAC_SHD, - .hc_fac_shd = RKVPSS_SCALE1_HC_FAC_SHD, - .vy_fac_shd = RKVPSS_SCALE1_VY_FAC_SHD, - .vc_fac_shd = RKVPSS_SCALE1_VC_FAC_SHD, - .hy_offs_shd = RKVPSS_SCALE1_HY_OFFS_SHD, - .hc_offs_shd = RKVPSS_SCALE1_HC_OFFS_SHD, - .vy_offs_shd = RKVPSS_SCALE1_VY_OFFS_SHD, - .vc_offs_shd = RKVPSS_SCALE1_VC_OFFS_SHD, - .hy_size_shd = RKVPSS_SCALE1_HY_SIZE_SHD, - .hc_size_shd = RKVPSS_SCALE1_HC_SIZE_SHD, - .hy_offs_mi_shd = RKVPSS_SCALE1_HY_OFFS_MI_SHD, - .hc_offs_mi_shd = RKVPSS_SCALE1_HC_OFFS_MI_SHD, - .in_crop_offs_shd = RKVPSS_SCALE1_IN_CROP_OFFSET_SHD, - }, - .mi = { - .ctrl = RKVPSS_MI_CHN1_WR_CTRL, - .stride = RKVPSS_MI_CHN1_WR_Y_STRIDE, - .y_base = RKVPSS_MI_CHN1_WR_Y_BASE, - .uv_base = RKVPSS_MI_CHN1_WR_CB_BASE, - .y_size = RKVPSS_MI_CHN1_WR_Y_SIZE, - .uv_size = RKVPSS_MI_CHN1_WR_CB_SIZE, - .y_offs_cnt = RKVPSS_MI_CHN1_WR_Y_OFFS_CNT, - .uv_offs_cnt = RKVPSS_MI_CHN1_WR_CB_OFFS_CNT, - .y_pic_width = RKVPSS_MI_CHN1_WR_Y_PIC_WIDTH, - .y_pic_size = RKVPSS_MI_CHN1_WR_Y_PIC_SIZE, - - .ctrl_shd = RKVPSS_MI_CHN1_WR_CTRL_SHD, - .y_shd = RKVPSS_MI_CHN1_WR_Y_BASE_SHD, - .uv_shd = RKVPSS_MI_CHN1_WR_CB_BASE_SHD, - }, -}; - -static struct stream_config scl2_config = { - .fmts = scl2_3_fmts, - .fmt_size = ARRAY_SIZE(scl2_3_fmts), - .frame_end_id = RKVPSS_MI_CHN2_FRM_END, - .crop = { - .ctrl = RKVPSS_CROP1_CTRL, - .update = RKVPSS_CROP1_UPDATE, - .h_offs = RKVPSS_CROP1_2_H_OFFS, - .v_offs = RKVPSS_CROP1_2_V_OFFS, - .h_size = RKVPSS_CROP1_2_H_SIZE, - .v_size = RKVPSS_CROP1_2_V_SIZE, - .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, - .h_offs_shd = RKVPSS_CROP1_2_H_OFFS_SHD, - .v_offs_shd = RKVPSS_CROP1_2_V_OFFS_SHD, - .h_size_shd = RKVPSS_CROP1_2_H_SIZE_SHD, - .v_size_shd = RKVPSS_CROP1_2_V_SIZE_SHD, - }, - .scale = { - .ctrl = RKVPSS_SCALE2_CTRL, - .update = RKVPSS_SCALE2_UPDATE, - .src_size = RKVPSS_SCALE2_SRC_SIZE, - .dst_size = RKVPSS_SCALE2_DST_SIZE, - .hy_fac = RKVPSS_SCALE2_HY_FAC, - .hc_fac = RKVPSS_SCALE2_HC_FAC, - .vy_fac = RKVPSS_SCALE2_VY_FAC, - .vc_fac = RKVPSS_SCALE2_VC_FAC, - .hy_offs = RKVPSS_SCALE2_HY_OFFS, - .hc_offs = RKVPSS_SCALE2_HC_OFFS, - .vy_offs = RKVPSS_SCALE2_VY_OFFS, - .vc_offs = RKVPSS_SCALE2_VC_OFFS, - .hy_size = RKVPSS_SCALE2_HY_SIZE, - .hc_size = RKVPSS_SCALE2_HC_SIZE, - .hy_offs_mi = RKVPSS_SCALE2_HY_OFFS_MI, - .hc_offs_mi = RKVPSS_SCALE2_HC_OFFS_MI, - .in_crop_offs = RKVPSS_SCALE2_IN_CROP_OFFSET, - .ctrl_shd = RKVPSS_SCALE2_CTRL_SHD, - .src_size_shd = RKVPSS_SCALE2_SRC_SIZE_SHD, - .dst_size_shd = RKVPSS_SCALE2_DST_SIZE_SHD, - .hy_fac_shd = RKVPSS_SCALE2_HY_FAC_SHD, - .hc_fac_shd = RKVPSS_SCALE2_HC_FAC_SHD, - .vy_fac_shd = RKVPSS_SCALE2_VY_FAC_SHD, - .vc_fac_shd = RKVPSS_SCALE2_VC_FAC_SHD, - .hy_offs_shd = RKVPSS_SCALE2_HY_OFFS_SHD, - .hc_offs_shd = RKVPSS_SCALE2_HC_OFFS_SHD, - .vy_offs_shd = RKVPSS_SCALE2_VY_OFFS_SHD, - .vc_offs_shd = RKVPSS_SCALE2_VC_OFFS_SHD, - .hy_size_shd = RKVPSS_SCALE2_HY_SIZE_SHD, - .hc_size_shd = RKVPSS_SCALE2_HC_SIZE_SHD, - .hy_offs_mi_shd = RKVPSS_SCALE2_HY_OFFS_MI_SHD, - .hc_offs_mi_shd = RKVPSS_SCALE2_HC_OFFS_MI_SHD, - .in_crop_offs_shd = RKVPSS_SCALE2_IN_CROP_OFFSET_SHD, - }, - .mi = { - .ctrl = RKVPSS_MI_CHN2_WR_CTRL, - .stride = RKVPSS_MI_CHN2_WR_Y_STRIDE, - .y_base = RKVPSS_MI_CHN2_WR_Y_BASE, - .uv_base = RKVPSS_MI_CHN2_WR_CB_BASE, - .y_size = RKVPSS_MI_CHN2_WR_Y_SIZE, - .uv_size = RKVPSS_MI_CHN2_WR_CB_SIZE, - .y_offs_cnt = RKVPSS_MI_CHN2_WR_Y_OFFS_CNT, - .uv_offs_cnt = RKVPSS_MI_CHN2_WR_CB_OFFS_CNT, - .y_pic_width = RKVPSS_MI_CHN2_WR_Y_PIC_WIDTH, - .y_pic_size = RKVPSS_MI_CHN2_WR_Y_PIC_SIZE, - - .ctrl_shd = RKVPSS_MI_CHN2_WR_CTRL_SHD, - .y_shd = RKVPSS_MI_CHN2_WR_Y_BASE_SHD, - .uv_shd = RKVPSS_MI_CHN2_WR_CB_BASE_SHD, - }, -}; - -static struct stream_config scl3_config = { - .fmts = scl2_3_fmts, - .fmt_size = ARRAY_SIZE(scl2_3_fmts), - .frame_end_id = RKVPSS_MI_CHN3_FRM_END, - .crop = { - .ctrl = RKVPSS_CROP1_CTRL, - .update = RKVPSS_CROP1_UPDATE, - .h_offs = RKVPSS_CROP1_3_H_OFFS, - .v_offs = RKVPSS_CROP1_3_V_OFFS, - .h_size = RKVPSS_CROP1_3_H_SIZE, - .v_size = RKVPSS_CROP1_3_V_SIZE, - .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, - .h_offs_shd = RKVPSS_CROP1_3_H_OFFS_SHD, - .v_offs_shd = RKVPSS_CROP1_3_V_OFFS_SHD, - .h_size_shd = RKVPSS_CROP1_3_H_SIZE_SHD, - .v_size_shd = RKVPSS_CROP1_3_V_SIZE_SHD, - }, - .scale = { - .ctrl = RKVPSS_SCALE3_CTRL, - .update = RKVPSS_SCALE3_UPDATE, - .src_size = RKVPSS_SCALE3_SRC_SIZE, - .dst_size = RKVPSS_SCALE3_DST_SIZE, - .hy_fac = RKVPSS_SCALE3_HY_FAC, - .hc_fac = RKVPSS_SCALE3_HC_FAC, - .vy_fac = RKVPSS_SCALE3_VY_FAC, - .vc_fac = RKVPSS_SCALE3_VC_FAC, - .hy_offs = RKVPSS_SCALE3_HY_OFFS, - .hc_offs = RKVPSS_SCALE3_HC_OFFS, - .vy_offs = RKVPSS_SCALE3_VY_OFFS, - .vc_offs = RKVPSS_SCALE3_VC_OFFS, - .hy_size = RKVPSS_SCALE3_HY_SIZE, - .hc_size = RKVPSS_SCALE3_HC_SIZE, - .hy_offs_mi = RKVPSS_SCALE3_HY_OFFS_MI, - .hc_offs_mi = RKVPSS_SCALE3_HC_OFFS_MI, - .in_crop_offs = RKVPSS_SCALE3_IN_CROP_OFFSET, - .ctrl_shd = RKVPSS_SCALE3_CTRL_SHD, - .src_size_shd = RKVPSS_SCALE3_SRC_SIZE_SHD, - .dst_size_shd = RKVPSS_SCALE3_DST_SIZE_SHD, - .hy_fac_shd = RKVPSS_SCALE3_HY_FAC_SHD, - .hc_fac_shd = RKVPSS_SCALE3_HC_FAC_SHD, - .vy_fac_shd = RKVPSS_SCALE3_VY_FAC_SHD, - .vc_fac_shd = RKVPSS_SCALE3_VC_FAC_SHD, - .hy_offs_shd = RKVPSS_SCALE3_HY_OFFS_SHD, - .hc_offs_shd = RKVPSS_SCALE3_HC_OFFS_SHD, - .vy_offs_shd = RKVPSS_SCALE3_VY_OFFS_SHD, - .vc_offs_shd = RKVPSS_SCALE3_VC_OFFS_SHD, - .hy_size_shd = RKVPSS_SCALE3_HY_SIZE_SHD, - .hc_size_shd = RKVPSS_SCALE3_HC_SIZE_SHD, - .hy_offs_mi_shd = RKVPSS_SCALE3_HY_OFFS_MI_SHD, - .hc_offs_mi_shd = RKVPSS_SCALE3_HC_OFFS_MI_SHD, - .in_crop_offs_shd = RKVPSS_SCALE3_IN_CROP_OFFSET_SHD, - }, - .mi = { - .ctrl = RKVPSS_MI_CHN3_WR_CTRL, - .stride = RKVPSS_MI_CHN3_WR_Y_STRIDE, - .y_base = RKVPSS_MI_CHN3_WR_Y_BASE, - .uv_base = RKVPSS_MI_CHN3_WR_CB_BASE, - .y_size = RKVPSS_MI_CHN3_WR_Y_SIZE, - .uv_size = RKVPSS_MI_CHN3_WR_CB_SIZE, - .y_offs_cnt = RKVPSS_MI_CHN3_WR_Y_OFFS_CNT, - .uv_offs_cnt = RKVPSS_MI_CHN3_WR_CB_OFFS_CNT, - .y_pic_width = RKVPSS_MI_CHN3_WR_Y_PIC_WIDTH, - .y_pic_size = RKVPSS_MI_CHN3_WR_Y_PIC_SIZE, - - .ctrl_shd = RKVPSS_MI_CHN3_WR_CTRL_SHD, - .y_shd = RKVPSS_MI_CHN3_WR_Y_BASE_SHD, - .uv_shd = RKVPSS_MI_CHN3_WR_CB_BASE_SHD, - }, -}; - -static void calc_unite_scl_params(struct rkvpss_stream *stream) +void rkvpss_cmsc_config(struct rkvpss_device *dev, bool sync) { - struct rkvpss_online_unite_params *params = &stream->unite_params; - u32 right_scl_need_size_y, right_scl_need_size_c; - u32 left_in_used_size_y, left_in_used_size_c; - u32 right_fst_position_y, right_fst_position_c; - u32 right_y_crop_total; - u32 right_c_crop_total; - - params->y_w_fac = (stream->crop.width - 1) * 4096 / - (stream->out_fmt.width - 1); - params->c_w_fac = (stream->crop.width / 2 - 1) * 4096 / - (stream->out_fmt.width / 2 - 1); - params->y_h_fac = (stream->crop.height - 1) * 4096 / - (stream->out_fmt.height - 1); - params->c_h_fac = (stream->crop.height - 1) * 4096 / - (stream->out_fmt.height - 1); - - right_fst_position_y = stream->out_fmt.width / 2 * - params->y_w_fac; - right_fst_position_c = stream->out_fmt.width / 2 / 2 * - params->c_w_fac; - - left_in_used_size_y = right_fst_position_y >> 12; - left_in_used_size_c = (right_fst_position_c >> 12) * 2; - - params->y_w_phase = right_fst_position_y & 0xfff; - params->c_w_phase = right_fst_position_c & 0xfff; - - right_scl_need_size_y = stream->crop.width - - left_in_used_size_y; - params->right_scl_need_size_y = right_scl_need_size_y; - right_scl_need_size_c = stream->crop.width - - left_in_used_size_c; - params->right_scl_need_size_c = right_scl_need_size_c; - - if (stream->id == 0 && stream->crop.width != stream->out_fmt.width) { - right_y_crop_total = stream->crop.width / 2 + - RKMOUDLE_UNITE_EXTEND_PIXEL - - right_scl_need_size_y - 3; - right_c_crop_total = stream->crop.width / 2 + - RKMOUDLE_UNITE_EXTEND_PIXEL - - right_scl_need_size_c - 6; - } else { - right_y_crop_total = stream->crop.width / 2 + - RKMOUDLE_UNITE_EXTEND_PIXEL - - right_scl_need_size_y; - right_c_crop_total = stream->crop.width / 2 + - RKMOUDLE_UNITE_EXTEND_PIXEL - - right_scl_need_size_c; - } - - params->quad_crop_w = ALIGN_DOWN(min(right_y_crop_total, right_c_crop_total), 2); - - params->scl_in_crop_w_y = right_y_crop_total - params->quad_crop_w; - params->scl_in_crop_w_c = right_c_crop_total - params->quad_crop_w; - - if (rkvpss_debug >= 2) { - v4l2_info(&stream->dev->v4l2_dev, - "%s ch:%d y_w_fac:%u c_w_fac:%u y_h_fac:%u c_h_fac:%u\n", - __func__, stream->id, params->y_w_fac, - params->c_w_fac, params->y_h_fac, params->c_h_fac); - v4l2_info(&stream->dev->v4l2_dev, - "\t%s y_w_phase:%u c_w_phase:%u quad_crop_w:%u scl_in_crop_w_y:%u scl_in_crop_w_c:%u\n", - __func__, params->y_w_phase, params->c_w_phase, - params->quad_crop_w, - params->scl_in_crop_w_y, params->scl_in_crop_w_c); - v4l2_info(&stream->dev->v4l2_dev, - "\t%s right_scl_need_size_y:%u right_scl_need_size_c:%u\n", - __func__, params->right_scl_need_size_y, - params->right_scl_need_size_c); - } + if (dev->vpss_ver == VPSS_V10) + rkvpss_cmsc_config_v1(dev, sync); } 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) -{ - struct rkvpss_device *dev = stream->dev; - - if (stream->is_crop_upd) { - if (dev->unite_mode) - calc_unite_scl_params(stream); - rkvpss_stream_scale(stream, true, !irq); - rkvpss_stream_crop(stream, true, !irq); - } - if (!irq && !stream->curr_buf && - !stream->dev->hw_dev->is_single) - stream->ops->update_mi(stream); -} - -static void scl_force_update(struct rkvpss_stream *stream) -{ - u32 val; - - switch (stream->id) { - case RKVPSS_OUTPUT_CH0: - val = RKVPSS_MI_CHN0_FORCE_UPD; - break; - case RKVPSS_OUTPUT_CH1: - val = RKVPSS_MI_CHN1_FORCE_UPD; - break; - case RKVPSS_OUTPUT_CH2: - val = RKVPSS_MI_CHN2_FORCE_UPD; - break; - case RKVPSS_OUTPUT_CH3: - val = RKVPSS_MI_CHN3_FORCE_UPD; - break; - default: - return; - } - /* need update two for online2 mode */ - rkvpss_unite_write(stream->dev, RKVPSS_MI_WR_INIT, val); - rkvpss_unite_write(stream->dev, RKVPSS_MI_WR_INIT, val); -} - -static void scl_update_mi(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - struct rkvpss_buffer *buf = NULL; - unsigned long lock_flags = 0; - u32 y_base, uv_base; - - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - if (!list_empty(&stream->buf_queue) && !stream->curr_buf) { - buf = list_first_entry(&stream->buf_queue, struct rkvpss_buffer, queue); - list_del(&buf->queue); - stream->curr_buf = buf; - } - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - - if (buf) { - y_base = buf->dma[0]; - uv_base = buf->dma[1]; - rkvpss_idx_write(dev, stream->config->mi.y_base, y_base, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, stream->config->mi.uv_base, uv_base, VPSS_UNITE_LEFT); - if (dev->unite_mode) { - y_base = buf->dma[0] + stream->out_fmt.width / 2; - uv_base = buf->dma[1] + stream->out_fmt.width / 2; - rkvpss_idx_write(dev, stream->config->mi.y_base, y_base, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, stream->config->mi.uv_base, uv_base, VPSS_UNITE_RIGHT); - } - - scl_force_update(stream); - if (stream->is_pause) { - stream->is_pause = false; - stream->ops->enable_mi(stream); - } - } else if (!stream->is_pause) { - stream->is_pause = true; - stream->ops->disable_mi(stream); - } - - v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, - "%s id:%d unite_index:%d Y:0x%x UV:0x%x | Y_SHD:0x%x\n", - __func__, stream->id, dev->unite_index, - rkvpss_idx_read(dev, stream->config->mi.y_base, dev->unite_index), - rkvpss_idx_read(dev, stream->config->mi.uv_base, dev->unite_index), - rkvpss_hw_read(dev->hw_dev, stream->config->mi.y_shd)); -} - -static void scl_config_mi(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - struct capture_fmt *fmt = &stream->out_cap_fmt; - struct v4l2_pix_format_mplane *out_fmt = &stream->out_fmt; - u32 reg, val, mask; - - val = out_fmt->plane_fmt[0].bytesperline; - reg = stream->config->mi.stride; - rkvpss_unite_write(dev, reg, val); - rkvpss_unite_write(dev, reg, val); - - switch (fmt->fourcc) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - val = out_fmt->plane_fmt[0].bytesperline / 2; - break; - case V4L2_PIX_FMT_XBGR32: - case V4L2_PIX_FMT_XRGB32: - val = out_fmt->plane_fmt[0].bytesperline / 4; - break; - default: - val = out_fmt->plane_fmt[0].bytesperline; - break; - } - - val = val * out_fmt->height; - reg = stream->config->mi.y_pic_size; - rkvpss_unite_write(dev, reg, val); - - val = out_fmt->plane_fmt[0].bytesperline * out_fmt->height; - reg = stream->config->mi.y_size; - rkvpss_unite_write(dev, reg, val); - - val = out_fmt->plane_fmt[1].sizeimage; - reg = stream->config->mi.uv_size; - rkvpss_unite_write(dev, reg, val); - - reg = stream->config->mi.y_offs_cnt; - rkvpss_unite_write(dev, reg, 0); - reg = stream->config->mi.uv_offs_cnt; - rkvpss_unite_write(dev, reg, 0); - - val = fmt->wr_fmt | fmt->output_fmt | fmt->swap | - RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; - reg = stream->config->mi.ctrl; - rkvpss_unite_write(dev, reg, val); - - switch (fmt->fourcc) { - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_VYUY: - mask = RKVPSS_MI_WR_UV_SWAP; - val = RKVPSS_MI_WR_UV_SWAP; - rkvpss_hw_set_bits(dev->hw_dev, RKVPSS_MI_WR_CTRL, mask, val); - break; - case V4L2_PIX_FMT_TILE420: - case V4L2_PIX_FMT_TILE422: - mask = RKVPSS_MI_WR_TILE_SEL(3); - val = RKVPSS_MI_WR_TILE_SEL(stream->id + 1); - rkvpss_hw_set_bits(dev->hw_dev, RKVPSS_MI_WR_CTRL, mask, val); - break; - default: - break; - } - - stream->is_mf_upd = true; - rkvpss_frame_end(stream); -} - -static void scl_enable_mi(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - u32 val, mask; - - switch (stream->id) { - case RKVPSS_OUTPUT_CH0: - val = RKVPSS_ISP2VPSS_CHN0_SEL(2); - mask = RKVPSS_ISP2VPSS_CHN0_SEL(3); - break; - case RKVPSS_OUTPUT_CH1: - val = RKVPSS_ISP2VPSS_CHN1_SEL(2); - mask = RKVPSS_ISP2VPSS_CHN1_SEL(3); - break; - case RKVPSS_OUTPUT_CH2: - val = RKVPSS_ISP2VPSS_CHN2_SEL(2); - mask = RKVPSS_ISP2VPSS_CHN2_SEL(3); - break; - case RKVPSS_OUTPUT_CH3: - val = RKVPSS_ISP2VPSS_CHN3_SEL(2); - mask = RKVPSS_ISP2VPSS_CHN3_SEL(3); - break; - default: - return; - } - val |= RKVPSS_ISP2VPSS_ONLINE2; - if (!dev->hw_dev->is_ofl_cmsc) - val |= RKVPSS_ISP2VPSS_ONLINE2_CMSC_EN; - rkvpss_unite_set_bits(dev, RKVPSS_VPSS_ONLINE, mask, val); - val = RKVPSS_ONLINE2_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD; - rkvpss_unite_write(dev, RKVPSS_VPSS_UPDATE, val); -} - -static void scl_disable_mi(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - u32 val; - - switch (stream->id) { - case RKVPSS_OUTPUT_CH0: - val = RKVPSS_ISP2VPSS_CHN0_SEL(3); - break; - case RKVPSS_OUTPUT_CH1: - val = RKVPSS_ISP2VPSS_CHN1_SEL(3); - break; - case RKVPSS_OUTPUT_CH2: - val = RKVPSS_ISP2VPSS_CHN2_SEL(3); - break; - case RKVPSS_OUTPUT_CH3: - val = RKVPSS_ISP2VPSS_CHN3_SEL(3); - break; - default: - return; - } - - rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_ONLINE, val); - val = RKVPSS_ONLINE2_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD; - rkvpss_unite_write(dev, RKVPSS_VPSS_UPDATE, val); -} - -static struct streams_ops scl_stream_ops = { - .config_mi = scl_config_mi, - .enable_mi = scl_enable_mi, - .disable_mi = scl_disable_mi, - .update_mi = scl_update_mi, - .frame_start = stream_frame_start, -}; - -static void rkvpss_buf_done_task(unsigned long arg) -{ - struct rkvpss_stream *stream = (struct rkvpss_stream *)arg; - struct rkvpss_vdev_node *node = &stream->vnode; - struct rkvpss_buffer *buf = NULL; - unsigned long lock_flags = 0; - LIST_HEAD(local_list); - - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - list_replace_init(&stream->buf_done_list, &local_list); - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - - while (!list_empty(&local_list)) { - buf = list_first_entry(&local_list, struct rkvpss_buffer, queue); - list_del(&buf->queue); - v4l2_dbg(2, rkvpss_debug, &stream->dev->v4l2_dev, - "%s stream:%d index:%d seq:%d buf:0x%x done\n", - node->vdev.name, stream->id, buf->vb.vb2_buf.index, - buf->vb.sequence, buf->dma[0]); - vb2_buffer_done(&buf->vb.vb2_buf, - stream->streaming ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR); - } -} - -static void rkvpss_stream_buf_done(struct rkvpss_stream *stream, - struct rkvpss_buffer *buf) -{ - unsigned long lock_flags = 0; - - if (!stream || !buf) - return; - - v4l2_dbg(3, rkvpss_debug, &stream->dev->v4l2_dev, - "stream:%d\n", stream->id); - - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - list_add_tail(&buf->queue, &stream->buf_done_list); - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - tasklet_schedule(&stream->buf_done_tasklet); -} - -static void rkvpss_fill_frame_info(struct rkvpss_frame_info *dst_info, - struct rkisp_vpss_frame_info *src_info) -{ - dst_info->timestamp = src_info->timestamp; - - dst_info->seq = src_info->seq; - dst_info->hdr = src_info->hdr; - dst_info->rolling_shutter_skew = src_info->rolling_shutter_skew; - - dst_info->sensor_exposure_time = src_info->sensor_exposure_time; - dst_info->sensor_analog_gain = src_info->sensor_analog_gain; - dst_info->sensor_digital_gain = src_info->sensor_digital_gain; - dst_info->isp_digital_gain = src_info->isp_digital_gain; - - dst_info->sensor_exposure_time_m = src_info->sensor_exposure_time_m; - dst_info->sensor_analog_gain_m = src_info->sensor_analog_gain_m; - dst_info->sensor_digital_gain_m = src_info->sensor_digital_gain_m; - dst_info->isp_digital_gain_m = src_info->isp_digital_gain_m; - - dst_info->sensor_exposure_time_l = src_info->sensor_exposure_time_l; - dst_info->sensor_analog_gain_l = src_info->sensor_analog_gain_l; - dst_info->sensor_digital_gain_l = src_info->sensor_digital_gain_l; - dst_info->isp_digital_gain_l = src_info->isp_digital_gain_l; -} - -static void rkvpss_frame_end(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - struct rkvpss_subdev *sdev = &dev->vpss_sdev; - struct rkvpss_buffer *buf = NULL; - unsigned long lock_flags = 0; - - v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, - "stream:%d\n", stream->id); - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - if (stream->curr_buf) { - buf = stream->curr_buf; - stream->curr_buf = NULL; - } - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - - if (buf) { - struct vb2_buffer *vb2_buf = &buf->vb.vb2_buf; - struct capture_fmt *fmt = &stream->out_cap_fmt; - u64 ns = sdev->frame_timestamp; - int i; - - for (i = 0; i < fmt->mplanes; i++) { - u32 payload_size = stream->out_fmt.plane_fmt[i].sizeimage; - - vb2_set_plane_payload(vb2_buf, i, payload_size); - - if (stream->is_attach_info && i == fmt->mplanes - 1) { - struct rkvpss_frame_info *dst_info = buf->vaddr[i] + payload_size; - struct rkisp_vpss_frame_info *src_info = &dev->frame_info; - - rkvpss_fill_frame_info(dst_info, src_info); - } - } - if (!ns) - 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); - } - - rkvpss_stream_mf(stream); - stream->ops->update_mi(stream); -} - -static int rkvpss_queue_setup(struct vb2_queue *queue, - unsigned int *num_buffers, - unsigned int *num_planes, - unsigned int sizes[], - struct device *alloc_ctxs[]) -{ - struct rkvpss_stream *stream = queue->drv_priv; - struct rkvpss_device *dev = stream->dev; - const struct v4l2_pix_format_mplane *pixm = NULL; - const struct capture_fmt *cap_fmt = NULL; - u32 i; - - pixm = &stream->out_fmt; - if (!pixm->width || !pixm->height) - return -EINVAL; - cap_fmt = &stream->out_cap_fmt; - *num_planes = cap_fmt->mplanes; - - for (i = 0; i < cap_fmt->mplanes; i++) { - const struct v4l2_plane_pix_format *plane_fmt; - - plane_fmt = &pixm->plane_fmt[i]; - sizes[i] = plane_fmt->sizeimage / pixm->height * ALIGN(pixm->height, 16); - - if (stream->is_attach_info && i == cap_fmt->mplanes - 1) - sizes[i] += sizeof(struct rkvpss_frame_info); - } - - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "%s stream:%d count %d, size %d\n", - v4l2_type_names[queue->type], - stream->id, *num_buffers, sizes[0]); - - return 0; -} - -static void rkvpss_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct rkvpss_buffer *vpssbuf = to_rkvpss_buffer(vbuf); - struct vb2_queue *queue = vb->vb2_queue; - struct rkvpss_stream *stream = queue->drv_priv; - struct rkvpss_device *dev = stream->dev; - struct v4l2_pix_format_mplane *pixm = &stream->out_fmt; - struct capture_fmt *cap_fmt = &stream->out_cap_fmt; - unsigned long lock_flags = 0; - struct sg_table *sgt; - u32 height, size; - int i; - - for (i = 0; i < cap_fmt->mplanes; i++) { - sgt = vb2_dma_sg_plane_desc(vb, i); - vpssbuf->dma[i] = sg_dma_address(sgt->sgl); - - vpssbuf->vaddr[i] = vb2_plane_vaddr(vb, i); - } - /* - * NOTE: plane_fmt[0].sizeimage is total size of all planes for single - * memory plane formats, so calculate the size explicitly. - */ - if (cap_fmt->mplanes == 1) { - for (i = 0; i < cap_fmt->cplanes - 1; i++) { - height = pixm->height; - size = (i == 0) ? - pixm->plane_fmt[i].bytesperline * height : - pixm->plane_fmt[i].sizeimage; - vpssbuf->dma[i + 1] = vpssbuf->dma[i] + size; - } - } - - v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, - "%s stream:%d index:%d buf:0x%x\n", __func__, - stream->id, vb->index, vpssbuf->dma[0]); - - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - list_add_tail(&vpssbuf->queue, &stream->buf_queue); - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - if (dev->hw_dev->is_single && - stream->streaming && !stream->curr_buf) - stream->ops->update_mi(stream); -} - -static void destroy_buf_queue(struct rkvpss_stream *stream, - enum vb2_buffer_state state) -{ - unsigned long lock_flags = 0; - struct rkvpss_buffer *buf; - LIST_HEAD(queue_local_list); - LIST_HEAD(done_local_list); - - spin_lock_irqsave(&stream->vbq_lock, lock_flags); - if (stream->curr_buf) { - list_add_tail(&stream->curr_buf->queue, &stream->buf_queue); - stream->curr_buf = NULL; - } - list_replace_init(&stream->buf_queue, &queue_local_list); - list_replace_init(&stream->buf_done_list, &done_local_list); - spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); - - while (!list_empty(&queue_local_list)) { - buf = list_first_entry(&queue_local_list, struct rkvpss_buffer, queue); - list_del(&buf->queue); - buf->vb.vb2_buf.synced = false; - vb2_buffer_done(&buf->vb.vb2_buf, state); - } - - while (!list_empty(&done_local_list)) { - buf = list_first_entry(&done_local_list, struct rkvpss_buffer, queue); - list_del(&buf->queue); - buf->vb.vb2_buf.synced = false; - vb2_buffer_done(&buf->vb.vb2_buf, state); - } -} - -static int rkvpss_stream_crop(struct rkvpss_stream *stream, bool on, bool sync) -{ - struct rkvpss_device *dev = stream->dev; - struct v4l2_rect *crop = &stream->crop; - u32 reg_ctrl = stream->config->crop.ctrl; - u32 reg_upd = stream->config->crop.update; - u32 reg_h_offs = stream->config->crop.h_offs; - u32 reg_v_offs = stream->config->crop.v_offs; - u32 reg_h_size = stream->config->crop.h_size; - u32 reg_v_size = stream->config->crop.v_size; - u32 val; - - val = RKVPSS_CROP_CHN_EN(stream->id); - if (!dev->unite_mode) { - if (on) { - rkvpss_unite_set_bits(dev, reg_ctrl, 0, val); - rkvpss_unite_write(dev, reg_h_offs, crop->left); - rkvpss_unite_write(dev, reg_v_offs, crop->top); - rkvpss_unite_write(dev, reg_h_size, crop->width); - rkvpss_unite_write(dev, reg_v_size, crop->height); - - v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, - "crop left:%d top:%d w:%d h:%d\n", - crop->left, crop->top, crop->width, crop->height); - } else { - rkvpss_unite_clear_bits(dev, reg_ctrl, val); - } - if (sync) { - val = RKVPSS_CROP_CHN_FORCE_UPD(stream->id); - val |= RKVPSS_CROP_GEN_UPD; - rkvpss_unite_write(dev, reg_upd, val); - rkvpss_unite_write(dev, reg_upd, val); - } - } else { - if (on) { - if (crop->left + crop->width / 2 != dev->vpss_sdev.in_fmt.width / 2) { - v4l2_err(&dev->v4l2_dev, - "unite need centered crop left:%d top:%d w:%d h:%d\n", - crop->left, crop->top, crop->width, crop->height); - return -EINVAL; - } - /* unite left */ - rkvpss_idx_set_bits(dev, reg_ctrl, 0, val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, reg_h_offs, crop->left, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, reg_v_offs, crop->top, VPSS_UNITE_LEFT); - /* if no scale left don't enlarge */ - if (crop->width == stream->out_fmt.width) - rkvpss_idx_write(dev, reg_h_size, crop->width / 2, - VPSS_UNITE_LEFT); - else - rkvpss_idx_write(dev, reg_h_size, crop->width / 2 + - RKMOUDLE_UNITE_EXTEND_PIXEL, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, reg_v_size, crop->height, VPSS_UNITE_LEFT); - v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, - "right crop left:%d top:%d w:%d h:%d\n", - rkvpss_idx_read(dev, reg_h_offs, VPSS_UNITE_LEFT), - rkvpss_idx_read(dev, reg_v_offs, VPSS_UNITE_LEFT), - rkvpss_idx_read(dev, reg_h_size, VPSS_UNITE_LEFT), - rkvpss_idx_read(dev, reg_v_size, VPSS_UNITE_LEFT)); - - /* unite right */ - rkvpss_idx_set_bits(dev, reg_ctrl, 0, val, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, reg_h_offs, stream->unite_params.quad_crop_w, - VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, reg_v_offs, crop->top, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, reg_h_size, crop->width / 2 + - RKMOUDLE_UNITE_EXTEND_PIXEL - - stream->unite_params.quad_crop_w, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, reg_v_size, crop->height, VPSS_UNITE_RIGHT); - v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, - "right crop left:%d top:%d w:%d h:%d\n", - rkvpss_idx_read(dev, reg_h_offs, VPSS_UNITE_RIGHT), - rkvpss_idx_read(dev, reg_v_offs, VPSS_UNITE_RIGHT), - rkvpss_idx_read(dev, reg_h_size, VPSS_UNITE_RIGHT), - rkvpss_idx_read(dev, reg_v_size, VPSS_UNITE_RIGHT)); - } else { - rkvpss_idx_clear_bits(dev, reg_ctrl, val, VPSS_UNITE_LEFT); - rkvpss_idx_clear_bits(dev, reg_ctrl, val, VPSS_UNITE_RIGHT); - } - if (sync) { - val = RKVPSS_CROP_CHN_FORCE_UPD(stream->id); - val |= RKVPSS_CROP_GEN_UPD; - rkvpss_idx_write(dev, reg_upd, val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, reg_upd, val, VPSS_UNITE_RIGHT); - } - } - stream->is_crop_upd = false; - return 0; -} - -static void poly_phase_scale(struct rkvpss_stream *stream, bool on, bool sync) -{ - struct rkvpss_device *dev = stream->dev; - u32 out_w = stream->out_fmt.width; - u32 out_h = stream->out_fmt.height; - u32 in_w = stream->crop.width; - u32 in_h = stream->crop.height; - u32 ctrl, y_xscl_fac, y_yscl_fac, uv_xscl_fac, uv_yscl_fac; - u32 i, j, idx, ratio, val, in_div, out_div, factor; - bool dering_en = false, yuv420_in = false, yuv422_to_420 = false; - - if (!on) { - rkvpss_unite_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0); - if (dev->unite_mode) { - rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0, VPSS_UNITE_RIGHT); - } - return; - } - - /*config scl clk gate*/ - if (in_w == out_w && in_h == out_h) - rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS); - else - rkvpss_unite_set_bits(dev, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS, - RKVPSS_SCL0_CKG_DIS); - - /* TODO diff for input and output format */ - if (yuv420_in) { - in_div = 2; - out_div = 2; - } else if (yuv422_to_420) { - in_div = 1; - out_div = 2; - } else { - in_div = 1; - out_div = 1; - } - - if (dev->unite_mode) { - /* unite left */ - if (in_w == out_w) - val = (in_w / 2 - 1) | ((in_h - 1) << 16); - else - val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL - 1) | - ((in_h - 1) << 16); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val, VPSS_UNITE_LEFT); - - /* unite right */ - val = (ALIGN(stream->unite_params.right_scl_need_size_y + 3, 2) - 1) | - ((in_h - 1) << 16); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val, VPSS_UNITE_RIGHT); - val = (ALIGN(stream->unite_params.right_scl_need_size_c + 6, 2) - 1) | - ((in_h - 1) << 16); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val, VPSS_UNITE_RIGHT); - - val = (out_w / 2 - 1) | ((out_h - 1) << 16); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_DST_SIZE, val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_DST_SIZE, val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_DST_SIZE, val, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_DST_SIZE, val, VPSS_UNITE_RIGHT); - } else { - val = (in_w - 1) | ((in_h - 1) << 16); - rkvpss_unite_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val); - val = (out_w - 1) | ((out_h - 1) << 16); - rkvpss_unite_write(dev, RKVPSS_ZME_Y_DST_SIZE, val); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_DST_SIZE, val); - } - - ctrl = RKVPSS_ZME_XSCL_MODE | RKVPSS_ZME_YSCL_MODE; - if (dering_en) { - ctrl |= RKVPSS_ZME_DERING_EN; - rkvpss_unite_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410); - - if (dev->unite_mode) { - rkvpss_idx_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410, VPSS_UNITE_RIGHT); - } - } - - if (in_w != out_w) { - if (in_w > out_w) { - factor = 4096; - ctrl |= RKVPSS_ZME_XSD_EN; - } else { - factor = 65536; - ctrl |= RKVPSS_ZME_XSU_EN; - } - y_xscl_fac = (in_w - 1) * factor / (out_w - 1); - uv_xscl_fac = (in_w / 2 - 1) * factor / (out_w / 2 - 1); - - ratio = y_xscl_fac * 10000 / factor; - idx = rkvpss_get_zme_tap_coe_index(ratio); - for (i = 0; i < 17; i++) { - for (j = 0; j < 8; j += 2) { - val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap8_coe[idx][i][j], - rkvpss_zme_tap8_coe[idx][i][j + 1]); - rkvpss_unite_write(dev, RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, val); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, val); - - if (dev->unite_mode) { - rkvpss_idx_write(dev, - RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, - RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, - RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, - RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_RIGHT); - } - } - } - } else { - y_xscl_fac = 0; - uv_xscl_fac = 0; - } - - if (dev->unite_mode) { - /* unite left */ - rkvpss_idx_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac, VPSS_UNITE_LEFT); - - /* unite right */ - val = y_xscl_fac | (stream->unite_params.y_w_phase << 16); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, val, VPSS_UNITE_RIGHT); - val = uv_xscl_fac | (stream->unite_params.c_w_phase << 16); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, val, VPSS_UNITE_RIGHT); - } else { - rkvpss_unite_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac); - } - - if (in_h != out_h) { - if (in_h > out_h) { - factor = 4096; - ctrl |= RKVPSS_ZME_YSD_EN; - } else { - factor = 65536; - ctrl |= RKVPSS_ZME_YSU_EN; - } - y_yscl_fac = (in_h - 1) * factor / (out_h - 1); - uv_yscl_fac = (in_h / in_div - 1) * factor / (out_h / out_div - 1); - - ratio = y_yscl_fac * 10000 / factor; - idx = rkvpss_get_zme_tap_coe_index(ratio); - for (i = 0; i < 17; i++) { - for (j = 0; j < 8; j += 2) { - val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap6_coe[idx][i][j], - rkvpss_zme_tap6_coe[idx][i][j + 1]); - rkvpss_unite_write(dev, RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, val); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, val); - - if (dev->unite_mode) { - rkvpss_idx_write(dev, - RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, - RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, - RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, - RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, - val, VPSS_UNITE_RIGHT); - } - } - } - } else { - y_yscl_fac = 0; - uv_yscl_fac = 0; - } - - if (dev->unite_mode) { - /* unite left */ - rkvpss_idx_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl, VPSS_UNITE_LEFT); - - /* unite right */ - rkvpss_idx_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl, VPSS_UNITE_RIGHT); - } else { - rkvpss_unite_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac); - rkvpss_unite_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl); - rkvpss_unite_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl); - } - - if (dev->unite_mode) { - /* unite left */ - val = out_w / 2; - rkvpss_idx_write(dev, RKVPSS_ZME_H_SIZE, (val << 16) | val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, 0, VPSS_UNITE_LEFT); - - /* unite right */ - rkvpss_idx_write(dev, RKVPSS_ZME_H_SIZE, (val << 16) | val, VPSS_UNITE_RIGHT); - rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, 0, VPSS_UNITE_RIGHT); - val = out_w / 2 - ALIGN_DOWN(out_w / 2, 16); - rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, (3 << 20) | (3 << 16) | - (stream->unite_params.scl_in_crop_w_y << 8) | - (stream->unite_params.scl_in_crop_w_c << 12) | - (val << 4) | val, VPSS_UNITE_RIGHT); - } - - ctrl = RKVPSS_ZME_GATING_EN; - if (yuv420_in) - ctrl |= RKVPSS_ZME_SCL_YUV420_REAL_EN; - if (yuv422_to_420) - ctrl |= RKVPSS_ZME_422TO420_EN; - if (dev->unite_mode) { - /* unite left */ - ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_8K_EN; - rkvpss_idx_write(dev, RKVPSS_ZME_CTRL, ctrl, VPSS_UNITE_LEFT); - - /* unite left */ - ctrl |= RKVPSS_ZME_IN_CLIP_EN; - rkvpss_idx_write(dev, RKVPSS_ZME_CTRL, ctrl, VPSS_UNITE_RIGHT); - } else { - rkvpss_unite_write(dev, RKVPSS_ZME_CTRL, ctrl); - } - - val = RKVPSS_ZME_GEN_UPD; - if (sync) - val |= RKVPSS_ZME_FORCE_UPD; - rkvpss_unite_write(dev, RKVPSS_ZME_UPDATE, val); - if (dev->unite_mode) { - rkvpss_idx_write(dev, RKVPSS_ZME_UPDATE, val, VPSS_UNITE_LEFT); - rkvpss_idx_write(dev, RKVPSS_ZME_UPDATE, val, VPSS_UNITE_RIGHT); - } -} - -static void bilinear_scale(struct rkvpss_stream *stream, bool on, bool sync) -{ - struct rkvpss_device *dev = stream->dev; - u32 out_w = stream->out_fmt.width; - u32 out_h = stream->out_fmt.height; - u32 in_w = stream->crop.width; - u32 in_h = stream->crop.height; - u32 in_div, out_div; - u32 reg, val, ctrl = 0, clk_mask = 0; - bool yuv420_in = false, yuv422_to_420 = false; - - if (!on) { - reg = stream->config->scale.ctrl; - rkvpss_unite_write(dev, reg, 0); - return; - } - - /*config scl clk gate*/ - switch (stream->id) { - case RKVPSS_OUTPUT_CH1: - clk_mask = RKVPSS_SCL1_CKG_DIS; - break; - case RKVPSS_OUTPUT_CH2: - clk_mask = RKVPSS_SCL2_CKG_DIS; - break; - case RKVPSS_OUTPUT_CH3: - clk_mask = RKVPSS_SCL3_CKG_DIS; - break; - default: - return; - } - if (in_w == out_w && in_h == out_h) - rkvpss_unite_clear_bits(dev, RKVPSS_SCL0_CKG_DIS, clk_mask); - else - rkvpss_unite_set_bits(dev, RKVPSS_SCL0_CKG_DIS, clk_mask, clk_mask); - - if (!dev->unite_mode) { - /* TODO diff for input and output format */ - if (yuv420_in) { - in_div = 2; - out_div = 2; - } else if (yuv422_to_420) { - in_div = 1; - out_div = 2; - } else { - in_div = 1; - out_div = 1; - } - - reg = stream->config->scale.hy_offs; - rkvpss_unite_write(dev, reg, 0); - reg = stream->config->scale.hc_offs; - rkvpss_unite_write(dev, reg, 0); - reg = stream->config->scale.vy_offs; - rkvpss_unite_write(dev, reg, 0); - reg = stream->config->scale.vc_offs; - rkvpss_unite_write(dev, reg, 0); - - val = in_w | (in_h << 16); - reg = stream->config->scale.src_size; - rkvpss_unite_write(dev, reg, val); - val = out_w | (out_h << 16); - reg = stream->config->scale.dst_size; - rkvpss_unite_write(dev, reg, val); - - if (in_w != out_w) { - val = (in_w - 1) * 4096 / (out_w - 1); - reg = stream->config->scale.hy_fac; - rkvpss_unite_write(dev, reg, val); - - val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1); - reg = stream->config->scale.hc_fac; - rkvpss_unite_write(dev, reg, val); - - ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; - } - if (in_h != out_h) { - val = (in_h - 1) * 4096 / (out_h - 1); - reg = stream->config->scale.vy_fac; - rkvpss_unite_write(dev, reg, val); - - val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1); - reg = stream->config->scale.vc_fac; - rkvpss_unite_write(dev, reg, val); - - ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; - } - - reg = stream->config->scale.ctrl; - rkvpss_unite_write(dev, reg, ctrl); - - val = RKVPSS_SCL_GEN_UPD; - if (sync) - val |= RKVPSS_SCL_FORCE_UPD; - reg = stream->config->scale.update; - rkvpss_unite_write(dev, reg, val); - } else { - /* unite left */ - reg = stream->config->scale.in_crop_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - - reg = stream->config->scale.hy_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - reg = stream->config->scale.hc_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - reg = stream->config->scale.vy_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - reg = stream->config->scale.vc_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - - reg = stream->config->scale.hy_offs_mi; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - reg = stream->config->scale.hc_offs_mi; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); - - if (in_w == out_w) - val = (in_w / 2) | (in_h << 16); - else - val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL) | (in_h << 16); - reg = stream->config->scale.src_size; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT); - val = (out_w / 2) | (out_h << 16); - reg = stream->config->scale.dst_size; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT); - - ctrl |= RKVPSS_SCL_CLIP_EN; - - if (in_w != out_w) { - reg = stream->config->scale.hy_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.y_w_fac, VPSS_UNITE_LEFT); - reg = stream->config->scale.hc_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.c_w_fac, VPSS_UNITE_LEFT); - ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; - } - - if (in_h != out_h) { - reg = stream->config->scale.vy_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.y_h_fac, VPSS_UNITE_LEFT); - reg = stream->config->scale.vc_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.c_h_fac, VPSS_UNITE_LEFT); - ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; - } - - reg = stream->config->scale.ctrl; - rkvpss_idx_write(dev, reg, ctrl, VPSS_UNITE_LEFT); - - val = RKVPSS_SCL_GEN_UPD; - if (sync) - val |= RKVPSS_SCL_FORCE_UPD; - reg = stream->config->scale.update; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT); - - /* unite right */ - ctrl = 0; - val = stream->unite_params.scl_in_crop_w_y | - (stream->unite_params.scl_in_crop_w_c << 4); - reg = stream->config->scale.in_crop_offs; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - - val = stream->unite_params.y_w_phase; - reg = stream->config->scale.hy_offs; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - val = stream->unite_params.c_w_phase; - reg = stream->config->scale.hc_offs; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - reg = stream->config->scale.vy_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_RIGHT); - reg = stream->config->scale.vc_offs; - rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_RIGHT); - - val = out_w / 2 - ALIGN_DOWN(out_w / 2, 16); - reg = stream->config->scale.hy_offs_mi; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - reg = stream->config->scale.hc_offs_mi; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - - val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL) | (in_h << 16); - reg = stream->config->scale.src_size; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - val = (out_w / 2) | (out_h << 16); - reg = stream->config->scale.dst_size; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - - ctrl |= RKVPSS_SCL_CLIP_EN | RKVPSS_SCL_IN_CLIP_EN; - - if (in_w != out_w) { - reg = stream->config->scale.hy_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.y_w_fac, VPSS_UNITE_RIGHT); - reg = stream->config->scale.hc_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.c_w_fac, VPSS_UNITE_RIGHT); - ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; - } - - if (in_h != out_h) { - reg = stream->config->scale.vy_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.y_h_fac, VPSS_UNITE_RIGHT); - reg = stream->config->scale.vc_fac; - rkvpss_idx_write(dev, reg, stream->unite_params.c_h_fac, VPSS_UNITE_RIGHT); - ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; - } - - reg = stream->config->scale.ctrl; - rkvpss_idx_write(dev, reg, ctrl, VPSS_UNITE_RIGHT); - - val = RKVPSS_SCL_GEN_UPD; - if (sync) - val |= RKVPSS_SCL_FORCE_UPD; - reg = stream->config->scale.update; - rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); - } -} - -static int rkvpss_stream_scale(struct rkvpss_stream *stream, bool on, bool sync) -{ - if (stream->id == RKVPSS_OUTPUT_CH0) - poly_phase_scale(stream, on, sync); - else - bilinear_scale(stream, on, sync); - return 0; -} - -static void rkvpss_stream_stop(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - int ret; - - stream->stopping = true; - if (atomic_read(&dev->pipe_stream_cnt) > 0) { - ret = wait_event_timeout(stream->done, !stream->streaming, - msecs_to_jiffies(300)); - if (!ret) - v4l2_warn(&dev->v4l2_dev, "%s id:%d timeout\n", __func__, stream->id); - } - stream->stopping = false; - stream->streaming = false; - if (stream->ops->disable_mi) - stream->ops->disable_mi(stream); - rkvpss_stream_crop(stream, false, true); - rkvpss_stream_scale(stream, false, true); -} - -static void rkvpss_stop_streaming(struct vb2_queue *queue) -{ - struct rkvpss_stream *stream = queue->drv_priv; - struct rkvpss_vdev_node *node = &stream->vnode; - struct rkvpss_device *dev = stream->dev; - struct rkvpss_hw_dev *hw = dev->hw_dev; - - if (!stream->streaming) - return; - - mutex_lock(&hw->dev_lock); - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "%s %s id:%d enter\n", __func__, - node->vdev.name, stream->id); - - if (atomic_read(&dev->pipe_stream_cnt) == 1) { - rkvpss_pipeline_stream(dev, false); - rkvpss_stream_stop(stream); - } else { - rkvpss_stream_stop(stream); - rkvpss_pipeline_stream(dev, false); - } - destroy_buf_queue(stream, VB2_BUF_STATE_ERROR); - rkvpss_pipeline_close(dev); - tasklet_disable(&stream->buf_done_tasklet); - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "%s %s id:%d exit\n", __func__, - node->vdev.name, stream->id); - mutex_unlock(&hw->dev_lock); -} - -static int check_wr_uvswap(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - struct rkvpss_stream *check_stream; - struct capture_fmt *fmt; - bool wr_uv_swap = false; - int i, ret = 0; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - check_stream = &dev->stream_vdev.stream[i]; - if (check_stream->streaming) { - fmt = &check_stream->out_cap_fmt; - switch (fmt->fourcc) { - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_VYUY: - wr_uv_swap = true; - break; - default: - break; - } - } - } - if (wr_uv_swap) { - switch (stream->out_cap_fmt.fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_UYVY: - v4l2_err(&dev->v4l2_dev, "wr_uv_swap need to be consistent\n"); - ret = -EINVAL; - break; - default: - break; - } - } - - return ret; -} - -static int rkvpss_stream_start(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; + struct rkvpss_device *vpss = stream->dev; int ret = 0; - if (dev->unite_mode) - calc_unite_scl_params(stream); - - stream->is_crop_upd = true; - ret = rkvpss_stream_scale(stream, true, true); - if (ret < 0) - return ret; - ret = rkvpss_stream_crop(stream, true, true); - if (ret < 0) - return ret; - ret = check_wr_uvswap(stream); - if (ret < 0) - return ret; - - if (stream->ops->config_mi) - stream->ops->config_mi(stream); - - stream->streaming = true; - stream->dbg.id = -1; - return ret; -} - -static int rkvpss_start_streaming(struct vb2_queue *queue, unsigned int count) -{ - struct rkvpss_stream *stream = queue->drv_priv; - struct rkvpss_vdev_node *node = &stream->vnode; - struct rkvpss_device *dev = stream->dev; - struct rkvpss_hw_dev *hw = dev->hw_dev; - int ret = -EINVAL; - - if (stream->streaming) - return -EBUSY; - - mutex_lock(&hw->dev_lock); - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "%s %s id:%d enter\n", __func__, - node->vdev.name, stream->id); - - stream->is_pause = true; - if (!dev->inp || !stream->linked) { - v4l2_err(&dev->v4l2_dev, "no link or invalid input source\n"); - goto free_buf_queue; - } - - if (hw->is_ofl_ch[stream->id]) { - v4l2_err(&dev->v4l2_dev, "channel[%d] already assigned to offline", stream->id); - goto free_buf_queue; - } - - rkvpss_pipeline_open(dev); - - ret = rkvpss_stream_start(stream); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "start %s failed\n", node->vdev.name); - goto pipe_close; - } - - ret = rkvpss_pipeline_stream(dev, true); - if (ret < 0) - goto stop_stream; - - tasklet_enable(&stream->buf_done_tasklet); - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "%s %s id:%d exit\n", __func__, - node->vdev.name, stream->id); - mutex_unlock(&hw->dev_lock); - return 0; -stop_stream: - stream->streaming = false; - rkvpss_stream_stop(stream); -pipe_close: - rkvpss_pipeline_close(dev); -free_buf_queue: - destroy_buf_queue(stream, VB2_BUF_STATE_QUEUED); - mutex_unlock(&hw->dev_lock); - return ret; -} - -static const struct vb2_ops stream_vb2_ops = { - .queue_setup = rkvpss_queue_setup, - .buf_queue = rkvpss_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .stop_streaming = rkvpss_stop_streaming, - .start_streaming = rkvpss_start_streaming, -}; - -static int rkvpss_init_vb2_queue(struct vb2_queue *q, - struct rkvpss_stream *stream, - enum v4l2_buf_type buf_type) -{ - q->type = buf_type; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; - q->drv_priv = stream; - q->ops = &stream_vb2_ops; - q->mem_ops = stream->dev->hw_dev->mem_ops; - q->buf_struct_size = sizeof(struct rkvpss_buffer); - q->min_buffers_needed = STREAM_OUT_REQ_BUFS_MIN; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - - q->lock = &stream->dev->apilock; - q->dev = stream->dev->hw_dev->dev; - q->allow_cache_hints = 1; - q->bidirectional = 1; - if (stream->dev->hw_dev->is_dma_contig) - q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS; - q->gfp_flags = GFP_DMA32; - return vb2_queue_init(q); -} - -static const -struct capture_fmt *find_fmt(struct rkvpss_stream *stream, u32 pixelfmt) -{ - const struct capture_fmt *fmt; - u32 i; - - for (i = 0; i < stream->config->fmt_size; i++) { - fmt = &stream->config->fmts[i]; - if (fmt->fourcc == pixelfmt) - return fmt; - } - return NULL; -} - -static int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs) -{ - switch (fcc) { - case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_YUV444M: - *xsubs = 1; - *ysubs = 1; - break; - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_VYUY: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUV422P: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_YVU422M: - case V4L2_PIX_FMT_FBC2: - *xsubs = 2; - *ysubs = 1; - break; - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21M: - case V4L2_PIX_FMT_NV12M: - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_FBC0: - *xsubs = 2; - *ysubs = 2; - break; - default: - return -EINVAL; - } - return 0; -} - -static int rkvpss_set_fmt(struct rkvpss_stream *stream, - struct v4l2_pix_format_mplane *pixm, - bool try) -{ - struct rkvpss_device *dev = stream->dev; - u32 i, planes, xsubs = 1, ysubs = 1, imagsize = 0; - const struct capture_fmt *fmt; - - fmt = find_fmt(stream, pixm->pixelformat); - if (!fmt) { - v4l2_err(&dev->v4l2_dev, - "nonsupport pixelformat:%c%c%c%c\n", - pixm->pixelformat, - pixm->pixelformat >> 8, - pixm->pixelformat >> 16, - pixm->pixelformat >> 24); - return -EINVAL; - } - - /* Tile4x4 writing of Channel0 and Channel1 only supports either one.*/ - if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) { - if (stream->id == 0) { - if (dev->stream_vdev.stream[1].streaming && - (dev->stream_vdev.stream[1].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 || - dev->stream_vdev.stream[1].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422)) { - v4l2_err(&dev->v4l2_dev, - "Tile4x4 writing of Ch0 and Cl1 only supports either one\n"); - return -EINVAL; - } - } - if (stream->id == 1) { - if (dev->stream_vdev.stream[0].streaming && - (dev->stream_vdev.stream[0].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 || - dev->stream_vdev.stream[0].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422)) { - v4l2_err(&dev->v4l2_dev, - "Tile4x4 writing of Ch0 and Cl1 only supports either one\n"); - return -EINVAL; - } - } - } - - pixm->num_planes = fmt->mplanes; - pixm->field = V4L2_FIELD_NONE; - if (!pixm->quantization) - pixm->quantization = V4L2_QUANTIZATION_FULL_RANGE; - fcc_xysubs(fmt->fourcc, &xsubs, &ysubs); - planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes; - for (i = 0; i < planes; i++) { - struct v4l2_plane_pix_format *plane_fmt; - u32 width, height, bytesperline, w, h; - - plane_fmt = pixm->plane_fmt + i; - w = pixm->width; - h = pixm->height; - width = i ? w / xsubs : w; - height = i ? h / ysubs : h; - - if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) - bytesperline = ALIGN(((width / 4) * fmt->bpp[i]), 16); - else - bytesperline = width * DIV_ROUND_UP(fmt->bpp[i], 8); - - if (i != 0 || plane_fmt->bytesperline < bytesperline) - plane_fmt->bytesperline = bytesperline; - - if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) - plane_fmt->sizeimage = plane_fmt->bytesperline * (height / 4); - else - plane_fmt->sizeimage = plane_fmt->bytesperline * height; - - imagsize += plane_fmt->sizeimage; - } - if (fmt->mplanes == 1) - pixm->plane_fmt[0].sizeimage = imagsize; - if (!try) { - stream->out_cap_fmt = *fmt; - stream->out_fmt = *pixm; - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "%s: stream:%d req(%d, %d) out(%d, %d)\n", - __func__, stream->id, pixm->width, pixm->height, - stream->out_fmt.width, stream->out_fmt.height); - } - return 0; -} - -/************************* v4l2_file_operations***************************/ - -static int rkvpss_fh_open(struct file *file) -{ - struct rkvpss_stream *stream = video_drvdata(file); - struct rkvpss_device *dev = stream->dev; - int ret; - - if (!dev->is_probe_end) - return -EINVAL; - - ret = v4l2_fh_open(file); - if (!ret) { - ret = v4l2_pipeline_pm_get(&stream->vnode.vdev.entity); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, - "pipeline power on failed %d\n", ret); - vb2_fop_release(file); - } - } - return ret; -} - -static int rkvpss_fh_release(struct file *file) -{ - struct rkvpss_stream *stream = video_drvdata(file); - int ret; - - ret = vb2_fop_release(file); - if (!ret) - v4l2_pipeline_pm_put(&stream->vnode.vdev.entity); - return ret; -} - -static const struct v4l2_file_operations rkvpss_fops = { - .open = rkvpss_fh_open, - .release = rkvpss_fh_release, - .unlocked_ioctl = video_ioctl2, - .poll = vb2_fop_poll, - .mmap = vb2_fop_mmap, -#ifdef CONFIG_COMPAT - .compat_ioctl32 = video_ioctl2, -#endif -}; - -static int rkvpss_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - if (input->index > 0) - return -EINVAL; - - input->type = V4L2_INPUT_TYPE_CAMERA; - strscpy(input->name, "Camera", sizeof(input->name)); - - return 0; -} - -static int rkvpss_try_fmt_vid_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct rkvpss_stream *stream = video_drvdata(file); - - return rkvpss_set_fmt(stream, &f->fmt.pix_mp, true); -} - -static int rkvpss_enum_fmt_vid_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct rkvpss_stream *stream = video_drvdata(file); - const struct capture_fmt *fmt = NULL; - - if (f->index >= stream->config->fmt_size) - return -EINVAL; - - fmt = &stream->config->fmts[f->index]; - f->pixelformat = fmt->fourcc; - - switch (f->pixelformat) { - case V4L2_PIX_FMT_TILE420: - strscpy(f->description, - "Rockchip yuv420 tile", - sizeof(f->description)); - break; - case V4L2_PIX_FMT_TILE422: - strscpy(f->description, - "Rockchip yuv422 tile", - sizeof(f->description)); - break; - default: - break; - } - - return 0; -} - -static int rkvpss_s_fmt_vid_mplane(struct file *file, - void *priv, struct v4l2_format *f) -{ - struct rkvpss_stream *stream = video_drvdata(file); - struct video_device *vdev = &stream->vnode.vdev; - struct rkvpss_vdev_node *node = vdev_to_node(vdev); - struct rkvpss_device *dev = stream->dev; - - /* Change not allowed if queue is streaming. */ - if (vb2_is_streaming(&node->buf_queue)) { - v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); - return -EBUSY; - } - - return rkvpss_set_fmt(stream, &f->fmt.pix_mp, false); -} - -static int rkvpss_g_fmt_vid_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct rkvpss_stream *stream = video_drvdata(file); - - f->fmt.pix_mp = stream->out_fmt; - - return 0; -} - -static int rkvpss_g_selection(struct file *file, void *prv, - struct v4l2_selection *sel) -{ - struct rkvpss_stream *stream = video_drvdata(file); - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - sel->r = stream->crop; - break; - default: - return -EINVAL; - } - return 0; -} - -static int rkvpss_s_selection(struct file *file, void *prv, - struct v4l2_selection *sel) -{ - struct rkvpss_stream *stream = video_drvdata(file); - struct rkvpss_device *dev = stream->dev; - struct v4l2_rect *crop = &stream->crop; - u32 max_w = dev->vpss_sdev.in_fmt.width; - u32 max_h = dev->vpss_sdev.in_fmt.height; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - if (sel->flags != 0) - return -EINVAL; - - sel->r.left = ALIGN(sel->r.left, 2); - sel->r.top = ALIGN(sel->r.top, 2); - sel->r.width = ALIGN(sel->r.width, 2); - sel->r.height = ALIGN(sel->r.height, 2); - if (!sel->r.width || !sel->r.height || - sel->r.width + sel->r.left > max_w || - sel->r.height + sel->r.top > max_h) { - v4l2_err(&dev->v4l2_dev, - "invalid crop left:%d top:%d w:%d h:%d for %dx%d\n", - sel->r.left, sel->r.top, sel->r.width, sel->r.height, max_w, max_h); - return -EINVAL; - } - - *crop = sel->r; - stream->is_crop_upd = true; - v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, - "stream %d crop(%d,%d)/%dx%d\n", stream->id, - crop->left, crop->top, crop->width, crop->height); - return 0; -} - -static int rkvpss_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct rkvpss_stream *stream = video_drvdata(file); - struct device *dev = stream->dev->dev; - struct video_device *vdev = video_devdata(file); - - strscpy(cap->card, vdev->name, sizeof(cap->card)); - snprintf(cap->driver, sizeof(cap->driver), - "%s_v%d", dev->driver->name, - stream->dev->vpss_ver >> 4); - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev_name(dev)); - - return 0; -} - -static void rkvpss_stream_mf(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - u32 val, mask; - - if (!stream->is_mf_upd) - return; - stream->is_mf_upd = false; - - if (!dev->hw_dev->is_ofl_cmsc) { - mask = RKVPSS_MIR_EN; - val = dev->mir_en ? RKVPSS_MIR_EN : 0; - rkvpss_unite_set_bits(dev, RKVPSS_VPSS_CTRL, mask, val); - } - mask = RKVPSS_MI_CHN_V_FLIP(stream->id); - /* Tile4x4 writing can't flip*/ - if (stream->out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 || - stream->out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422) - stream->flip_en = false; - val = stream->flip_en ? mask : 0; - rkvpss_unite_set_bits(dev, RKVPSS_MI_WR_VFLIP_CTRL, mask, val); -} - -static int rkvpss_get_mirror_flip(struct rkvpss_stream *stream, - struct rkvpss_mirror_flip *cfg) -{ - cfg->mirror = stream->dev->mir_en; - cfg->flip = stream->flip_en; - return 0; -} - -static int rkvpss_set_mirror_flip(struct rkvpss_stream *stream, - struct rkvpss_mirror_flip *cfg) -{ - struct rkvpss_device *dev = stream->dev; - - if (dev->hw_dev->is_ofl_cmsc && cfg->mirror) { - cfg->mirror = 0; - v4l2_warn(&stream->dev->v4l2_dev, - "mirror mux to vpss offline mode\n"); - } - if (dev->mir_en != !!cfg->mirror || - stream->flip_en != !!cfg->flip) - stream->is_mf_upd = true; - dev->mir_en = !!cfg->mirror; - stream->flip_en = !!cfg->flip; - return 0; -} - -static void cmsc_config_hw(struct rkvpss_device *dev, struct rkvpss_cmsc_cfg *cfg, bool sync, - int unite_index) -{ - int i, j, k, slope, hor; - u32 win_en, mode, val, ctrl = 0; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - win_en = cfg->win[i].win_en; - v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, - "%s unite:%d, unite_index:%d ch:%d win_en:0x%x\n", __func__, - dev->unite_mode, unite_index, i, win_en); - if (win_en) - ctrl |= RKVPSS_CMSC_CHN_EN(i); - rkvpss_idx_write(dev, RKVPSS_CMSC_CHN0_WIN + i * 4, win_en, unite_index); - - mode = cfg->win[i].mode; - rkvpss_idx_write(dev, RKVPSS_CMSC_CHN0_MODE + i * 4, mode, unite_index); - - for (j = 0; j < RKVPSS_CMSC_WIN_MAX && win_en; j++) { - if (!(win_en & BIT(j))) - continue; - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - val = RKVPSS_CMSC_WIN_VTX(cfg->win[j].point[k].x, - cfg->win[j].point[k].y); - rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_L0_VTX + k * 8 + j * 32, val, - unite_index); - - rkvpss_cmsc_slop(&cfg->win[j].point[k], - (k + 1 == RKVPSS_CMSC_POINT_MAX) ? - &cfg->win[j].point[0] : &cfg->win[j].point[k + 1], - &slope, &hor); - val = RKVPSS_CMSC_WIN_SLP(slope, hor); - rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_L0_SLP + k * 8 + j * 32, val, - unite_index); - v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, - "%s unite:%d unite_index:%d ch:%d win:%d point:%d x:%u y:%u", - __func__, dev->unite_mode, unite_index, i, j, k, - cfg->win[j].point[k].x, - cfg->win[j].point[k].y); - } - - if ((mode & BIT(j))) - continue; - val = RKVPSS_CMSK_WIN_YUV(cfg->win[j].cover_color_y, - cfg->win[j].cover_color_u, - cfg->win[j].cover_color_v); - val |= RKVPSS_CMSC_WIN_ALPHA(cfg->win[j].cover_color_a); - rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_PARA + j * 4, val, unite_index); - } - } - - if (ctrl) { - ctrl |= RKVPSS_CMSC_EN; - ctrl |= RKVPSS_CMSC_BLK_SZIE(cfg->mosaic_block); - } - rkvpss_idx_write(dev, RKVPSS_CMSC_CTRL, ctrl, unite_index); - val = RKVPSS_CMSC_GEN_UPD; - if (sync) - val |= RKVPSS_CMSC_FORCE_UPD; - rkvpss_idx_write(dev, RKVPSS_CMSC_UPDATE, val, unite_index); -} - -void rkvpss_cmsc_config(struct rkvpss_device *dev, bool sync) -{ - unsigned long lock_flags = 0; - struct rkvpss_cmsc_cfg left_cfg = {0}, right_cfg = {0}; - struct rkvpss_cmsc_win *win; - struct rkvpss_cmsc_point *point; - int i, j, k; - u32 mask; - - spin_lock_irqsave(&dev->cmsc_lock, lock_flags); - if (dev->hw_dev->is_ofl_cmsc || !dev->cmsc_upd) { - spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); - return; - } - dev->cmsc_upd = false; - spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); - - if (dev->unite_mode) { - /* unite left */ - for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { - left_cfg.win[i].cover_color_y = dev->cmsc_cfg.win[i].cover_color_y; - left_cfg.win[i].cover_color_u = dev->cmsc_cfg.win[i].cover_color_u; - left_cfg.win[i].cover_color_v = dev->cmsc_cfg.win[i].cover_color_v; - left_cfg.win[i].cover_color_a = dev->cmsc_cfg.win[i].cover_color_a; - for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) - left_cfg.win[i].point[j] = dev->cmsc_cfg.win[i].point[j]; - } - left_cfg.mosaic_block = dev->cmsc_cfg.mosaic_block; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - left_cfg.win[i].win_en = dev->cmsc_cfg.win[i].win_en; - left_cfg.win[i].mode = dev->cmsc_cfg.win[i].mode; - } - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - win = &left_cfg.win[j]; - if (!(left_cfg.win[i].win_en & BIT(j))) - continue; - mask = 0; - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - point = &win->point[k]; - if (point->x >= dev->vpss_sdev.in_fmt.width / 2) - mask |= BIT(k); - else - mask &= ~BIT(k); - } - if (mask == 0xf) { - /** all right **/ - left_cfg.win[i].win_en &= ~BIT(j); - } else if (mask != 0) { - /** middle need avoid pentagon **/ - if (win->point[0].x != win->point[3].x || - win->point[1].x != win->point[2].x) { - left_cfg.win[i].win_en &= ~BIT(j); - } else { - win->point[1].x = dev->vpss_sdev.in_fmt.width / 2; - win->point[2].x = dev->vpss_sdev.in_fmt.width / 2; - } - } - } - } - - /* unite right */ - for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { - right_cfg.win[i].cover_color_y = dev->cmsc_cfg.win[i].cover_color_y; - right_cfg.win[i].cover_color_u = dev->cmsc_cfg.win[i].cover_color_u; - right_cfg.win[i].cover_color_v = dev->cmsc_cfg.win[i].cover_color_v; - right_cfg.win[i].cover_color_a = dev->cmsc_cfg.win[i].cover_color_a; - - for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) - right_cfg.win[i].point[j] = dev->cmsc_cfg.win[i].point[j]; - } - right_cfg.mosaic_block = dev->cmsc_cfg.mosaic_block; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - right_cfg.win[i].win_en = dev->cmsc_cfg.win[i].win_en; - right_cfg.win[i].mode = dev->cmsc_cfg.win[i].mode; - } - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - win = &right_cfg.win[j]; - if (!(right_cfg.win[i].win_en & BIT(j))) - continue; - mask = 0; - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - point = &win->point[k]; - if (point->x <= dev->vpss_sdev.in_fmt.width / 2) - mask |= BIT(k); - else - mask &= ~BIT(k); - } - if (mask == 0xf) { - /** all left **/ - right_cfg.win[i].win_en &= ~BIT(j); - } else if (mask != 0) { - /** middle need avoid pentagon **/ - if (win->point[0].x != win->point[3].x || - win->point[1].x != win->point[2].x) { - right_cfg.win[i].win_en &= ~BIT(j); - } else { - win->point[0].x = RKMOUDLE_UNITE_EXTEND_PIXEL; - win->point[3].x = RKMOUDLE_UNITE_EXTEND_PIXEL; - win->point[1].x = win->point[1].x - - (dev->vpss_sdev.in_fmt.width / 2) + - RKMOUDLE_UNITE_EXTEND_PIXEL; - win->point[2].x = win->point[2].x - - (dev->vpss_sdev.in_fmt.width / 2) + - RKMOUDLE_UNITE_EXTEND_PIXEL; - } - } else { - /** all right **/ - win->point[0].x = win->point[0].x - - (dev->vpss_sdev.in_fmt.width / 2) + - RKMOUDLE_UNITE_EXTEND_PIXEL; - win->point[1].x = win->point[1].x - - (dev->vpss_sdev.in_fmt.width / 2) + - RKMOUDLE_UNITE_EXTEND_PIXEL; - win->point[2].x = win->point[2].x - - (dev->vpss_sdev.in_fmt.width / 2) + - RKMOUDLE_UNITE_EXTEND_PIXEL; - win->point[3].x = win->point[3].x - - (dev->vpss_sdev.in_fmt.width / 2) + - RKMOUDLE_UNITE_EXTEND_PIXEL; - } - } - } - - cmsc_config_hw(dev, &left_cfg, sync, VPSS_UNITE_LEFT); - cmsc_config_hw(dev, &right_cfg, sync, VPSS_UNITE_RIGHT); - } else { - cmsc_config_hw(dev, &dev->cmsc_cfg, sync, VPSS_UNITE_LEFT); - } -} - -static int rkvpss_get_cmsc(struct rkvpss_stream *stream, struct rkvpss_cmsc_cfg *cfg) -{ - struct rkvpss_device *dev = stream->dev; - unsigned long lock_flags = 0; - u32 i, win_en, mode; - - spin_lock_irqsave(&dev->cmsc_lock, lock_flags); - *cfg = dev->cmsc_cfg; - spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); - - win_en = cfg->win[stream->id].win_en; - mode = cfg->win[stream->id].mode; - for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { - cfg->win[i].win_en = !!(win_en & BIT(i)); - cfg->win[i].mode = !!(mode & BIT(i)); - } - cfg->width_ro = dev->vpss_sdev.in_fmt.width; - cfg->height_ro = dev->vpss_sdev.in_fmt.height; - return 0; -} - -static int rkvpss_set_cmsc(struct rkvpss_stream *stream, struct rkvpss_cmsc_cfg *cfg) -{ - struct rkvpss_device *dev = stream->dev; - unsigned long lock_flags = 0; - u16 i, j, k, win_en = 0, mode = 0; - int ret = 0; - - if (dev->hw_dev->is_ofl_cmsc) - return -EINVAL; - - spin_lock_irqsave(&dev->cmsc_lock, lock_flags); - for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { - if (!cfg->win[i].win_en) - continue; - - win_en |= BIT(i); - mode |= cfg->win[i].mode ? BIT(i) : 0; - - for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) { - for (k = j + 1; k < RKVPSS_CMSC_POINT_MAX; k++) { - if (cfg->win[i].point[j].x == cfg->win[i].point[k].x && - cfg->win[i].point[j].y == cfg->win[i].point[k].y) { - v4l2_warn(&dev->v4l2_dev, - "points should different, P%d(%d,%d) P%d(%d,%d)\n", - j, cfg->win[i].point[j].x, cfg->win[i].point[j].y, - k, cfg->win[i].point[k].x, cfg->win[i].point[k].y); - ret = -EINVAL; - goto unlock; - } - } - } - if (!cfg->win[i].mode) { - dev->cmsc_cfg.win[i].cover_color_y = cfg->win[i].cover_color_y; - dev->cmsc_cfg.win[i].cover_color_u = cfg->win[i].cover_color_u; - dev->cmsc_cfg.win[i].cover_color_v = cfg->win[i].cover_color_v; - dev->cmsc_cfg.win[i].cover_color_a = cfg->win[i].cover_color_a; - if (dev->cmsc_cfg.win[i].cover_color_a > 15) - dev->cmsc_cfg.win[i].cover_color_a = 15; - } - for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) - dev->cmsc_cfg.win[i].point[j] = cfg->win[i].point[j]; - } - dev->cmsc_cfg.win[stream->id].win_en = win_en; - dev->cmsc_cfg.win[stream->id].mode = mode; - dev->cmsc_cfg.mosaic_block = cfg->mosaic_block; - dev->cmsc_upd = true; - v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, - "%s ch:%d win_en:0x%x", - __func__, stream->id, - dev->cmsc_cfg.win[stream->id].win_en); - -unlock: - spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); - return ret; -} - -static long rkvpss_ioctl_default(struct file *file, void *fh, - bool valid_prio, unsigned int cmd, void *arg) -{ - struct rkvpss_stream *stream = video_drvdata(file); - long ret = 0; - - if (!arg) - return -EINVAL; - - switch (cmd) { - case RKVPSS_CMD_GET_MIRROR_FLIP: - ret = rkvpss_get_mirror_flip(stream, arg); - break; - case RKVPSS_CMD_SET_MIRROR_FLIP: - ret = rkvpss_set_mirror_flip(stream, arg); - break; - case RKVPSS_CMD_GET_CMSC: - ret = rkvpss_get_cmsc(stream, arg); - break; - case RKVPSS_CMD_SET_CMSC: - ret = rkvpss_set_cmsc(stream, arg); - break; - case RKVPSS_CMD_STREAM_ATTACH_INFO: - stream->is_attach_info = *(int *)arg; - break; - default: - ret = -EINVAL; - } + if (vpss->vpss_ver == VPSS_V10) + ret = rkvpss_stream_buf_cnt_v1(stream); return ret; } -static const struct v4l2_ioctl_ops rkvpss_v4l2_ioctl_ops = { - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_enum_input = rkvpss_enum_input, - .vidioc_try_fmt_vid_cap_mplane = rkvpss_try_fmt_vid_mplane, - .vidioc_enum_fmt_vid_cap = rkvpss_enum_fmt_vid_mplane, - .vidioc_s_fmt_vid_cap_mplane = rkvpss_s_fmt_vid_mplane, - .vidioc_g_fmt_vid_cap_mplane = rkvpss_g_fmt_vid_mplane, - .vidioc_s_selection = rkvpss_s_selection, - .vidioc_g_selection = rkvpss_g_selection, - .vidioc_querycap = rkvpss_querycap, - .vidioc_default = rkvpss_ioctl_default, -}; - -static void rkvpss_unregister_stream_video(struct rkvpss_stream *stream) -{ - tasklet_kill(&stream->buf_done_tasklet); - media_entity_cleanup(&stream->vnode.vdev.entity); - video_unregister_device(&stream->vnode.vdev); -} - -static int rkvpss_register_stream_video(struct rkvpss_stream *stream) -{ - struct rkvpss_device *dev = stream->dev; - struct v4l2_device *v4l2_dev = &dev->v4l2_dev; - struct video_device *vdev = &stream->vnode.vdev; - struct rkvpss_vdev_node *node; - enum v4l2_buf_type buf_type; - int ret = 0; - - node = vdev_to_node(vdev); - vdev->release = video_device_release_empty; - vdev->fops = &rkvpss_fops; - vdev->minor = -1; - vdev->v4l2_dev = v4l2_dev; - vdev->lock = &dev->apilock; - video_set_drvdata(vdev, stream); - - vdev->ioctl_ops = &rkvpss_v4l2_ioctl_ops; - vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; - vdev->vfl_dir = VFL_DIR_RX; - node->pad.flags = MEDIA_PAD_FL_SINK; - buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - - rkvpss_init_vb2_queue(&node->buf_queue, stream, buf_type); - vdev->queue = &node->buf_queue; - - ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); - if (ret < 0) { - v4l2_err(v4l2_dev, - "video register failed with error %d\n", ret); - return ret; - } - - ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); - if (ret < 0) - goto unreg; - - INIT_LIST_HEAD(&stream->buf_done_list); - tasklet_init(&stream->buf_done_tasklet, - rkvpss_buf_done_task, (unsigned long)stream); - tasklet_disable(&stream->buf_done_tasklet); - return 0; -unreg: - video_unregister_device(vdev); - return ret; -} - -void rkvpss_stream_default_fmt(struct rkvpss_device *dev, u32 id, - u32 width, u32 height, u32 pixelformat) -{ - struct rkvpss_stream *stream = &dev->stream_vdev.stream[id]; - struct v4l2_pix_format_mplane pixm = { 0 }; - - if (pixelformat) - pixm.pixelformat = pixelformat; - else - pixm.pixelformat = stream->out_cap_fmt.fourcc; - if (!pixm.pixelformat) - return; - - stream->crop.left = 0; - stream->crop.top = 0; - stream->crop.width = width; - stream->crop.height = height; - - pixm.width = width; - pixm.height = height; - rkvpss_set_fmt(stream, &pixm, false); -} - int rkvpss_register_stream_vdevs(struct rkvpss_device *dev) { - struct rkvpss_stream_vdev *stream_vdev; - struct rkvpss_stream *stream; - struct video_device *vdev; - char *vdev_name; - int i, j, ret = 0; + int ret = -EINVAL; - stream_vdev = &dev->stream_vdev; - memset(stream_vdev, 0, sizeof(*stream_vdev)); + if (dev->vpss_ver == VPSS_V10) + ret = rkvpss_register_stream_vdevs_v1(dev); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - stream = &stream_vdev->stream[i]; - stream->id = i; - stream->dev = dev; - vdev = &stream->vnode.vdev; - INIT_LIST_HEAD(&stream->buf_queue); - init_waitqueue_head(&stream->done); - spin_lock_init(&stream->vbq_lock); - switch (i) { - case RKVPSS_OUTPUT_CH0: - vdev_name = S0_VDEV_NAME; - stream->ops = &scl_stream_ops; - stream->config = &scl0_config; - break; - case RKVPSS_OUTPUT_CH1: - vdev_name = S1_VDEV_NAME; - stream->ops = &scl_stream_ops; - stream->config = &scl1_config; - break; - case RKVPSS_OUTPUT_CH2: - vdev_name = S2_VDEV_NAME; - stream->ops = &scl_stream_ops; - stream->config = &scl2_config; - break; - case RKVPSS_OUTPUT_CH3: - vdev_name = S3_VDEV_NAME; - stream->ops = &scl_stream_ops; - stream->config = &scl3_config; - break; - default: - v4l2_err(&dev->v4l2_dev, "Invalid stream:%d\n", i); - return -EINVAL; - } - strscpy(vdev->name, vdev_name, sizeof(vdev->name)); - ret = rkvpss_register_stream_video(stream); - if (ret < 0) - goto err; - } - return 0; -err: - for (j = 0; j < i; j++) { - stream = &stream_vdev->stream[j]; - rkvpss_unregister_stream_video(stream); - } return ret; } void rkvpss_unregister_stream_vdevs(struct rkvpss_device *dev) { - struct rkvpss_stream_vdev *stream_vdev; - struct rkvpss_stream *stream; - int i; + if (dev->vpss_ver == VPSS_V10) + rkvpss_unregister_stream_vdevs_v1(dev); +} - stream_vdev = &dev->stream_vdev; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - stream = &stream_vdev->stream[i]; - rkvpss_unregister_stream_video(stream); - } +void rkvpss_stream_default_fmt(struct rkvpss_device *dev, u32 id, + u32 width, u32 height, u32 pixelformat) +{ + if (dev->vpss_ver == VPSS_V10) + rkvpss_stream_default_fmt_v1(dev, id, width, height, pixelformat); } 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) - rkvpss_check_idle(dev, VPSS_FRAME_END); + if (dev->vpss_ver == VPSS_V10) + rkvpss_isr_v1(dev, mis_val); } void rkvpss_mi_isr(struct rkvpss_device *dev, u32 mis_val) { - struct rkvpss_stream *stream; - int i, ris = readl(dev->hw_dev->base_addr + RKVPSS_MI_RIS); - - v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, - "mi isr:0x%x, ris:0x%x\n", mis_val, ris); - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - stream = &dev->stream_vdev.stream[i]; - - if (!stream->streaming || - !(ris & stream->config->frame_end_id)) - continue; - writel(stream->config->frame_end_id, dev->hw_dev->base_addr + RKVPSS_MI_ICR); - - if (!dev->unite_mode || dev->unite_index == VPSS_UNITE_RIGHT) { - if (stream->stopping) { - stream->stopping = false; - stream->streaming = false; - stream->ops->disable_mi(stream); - wake_up(&stream->done); - } else { - rkvpss_frame_end(stream); - } - } - } - rkvpss_check_idle(dev, (ris & 0xf) << 3); + if (dev->vpss_ver == VPSS_V10) + rkvpss_mi_isr_v1(dev, mis_val); } diff --git a/drivers/media/platform/rockchip/vpss/stream.h b/drivers/media/platform/rockchip/vpss/stream.h index 3a54d5e1c71b..d8bee7c96a8b 100644 --- a/drivers/media/platform/rockchip/vpss/stream.h +++ b/drivers/media/platform/rockchip/vpss/stream.h @@ -4,8 +4,23 @@ #ifndef _RKVPSS_STREAM_H #define _RKVPSS_STREAM_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include -#include "common.h" + +#define STREAM_OUT_REQ_BUFS_MIN 0 struct rkvpss_stream; diff --git a/drivers/media/platform/rockchip/vpss/stream_v1.c b/drivers/media/platform/rockchip/vpss/stream_v1.c new file mode 100644 index 000000000000..939bd68f6910 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/stream_v1.c @@ -0,0 +1,2823 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ + +#include "vpss.h" +#include "common.h" +#include "stream.h" +#include "dev.h" +#include "vpss_offline.h" +#include "hw.h" +#include "procfs.h" +#include "regs_v1.h" + +#include "stream_v1.h" + + +#define STREAM_OUT_REQ_BUFS_MIN 0 + +static void rkvpss_frame_end(struct rkvpss_stream *stream); +static int rkvpss_stream_crop(struct rkvpss_stream *stream, bool on, bool sync); +static int rkvpss_stream_scale(struct rkvpss_stream *stream, bool on, bool sync); +static void rkvpss_stream_mf(struct rkvpss_stream *stream); + +static const struct capture_fmt scl0_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_GREY, + .fmt_type = FMT_YUV, + .bpp = { 8 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV61, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_TILE420, + .fmt_type = FMT_YUV, + .bpp = { 24 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_TILE422, + .fmt_type = FMT_YUV, + .bpp = { 32 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + } +}; + +static const struct capture_fmt scl1_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_GREY, + .fmt_type = FMT_YUV, + .bpp = { 8 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, + .fmt_type = FMT_RGB, + .bpp = { 16 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = RKVPSS_MI_CHN_WR_RB_SWAP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB565, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, + .fmt_type = FMT_RGB, + .bpp = { 24 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = RKVPSS_MI_CHN_WR_RB_SWAP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB888, + }, { + .fourcc = V4L2_PIX_FMT_XBGR32, + .fmt_type = FMT_RGB, + .bpp = { 32 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_ARGB888, + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, + .fmt_type = FMT_RGB, + .bpp = { 16 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB565, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, + .fmt_type = FMT_RGB, + .bpp = { 24 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB888, + }, { + .fourcc = V4L2_PIX_FMT_XRGB32, + .fmt_type = FMT_RGB, + .bpp = { 32 }, + .mplanes = 1, + .cplanes = 1, + .wr_fmt = 0, + .swap = RKVPSS_MI_CHN_WR_RB_SWAP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_ARGB888, + }, { + .fourcc = V4L2_PIX_FMT_NV61, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_TILE420, + .fmt_type = FMT_YUV, + .bpp = { 24 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_TILE422, + .fmt_type = FMT_YUV, + .bpp = { 32 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = 0, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + } +}; + +static const struct capture_fmt scl2_3_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_GREY, + .fmt_type = FMT_YUV, + .bpp = { 8 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV61, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .fmt_type = FMT_YUV, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_42XSP, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .fmt_type = FMT_YUV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .swap = 0, + .wr_fmt = RKVPSS_MI_CHN_WR_422P, + .output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422, + } +}; + + +static struct stream_config scl0_config = { + .fmts = scl0_fmts, + .fmt_size = ARRAY_SIZE(scl0_fmts), + .frame_end_id = RKVPSS_MI_CHN0_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .h_offs = RKVPSS_CROP1_0_H_OFFS, + .v_offs = RKVPSS_CROP1_0_V_OFFS, + .h_size = RKVPSS_CROP1_0_H_SIZE, + .v_size = RKVPSS_CROP1_0_V_SIZE, + .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, + .h_offs_shd = RKVPSS_CROP1_0_H_OFFS_SHD, + .v_offs_shd = RKVPSS_CROP1_0_V_OFFS_SHD, + .h_size_shd = RKVPSS_CROP1_0_H_SIZE_SHD, + .v_size_shd = RKVPSS_CROP1_0_V_SIZE_SHD, + }, + .mi = { + .ctrl = RKVPSS_MI_CHN0_WR_CTRL, + .stride = RKVPSS_MI_CHN0_WR_Y_STRIDE, + .y_base = RKVPSS_MI_CHN0_WR_Y_BASE, + .uv_base = RKVPSS_MI_CHN0_WR_CB_BASE, + .y_size = RKVPSS_MI_CHN0_WR_Y_SIZE, + .uv_size = RKVPSS_MI_CHN0_WR_CB_SIZE, + .y_offs_cnt = RKVPSS_MI_CHN0_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS_MI_CHN0_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS_MI_CHN0_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS_MI_CHN0_WR_Y_PIC_SIZE, + + .ctrl_shd = RKVPSS_MI_CHN0_WR_CTRL_SHD, + .y_shd = RKVPSS_MI_CHN0_WR_Y_BASE_SHD, + .uv_shd = RKVPSS_MI_CHN0_WR_CB_BASE_SHD, + }, +}; + +static struct stream_config scl1_config = { + .fmts = scl1_fmts, + .fmt_size = ARRAY_SIZE(scl1_fmts), + .frame_end_id = RKVPSS_MI_CHN1_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .h_offs = RKVPSS_CROP1_1_H_OFFS, + .v_offs = RKVPSS_CROP1_1_V_OFFS, + .h_size = RKVPSS_CROP1_1_H_SIZE, + .v_size = RKVPSS_CROP1_1_V_SIZE, + .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, + .h_offs_shd = RKVPSS_CROP1_1_H_OFFS_SHD, + .v_offs_shd = RKVPSS_CROP1_1_V_OFFS_SHD, + .h_size_shd = RKVPSS_CROP1_1_H_SIZE_SHD, + .v_size_shd = RKVPSS_CROP1_1_V_SIZE_SHD, + }, + .scale = { + .ctrl = RKVPSS_SCALE1_CTRL, + .update = RKVPSS_SCALE1_UPDATE, + .src_size = RKVPSS_SCALE1_SRC_SIZE, + .dst_size = RKVPSS_SCALE1_DST_SIZE, + .hy_fac = RKVPSS_SCALE1_HY_FAC, + .hc_fac = RKVPSS_SCALE1_HC_FAC, + .vy_fac = RKVPSS_SCALE1_VY_FAC, + .vc_fac = RKVPSS_SCALE1_VC_FAC, + .hy_offs = RKVPSS_SCALE1_HY_OFFS, + .hc_offs = RKVPSS_SCALE1_HC_OFFS, + .vy_offs = RKVPSS_SCALE1_VY_OFFS, + .vc_offs = RKVPSS_SCALE1_VC_OFFS, + .hy_size = RKVPSS_SCALE1_HY_SIZE, + .hc_size = RKVPSS_SCALE1_HC_SIZE, + .hy_offs_mi = RKVPSS_SCALE1_HY_OFFS_MI, + .hc_offs_mi = RKVPSS_SCALE1_HC_OFFS_MI, + .in_crop_offs = RKVPSS_SCALE1_IN_CROP_OFFSET, + .ctrl_shd = RKVPSS_SCALE1_CTRL_SHD, + .src_size_shd = RKVPSS_SCALE1_SRC_SIZE_SHD, + .dst_size_shd = RKVPSS_SCALE1_DST_SIZE_SHD, + .hy_fac_shd = RKVPSS_SCALE1_HY_FAC_SHD, + .hc_fac_shd = RKVPSS_SCALE1_HC_FAC_SHD, + .vy_fac_shd = RKVPSS_SCALE1_VY_FAC_SHD, + .vc_fac_shd = RKVPSS_SCALE1_VC_FAC_SHD, + .hy_offs_shd = RKVPSS_SCALE1_HY_OFFS_SHD, + .hc_offs_shd = RKVPSS_SCALE1_HC_OFFS_SHD, + .vy_offs_shd = RKVPSS_SCALE1_VY_OFFS_SHD, + .vc_offs_shd = RKVPSS_SCALE1_VC_OFFS_SHD, + .hy_size_shd = RKVPSS_SCALE1_HY_SIZE_SHD, + .hc_size_shd = RKVPSS_SCALE1_HC_SIZE_SHD, + .hy_offs_mi_shd = RKVPSS_SCALE1_HY_OFFS_MI_SHD, + .hc_offs_mi_shd = RKVPSS_SCALE1_HC_OFFS_MI_SHD, + .in_crop_offs_shd = RKVPSS_SCALE1_IN_CROP_OFFSET_SHD, + }, + .mi = { + .ctrl = RKVPSS_MI_CHN1_WR_CTRL, + .stride = RKVPSS_MI_CHN1_WR_Y_STRIDE, + .y_base = RKVPSS_MI_CHN1_WR_Y_BASE, + .uv_base = RKVPSS_MI_CHN1_WR_CB_BASE, + .y_size = RKVPSS_MI_CHN1_WR_Y_SIZE, + .uv_size = RKVPSS_MI_CHN1_WR_CB_SIZE, + .y_offs_cnt = RKVPSS_MI_CHN1_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS_MI_CHN1_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS_MI_CHN1_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS_MI_CHN1_WR_Y_PIC_SIZE, + + .ctrl_shd = RKVPSS_MI_CHN1_WR_CTRL_SHD, + .y_shd = RKVPSS_MI_CHN1_WR_Y_BASE_SHD, + .uv_shd = RKVPSS_MI_CHN1_WR_CB_BASE_SHD, + }, +}; + +static struct stream_config scl2_config = { + .fmts = scl2_3_fmts, + .fmt_size = ARRAY_SIZE(scl2_3_fmts), + .frame_end_id = RKVPSS_MI_CHN2_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .h_offs = RKVPSS_CROP1_2_H_OFFS, + .v_offs = RKVPSS_CROP1_2_V_OFFS, + .h_size = RKVPSS_CROP1_2_H_SIZE, + .v_size = RKVPSS_CROP1_2_V_SIZE, + .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, + .h_offs_shd = RKVPSS_CROP1_2_H_OFFS_SHD, + .v_offs_shd = RKVPSS_CROP1_2_V_OFFS_SHD, + .h_size_shd = RKVPSS_CROP1_2_H_SIZE_SHD, + .v_size_shd = RKVPSS_CROP1_2_V_SIZE_SHD, + }, + .scale = { + .ctrl = RKVPSS_SCALE2_CTRL, + .update = RKVPSS_SCALE2_UPDATE, + .src_size = RKVPSS_SCALE2_SRC_SIZE, + .dst_size = RKVPSS_SCALE2_DST_SIZE, + .hy_fac = RKVPSS_SCALE2_HY_FAC, + .hc_fac = RKVPSS_SCALE2_HC_FAC, + .vy_fac = RKVPSS_SCALE2_VY_FAC, + .vc_fac = RKVPSS_SCALE2_VC_FAC, + .hy_offs = RKVPSS_SCALE2_HY_OFFS, + .hc_offs = RKVPSS_SCALE2_HC_OFFS, + .vy_offs = RKVPSS_SCALE2_VY_OFFS, + .vc_offs = RKVPSS_SCALE2_VC_OFFS, + .hy_size = RKVPSS_SCALE2_HY_SIZE, + .hc_size = RKVPSS_SCALE2_HC_SIZE, + .hy_offs_mi = RKVPSS_SCALE2_HY_OFFS_MI, + .hc_offs_mi = RKVPSS_SCALE2_HC_OFFS_MI, + .in_crop_offs = RKVPSS_SCALE2_IN_CROP_OFFSET, + .ctrl_shd = RKVPSS_SCALE2_CTRL_SHD, + .src_size_shd = RKVPSS_SCALE2_SRC_SIZE_SHD, + .dst_size_shd = RKVPSS_SCALE2_DST_SIZE_SHD, + .hy_fac_shd = RKVPSS_SCALE2_HY_FAC_SHD, + .hc_fac_shd = RKVPSS_SCALE2_HC_FAC_SHD, + .vy_fac_shd = RKVPSS_SCALE2_VY_FAC_SHD, + .vc_fac_shd = RKVPSS_SCALE2_VC_FAC_SHD, + .hy_offs_shd = RKVPSS_SCALE2_HY_OFFS_SHD, + .hc_offs_shd = RKVPSS_SCALE2_HC_OFFS_SHD, + .vy_offs_shd = RKVPSS_SCALE2_VY_OFFS_SHD, + .vc_offs_shd = RKVPSS_SCALE2_VC_OFFS_SHD, + .hy_size_shd = RKVPSS_SCALE2_HY_SIZE_SHD, + .hc_size_shd = RKVPSS_SCALE2_HC_SIZE_SHD, + .hy_offs_mi_shd = RKVPSS_SCALE2_HY_OFFS_MI_SHD, + .hc_offs_mi_shd = RKVPSS_SCALE2_HC_OFFS_MI_SHD, + .in_crop_offs_shd = RKVPSS_SCALE2_IN_CROP_OFFSET_SHD, + }, + .mi = { + .ctrl = RKVPSS_MI_CHN2_WR_CTRL, + .stride = RKVPSS_MI_CHN2_WR_Y_STRIDE, + .y_base = RKVPSS_MI_CHN2_WR_Y_BASE, + .uv_base = RKVPSS_MI_CHN2_WR_CB_BASE, + .y_size = RKVPSS_MI_CHN2_WR_Y_SIZE, + .uv_size = RKVPSS_MI_CHN2_WR_CB_SIZE, + .y_offs_cnt = RKVPSS_MI_CHN2_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS_MI_CHN2_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS_MI_CHN2_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS_MI_CHN2_WR_Y_PIC_SIZE, + + .ctrl_shd = RKVPSS_MI_CHN2_WR_CTRL_SHD, + .y_shd = RKVPSS_MI_CHN2_WR_Y_BASE_SHD, + .uv_shd = RKVPSS_MI_CHN2_WR_CB_BASE_SHD, + }, +}; + +static struct stream_config scl3_config = { + .fmts = scl2_3_fmts, + .fmt_size = ARRAY_SIZE(scl2_3_fmts), + .frame_end_id = RKVPSS_MI_CHN3_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .h_offs = RKVPSS_CROP1_3_H_OFFS, + .v_offs = RKVPSS_CROP1_3_V_OFFS, + .h_size = RKVPSS_CROP1_3_H_SIZE, + .v_size = RKVPSS_CROP1_3_V_SIZE, + .ctrl_shd = RKVPSS_CROP1_CTRL_SHD, + .h_offs_shd = RKVPSS_CROP1_3_H_OFFS_SHD, + .v_offs_shd = RKVPSS_CROP1_3_V_OFFS_SHD, + .h_size_shd = RKVPSS_CROP1_3_H_SIZE_SHD, + .v_size_shd = RKVPSS_CROP1_3_V_SIZE_SHD, + }, + .scale = { + .ctrl = RKVPSS_SCALE3_CTRL, + .update = RKVPSS_SCALE3_UPDATE, + .src_size = RKVPSS_SCALE3_SRC_SIZE, + .dst_size = RKVPSS_SCALE3_DST_SIZE, + .hy_fac = RKVPSS_SCALE3_HY_FAC, + .hc_fac = RKVPSS_SCALE3_HC_FAC, + .vy_fac = RKVPSS_SCALE3_VY_FAC, + .vc_fac = RKVPSS_SCALE3_VC_FAC, + .hy_offs = RKVPSS_SCALE3_HY_OFFS, + .hc_offs = RKVPSS_SCALE3_HC_OFFS, + .vy_offs = RKVPSS_SCALE3_VY_OFFS, + .vc_offs = RKVPSS_SCALE3_VC_OFFS, + .hy_size = RKVPSS_SCALE3_HY_SIZE, + .hc_size = RKVPSS_SCALE3_HC_SIZE, + .hy_offs_mi = RKVPSS_SCALE3_HY_OFFS_MI, + .hc_offs_mi = RKVPSS_SCALE3_HC_OFFS_MI, + .in_crop_offs = RKVPSS_SCALE3_IN_CROP_OFFSET, + .ctrl_shd = RKVPSS_SCALE3_CTRL_SHD, + .src_size_shd = RKVPSS_SCALE3_SRC_SIZE_SHD, + .dst_size_shd = RKVPSS_SCALE3_DST_SIZE_SHD, + .hy_fac_shd = RKVPSS_SCALE3_HY_FAC_SHD, + .hc_fac_shd = RKVPSS_SCALE3_HC_FAC_SHD, + .vy_fac_shd = RKVPSS_SCALE3_VY_FAC_SHD, + .vc_fac_shd = RKVPSS_SCALE3_VC_FAC_SHD, + .hy_offs_shd = RKVPSS_SCALE3_HY_OFFS_SHD, + .hc_offs_shd = RKVPSS_SCALE3_HC_OFFS_SHD, + .vy_offs_shd = RKVPSS_SCALE3_VY_OFFS_SHD, + .vc_offs_shd = RKVPSS_SCALE3_VC_OFFS_SHD, + .hy_size_shd = RKVPSS_SCALE3_HY_SIZE_SHD, + .hc_size_shd = RKVPSS_SCALE3_HC_SIZE_SHD, + .hy_offs_mi_shd = RKVPSS_SCALE3_HY_OFFS_MI_SHD, + .hc_offs_mi_shd = RKVPSS_SCALE3_HC_OFFS_MI_SHD, + .in_crop_offs_shd = RKVPSS_SCALE3_IN_CROP_OFFSET_SHD, + }, + .mi = { + .ctrl = RKVPSS_MI_CHN3_WR_CTRL, + .stride = RKVPSS_MI_CHN3_WR_Y_STRIDE, + .y_base = RKVPSS_MI_CHN3_WR_Y_BASE, + .uv_base = RKVPSS_MI_CHN3_WR_CB_BASE, + .y_size = RKVPSS_MI_CHN3_WR_Y_SIZE, + .uv_size = RKVPSS_MI_CHN3_WR_CB_SIZE, + .y_offs_cnt = RKVPSS_MI_CHN3_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS_MI_CHN3_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS_MI_CHN3_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS_MI_CHN3_WR_Y_PIC_SIZE, + + .ctrl_shd = RKVPSS_MI_CHN3_WR_CTRL_SHD, + .y_shd = RKVPSS_MI_CHN3_WR_Y_BASE_SHD, + .uv_shd = RKVPSS_MI_CHN3_WR_CB_BASE_SHD, + }, +}; + +static void calc_unite_scl_params(struct rkvpss_stream *stream) +{ + struct rkvpss_online_unite_params *params = &stream->unite_params; + u32 right_scl_need_size_y, right_scl_need_size_c; + u32 left_in_used_size_y, left_in_used_size_c; + u32 right_fst_position_y, right_fst_position_c; + u32 right_y_crop_total; + u32 right_c_crop_total; + + params->y_w_fac = (stream->crop.width - 1) * 4096 / + (stream->out_fmt.width - 1); + params->c_w_fac = (stream->crop.width / 2 - 1) * 4096 / + (stream->out_fmt.width / 2 - 1); + params->y_h_fac = (stream->crop.height - 1) * 4096 / + (stream->out_fmt.height - 1); + params->c_h_fac = (stream->crop.height - 1) * 4096 / + (stream->out_fmt.height - 1); + + right_fst_position_y = stream->out_fmt.width / 2 * + params->y_w_fac; + right_fst_position_c = stream->out_fmt.width / 2 / 2 * + params->c_w_fac; + + left_in_used_size_y = right_fst_position_y >> 12; + left_in_used_size_c = (right_fst_position_c >> 12) * 2; + + params->y_w_phase = right_fst_position_y & 0xfff; + params->c_w_phase = right_fst_position_c & 0xfff; + + right_scl_need_size_y = stream->crop.width - + left_in_used_size_y; + params->right_scl_need_size_y = right_scl_need_size_y; + right_scl_need_size_c = stream->crop.width - + left_in_used_size_c; + params->right_scl_need_size_c = right_scl_need_size_c; + + if (stream->id == 0 && stream->crop.width != stream->out_fmt.width) { + right_y_crop_total = stream->crop.width / 2 + + RKMOUDLE_UNITE_EXTEND_PIXEL - + right_scl_need_size_y - 3; + right_c_crop_total = stream->crop.width / 2 + + RKMOUDLE_UNITE_EXTEND_PIXEL - + right_scl_need_size_c - 6; + } else { + right_y_crop_total = stream->crop.width / 2 + + RKMOUDLE_UNITE_EXTEND_PIXEL - + right_scl_need_size_y; + right_c_crop_total = stream->crop.width / 2 + + RKMOUDLE_UNITE_EXTEND_PIXEL - + right_scl_need_size_c; + } + + params->quad_crop_w = ALIGN_DOWN(min(right_y_crop_total, right_c_crop_total), 2); + + params->scl_in_crop_w_y = right_y_crop_total - params->quad_crop_w; + params->scl_in_crop_w_c = right_c_crop_total - params->quad_crop_w; + + if (rkvpss_debug >= 2) { + v4l2_info(&stream->dev->v4l2_dev, + "%s ch:%d y_w_fac:%u c_w_fac:%u y_h_fac:%u c_h_fac:%u\n", + __func__, stream->id, params->y_w_fac, + params->c_w_fac, params->y_h_fac, params->c_h_fac); + v4l2_info(&stream->dev->v4l2_dev, + "\t%s y_w_phase:%u c_w_phase:%u quad_crop_w:%u scl_in_crop_w_y:%u scl_in_crop_w_c:%u\n", + __func__, params->y_w_phase, params->c_w_phase, + params->quad_crop_w, + params->scl_in_crop_w_y, params->scl_in_crop_w_c); + v4l2_info(&stream->dev->v4l2_dev, + "\t%s right_scl_need_size_y:%u right_scl_need_size_c:%u\n", + __func__, params->right_scl_need_size_y, + params->right_scl_need_size_c); + } +} + +int rkvpss_stream_buf_cnt_v1(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) +{ + struct rkvpss_device *dev = stream->dev; + + if (stream->is_crop_upd) { + if (dev->unite_mode) + calc_unite_scl_params(stream); + rkvpss_stream_scale(stream, true, !irq); + rkvpss_stream_crop(stream, true, !irq); + } + if (!irq && !stream->curr_buf && + !stream->dev->hw_dev->is_single) + stream->ops->update_mi(stream); +} + +static void scl_force_update(struct rkvpss_stream *stream) +{ + u32 val; + + switch (stream->id) { + case RKVPSS_OUTPUT_CH0: + val = RKVPSS_MI_CHN0_FORCE_UPD; + break; + case RKVPSS_OUTPUT_CH1: + val = RKVPSS_MI_CHN1_FORCE_UPD; + break; + case RKVPSS_OUTPUT_CH2: + val = RKVPSS_MI_CHN2_FORCE_UPD; + break; + case RKVPSS_OUTPUT_CH3: + val = RKVPSS_MI_CHN3_FORCE_UPD; + break; + default: + return; + } + /* need update two for online2 mode */ + rkvpss_unite_write(stream->dev, RKVPSS_MI_WR_INIT, val); + rkvpss_unite_write(stream->dev, RKVPSS_MI_WR_INIT, val); +} + +static void scl_update_mi(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct rkvpss_buffer *buf = NULL; + unsigned long lock_flags = 0; + u32 y_base, uv_base; + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (!list_empty(&stream->buf_queue) && !stream->curr_buf) { + buf = list_first_entry(&stream->buf_queue, struct rkvpss_buffer, queue); + list_del(&buf->queue); + stream->curr_buf = buf; + } + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + if (buf) { + y_base = buf->dma[0]; + uv_base = buf->dma[1]; + rkvpss_idx_write(dev, stream->config->mi.y_base, y_base, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, stream->config->mi.uv_base, uv_base, VPSS_UNITE_LEFT); + if (dev->unite_mode) { + y_base = buf->dma[0] + stream->out_fmt.width / 2; + uv_base = buf->dma[1] + stream->out_fmt.width / 2; + rkvpss_idx_write(dev, stream->config->mi.y_base, y_base, + VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, stream->config->mi.uv_base, uv_base, + VPSS_UNITE_RIGHT); + } + + scl_force_update(stream); + if (stream->is_pause) { + stream->is_pause = false; + stream->ops->enable_mi(stream); + } + } else if (!stream->is_pause) { + stream->is_pause = true; + stream->ops->disable_mi(stream); + } + + v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d unite_index:%d Y:0x%x UV:0x%x | Y_SHD:0x%x\n", + __func__, stream->id, dev->unite_index, + rkvpss_idx_read(dev, stream->config->mi.y_base, dev->unite_index), + rkvpss_idx_read(dev, stream->config->mi.uv_base, dev->unite_index), + rkvpss_hw_read(dev->hw_dev, stream->config->mi.y_shd)); +} + +static void scl_config_mi(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct capture_fmt *fmt = &stream->out_cap_fmt; + struct v4l2_pix_format_mplane *out_fmt = &stream->out_fmt; + u32 reg, val, mask; + + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "%s stream:%d\n", __func__, stream->id); + + val = out_fmt->plane_fmt[0].bytesperline; + reg = stream->config->mi.stride; + rkvpss_unite_write(dev, reg, val); + rkvpss_unite_write(dev, reg, val); + + switch (fmt->fourcc) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + val = out_fmt->plane_fmt[0].bytesperline / 2; + break; + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_XRGB32: + val = out_fmt->plane_fmt[0].bytesperline / 4; + break; + default: + val = out_fmt->plane_fmt[0].bytesperline; + break; + } + + val = val * out_fmt->height; + reg = stream->config->mi.y_pic_size; + rkvpss_unite_write(dev, reg, val); + + val = out_fmt->plane_fmt[0].bytesperline * out_fmt->height; + reg = stream->config->mi.y_size; + rkvpss_unite_write(dev, reg, val); + + val = out_fmt->plane_fmt[1].sizeimage; + reg = stream->config->mi.uv_size; + rkvpss_unite_write(dev, reg, val); + + reg = stream->config->mi.y_offs_cnt; + rkvpss_unite_write(dev, reg, 0); + reg = stream->config->mi.uv_offs_cnt; + rkvpss_unite_write(dev, reg, 0); + + val = fmt->wr_fmt | fmt->output_fmt | fmt->swap | + RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; + reg = stream->config->mi.ctrl; + rkvpss_unite_write(dev, reg, val); + + switch (fmt->fourcc) { + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_VYUY: + mask = RKVPSS_MI_WR_UV_SWAP; + val = RKVPSS_MI_WR_UV_SWAP; + rkvpss_hw_set_bits(dev->hw_dev, RKVPSS_MI_WR_CTRL, mask, val); + break; + case V4L2_PIX_FMT_TILE420: + case V4L2_PIX_FMT_TILE422: + mask = RKVPSS_MI_WR_TILE_SEL(3); + val = RKVPSS_MI_WR_TILE_SEL(stream->id + 1); + rkvpss_hw_set_bits(dev->hw_dev, RKVPSS_MI_WR_CTRL, mask, val); + break; + default: + break; + } + + stream->is_mf_upd = true; + rkvpss_frame_end(stream); +} + +static void scl_enable_mi(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + u32 val, mask; + + switch (stream->id) { + case RKVPSS_OUTPUT_CH0: + val = RKVPSS_ISP2VPSS_CHN0_SEL(2); + mask = RKVPSS_ISP2VPSS_CHN0_SEL(3); + break; + case RKVPSS_OUTPUT_CH1: + val = RKVPSS_ISP2VPSS_CHN1_SEL(2); + mask = RKVPSS_ISP2VPSS_CHN1_SEL(3); + break; + case RKVPSS_OUTPUT_CH2: + val = RKVPSS_ISP2VPSS_CHN2_SEL(2); + mask = RKVPSS_ISP2VPSS_CHN2_SEL(3); + break; + case RKVPSS_OUTPUT_CH3: + val = RKVPSS_ISP2VPSS_CHN3_SEL(2); + mask = RKVPSS_ISP2VPSS_CHN3_SEL(3); + break; + default: + return; + } + val |= RKVPSS_ISP2VPSS_ONLINE2; + if (!dev->hw_dev->is_ofl_cmsc) + val |= RKVPSS_ISP2VPSS_ONLINE2_CMSC_EN; + rkvpss_unite_set_bits(dev, RKVPSS_VPSS_ONLINE, mask, val); + val = RKVPSS_ONLINE2_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD; + rkvpss_unite_write(dev, RKVPSS_VPSS_UPDATE, val); + v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d\n", __func__, + stream->id); +} + +static void scl_disable_mi(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + u32 val; + + switch (stream->id) { + case RKVPSS_OUTPUT_CH0: + val = RKVPSS_ISP2VPSS_CHN0_SEL(3); + break; + case RKVPSS_OUTPUT_CH1: + val = RKVPSS_ISP2VPSS_CHN1_SEL(3); + break; + case RKVPSS_OUTPUT_CH2: + val = RKVPSS_ISP2VPSS_CHN2_SEL(3); + break; + case RKVPSS_OUTPUT_CH3: + val = RKVPSS_ISP2VPSS_CHN3_SEL(3); + break; + default: + return; + } + + rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_ONLINE, val); + val = RKVPSS_ONLINE2_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD; + rkvpss_unite_write(dev, RKVPSS_VPSS_UPDATE, val); + v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d\n", __func__, + stream->id); +} + +static struct streams_ops scl_stream_ops = { + .config_mi = scl_config_mi, + .enable_mi = scl_enable_mi, + .disable_mi = scl_disable_mi, + .update_mi = scl_update_mi, + .frame_start = stream_frame_start, +}; + +static void rkvpss_buf_done_task(unsigned long arg) +{ + struct rkvpss_stream *stream = (struct rkvpss_stream *)arg; + struct rkvpss_vdev_node *node = &stream->vnode; + struct rkvpss_buffer *buf = NULL; + unsigned long lock_flags = 0; + LIST_HEAD(local_list); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + list_replace_init(&stream->buf_done_list, &local_list); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + while (!list_empty(&local_list)) { + buf = list_first_entry(&local_list, struct rkvpss_buffer, queue); + list_del(&buf->queue); + v4l2_dbg(2, rkvpss_debug, &stream->dev->v4l2_dev, + "%s stream:%d index:%d seq:%d buf:0x%x done\n", + node->vdev.name, stream->id, buf->vb.vb2_buf.index, + buf->vb.sequence, buf->dma[0]); + vb2_buffer_done(&buf->vb.vb2_buf, + stream->streaming ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR); + } +} + +static void rkvpss_stream_buf_done(struct rkvpss_stream *stream, + struct rkvpss_buffer *buf) +{ + unsigned long lock_flags = 0; + + if (!stream || !buf) + return; + + v4l2_dbg(3, rkvpss_debug, &stream->dev->v4l2_dev, + "stream:%d\n", stream->id); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + list_add_tail(&buf->queue, &stream->buf_done_list); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + tasklet_schedule(&stream->buf_done_tasklet); +} + +static void rkvpss_fill_frame_info(struct rkvpss_frame_info *dst_info, + struct rkisp_vpss_frame_info *src_info) +{ + dst_info->timestamp = src_info->timestamp; + + dst_info->seq = src_info->seq; + dst_info->hdr = src_info->hdr; + dst_info->rolling_shutter_skew = src_info->rolling_shutter_skew; + + dst_info->sensor_exposure_time = src_info->sensor_exposure_time; + dst_info->sensor_analog_gain = src_info->sensor_analog_gain; + dst_info->sensor_digital_gain = src_info->sensor_digital_gain; + dst_info->isp_digital_gain = src_info->isp_digital_gain; + + dst_info->sensor_exposure_time_m = src_info->sensor_exposure_time_m; + dst_info->sensor_analog_gain_m = src_info->sensor_analog_gain_m; + dst_info->sensor_digital_gain_m = src_info->sensor_digital_gain_m; + dst_info->isp_digital_gain_m = src_info->isp_digital_gain_m; + + dst_info->sensor_exposure_time_l = src_info->sensor_exposure_time_l; + dst_info->sensor_analog_gain_l = src_info->sensor_analog_gain_l; + dst_info->sensor_digital_gain_l = src_info->sensor_digital_gain_l; + dst_info->isp_digital_gain_l = src_info->isp_digital_gain_l; +} + +static void rkvpss_frame_end(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct rkvpss_subdev *sdev = &dev->vpss_sdev; + struct rkvpss_buffer *buf = NULL; + unsigned long lock_flags = 0; + + v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, + "%s stream:%d\n", __func__, stream->id); + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (stream->curr_buf) { + buf = stream->curr_buf; + stream->curr_buf = NULL; + } + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + if (buf) { + struct vb2_buffer *vb2_buf = &buf->vb.vb2_buf; + struct capture_fmt *fmt = &stream->out_cap_fmt; + u64 ns = sdev->frame_timestamp; + int i; + + for (i = 0; i < fmt->mplanes; i++) { + u32 payload_size = stream->out_fmt.plane_fmt[i].sizeimage; + + vb2_set_plane_payload(vb2_buf, i, payload_size); + + if (stream->is_attach_info && i == fmt->mplanes - 1) { + struct rkvpss_frame_info *dst_info = buf->vaddr[i] + payload_size; + struct rkisp_vpss_frame_info *src_info = &dev->frame_info; + + rkvpss_fill_frame_info(dst_info, src_info); + } + } + if (!ns) + 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); + } + + rkvpss_stream_mf(stream); + stream->ops->update_mi(stream); +} + +static int rkvpss_queue_setup(struct vb2_queue *queue, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_ctxs[]) +{ + struct rkvpss_stream *stream = queue->drv_priv; + struct rkvpss_device *dev = stream->dev; + const struct v4l2_pix_format_mplane *pixm = NULL; + const struct capture_fmt *cap_fmt = NULL; + u32 i; + + pixm = &stream->out_fmt; + if (!pixm->width || !pixm->height) + return -EINVAL; + cap_fmt = &stream->out_cap_fmt; + *num_planes = cap_fmt->mplanes; + + for (i = 0; i < cap_fmt->mplanes; i++) { + const struct v4l2_plane_pix_format *plane_fmt; + + plane_fmt = &pixm->plane_fmt[i]; + sizes[i] = plane_fmt->sizeimage / pixm->height * ALIGN(pixm->height, 16); + + if (stream->is_attach_info && i == cap_fmt->mplanes - 1) + sizes[i] += sizeof(struct rkvpss_frame_info); + } + + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s stream:%d count %d, size %d\n", + v4l2_type_names[queue->type], + stream->id, *num_buffers, sizes[0]); + + return 0; +} + +static void rkvpss_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkvpss_buffer *vpssbuf = to_rkvpss_buffer(vbuf); + struct vb2_queue *queue = vb->vb2_queue; + struct rkvpss_stream *stream = queue->drv_priv; + struct rkvpss_device *dev = stream->dev; + struct v4l2_pix_format_mplane *pixm = &stream->out_fmt; + struct capture_fmt *cap_fmt = &stream->out_cap_fmt; + unsigned long lock_flags = 0; + struct sg_table *sgt; + u32 height, size; + int i; + + for (i = 0; i < cap_fmt->mplanes; i++) { + sgt = vb2_dma_sg_plane_desc(vb, i); + vpssbuf->dma[i] = sg_dma_address(sgt->sgl); + + vpssbuf->vaddr[i] = vb2_plane_vaddr(vb, i); + } + /* + * NOTE: plane_fmt[0].sizeimage is total size of all planes for single + * memory plane formats, so calculate the size explicitly. + */ + if (cap_fmt->mplanes == 1) { + for (i = 0; i < cap_fmt->cplanes - 1; i++) { + height = pixm->height; + size = (i == 0) ? + pixm->plane_fmt[i].bytesperline * height : + pixm->plane_fmt[i].sizeimage; + vpssbuf->dma[i + 1] = vpssbuf->dma[i] + size; + } + } + + v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev, + "%s stream:%d index:%d buf:0x%x\n", __func__, + stream->id, vb->index, vpssbuf->dma[0]); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + list_add_tail(&vpssbuf->queue, &stream->buf_queue); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + if (dev->hw_dev->is_single && + stream->streaming && !stream->curr_buf) + stream->ops->update_mi(stream); +} + +static void destroy_buf_queue(struct rkvpss_stream *stream, + enum vb2_buffer_state state) +{ + unsigned long lock_flags = 0; + struct rkvpss_buffer *buf; + LIST_HEAD(queue_local_list); + LIST_HEAD(done_local_list); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (stream->curr_buf) { + list_add_tail(&stream->curr_buf->queue, &stream->buf_queue); + stream->curr_buf = NULL; + } + list_replace_init(&stream->buf_queue, &queue_local_list); + list_replace_init(&stream->buf_done_list, &done_local_list); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + while (!list_empty(&queue_local_list)) { + buf = list_first_entry(&queue_local_list, struct rkvpss_buffer, queue); + list_del(&buf->queue); + buf->vb.vb2_buf.synced = false; + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + while (!list_empty(&done_local_list)) { + buf = list_first_entry(&done_local_list, struct rkvpss_buffer, queue); + list_del(&buf->queue); + buf->vb.vb2_buf.synced = false; + vb2_buffer_done(&buf->vb.vb2_buf, state); + } +} + +static int rkvpss_stream_crop(struct rkvpss_stream *stream, bool on, bool sync) +{ + struct rkvpss_device *dev = stream->dev; + struct v4l2_rect *crop = &stream->crop; + u32 reg_ctrl = stream->config->crop.ctrl; + u32 reg_upd = stream->config->crop.update; + u32 reg_h_offs = stream->config->crop.h_offs; + u32 reg_v_offs = stream->config->crop.v_offs; + u32 reg_h_size = stream->config->crop.h_size; + u32 reg_v_size = stream->config->crop.v_size; + u32 val; + + val = RKVPSS_CROP_CHN_EN(stream->id); + if (!dev->unite_mode) { + if (on) { + rkvpss_unite_set_bits(dev, reg_ctrl, 0, val); + rkvpss_unite_write(dev, reg_h_offs, crop->left); + rkvpss_unite_write(dev, reg_v_offs, crop->top); + rkvpss_unite_write(dev, reg_h_size, crop->width); + rkvpss_unite_write(dev, reg_v_size, crop->height); + + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "crop left:%d top:%d w:%d h:%d\n", + crop->left, crop->top, crop->width, crop->height); + } else { + rkvpss_unite_clear_bits(dev, reg_ctrl, val); + } + if (sync) { + val = RKVPSS_CROP_CHN_FORCE_UPD(stream->id); + val |= RKVPSS_CROP_GEN_UPD; + rkvpss_unite_write(dev, reg_upd, val); + rkvpss_unite_write(dev, reg_upd, val); + } + } else { + if (on) { + if (crop->left + crop->width / 2 != dev->vpss_sdev.in_fmt.width / 2) { + v4l2_err(&dev->v4l2_dev, + "unite need centered crop left:%d top:%d w:%d h:%d\n", + crop->left, crop->top, crop->width, crop->height); + return -EINVAL; + } + /* unite left */ + rkvpss_idx_set_bits(dev, reg_ctrl, 0, val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, reg_h_offs, crop->left, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, reg_v_offs, crop->top, VPSS_UNITE_LEFT); + /* if no scale left don't enlarge */ + if (crop->width == stream->out_fmt.width) + rkvpss_idx_write(dev, reg_h_size, crop->width / 2, + VPSS_UNITE_LEFT); + else + rkvpss_idx_write(dev, reg_h_size, crop->width / 2 + + RKMOUDLE_UNITE_EXTEND_PIXEL, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, reg_v_size, crop->height, VPSS_UNITE_LEFT); + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "left crop left:%d top:%d w:%d h:%d\n", + rkvpss_idx_read(dev, reg_h_offs, VPSS_UNITE_LEFT), + rkvpss_idx_read(dev, reg_v_offs, VPSS_UNITE_LEFT), + rkvpss_idx_read(dev, reg_h_size, VPSS_UNITE_LEFT), + rkvpss_idx_read(dev, reg_v_size, VPSS_UNITE_LEFT)); + + /* unite right */ + rkvpss_idx_set_bits(dev, reg_ctrl, 0, val, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, reg_h_offs, stream->unite_params.quad_crop_w, + VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, reg_v_offs, crop->top, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, reg_h_size, crop->width / 2 + + RKMOUDLE_UNITE_EXTEND_PIXEL - + stream->unite_params.quad_crop_w, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, reg_v_size, crop->height, VPSS_UNITE_RIGHT); + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "right crop left:%d top:%d w:%d h:%d\n", + rkvpss_idx_read(dev, reg_h_offs, VPSS_UNITE_RIGHT), + rkvpss_idx_read(dev, reg_v_offs, VPSS_UNITE_RIGHT), + rkvpss_idx_read(dev, reg_h_size, VPSS_UNITE_RIGHT), + rkvpss_idx_read(dev, reg_v_size, VPSS_UNITE_RIGHT)); + } else { + rkvpss_idx_clear_bits(dev, reg_ctrl, val, VPSS_UNITE_LEFT); + rkvpss_idx_clear_bits(dev, reg_ctrl, val, VPSS_UNITE_RIGHT); + } + if (sync) { + val = RKVPSS_CROP_CHN_FORCE_UPD(stream->id); + val |= RKVPSS_CROP_GEN_UPD; + rkvpss_idx_write(dev, reg_upd, val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, reg_upd, val, VPSS_UNITE_RIGHT); + } + } + stream->is_crop_upd = false; + return 0; +} + +static void poly_phase_scale(struct rkvpss_stream *stream, bool on, bool sync) +{ + struct rkvpss_device *dev = stream->dev; + u32 out_w = stream->out_fmt.width; + u32 out_h = stream->out_fmt.height; + u32 in_w = stream->crop.width; + u32 in_h = stream->crop.height; + u32 ctrl, y_xscl_fac, y_yscl_fac, uv_xscl_fac, uv_yscl_fac; + u32 i, j, idx, ratio, val, in_div, out_div, factor; + bool dering_en = false, yuv420_in = false, yuv422_to_420 = false; + + if (!on) { + rkvpss_unite_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0); + if (dev->unite_mode) { + rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0, VPSS_UNITE_RIGHT); + } + return; + } + + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d in_w:%d in_h:%d out_w:%d out_h:%d\n", __func__, + stream->id, in_w, in_h, out_w, out_h); + + /* constraints */ + if (((out_w >= in_w) && (in_w * 8 < out_w)) || + ((in_w > out_w) && (out_w * 8 < in_w)) || + ((out_h >= in_h) && (in_h * 6 < out_h)) || + ((in_h > out_h) && (out_h * 6 < in_h))) { + v4l2_err(&dev->v4l2_dev, "stream:%d scale size error\n", stream->id); + return; + } + + /*config scl clk gate*/ + if (in_w == out_w && in_h == out_h) + rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS); + else + rkvpss_unite_set_bits(dev, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS, + RKVPSS_SCL0_CKG_DIS); + + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + if (dev->unite_mode) { + /* unite left */ + if (in_w == out_w) + val = (in_w / 2 - 1) | ((in_h - 1) << 16); + else + val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL - 1) | + ((in_h - 1) << 16); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val, VPSS_UNITE_LEFT); + + /* unite right */ + val = (ALIGN(stream->unite_params.right_scl_need_size_y + 3, 2) - 1) | + ((in_h - 1) << 16); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val, VPSS_UNITE_RIGHT); + val = (ALIGN(stream->unite_params.right_scl_need_size_c + 6, 2) - 1) | + ((in_h - 1) << 16); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val, VPSS_UNITE_RIGHT); + + val = (out_w / 2 - 1) | ((out_h - 1) << 16); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_DST_SIZE, val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_DST_SIZE, val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_DST_SIZE, val, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_DST_SIZE, val, VPSS_UNITE_RIGHT); + } else { + val = (in_w - 1) | ((in_h - 1) << 16); + rkvpss_unite_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val); + val = (out_w - 1) | ((out_h - 1) << 16); + rkvpss_unite_write(dev, RKVPSS_ZME_Y_DST_SIZE, val); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_DST_SIZE, val); + } + + ctrl = RKVPSS_ZME_XSCL_MODE | RKVPSS_ZME_YSCL_MODE; + if (dering_en) { + ctrl |= RKVPSS_ZME_DERING_EN; + rkvpss_unite_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410); + + if (dev->unite_mode) { + rkvpss_idx_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410, + VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410, + VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410, + VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410, + VPSS_UNITE_RIGHT); + } + } + + if (in_w != out_w) { + if (in_w > out_w) { + factor = 4096; + ctrl |= RKVPSS_ZME_XSD_EN; + } else { + factor = 65536; + ctrl |= RKVPSS_ZME_XSU_EN; + } + y_xscl_fac = (in_w - 1) * factor / (out_w - 1); + uv_xscl_fac = (in_w / 2 - 1) * factor / (out_w / 2 - 1); + + ratio = y_xscl_fac * 10000 / factor; + idx = rkvpss_get_zme_tap_coe_index(ratio); + for (i = 0; i < 17; i++) { + for (j = 0; j < 8; j += 2) { + val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap8_coe[idx][i][j], + rkvpss_zme_tap8_coe[idx][i][j + 1]); + rkvpss_unite_write(dev, + RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, + val); + rkvpss_unite_write(dev, + RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, + val); + + if (dev->unite_mode) { + rkvpss_idx_write(dev, + RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, + RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, + RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, + RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_RIGHT); + } + } + } + } else { + y_xscl_fac = 0; + uv_xscl_fac = 0; + } + + if (dev->unite_mode) { + /* unite left */ + rkvpss_idx_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac, VPSS_UNITE_LEFT); + + /* unite right */ + val = y_xscl_fac | (stream->unite_params.y_w_phase << 16); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, val, VPSS_UNITE_RIGHT); + val = uv_xscl_fac | (stream->unite_params.c_w_phase << 16); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, val, VPSS_UNITE_RIGHT); + } else { + rkvpss_unite_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac); + } + + if (in_h != out_h) { + if (in_h > out_h) { + factor = 4096; + ctrl |= RKVPSS_ZME_YSD_EN; + } else { + factor = 65536; + ctrl |= RKVPSS_ZME_YSU_EN; + } + y_yscl_fac = (in_h - 1) * factor / (out_h - 1); + uv_yscl_fac = (in_h / in_div - 1) * factor / (out_h / out_div - 1); + + ratio = y_yscl_fac * 10000 / factor; + idx = rkvpss_get_zme_tap_coe_index(ratio); + for (i = 0; i < 17; i++) { + for (j = 0; j < 8; j += 2) { + val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap6_coe[idx][i][j], + rkvpss_zme_tap6_coe[idx][i][j + 1]); + rkvpss_unite_write(dev, + RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, + val); + rkvpss_unite_write(dev, + RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, + val); + + if (dev->unite_mode) { + rkvpss_idx_write(dev, + RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, + RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, + RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, + RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, + val, VPSS_UNITE_RIGHT); + } + } + } + } else { + y_yscl_fac = 0; + uv_yscl_fac = 0; + } + + if (dev->unite_mode) { + /* unite left */ + rkvpss_idx_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl, VPSS_UNITE_LEFT); + + /* unite right */ + rkvpss_idx_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl, VPSS_UNITE_RIGHT); + } else { + rkvpss_unite_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac); + rkvpss_unite_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl); + rkvpss_unite_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl); + } + + if (dev->unite_mode) { + /* unite left */ + val = out_w / 2; + rkvpss_idx_write(dev, RKVPSS_ZME_H_SIZE, (val << 16) | val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, 0, VPSS_UNITE_LEFT); + + /* unite right */ + rkvpss_idx_write(dev, RKVPSS_ZME_H_SIZE, (val << 16) | val, VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, 0, VPSS_UNITE_RIGHT); + val = out_w / 2 - ALIGN_DOWN(out_w / 2, 16); + rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, (3 << 20) | (3 << 16) | + (stream->unite_params.scl_in_crop_w_y << 8) | + (stream->unite_params.scl_in_crop_w_c << 12) | + (val << 4) | val, VPSS_UNITE_RIGHT); + } + + ctrl = RKVPSS_ZME_GATING_EN; + if (yuv420_in) + ctrl |= RKVPSS_ZME_SCL_YUV420_REAL_EN; + if (yuv422_to_420) + ctrl |= RKVPSS_ZME_422TO420_EN; + if (dev->unite_mode) { + /* unite left */ + ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_8K_EN; + rkvpss_idx_write(dev, RKVPSS_ZME_CTRL, ctrl, VPSS_UNITE_LEFT); + + /* unite left */ + ctrl |= RKVPSS_ZME_IN_CLIP_EN; + rkvpss_idx_write(dev, RKVPSS_ZME_CTRL, ctrl, VPSS_UNITE_RIGHT); + } else { + rkvpss_unite_write(dev, RKVPSS_ZME_CTRL, ctrl); + } + + val = RKVPSS_ZME_GEN_UPD; + if (sync) + val |= RKVPSS_ZME_FORCE_UPD; + rkvpss_unite_write(dev, RKVPSS_ZME_UPDATE, val); + if (dev->unite_mode) { + rkvpss_idx_write(dev, RKVPSS_ZME_UPDATE, val, VPSS_UNITE_LEFT); + rkvpss_idx_write(dev, RKVPSS_ZME_UPDATE, val, VPSS_UNITE_RIGHT); + } +} + +static void bilinear_scale(struct rkvpss_stream *stream, bool on, bool sync) +{ + struct rkvpss_device *dev = stream->dev; + u32 out_w = stream->out_fmt.width; + u32 out_h = stream->out_fmt.height; + u32 in_w = stream->crop.width; + u32 in_h = stream->crop.height; + u32 in_div, out_div; + u32 reg, val, ctrl = 0, clk_mask = 0; + bool yuv420_in = false, yuv422_to_420 = false; + + if (!on) { + reg = stream->config->scale.ctrl; + rkvpss_unite_write(dev, reg, 0); + return; + } + + /*config scl clk gate*/ + switch (stream->id) { + case RKVPSS_OUTPUT_CH1: + clk_mask = RKVPSS_SCL1_CKG_DIS; + break; + case RKVPSS_OUTPUT_CH2: + clk_mask = RKVPSS_SCL2_CKG_DIS; + break; + case RKVPSS_OUTPUT_CH3: + clk_mask = RKVPSS_SCL3_CKG_DIS; + break; + default: + return; + } + if (in_w == out_w && in_h == out_h) + rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_CLK_GATE, clk_mask); + else + rkvpss_unite_set_bits(dev, RKVPSS_VPSS_CLK_GATE, clk_mask, clk_mask); + + if (!dev->unite_mode) { + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + reg = stream->config->scale.hy_offs; + rkvpss_unite_write(dev, reg, 0); + reg = stream->config->scale.hc_offs; + rkvpss_unite_write(dev, reg, 0); + reg = stream->config->scale.vy_offs; + rkvpss_unite_write(dev, reg, 0); + reg = stream->config->scale.vc_offs; + rkvpss_unite_write(dev, reg, 0); + + val = in_w | (in_h << 16); + reg = stream->config->scale.src_size; + rkvpss_unite_write(dev, reg, val); + + /* ch2 ch3 scale out width exceed 1920 */ + if ((stream->id >= RKVPSS_OUTPUT_CH2) && + (in_w != out_w) && (out_w > 1920)) + v4l2_err(&dev->v4l2_dev, + "stream:%d scale out width exceed 1920\n", + stream->id); + + val = out_w | (out_h << 16); + reg = stream->config->scale.dst_size; + rkvpss_unite_write(dev, reg, val); + + if (in_w != out_w || !sync) { + val = (in_w - 1) * 4096 / (out_w - 1); + reg = stream->config->scale.hy_fac; + rkvpss_unite_write(dev, reg, val); + + val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1); + reg = stream->config->scale.hc_fac; + rkvpss_unite_write(dev, reg, val); + + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + if (in_h != out_h || !sync) { + val = (in_h - 1) * 4096 / (out_h - 1); + reg = stream->config->scale.vy_fac; + rkvpss_unite_write(dev, reg, val); + + val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1); + reg = stream->config->scale.vc_fac; + rkvpss_unite_write(dev, reg, val); + + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } + + reg = stream->config->scale.ctrl; + rkvpss_unite_write(dev, reg, ctrl); + + val = RKVPSS_SCL_GEN_UPD; + if (sync) + val |= RKVPSS_SCL_FORCE_UPD; + reg = stream->config->scale.update; + rkvpss_unite_write(dev, reg, val); + } else { + /* unite left */ + reg = stream->config->scale.in_crop_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + + reg = stream->config->scale.hy_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + reg = stream->config->scale.hc_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + reg = stream->config->scale.vy_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + reg = stream->config->scale.vc_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + + reg = stream->config->scale.hy_offs_mi; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + reg = stream->config->scale.hc_offs_mi; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT); + + if (in_w == out_w) + val = (in_w / 2) | (in_h << 16); + else + val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL) | (in_h << 16); + reg = stream->config->scale.src_size; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT); + + /* ch2 ch3 scale out width exceed 1920 */ + if ((stream->id >= RKVPSS_OUTPUT_CH2) && + (in_w != out_w) && (out_w / 2 > 1920)) + v4l2_err(&dev->v4l2_dev, + "stream:%d scale out width exceed 1920\n", + stream->id); + + val = (out_w / 2) | (out_h << 16); + reg = stream->config->scale.dst_size; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT); + + ctrl |= RKVPSS_SCL_CLIP_EN; + + if (in_w != out_w || !sync) { + reg = stream->config->scale.hy_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.y_w_fac, VPSS_UNITE_LEFT); + reg = stream->config->scale.hc_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.c_w_fac, VPSS_UNITE_LEFT); + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + + if (in_h != out_h || !sync) { + reg = stream->config->scale.vy_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.y_h_fac, VPSS_UNITE_LEFT); + reg = stream->config->scale.vc_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.c_h_fac, VPSS_UNITE_LEFT); + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } + + reg = stream->config->scale.ctrl; + rkvpss_idx_write(dev, reg, ctrl, VPSS_UNITE_LEFT); + + val = RKVPSS_SCL_GEN_UPD; + if (sync) + val |= RKVPSS_SCL_FORCE_UPD; + reg = stream->config->scale.update; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT); + + /* unite right */ + ctrl = 0; + val = stream->unite_params.scl_in_crop_w_y | + (stream->unite_params.scl_in_crop_w_c << 4); + reg = stream->config->scale.in_crop_offs; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + + val = stream->unite_params.y_w_phase; + reg = stream->config->scale.hy_offs; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + val = stream->unite_params.c_w_phase; + reg = stream->config->scale.hc_offs; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + reg = stream->config->scale.vy_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_RIGHT); + reg = stream->config->scale.vc_offs; + rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_RIGHT); + + val = out_w / 2 - ALIGN_DOWN(out_w / 2, 16); + reg = stream->config->scale.hy_offs_mi; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + reg = stream->config->scale.hc_offs_mi; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + + val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL) | (in_h << 16); + reg = stream->config->scale.src_size; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + + /* ch2 ch3 scale out width exceed 1920 */ + if ((stream->id >= RKVPSS_OUTPUT_CH2) && + (in_w != out_w) && (out_w / 2 > 1920)) + v4l2_err(&dev->v4l2_dev, + "stream:%d scale out width exceed 1920\n", + stream->id); + + val = (out_w / 2) | (out_h << 16); + reg = stream->config->scale.dst_size; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + + ctrl |= RKVPSS_SCL_CLIP_EN | RKVPSS_SCL_IN_CLIP_EN; + + if (in_w != out_w || !sync) { + reg = stream->config->scale.hy_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.y_w_fac, VPSS_UNITE_RIGHT); + reg = stream->config->scale.hc_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.c_w_fac, VPSS_UNITE_RIGHT); + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + + if (in_h != out_h || !sync) { + reg = stream->config->scale.vy_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.y_h_fac, VPSS_UNITE_RIGHT); + reg = stream->config->scale.vc_fac; + rkvpss_idx_write(dev, reg, stream->unite_params.c_h_fac, VPSS_UNITE_RIGHT); + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } + + reg = stream->config->scale.ctrl; + rkvpss_idx_write(dev, reg, ctrl, VPSS_UNITE_RIGHT); + + val = RKVPSS_SCL_GEN_UPD; + if (sync) + val |= RKVPSS_SCL_FORCE_UPD; + reg = stream->config->scale.update; + rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT); + } +} + +static int rkvpss_stream_scale(struct rkvpss_stream *stream, bool on, bool sync) +{ + if (stream->id == RKVPSS_OUTPUT_CH0) + poly_phase_scale(stream, on, sync); + else + bilinear_scale(stream, on, sync); + return 0; +} + +static void rkvpss_stream_stop(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + int ret; + + stream->stopping = true; + if (atomic_read(&dev->pipe_stream_cnt) > 0) { + ret = wait_event_timeout(stream->done, !stream->streaming, + msecs_to_jiffies(300)); + if (!ret) + v4l2_warn(&dev->v4l2_dev, "%s id:%d timeout\n", __func__, stream->id); + } + stream->stopping = false; + stream->streaming = false; + if (stream->ops->disable_mi) + stream->ops->disable_mi(stream); + rkvpss_stream_crop(stream, false, true); + rkvpss_stream_scale(stream, false, true); + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d\n", __func__, + stream->id); +} + +static void rkvpss_stop_streaming(struct vb2_queue *queue) +{ + struct rkvpss_stream *stream = queue->drv_priv; + struct rkvpss_vdev_node *node = &stream->vnode; + struct rkvpss_device *dev = stream->dev; + struct rkvpss_hw_dev *hw = dev->hw_dev; + + if (!stream->streaming) + return; + + mutex_lock(&hw->dev_lock); + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s %s id:%d enter\n", __func__, + node->vdev.name, stream->id); + + if (atomic_read(&dev->pipe_stream_cnt) == 1) { + rkvpss_pipeline_stream(dev, false); + rkvpss_stream_stop(stream); + } else { + rkvpss_stream_stop(stream); + rkvpss_pipeline_stream(dev, false); + } + destroy_buf_queue(stream, VB2_BUF_STATE_ERROR); + rkvpss_pipeline_close(dev); + tasklet_disable(&stream->buf_done_tasklet); + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s %s id:%d exit\n", __func__, + node->vdev.name, stream->id); + mutex_unlock(&hw->dev_lock); +} + +static int check_wr_uvswap(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct rkvpss_stream *check_stream; + struct capture_fmt *fmt; + bool wr_uv_swap = false; + int i, ret = 0; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + check_stream = &dev->stream_vdev.stream[i]; + if (check_stream->streaming) { + fmt = &check_stream->out_cap_fmt; + switch (fmt->fourcc) { + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_VYUY: + wr_uv_swap = true; + break; + default: + break; + } + } + } + if (wr_uv_swap) { + switch (stream->out_cap_fmt.fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_UYVY: + v4l2_err(&dev->v4l2_dev, "wr_uv_swap need to be consistent\n"); + ret = -EINVAL; + break; + default: + break; + } + } + + return ret; +} + +static int rkvpss_stream_start(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + int ret = 0; + + if (dev->unite_mode) + calc_unite_scl_params(stream); + + stream->is_crop_upd = true; + ret = rkvpss_stream_scale(stream, true, true); + if (ret < 0) + return ret; + ret = rkvpss_stream_crop(stream, true, true); + if (ret < 0) + return ret; + ret = check_wr_uvswap(stream); + if (ret < 0) + return ret; + + stream->is_pause = true; + + if (stream->ops->config_mi) + stream->ops->config_mi(stream); + + stream->streaming = true; + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s id:%d enter\n", __func__, + stream->id); + return ret; +} + +static int rkvpss_start_streaming(struct vb2_queue *queue, unsigned int count) +{ + struct rkvpss_stream *stream = queue->drv_priv; + struct rkvpss_vdev_node *node = &stream->vnode; + struct rkvpss_device *dev = stream->dev; + struct rkvpss_hw_dev *hw = dev->hw_dev; + int ret = -EINVAL; + + if (stream->streaming) + return -EBUSY; + + mutex_lock(&hw->dev_lock); + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s %s id:%d enter\n", __func__, + node->vdev.name, stream->id); + + if (!dev->inp || !stream->linked) { + v4l2_err(&dev->v4l2_dev, "no link or invalid input source\n"); + goto free_buf_queue; + } + + if (hw->is_ofl_ch[stream->id]) { + v4l2_err(&dev->v4l2_dev, "channel[%d] already assigned to offline", stream->id); + goto free_buf_queue; + } + + rkvpss_pipeline_open(dev); + + ret = rkvpss_stream_start(stream); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "start %s failed\n", node->vdev.name); + goto pipe_close; + } + + ret = rkvpss_pipeline_stream(dev, true); + if (ret < 0) + goto stop_stream; + + tasklet_enable(&stream->buf_done_tasklet); + stream->dbg.id = -1; + stream->dbg.frameloss = 0; + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s %s id:%d exit\n", __func__, + node->vdev.name, stream->id); + mutex_unlock(&hw->dev_lock); + return 0; +stop_stream: + stream->streaming = false; + rkvpss_stream_stop(stream); +pipe_close: + rkvpss_pipeline_close(dev); +free_buf_queue: + destroy_buf_queue(stream, VB2_BUF_STATE_QUEUED); + mutex_unlock(&hw->dev_lock); + return ret; +} + +static const struct vb2_ops stream_vb2_ops = { + .queue_setup = rkvpss_queue_setup, + .buf_queue = rkvpss_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .stop_streaming = rkvpss_stop_streaming, + .start_streaming = rkvpss_start_streaming, +}; + +static int rkvpss_init_vb2_queue(struct vb2_queue *q, + struct rkvpss_stream *stream, + enum v4l2_buf_type buf_type) +{ + q->type = buf_type; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; + q->drv_priv = stream; + q->ops = &stream_vb2_ops; + q->mem_ops = stream->dev->hw_dev->mem_ops; + q->buf_struct_size = sizeof(struct rkvpss_buffer); + q->min_buffers_needed = STREAM_OUT_REQ_BUFS_MIN; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + q->lock = &stream->dev->apilock; + q->dev = stream->dev->hw_dev->dev; + q->allow_cache_hints = 1; + q->bidirectional = 1; + if (stream->dev->hw_dev->is_dma_contig) + q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS; + q->gfp_flags = GFP_DMA32; + return vb2_queue_init(q); +} + +static const +struct capture_fmt *find_fmt(struct rkvpss_stream *stream, u32 pixelfmt) +{ + const struct capture_fmt *fmt; + u32 i; + + for (i = 0; i < stream->config->fmt_size; i++) { + fmt = &stream->config->fmts[i]; + if (fmt->fourcc == pixelfmt) + return fmt; + } + return NULL; +} + +static int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs) +{ + switch (fcc) { + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_YUV444M: + *xsubs = 1; + *ysubs = 1; + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_YVU422M: + case V4L2_PIX_FMT_FBC2: + *xsubs = 2; + *ysubs = 1; + break; + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21M: + case V4L2_PIX_FMT_NV12M: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_FBC0: + *xsubs = 2; + *ysubs = 2; + break; + default: + return -EINVAL; + } + return 0; +} + +static int rkvpss_set_fmt(struct rkvpss_stream *stream, + struct v4l2_pix_format_mplane *pixm, + bool try) +{ + struct rkvpss_device *dev = stream->dev; + u32 i, planes, xsubs = 1, ysubs = 1, imagsize = 0; + const struct capture_fmt *fmt; + + fmt = find_fmt(stream, pixm->pixelformat); + if (!fmt) { + v4l2_err(&dev->v4l2_dev, + "nonsupport pixelformat:%c%c%c%c\n", + pixm->pixelformat, + pixm->pixelformat >> 8, + pixm->pixelformat >> 16, + pixm->pixelformat >> 24); + return -EINVAL; + } + + /* Tile4x4 writing of Channel0 and Channel1 only supports either one.*/ + if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) { + if ((stream->id == 0) && + dev->stream_vdev.stream[1].streaming && + (dev->stream_vdev.stream[1].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 || + dev->stream_vdev.stream[1].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422)) { + v4l2_err(&dev->v4l2_dev, + "Tile4x4 writing of Ch0 and Cl1 only supports either one\n"); + return -EINVAL; + } + if ((stream->id == 1) && + dev->stream_vdev.stream[0].streaming && + (dev->stream_vdev.stream[0].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 || + dev->stream_vdev.stream[0].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422)) { + v4l2_err(&dev->v4l2_dev, + "Tile4x4 writing of Ch0 and Cl1 only supports either one\n"); + return -EINVAL; + } + } + + pixm->num_planes = fmt->mplanes; + pixm->field = V4L2_FIELD_NONE; + if (!pixm->quantization) + pixm->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fcc_xysubs(fmt->fourcc, &xsubs, &ysubs); + planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes; + for (i = 0; i < planes; i++) { + struct v4l2_plane_pix_format *plane_fmt; + u32 width, height, bytesperline, w, h; + + plane_fmt = pixm->plane_fmt + i; + w = pixm->width; + h = pixm->height; + width = i ? w / xsubs : w; + height = i ? h / ysubs : h; + + if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) + bytesperline = ALIGN(((width / 4) * fmt->bpp[i]), 16); + else + bytesperline = width * DIV_ROUND_UP(fmt->bpp[i], 8); + + if (i != 0 || plane_fmt->bytesperline < bytesperline) + plane_fmt->bytesperline = bytesperline; + + if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) + plane_fmt->sizeimage = plane_fmt->bytesperline * (height / 4); + else + plane_fmt->sizeimage = plane_fmt->bytesperline * height; + + imagsize += plane_fmt->sizeimage; + } + if (fmt->mplanes == 1) + pixm->plane_fmt[0].sizeimage = imagsize; + if (!try) { + stream->out_cap_fmt = *fmt; + stream->out_fmt = *pixm; + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "%s: stream:%d req(%d, %d) out(%d, %d)\n", + __func__, stream->id, pixm->width, pixm->height, + stream->out_fmt.width, stream->out_fmt.height); + } + return 0; +} + +/************************* v4l2_file_operations***************************/ + +static int rkvpss_fh_open(struct file *file) +{ + struct rkvpss_stream *stream = video_drvdata(file); + struct rkvpss_device *dev = stream->dev; + int ret; + + if (!dev->is_probe_end) + return -EINVAL; + + ret = v4l2_fh_open(file); + if (!ret) { + ret = v4l2_pipeline_pm_get(&stream->vnode.vdev.entity); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, + "pipeline power on failed %d\n", ret); + vb2_fop_release(file); + } + } + return ret; +} + +static int rkvpss_fh_release(struct file *file) +{ + struct rkvpss_stream *stream = video_drvdata(file); + int ret; + + ret = vb2_fop_release(file); + if (!ret) + v4l2_pipeline_pm_put(&stream->vnode.vdev.entity); + return ret; +} + +static const struct v4l2_file_operations rkvpss_fops = { + .open = rkvpss_fh_open, + .release = rkvpss_fh_release, + .unlocked_ioctl = video_ioctl2, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = video_ioctl2, +#endif +}; + +static int rkvpss_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + if (input->index > 0) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(input->name, "Camera", sizeof(input->name)); + + return 0; +} + +static int rkvpss_try_fmt_vid_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rkvpss_stream *stream = video_drvdata(file); + + return rkvpss_set_fmt(stream, &f->fmt.pix_mp, true); +} + +static int rkvpss_enum_fmt_vid_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct rkvpss_stream *stream = video_drvdata(file); + const struct capture_fmt *fmt = NULL; + + if (f->index >= stream->config->fmt_size) + return -EINVAL; + + fmt = &stream->config->fmts[f->index]; + f->pixelformat = fmt->fourcc; + + switch (f->pixelformat) { + case V4L2_PIX_FMT_TILE420: + strscpy(f->description, + "Rockchip yuv420 tile", + sizeof(f->description)); + break; + case V4L2_PIX_FMT_TILE422: + strscpy(f->description, + "Rockchip yuv422 tile", + sizeof(f->description)); + break; + default: + break; + } + + return 0; +} + +static int rkvpss_s_fmt_vid_mplane(struct file *file, + void *priv, struct v4l2_format *f) +{ + struct rkvpss_stream *stream = video_drvdata(file); + struct video_device *vdev = &stream->vnode.vdev; + struct rkvpss_vdev_node *node = vdev_to_node(vdev); + struct rkvpss_device *dev = stream->dev; + + /* Change not allowed if queue is streaming. */ + if (vb2_is_streaming(&node->buf_queue)) { + v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + return rkvpss_set_fmt(stream, &f->fmt.pix_mp, false); +} + +static int rkvpss_g_fmt_vid_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rkvpss_stream *stream = video_drvdata(file); + + f->fmt.pix_mp = stream->out_fmt; + + return 0; +} + +static int rkvpss_g_selection(struct file *file, void *prv, + struct v4l2_selection *sel) +{ + struct rkvpss_stream *stream = video_drvdata(file); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = stream->crop; + break; + default: + return -EINVAL; + } + return 0; +} + +static int rkvpss_s_selection(struct file *file, void *prv, + struct v4l2_selection *sel) +{ + struct rkvpss_stream *stream = video_drvdata(file); + struct rkvpss_device *dev = stream->dev; + struct v4l2_rect *crop = &stream->crop; + u32 max_w = dev->vpss_sdev.in_fmt.width; + u32 max_h = dev->vpss_sdev.in_fmt.height; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + if (sel->flags != 0) + return -EINVAL; + + sel->r.left = ALIGN(sel->r.left, 2); + sel->r.top = ALIGN(sel->r.top, 2); + sel->r.width = ALIGN(sel->r.width, 2); + sel->r.height = ALIGN(sel->r.height, 2); + if (!sel->r.width || !sel->r.height || + sel->r.width + sel->r.left > max_w || + sel->r.height + sel->r.top > max_h) { + v4l2_err(&dev->v4l2_dev, + "invalid crop left:%d top:%d w:%d h:%d for %dx%d\n", + sel->r.left, sel->r.top, sel->r.width, sel->r.height, max_w, max_h); + return -EINVAL; + } + + *crop = sel->r; + stream->is_crop_upd = true; + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, + "stream %d crop(%d,%d)/%dx%d\n", stream->id, + crop->left, crop->top, crop->width, crop->height); + return 0; +} + +static int rkvpss_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct rkvpss_stream *stream = video_drvdata(file); + struct device *dev = stream->dev->dev; + struct video_device *vdev = video_devdata(file); + + strscpy(cap->card, vdev->name, sizeof(cap->card)); + snprintf(cap->driver, sizeof(cap->driver), + "%s_v%d", dev->driver->name, + stream->dev->vpss_ver >> 4); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(dev)); + + return 0; +} + +static void rkvpss_stream_mf(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + u32 val, mask; + + if (!stream->is_mf_upd) + return; + stream->is_mf_upd = false; + + if (!dev->hw_dev->is_ofl_cmsc) { + mask = RKVPSS_MIR_EN; + val = dev->mir_en ? RKVPSS_MIR_EN : 0; + rkvpss_unite_set_bits(dev, RKVPSS_VPSS_CTRL, mask, val); + } + mask = RKVPSS_MI_CHN_V_FLIP(stream->id); + /* Tile4x4 writing can't flip*/ + if (stream->out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 || + stream->out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422) + stream->flip_en = false; + val = stream->flip_en ? mask : 0; + rkvpss_unite_set_bits(dev, RKVPSS_MI_WR_VFLIP_CTRL, mask, val); +} + +static int rkvpss_get_mirror_flip(struct rkvpss_stream *stream, + struct rkvpss_mirror_flip *cfg) +{ + cfg->mirror = stream->dev->mir_en; + cfg->flip = stream->flip_en; + return 0; +} + +static int rkvpss_set_mirror_flip(struct rkvpss_stream *stream, + struct rkvpss_mirror_flip *cfg) +{ + struct rkvpss_device *dev = stream->dev; + + if (dev->hw_dev->is_ofl_cmsc && cfg->mirror) { + cfg->mirror = 0; + v4l2_warn(&stream->dev->v4l2_dev, + "mirror mux to vpss offline mode\n"); + } + if (dev->mir_en != !!cfg->mirror || + stream->flip_en != !!cfg->flip) + stream->is_mf_upd = true; + dev->mir_en = !!cfg->mirror; + stream->flip_en = !!cfg->flip; + return 0; +} + +static void cmsc_config_hw(struct rkvpss_device *dev, struct rkvpss_cmsc_cfg *cfg, bool sync, + int unite_index) +{ + int i, j, k, slope, hor; + u32 win_en, mode, val, ctrl = 0; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + win_en = cfg->win[i].win_en; + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "%s unite:%d, unite_index:%d ch:%d win_en:0x%x\n", __func__, + dev->unite_mode, unite_index, i, win_en); + if (win_en) + ctrl |= RKVPSS_CMSC_CHN_EN(i); + rkvpss_idx_write(dev, RKVPSS_CMSC_CHN0_WIN + i * 4, win_en, unite_index); + + mode = cfg->win[i].mode; + rkvpss_idx_write(dev, RKVPSS_CMSC_CHN0_MODE + i * 4, mode, unite_index); + + for (j = 0; j < RKVPSS_CMSC_WIN_MAX && win_en; j++) { + if (!(win_en & BIT(j))) + continue; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + val = RKVPSS_CMSC_WIN_VTX(cfg->win[j].point[k].x, + cfg->win[j].point[k].y); + rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_L0_VTX + k * 8 + j * 32, val, + unite_index); + + rkvpss_cmsc_slop(&cfg->win[j].point[k], + (k + 1 == RKVPSS_CMSC_POINT_MAX) ? + &cfg->win[j].point[0] : &cfg->win[j].point[k + 1], + &slope, &hor); + val = RKVPSS_CMSC_WIN_SLP(slope, hor); + rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_L0_SLP + k * 8 + j * 32, val, + unite_index); + v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev, + "%s unite:%d unite_index:%d ch:%d win:%d point:%d x:%u y:%u", + __func__, dev->unite_mode, unite_index, i, j, k, + cfg->win[j].point[k].x, + cfg->win[j].point[k].y); + } + + if ((mode & BIT(j))) + continue; + val = RKVPSS_CMSK_WIN_YUV(cfg->win[j].cover_color_y, + cfg->win[j].cover_color_u, + cfg->win[j].cover_color_v); + val |= RKVPSS_CMSC_WIN_ALPHA(cfg->win[j].cover_color_a); + rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_PARA + j * 4, val, unite_index); + } + } + + if (ctrl) { + ctrl |= RKVPSS_CMSC_EN; + ctrl |= RKVPSS_CMSC_BLK_SZIE(cfg->mosaic_block); + } + rkvpss_idx_write(dev, RKVPSS_CMSC_CTRL, ctrl, unite_index); + val = RKVPSS_CMSC_GEN_UPD; + if (sync) + val |= RKVPSS_CMSC_FORCE_UPD; + rkvpss_idx_write(dev, RKVPSS_CMSC_UPDATE, val, unite_index); +} + +void rkvpss_cmsc_config_v1(struct rkvpss_device *dev, bool sync) +{ + unsigned long lock_flags = 0; + struct rkvpss_cmsc_cfg left_cfg = {0}, right_cfg = {0}; + struct rkvpss_cmsc_win *win; + struct rkvpss_cmsc_point *point; + int i, j, k; + u32 mask; + + spin_lock_irqsave(&dev->cmsc_lock, lock_flags); + if (dev->hw_dev->is_ofl_cmsc || !dev->cmsc_upd) { + spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); + return; + } + dev->cmsc_upd = false; + spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); + + if (dev->unite_mode) { + /* unite left */ + for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { + left_cfg.win[i].cover_color_y = dev->cmsc_cfg.win[i].cover_color_y; + left_cfg.win[i].cover_color_u = dev->cmsc_cfg.win[i].cover_color_u; + left_cfg.win[i].cover_color_v = dev->cmsc_cfg.win[i].cover_color_v; + left_cfg.win[i].cover_color_a = dev->cmsc_cfg.win[i].cover_color_a; + for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) + left_cfg.win[i].point[j] = dev->cmsc_cfg.win[i].point[j]; + } + left_cfg.mosaic_block = dev->cmsc_cfg.mosaic_block; + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + left_cfg.win[i].win_en = dev->cmsc_cfg.win[i].win_en; + left_cfg.win[i].mode = dev->cmsc_cfg.win[i].mode; + } + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + win = &left_cfg.win[j]; + if (!(left_cfg.win[i].win_en & BIT(j))) + continue; + mask = 0; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + point = &win->point[k]; + if (point->x >= dev->vpss_sdev.in_fmt.width / 2) + mask |= BIT(k); + else + mask &= ~BIT(k); + } + if (mask == 0xf) { + /** all right **/ + left_cfg.win[i].win_en &= ~BIT(j); + } else if (mask != 0) { + /** middle need avoid pentagon **/ + if (win->point[0].x != win->point[3].x || + win->point[1].x != win->point[2].x) { + left_cfg.win[i].win_en &= ~BIT(j); + } else { + win->point[1].x = dev->vpss_sdev.in_fmt.width / 2; + win->point[2].x = dev->vpss_sdev.in_fmt.width / 2; + } + } + } + } + + /* unite right */ + for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { + right_cfg.win[i].cover_color_y = dev->cmsc_cfg.win[i].cover_color_y; + right_cfg.win[i].cover_color_u = dev->cmsc_cfg.win[i].cover_color_u; + right_cfg.win[i].cover_color_v = dev->cmsc_cfg.win[i].cover_color_v; + right_cfg.win[i].cover_color_a = dev->cmsc_cfg.win[i].cover_color_a; + + for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) + right_cfg.win[i].point[j] = dev->cmsc_cfg.win[i].point[j]; + } + right_cfg.mosaic_block = dev->cmsc_cfg.mosaic_block; + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + right_cfg.win[i].win_en = dev->cmsc_cfg.win[i].win_en; + right_cfg.win[i].mode = dev->cmsc_cfg.win[i].mode; + } + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + win = &right_cfg.win[j]; + if (!(right_cfg.win[i].win_en & BIT(j))) + continue; + mask = 0; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + point = &win->point[k]; + if (point->x <= dev->vpss_sdev.in_fmt.width / 2) + mask |= BIT(k); + else + mask &= ~BIT(k); + } + if (mask == 0xf) { + /** all left **/ + right_cfg.win[i].win_en &= ~BIT(j); + } else if (mask != 0) { + /** middle need avoid pentagon **/ + if (win->point[0].x != win->point[3].x || + win->point[1].x != win->point[2].x) { + right_cfg.win[i].win_en &= ~BIT(j); + } else { + win->point[0].x = RKMOUDLE_UNITE_EXTEND_PIXEL; + win->point[3].x = RKMOUDLE_UNITE_EXTEND_PIXEL; + win->point[1].x = win->point[1].x - + (dev->vpss_sdev.in_fmt.width / 2) + + RKMOUDLE_UNITE_EXTEND_PIXEL; + win->point[2].x = win->point[2].x - + (dev->vpss_sdev.in_fmt.width / 2) + + RKMOUDLE_UNITE_EXTEND_PIXEL; + } + } else { + /** all right **/ + win->point[0].x = win->point[0].x - + (dev->vpss_sdev.in_fmt.width / 2) + + RKMOUDLE_UNITE_EXTEND_PIXEL; + win->point[1].x = win->point[1].x - + (dev->vpss_sdev.in_fmt.width / 2) + + RKMOUDLE_UNITE_EXTEND_PIXEL; + win->point[2].x = win->point[2].x - + (dev->vpss_sdev.in_fmt.width / 2) + + RKMOUDLE_UNITE_EXTEND_PIXEL; + win->point[3].x = win->point[3].x - + (dev->vpss_sdev.in_fmt.width / 2) + + RKMOUDLE_UNITE_EXTEND_PIXEL; + } + } + } + + cmsc_config_hw(dev, &left_cfg, sync, VPSS_UNITE_LEFT); + cmsc_config_hw(dev, &right_cfg, sync, VPSS_UNITE_RIGHT); + } else { + cmsc_config_hw(dev, &dev->cmsc_cfg, sync, VPSS_UNITE_LEFT); + } +} + +static int rkvpss_get_cmsc(struct rkvpss_stream *stream, struct rkvpss_cmsc_cfg *cfg) +{ + struct rkvpss_device *dev = stream->dev; + unsigned long lock_flags = 0; + u32 i, win_en, mode; + + spin_lock_irqsave(&dev->cmsc_lock, lock_flags); + *cfg = dev->cmsc_cfg; + spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); + + win_en = cfg->win[stream->id].win_en; + mode = cfg->win[stream->id].mode; + for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { + cfg->win[i].win_en = !!(win_en & BIT(i)); + cfg->win[i].mode = !!(mode & BIT(i)); + } + cfg->width_ro = dev->vpss_sdev.in_fmt.width; + cfg->height_ro = dev->vpss_sdev.in_fmt.height; + return 0; +} + +static int rkvpss_set_cmsc(struct rkvpss_stream *stream, struct rkvpss_cmsc_cfg *cfg) +{ + struct rkvpss_device *dev = stream->dev; + unsigned long lock_flags = 0; + u16 i, j, k, win_en = 0, mode = 0; + int ret = 0; + + if (dev->hw_dev->is_ofl_cmsc) + return -EINVAL; + + spin_lock_irqsave(&dev->cmsc_lock, lock_flags); + for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) { + if (!cfg->win[i].win_en) + continue; + + win_en |= BIT(i); + mode |= cfg->win[i].mode ? BIT(i) : 0; + + for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) { + for (k = j + 1; k < RKVPSS_CMSC_POINT_MAX; k++) { + if (cfg->win[i].point[j].x == cfg->win[i].point[k].x && + cfg->win[i].point[j].y == cfg->win[i].point[k].y) { + v4l2_warn(&dev->v4l2_dev, + "points should different, P%d(%d,%d) P%d(%d,%d)\n", + j, + cfg->win[i].point[j].x, + cfg->win[i].point[j].y, + k, + cfg->win[i].point[k].x, + cfg->win[i].point[k].y); + ret = -EINVAL; + goto unlock; + } + } + } + if (!cfg->win[i].mode) { + dev->cmsc_cfg.win[i].cover_color_y = cfg->win[i].cover_color_y; + dev->cmsc_cfg.win[i].cover_color_u = cfg->win[i].cover_color_u; + dev->cmsc_cfg.win[i].cover_color_v = cfg->win[i].cover_color_v; + dev->cmsc_cfg.win[i].cover_color_a = cfg->win[i].cover_color_a; + if (dev->cmsc_cfg.win[i].cover_color_a > 15) + dev->cmsc_cfg.win[i].cover_color_a = 15; + } + for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) + dev->cmsc_cfg.win[i].point[j] = cfg->win[i].point[j]; + } + dev->cmsc_cfg.win[stream->id].win_en = win_en; + dev->cmsc_cfg.win[stream->id].mode = mode; + dev->cmsc_cfg.mosaic_block = cfg->mosaic_block; + dev->cmsc_upd = true; + v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, + "%s ch:%d win_en:0x%x", + __func__, stream->id, + dev->cmsc_cfg.win[stream->id].win_en); + +unlock: + spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags); + return ret; +} + +static long rkvpss_ioctl_default(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + struct rkvpss_stream *stream = video_drvdata(file); + long ret = 0; + + if (!arg) + return -EINVAL; + + switch (cmd) { + case RKVPSS_CMD_GET_MIRROR_FLIP: + ret = rkvpss_get_mirror_flip(stream, arg); + break; + case RKVPSS_CMD_SET_MIRROR_FLIP: + ret = rkvpss_set_mirror_flip(stream, arg); + break; + case RKVPSS_CMD_GET_CMSC: + ret = rkvpss_get_cmsc(stream, arg); + break; + case RKVPSS_CMD_SET_CMSC: + ret = rkvpss_set_cmsc(stream, arg); + break; + case RKVPSS_CMD_STREAM_ATTACH_INFO: + stream->is_attach_info = *(int *)arg; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ioctl_ops rkvpss_v4l2_ioctl_ops = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = rkvpss_enum_input, + .vidioc_try_fmt_vid_cap_mplane = rkvpss_try_fmt_vid_mplane, + .vidioc_enum_fmt_vid_cap = rkvpss_enum_fmt_vid_mplane, + .vidioc_s_fmt_vid_cap_mplane = rkvpss_s_fmt_vid_mplane, + .vidioc_g_fmt_vid_cap_mplane = rkvpss_g_fmt_vid_mplane, + .vidioc_s_selection = rkvpss_s_selection, + .vidioc_g_selection = rkvpss_g_selection, + .vidioc_querycap = rkvpss_querycap, + .vidioc_default = rkvpss_ioctl_default, +}; + +static void rkvpss_unregister_stream_video(struct rkvpss_stream *stream) +{ + tasklet_kill(&stream->buf_done_tasklet); + media_entity_cleanup(&stream->vnode.vdev.entity); + video_unregister_device(&stream->vnode.vdev); +} + +static int rkvpss_register_stream_video(struct rkvpss_stream *stream) +{ + struct rkvpss_device *dev = stream->dev; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct video_device *vdev = &stream->vnode.vdev; + struct rkvpss_vdev_node *node; + enum v4l2_buf_type buf_type; + int ret = 0; + + node = vdev_to_node(vdev); + vdev->release = video_device_release_empty; + vdev->fops = &rkvpss_fops; + vdev->minor = -1; + vdev->v4l2_dev = v4l2_dev; + vdev->lock = &dev->apilock; + video_set_drvdata(vdev, stream); + + vdev->ioctl_ops = &rkvpss_v4l2_ioctl_ops; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; + vdev->vfl_dir = VFL_DIR_RX; + node->pad.flags = MEDIA_PAD_FL_SINK; + buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + + rkvpss_init_vb2_queue(&node->buf_queue, stream, buf_type); + vdev->queue = &node->buf_queue; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + v4l2_err(v4l2_dev, + "video register failed with error %d\n", ret); + return ret; + } + + ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); + if (ret < 0) + goto unreg; + + INIT_LIST_HEAD(&stream->buf_done_list); + tasklet_init(&stream->buf_done_tasklet, + rkvpss_buf_done_task, (unsigned long)stream); + tasklet_disable(&stream->buf_done_tasklet); + return 0; +unreg: + video_unregister_device(vdev); + return ret; +} + +void rkvpss_stream_default_fmt_v1(struct rkvpss_device *dev, u32 id, + u32 width, u32 height, u32 pixelformat) +{ + struct rkvpss_stream *stream = &dev->stream_vdev.stream[id]; + struct v4l2_pix_format_mplane pixm = { 0 }; + + if (pixelformat) + pixm.pixelformat = pixelformat; + else + pixm.pixelformat = stream->out_cap_fmt.fourcc; + if (!pixm.pixelformat) + return; + + stream->crop.left = 0; + stream->crop.top = 0; + stream->crop.width = width; + stream->crop.height = height; + + pixm.width = width; + pixm.height = height; + rkvpss_set_fmt(stream, &pixm, false); +} + +int rkvpss_register_stream_vdevs_v1(struct rkvpss_device *dev) +{ + struct rkvpss_stream_vdev *stream_vdev; + struct rkvpss_stream *stream; + struct video_device *vdev; + char *vdev_name; + int i, j, ret = 0; + + stream_vdev = &dev->stream_vdev; + memset(stream_vdev, 0, sizeof(*stream_vdev)); + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + stream = &stream_vdev->stream[i]; + stream->id = i; + stream->dev = dev; + vdev = &stream->vnode.vdev; + INIT_LIST_HEAD(&stream->buf_queue); + init_waitqueue_head(&stream->done); + spin_lock_init(&stream->vbq_lock); + switch (i) { + case RKVPSS_OUTPUT_CH0: + vdev_name = S0_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl0_config; + break; + case RKVPSS_OUTPUT_CH1: + vdev_name = S1_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl1_config; + break; + case RKVPSS_OUTPUT_CH2: + vdev_name = S2_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl2_config; + break; + case RKVPSS_OUTPUT_CH3: + vdev_name = S3_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl3_config; + break; + default: + v4l2_err(&dev->v4l2_dev, "Invalid stream:%d\n", i); + return -EINVAL; + } + strscpy(vdev->name, vdev_name, sizeof(vdev->name)); + ret = rkvpss_register_stream_video(stream); + if (ret < 0) + goto err; + } + return 0; +err: + for (j = 0; j < i; j++) { + stream = &stream_vdev->stream[j]; + rkvpss_unregister_stream_video(stream); + } + return ret; +} + +void rkvpss_unregister_stream_vdevs_v1(struct rkvpss_device *dev) +{ + struct rkvpss_stream_vdev *stream_vdev; + struct rkvpss_stream *stream; + int i; + + stream_vdev = &dev->stream_vdev; + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + stream = &stream_vdev->stream[i]; + rkvpss_unregister_stream_video(stream); + } +} + +void rkvpss_isr_v1(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) + rkvpss_check_idle(dev, VPSS_FRAME_END); +} + +void rkvpss_mi_isr_v1(struct rkvpss_device *dev, u32 mis_val) +{ + struct rkvpss_stream *stream; + int i, ris = readl(dev->hw_dev->base_addr + RKVPSS_MI_RIS); + + v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, + "mi isr:0x%x, ris:0x%x\n", mis_val, ris); + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + stream = &dev->stream_vdev.stream[i]; + + if (!stream->streaming || + !(ris & stream->config->frame_end_id)) + continue; + writel(stream->config->frame_end_id, dev->hw_dev->base_addr + RKVPSS_MI_ICR); + + if (!dev->unite_mode || dev->unite_index == VPSS_UNITE_RIGHT) { + if (stream->stopping) { + stream->stopping = false; + stream->streaming = false; + stream->ops->disable_mi(stream); + wake_up(&stream->done); + } else { + rkvpss_frame_end(stream); + } + } + } + rkvpss_check_idle(dev, (ris & 0xf) << 3); +} diff --git a/drivers/media/platform/rockchip/vpss/stream_v1.h b/drivers/media/platform/rockchip/vpss/stream_v1.h new file mode 100644 index 000000000000..df0071022384 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/stream_v1.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKVPSS_STREAM_V1_H +#define _RKVPSS_STREAM_V1_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_VPSS_V1) +int rkvpss_register_stream_vdevs_v1(struct rkvpss_device *dev); +void rkvpss_unregister_stream_vdevs_v1(struct rkvpss_device *dev); +void rkvpss_stream_default_fmt_v1(struct rkvpss_device *dev, u32 id, + u32 width, u32 height, u32 pixelformat); +void rkvpss_isr_v1(struct rkvpss_device *dev, u32 mis_val); +void rkvpss_mi_isr_v1(struct rkvpss_device *dev, u32 mis_val); +void rkvpss_cmsc_config_v1(struct rkvpss_device *dev, bool sync); +int rkvpss_stream_buf_cnt_v1(struct rkvpss_stream *stream); + +#else +static inline int rkvpss_register_stream_vdevs_v1(struct rkvpss_device *dev) {return -EINVAL; } +static inline void rkvpss_unregister_stream_vdevs_v1(struct rkvpss_device *dev) {} +static inline void rkvpss_stream_default_fmt_v1(struct rkvpss_device *dev, u32 id, u32 width, u32 height, u32 pixelformat) {} +static inline void rkvpss_isr_v1(struct rkvpss_device *dev, u32 mis_val) {} +static inline void rkvpss_mi_isr_v1(struct rkvpss_device *dev, u32 mis_val) {} +static inline void rkvpss_cmsc_config_v1(struct rkvpss_device *dev, bool sync) {} +static inline int rkvpss_stream_buf_cnt_v1(struct rkvpss_stream *stream) {return -EINVAL; } + +#endif + +#endif diff --git a/drivers/media/platform/rockchip/vpss/version.h b/drivers/media/platform/rockchip/vpss/version.h index 50db86e796a3..0afe1a22271b 100644 --- a/drivers/media/platform/rockchip/vpss/version.h +++ b/drivers/media/platform/rockchip/vpss/version.h @@ -4,7 +4,6 @@ #ifndef _RKVPSS_VERSION_H #define _RKVPSS_VERSION_H #include -#include /* * RKVPSS DRIVER VERSION NOTE diff --git a/drivers/media/platform/rockchip/vpss/vpss.c b/drivers/media/platform/rockchip/vpss/vpss.c index 5da9d9937cf5..f2637ec6a446 100644 --- a/drivers/media/platform/rockchip/vpss/vpss.c +++ b/drivers/media/platform/rockchip/vpss/vpss.c @@ -1,16 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ -#include -#include -#include -#include -#include -#include -#include - +#include "vpss.h" +#include "common.h" +#include "stream.h" #include "dev.h" -#include "regs.h" +#include "vpss_offline.h" +#include "hw.h" +#include "procfs.h" +#include "regs_v1.h" static const struct vpsssd_fmt rkvpss_formats[] = { { @@ -19,6 +17,22 @@ static const struct vpsssd_fmt rkvpss_formats[] = { }, }; +static inline const char *s_dev_name(int i) +{ + switch (i) { + case 0: + return S0_VDEV_NAME; + case 1: + return S1_VDEV_NAME; + case 2: + return S2_VDEV_NAME; + case 3: + return S3_VDEV_NAME; + default: + return S0_VDEV_NAME; + } +} + static int rkvpss_subdev_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, @@ -29,6 +43,7 @@ static int rkvpss_subdev_link_setup(struct media_entity *entity, struct rkvpss_device *dev; struct rkvpss_stream_vdev *vdev; struct rkvpss_stream *stream = NULL; + int i; if (local->index != RKVPSS_PAD_SINK && local->index != RKVPSS_PAD_SOURCE) @@ -46,22 +61,23 @@ static int rkvpss_subdev_link_setup(struct media_entity *entity, if (vpss_sdev->state & VPSS_START) return -EBUSY; - if (!strcmp(remote->entity->name, S0_VDEV_NAME)) { - stream = &vdev->stream[RKVPSS_OUTPUT_CH0]; - } else if (!strcmp(remote->entity->name, S1_VDEV_NAME)) { - stream = &vdev->stream[RKVPSS_OUTPUT_CH1]; - } else if (!strcmp(remote->entity->name, S2_VDEV_NAME)) { - stream = &vdev->stream[RKVPSS_OUTPUT_CH2]; - } else if (!strcmp(remote->entity->name, S3_VDEV_NAME)) { - stream = &vdev->stream[RKVPSS_OUTPUT_CH3]; - } else if (strstr(remote->entity->name, "rkisp")) { + for (i = 0; i < vpss_outchn_max(dev->hw_dev->vpss_ver); i++) { + if (!strcmp(remote->entity->name, s_dev_name(i))) { + stream = &vdev->stream[i]; + break; + } + } + + if (stream) + stream->linked = flags & MEDIA_LNK_FL_ENABLED; + + if (strstr(remote->entity->name, "rkisp")) { if (flags & MEDIA_LNK_FL_ENABLED) dev->inp = INP_ISP; else dev->inp = INP_INVAL; } - if (stream) - stream->linked = flags & MEDIA_LNK_FL_ENABLED; + v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev, "input:%d\n", dev->inp); return 0; } @@ -253,7 +269,7 @@ static int rkvpss_sof(struct rkvpss_subdev *sdev, struct rkisp_vpss_sof *info) dev->unite_mode, dev->unite_index, info->seq); rkvpss_cmsc_config(dev, !info->irq); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + for (i = 0; i < vpss_outchn_max(dev->hw_dev->vpss_ver); i++) { stream = &dev->stream_vdev.stream[i]; if (!stream->streaming) continue; @@ -287,7 +303,7 @@ static int rkvpss_sof(struct rkvpss_subdev *sdev, struct rkisp_vpss_sof *info) /* force update mi write */ vpss_online = rkvpss_hw_read(hw, RKVPSS_VPSS_ONLINE); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + for (i = 0; i < vpss_outchn_max(dev->hw_dev->vpss_ver); i++) { if (((vpss_online >> (2 * i)) & 0x3) == 0x2) val |= BIT(i); } @@ -296,7 +312,7 @@ static int rkvpss_sof(struct rkvpss_subdev *sdev, struct rkisp_vpss_sof *info) } dev->irq_ends_mask = VPSS_FRAME_END; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { + for (i = 0; i < vpss_outchn_max(dev->hw_dev->vpss_ver); i++) { if (hw->is_ofl_ch[i]) continue; if (rkvpss_hw_read(dev->hw_dev, RKVPSS_MI_CHN0_WR_CTRL_SHD + i * 0x100) & 0x1) @@ -426,16 +442,29 @@ static void rkvpss_end_notify_isp(struct rkvpss_device *dev) void rkvpss_check_idle(struct rkvpss_device *dev, u32 irq) { + unsigned long lock_flags = 0; + + spin_lock_irqsave(&dev->idle_lock, lock_flags); dev->irq_ends |= (irq & dev->irq_ends_mask); v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev, "%s irq:0x%x ends:0x%x mask:0x%x\n", __func__, irq, dev->irq_ends, dev->irq_ends_mask); - if ((dev->irq_ends & dev->irq_ends_mask) != dev->irq_ends_mask) + if ((dev->irq_ends & dev->irq_ends_mask) != dev->irq_ends_mask) { + spin_unlock_irqrestore(&dev->idle_lock, lock_flags); return; + } + + /* offline MI frame end */ + if (!dev->irq_ends_mask) { + spin_unlock_irqrestore(&dev->idle_lock, lock_flags); + return; + } dev->irq_ends = 0; + spin_unlock_irqrestore(&dev->idle_lock, lock_flags); + rkvpss_end_notify_isp(dev); dev->is_idle = true; diff --git a/drivers/media/platform/rockchip/vpss/vpss.h b/drivers/media/platform/rockchip/vpss/vpss.h index 7b17ccf66288..da8037c72e63 100644 --- a/drivers/media/platform/rockchip/vpss/vpss.h +++ b/drivers/media/platform/rockchip/vpss/vpss.h @@ -4,7 +4,31 @@ #ifndef _RKVPSS_VPSS_H #define _RKVPSS_VPSS_H -#include "common.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + #define GRP_ID_VPSS BIT(0) diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.c b/drivers/media/platform/rockchip/vpss/vpss_offline.c index a26cf8554c6b..f7fcfa659975 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.c +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.c @@ -1,2231 +1,92 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include "vpss.h" +#include "common.h" +#include "stream.h" #include "dev.h" +#include "vpss_offline.h" #include "hw.h" -#include "regs.h" +#include "regs_v1.h" -struct rkvpss_output_ch { - u32 ctrl; - u32 size; - u32 c_offs; -}; +#include "vpss_offline_v1.h" -struct rkvpss_offline_buf { - struct list_head list; - struct vb2_buffer vb; - struct vb2_queue vb2_queue; - struct file *file; - struct dma_buf *dbuf; - void *mem; - int dev_id; - int fd; - bool alloc; -}; - -static void init_vb2(struct rkvpss_offline_dev *ofl, - struct rkvpss_offline_buf *buf) +void rkvpss_dump_reg(struct rkvpss_offline_dev *ofl, int sequence, int size) { struct rkvpss_hw_dev *hw = ofl->hw; - unsigned long attrs = DMA_ATTR_NO_KERNEL_MAPPING; + struct file *filep = NULL; + char buf[256] = {0}; + loff_t pos = 0; + int i, j; - if (!buf) + if (!IS_ENABLED(CONFIG_NO_GKI)) return; - memset(&buf->vb, 0, sizeof(buf->vb)); - memset(&buf->vb2_queue, 0, sizeof(buf->vb2_queue)); - buf->vb2_queue.gfp_flags = GFP_KERNEL | GFP_DMA32; - buf->vb2_queue.dma_dir = DMA_BIDIRECTIONAL; - if (hw->is_dma_contig) - attrs |= DMA_ATTR_FORCE_CONTIGUOUS; - buf->vb2_queue.dma_attrs = attrs; - buf->vb.vb2_queue = &buf->vb2_queue; -} -static void buf_del(struct file *file, int id, int fd, bool is_all, bool running) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - const struct vb2_mem_ops *ops = hw->mem_ops; - struct rkvpss_offline_buf *buf, *next; - - mutex_lock(&hw->dev_lock); - list_for_each_entry_safe(buf, next, &ofl->list, list) { - if (buf->file == file && (is_all || buf->fd == fd)) { - if (!is_all && running && buf->alloc) - break; - v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, - "%s file:%p dev_id:%d fd:%d dbuf:%p\n", - __func__, file, buf->dev_id, buf->fd, buf->dbuf); - if (!buf->alloc) { - ops->unmap_dmabuf(buf->mem); - ops->detach_dmabuf(buf->mem); - dma_buf_put(buf->dbuf); - } else { - dma_buf_put(buf->dbuf); - ops->put(buf->mem); - } - 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 struct rkvpss_offline_buf *buf_add(struct file *file, int id, int fd, int size) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - const struct vb2_mem_ops *ops = hw->mem_ops; - struct rkvpss_offline_buf *buf = NULL, *next = NULL; - struct dma_buf *dbuf; - void *mem = NULL; - bool is_add = true; - - dbuf = dma_buf_get(fd); - if (IS_ERR_OR_NULL(dbuf)) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d invalid dmabuf fd:%d", id, fd); - return buf; - } - if (size && dbuf->size < size) { - v4l2_err(&ofl->v4l2_dev, - "dev_id:%d input fd:%d size error:%zu < %u\n", - id, 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->file == file && buf->fd == fd && buf->dbuf == dbuf) { - is_add = false; - break; - } - } - - if (is_add) { - buf = kzalloc(sizeof(struct rkvpss_offline_buf), GFP_KERNEL); - if (!buf) - goto end; - init_vb2(ofl, buf); - mem = ops->attach_dmabuf(&buf->vb, hw->dev, dbuf, dbuf->size); - if (IS_ERR(mem)) { - v4l2_err(&ofl->v4l2_dev, "failed to attach dmabuf, fd:%d\n", fd); - dma_buf_put(dbuf); - kfree(buf); - buf = NULL; - goto end; - } - if (ops->map_dmabuf(mem)) { - v4l2_err(&ofl->v4l2_dev, "failed to map, fd:%d\n", fd); - ops->detach_dmabuf(mem); - dma_buf_put(dbuf); - mem = NULL; - kfree(buf); - buf = NULL; - goto end; - } - buf->dev_id = id; - buf->fd = fd; - buf->file = file; - buf->dbuf = dbuf; - buf->mem = mem; - buf->alloc = false; - list_add_tail(&buf->list, &ofl->list); - v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, - "%s file:%p dev_id:%d fd:%d dbuf:%p size:%d\n", __func__, - file, id, fd, dbuf, size); - } else { - dma_buf_put(dbuf); - } -end: - mutex_unlock(&hw->dev_lock); - return buf; -} - -static int internal_buf_alloc(struct file *file, struct rkvpss_buf_info *info) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - const struct vb2_mem_ops *ops = hw->mem_ops; - struct rkvpss_offline_buf *buf; - struct dma_buf *dbuf; - int fd, i, size; - void *mem; - - for (i = 0; i < info->buf_cnt; i++) { - info->buf_fd[i] = -1; - size = PAGE_ALIGN(info->buf_size[i]); - if (!size) - continue; - buf = kzalloc(sizeof(struct rkvpss_offline_buf), GFP_KERNEL); - if (!buf) - goto err; - init_vb2(ofl, buf); - mem = ops->alloc(&buf->vb, hw->dev, size); - if (IS_ERR_OR_NULL(mem)) { - kfree(buf); - goto err; - } - dbuf = ops->get_dmabuf(&buf->vb, mem, O_RDWR); - if (IS_ERR_OR_NULL(dbuf)) { - ops->put(mem); - kfree(buf); - goto err; - } - fd = dma_buf_fd(dbuf, O_CLOEXEC); - if (fd < 0) { - dma_buf_put(dbuf); - ops->put(mem); - kfree(buf); - goto err; - } - get_dma_buf(dbuf); - - info->buf_fd[i] = fd; - buf->fd = fd; - buf->file = file; - buf->dbuf = dbuf; - buf->mem = mem; - buf->alloc = true; - buf->dev_id = info->dev_id; - ops->prepare(buf->mem); - mutex_lock(&hw->dev_lock); - list_add_tail(&buf->list, &ofl->list); - mutex_unlock(&hw->dev_lock); - v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, - "%s file:%p dev_id:%d fd:%d dbuf:%p\n", - __func__, file, buf->dev_id, fd, dbuf); - } - return 0; -err: - for (i -= 1; i >= 0; i--) - buf_del(file, info->dev_id, info->buf_fd[i], false, false); - return -ENOMEM; -} - -static int external_buf_add(struct file *file, struct rkvpss_buf_info *info) -{ - void *mem; - int i; - - for (i = 0; i < info->buf_cnt; i++) { - mem = buf_add(file, info->dev_id, info->buf_fd[i], info->buf_size[i]); - if (!mem) - goto err; - } - return 0; -err: - for (i -= 1; i >= 0; i--) - buf_del(file, info->dev_id, info->buf_fd[i], false, false); - return -ENOMEM; -} - -static int rkvpss_ofl_buf_add(struct file *file, struct rkvpss_buf_info *info) -{ - int ret; - - if (info->buf_alloc) - ret = internal_buf_alloc(file, info); - else - ret = external_buf_add(file, info); - return ret; -} - -static void rkvpss_ofl_buf_del(struct file *file, struct rkvpss_buf_info *info) -{ - int i; - - for (i = 0; i < info->buf_cnt; i++) - buf_del(file, info->dev_id, info->buf_fd[i], false, false); -} - -static void poly_phase_scale(struct rkvpss_frame_cfg *frame_cfg, - struct rkvpss_offline_dev *ofl, - struct rkvpss_output_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_hw_dev *hw = ofl->hw; - u32 in_w = cfg->crop_width, in_h = cfg->crop_height; - u32 out_w = cfg->scl_width, out_h = cfg->scl_height; - u32 ctrl, y_xscl_fac, y_yscl_fac, uv_xscl_fac, uv_yscl_fac; - u32 i, j, idx, ratio, val, in_div, out_div, factor; - bool dering_en = false, yuv420_in = false, yuv422_to_420 = false; - - if (in_w == out_w && in_h == out_w) { - rkvpss_hw_write(hw, RKVPSS_ZME_Y_SCL_CTRL, 0); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_SCL_CTRL, 0); - rkvpss_hw_clear_bits(hw, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS); - goto end; - } else { - rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS, - RKVPSS_SCL0_CKG_DIS); - } - - /* TODO diff for input and output format */ - if (yuv420_in) { - in_div = 2; - out_div = 2; - } else if (yuv422_to_420) { - in_div = 1; - out_div = 2; - } else { - in_div = 1; - out_div = 1; - } - - if (unite) { - if (left) { - if (in_w == out_w) - val = (cfg->crop_width / 2 - 1) | - ((cfg->crop_height - 1) << 16); - else - val = (cfg->crop_width / 2 + UNITE_ENLARGE - 1) | - ((cfg->crop_height - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); - } else { - val = (ALIGN(ofl->unite_params[0].right_scl_need_size_y + 3, 2) - 1) | - ((cfg->crop_height - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); - val = (ALIGN(ofl->unite_params[0].right_scl_need_size_c + 6, 2) - 1) | - ((cfg->crop_height - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); - } - val = (cfg->scl_width / 2 - 1) | ((cfg->scl_height - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_DST_SIZE, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_DST_SIZE, val); - } else { - val = (in_w - 1) | ((in_h - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); - val = (out_w - 1) | ((out_h - 1) << 16); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_DST_SIZE, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_DST_SIZE, val); - } - - ctrl = RKVPSS_ZME_XSCL_MODE | RKVPSS_ZME_YSCL_MODE; - if (dering_en) { - ctrl |= RKVPSS_ZME_DERING_EN; - rkvpss_hw_write(hw, RKVPSS_ZME_Y_DERING_PARA, 0xd10410); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_DERING_PARA, 0xd10410); - } - if (in_w != out_w) { - if (in_w > out_w) { - factor = 4096; - ctrl |= RKVPSS_ZME_XSD_EN; - } else { - factor = 65536; - ctrl |= RKVPSS_ZME_XSU_EN; - } - y_xscl_fac = (in_w - 1) * factor / (out_w - 1); - uv_xscl_fac = (in_w / 2 - 1) * factor / (out_w / 2 - 1); - - ratio = y_xscl_fac * 10000 / factor; - idx = rkvpss_get_zme_tap_coe_index(ratio); - for (i = 0; i < 17; i++) { - for (j = 0; j < 8; j += 2) { - val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap8_coe[idx][i][j], - rkvpss_zme_tap8_coe[idx][i][j + 1]); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, val); - } - } - } else { - y_xscl_fac = 0; - uv_xscl_fac = 0; - } - if (unite && !left) { - rkvpss_hw_write(hw, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac | - (ofl->unite_params[0].y_w_phase << 16)); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac | - (ofl->unite_params[0].c_w_phase << 16)); - } else { - rkvpss_hw_write(hw, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac); - } - - if (in_h != out_h) { - if (in_h > out_h) { - factor = 4096; - ctrl |= RKVPSS_ZME_YSD_EN; - } else { - factor = 65536; - ctrl |= RKVPSS_ZME_YSU_EN; - } - y_yscl_fac = (in_h - 1) * factor / (out_h - 1); - uv_yscl_fac = (in_h / in_div - 1) * factor / (out_h / out_div - 1); - - ratio = y_yscl_fac * 10000 / factor; - idx = rkvpss_get_zme_tap_coe_index(ratio); - for (i = 0; i < 17; i++) { - for (j = 0; j < 8; j += 2) { - val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap6_coe[idx][i][j], - rkvpss_zme_tap6_coe[idx][i][j + 1]); - rkvpss_hw_write(hw, RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, val); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, val); - } - } - } else { - y_yscl_fac = 0; - uv_yscl_fac = 0; - } - rkvpss_hw_write(hw, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac); - - rkvpss_hw_write(hw, RKVPSS_ZME_Y_SCL_CTRL, ctrl); - rkvpss_hw_write(hw, RKVPSS_ZME_UV_SCL_CTRL, ctrl); - - if (unite) { - val = cfg->scl_width / 2; - rkvpss_hw_write(hw, RKVPSS_ZME_H_SIZE, (val << 16) | val); - rkvpss_hw_write(hw, RKVPSS_ZME_H_OFFS, 0); - if (!left) { - val = cfg->scl_width / 2 - ALIGN_DOWN(cfg->scl_width / 2, 16); - rkvpss_hw_write(hw, RKVPSS_ZME_H_OFFS, (3 << 20) | (3 << 16) | - (ofl->unite_params[0].scl_in_crop_w_y << 8) | - (ofl->unite_params[0].scl_in_crop_w_c << 12) | - (val << 4) | val); - } - } - - ctrl = RKVPSS_ZME_GATING_EN; - if (yuv420_in) - ctrl |= RKVPSS_ZME_SCL_YUV420_REAL_EN; - if (yuv422_to_420) - ctrl |= RKVPSS_ZME_422TO420_EN; - if (unite) { - if (left) - ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_8K_EN; - else - ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_IN_CLIP_EN | RKVPSS_ZME_8K_EN; - } - rkvpss_hw_write(hw, RKVPSS_ZME_CTRL, ctrl); -end: - val = RKVPSS_ZME_GEN_UPD | RKVPSS_ZME_FORCE_UPD; - rkvpss_hw_write(hw, RKVPSS_ZME_UPDATE, val); - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s unite:%d left:%d hw ctrl:0x%x y_src_size:0x%x y_dst_size:0x%x\n", - __func__, unite, left, - rkvpss_hw_read(hw, RKVPSS_ZME_CTRL), - rkvpss_hw_read(hw, RKVPSS_ZME_Y_SRC_SIZE), - rkvpss_hw_read(hw, RKVPSS_ZME_Y_DST_SIZE)); -} - -static void bilinear_scale(struct rkvpss_frame_cfg *frame_cfg, - struct rkvpss_offline_dev *ofl, - struct rkvpss_output_cfg *cfg, int idx, bool unite, bool left) -{ - struct rkvpss_hw_dev *hw = ofl->hw; - u32 in_w = cfg->crop_width, in_h = cfg->crop_height; - u32 out_w = cfg->scl_width, out_h = cfg->scl_height; - u32 reg_base, in_div, out_div, val, ctrl = 0, clk_mask = 0; - bool yuv420_in = false, yuv422_to_420 = false; - - switch (idx) { - case RKVPSS_OUTPUT_CH1: - reg_base = RKVPSS_SCALE1_BASE; - clk_mask = RKVPSS_SCL1_CKG_DIS; - break; - case RKVPSS_OUTPUT_CH2: - reg_base = RKVPSS_SCALE2_BASE; - clk_mask = RKVPSS_SCL2_CKG_DIS; - break; - case RKVPSS_OUTPUT_CH3: - reg_base = RKVPSS_SCALE3_BASE; - clk_mask = RKVPSS_SCL3_CKG_DIS; - break; - default: + filep = filp_open(rkvpss_regfile, O_RDWR | O_APPEND | O_CREAT, 0644); + if (IS_ERR(filep)) { + v4l2_err(&ofl->v4l2_dev, "Open file %s error\n", + rkvpss_regfile); return; - } - - /*config scl clk gate*/ - if (in_w == out_w && in_h == out_h) - rkvpss_hw_clear_bits(hw, RKVPSS_VPSS_CLK_GATE, clk_mask); - else - rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CLK_GATE, clk_mask, clk_mask); - - if (!unite) { - if (in_w == out_w && in_h == out_w) - goto end; - - /* TODO diff for input and output format */ - if (yuv420_in) { - in_div = 2; - out_div = 2; - } else if (yuv422_to_420) { - in_div = 1; - out_div = 2; - } else { - in_div = 1; - out_div = 1; - } - - val = in_w | (in_h << 16); - rkvpss_hw_write(hw, reg_base + 0x8, val); - val = out_w | (out_h << 16); - rkvpss_hw_write(hw, reg_base + 0xc, val); - - if (in_w != out_w) { - val = (in_w - 1) * 4096 / (out_w - 1); - rkvpss_hw_write(hw, reg_base + 0x10, val); - val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1); - rkvpss_hw_write(hw, reg_base + 0x14, val); - - ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; - } - if (in_h != out_h) { - val = (in_h - 1) * 4096 / (out_h - 1); - rkvpss_hw_write(hw, reg_base + 0x18, val); - val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1); - rkvpss_hw_write(hw, reg_base + 0x1c, val); - - ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; - } } else { - if (left) { - rkvpss_hw_write(hw, reg_base + 0x50, 0); - rkvpss_hw_write(hw, reg_base + 0x20, 0); - rkvpss_hw_write(hw, reg_base + 0x24, 0); - rkvpss_hw_write(hw, reg_base + 0x48, 0); - rkvpss_hw_write(hw, reg_base + 0x4c, 0); - if (in_w == out_w) - val = (cfg->crop_width / 2) | (cfg->crop_height << 16); - else - val = (cfg->crop_width / 2 + UNITE_ENLARGE) | - (cfg->crop_height << 16); - rkvpss_hw_write(hw, reg_base + 0x8, val); - val = cfg->scl_width / 2 | (cfg->scl_height << 16); - rkvpss_hw_write(hw, reg_base + 0xc, val); - ctrl |= RKVPSS_SCL_CLIP_EN; - } else { - val = ofl->unite_params[idx].scl_in_crop_w_y | - (ofl->unite_params[idx].scl_in_crop_w_c << 4); - rkvpss_hw_write(hw, reg_base + 0x50, val); - rkvpss_hw_write(hw, reg_base + 0x20, ofl->unite_params[idx].y_w_phase); - rkvpss_hw_write(hw, reg_base + 0x24, ofl->unite_params[idx].c_w_phase); - val = cfg->scl_width / 2 - ALIGN_DOWN(cfg->scl_width / 2, 16); - rkvpss_hw_write(hw, reg_base + 0x48, val); - rkvpss_hw_write(hw, reg_base + 0x4c, val); - val = (cfg->crop_width / 2 + ofl->unite_right_enlarge) | - (cfg->crop_height << 16); - rkvpss_hw_write(hw, reg_base + 0x8, val); - val = cfg->scl_width / 2 | (cfg->scl_height << 16); - rkvpss_hw_write(hw, reg_base + 0xc, val); - ctrl |= RKVPSS_SCL_CLIP_EN | RKVPSS_SCL_IN_CLIP_EN; + memset(buf, 0, sizeof(buf)); + sprintf(buf, "sequence:%d\n", sequence); + kernel_write(filep, buf, strlen(buf), &pos); + if (pos < 0) + v4l2_err(&ofl->v4l2_dev, "Write data to %s failed\n", + rkvpss_regfile); + + for (i = 0; i < size; i = i + 16) { + memset(buf, 0, sizeof(buf)); + sprintf(buf, "%04x:", i); + kernel_write(filep, buf, strlen(buf), &pos); + if (pos < 0) + v4l2_err(&ofl->v4l2_dev, "Write data to %s failed\n", + rkvpss_regfile); + + for (j = 0; j < 16; j = j + 4) { + memset(buf, 0, sizeof(buf)); + sprintf(buf, " %08x", rkvpss_hw_read(hw, i + j)); + kernel_write(filep, buf, strlen(buf), &pos); + if (pos < 0) + v4l2_err(&ofl->v4l2_dev, "Write data to %s failed\n", + rkvpss_regfile); + } + memset(buf, 0, sizeof(buf)); + sprintf(buf, "%s\n", ""); + kernel_write(filep, buf, strlen(buf), &pos); + if (pos < 0) { + v4l2_err(&ofl->v4l2_dev, "Write data to %s failed\n", + rkvpss_regfile); + } } - if (cfg->scl_width != frame_cfg->input.width) { - rkvpss_hw_write(hw, reg_base + 0x10, ofl->unite_params[idx].y_w_fac); - rkvpss_hw_write(hw, reg_base + 0x14, ofl->unite_params[idx].c_w_fac); - ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; - } - if (cfg->scl_height != frame_cfg->input.height) { - rkvpss_hw_write(hw, reg_base + 0x18, ofl->unite_params[idx].y_h_fac); - rkvpss_hw_write(hw, reg_base + 0x1c, ofl->unite_params[idx].c_h_fac); - ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; - } - } - -end: - rkvpss_hw_write(hw, reg_base, ctrl); - val = RKVPSS_SCL_GEN_UPD | RKVPSS_SCL_FORCE_UPD; - rkvpss_hw_write(hw, reg_base + 0x4, val); - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s unite:%d left:%d hw ch:%d ctrl:0x%x y_src_size:0x%x y_dst_size:0x%x\n", - __func__, unite, left, idx, - rkvpss_hw_read(hw, reg_base), - rkvpss_hw_read(hw, reg_base + 0x8), - rkvpss_hw_read(hw, reg_base + 0xc)); -} - -static void scale_config(struct file *file, - struct rkvpss_frame_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - int i; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - - if (i == RKVPSS_OUTPUT_CH0) - poly_phase_scale(cfg, ofl, &cfg->output[i], unite, left); - else - bilinear_scale(cfg, ofl, &cfg->output[i], i, unite, left); + filp_close(filep, NULL); } } -static int cmsc_config(struct rkvpss_offline_dev *ofl, - struct rkvpss_frame_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_hw_dev *hw = ofl->hw; - struct rkvpss_cmsc_cfg *cmsc_cfg, tmp_cfg = {0}; - struct rkvpss_cmsc_win *win; - struct rkvpss_cmsc_point *point; - int i, j, k; - u32 ch_win_en[RKVPSS_OUTPUT_MAX]; - u32 ch_win_mode[RKVPSS_OUTPUT_MAX]; - u32 win_color[RKVPSS_CMSC_WIN_MAX]; - u32 val, slope, hor, mask, mosaic_block = 0, ctrl = 0; - u32 hw_in_w, hw_in_h; - - if (!hw->is_ofl_cmsc) - return 0; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - ch_win_en[i] = 0; - ch_win_mode[i] = 0; - cmsc_cfg = &cfg->output[i].cmsc; - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - if (i == 0) - win_color[j] = 0; - if (!cmsc_cfg->win[j].win_en) - continue; - ch_win_en[i] |= BIT(j); - ch_win_mode[i] |= cmsc_cfg->win[j].mode ? BIT(j) : 0; - /** mosaic_block use the last channel **/ - if (cmsc_cfg->win[j].mode) - mosaic_block = cfg->output[i].cmsc.mosaic_block; - /** window cover all channel consistent **/ - if (!cfg->output[i].cmsc.win[j].mode) { - win_color[j] = RKVPSS_CMSK_WIN_YUV(cfg->output[i].cmsc.win[j].cover_color_y, - cfg->output[i].cmsc.win[j].cover_color_u, - cfg->output[i].cmsc.win[j].cover_color_v); - if (cfg->output[i].cmsc.win[j].cover_color_a > 15) - cfg->output[i].cmsc.win[j].cover_color_a = 15; - win_color[j] |= RKVPSS_CMSC_WIN_ALPHA(cfg->output[i].cmsc.win[j].cover_color_a); - } - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) - tmp_cfg.win[j].point[k] = cmsc_cfg->win[j].point[k]; - } - } - - /* deal unite left params */ - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!unite || !left) - break; - if (!cfg->output[i].enable) - continue; - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - win = &tmp_cfg.win[j]; - if (!(ch_win_en[i] & BIT(j))) - continue; - mask = 0; - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - point = &win->point[k]; - if (point->x >= cfg->input.width / 2) - mask |= BIT(k); - else - mask &= ~BIT(k); - } - if (mask == 0xf) { - /** all right **/ - ch_win_en[i] &= ~BIT(j); - } else if (mask != 0) { - /** middle need avoid pentagon **/ - if (win->point[0].x != win->point[3].x || - win->point[1].x != win->point[2].x) { - ch_win_en[i] &= ~BIT(j); - } else { - win->point[1].x = cfg->input.width / 2; - win->point[2].x = cfg->input.width / 2; - } - } - } - } - - /* deal unite right params */ - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!unite || left) - break; - if (!cfg->output[i].enable) - continue; - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - win = &tmp_cfg.win[j]; - if (!(ch_win_en[i] & BIT(j))) - continue; - mask = 0; - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - point = &win->point[k]; - if (point->x <= cfg->input.width / 2) - mask |= BIT(k); - else - mask &= ~BIT(k); - } - if (mask == 0xf) { - /** all left **/ - ch_win_en[i] &= ~BIT(j); - } else if (mask != 0) { - /** middle need avoid pentagon **/ - if (win->point[0].x != win->point[3].x || - win->point[1].x != win->point[2].x) { - ch_win_en[i] &= ~BIT(j); - } else { - win->point[0].x = ofl->unite_right_enlarge; - win->point[3].x = ofl->unite_right_enlarge; - win->point[1].x = win->point[1].x - - (cfg->input.width / 2) + - ofl->unite_right_enlarge; - win->point[2].x = win->point[2].x - - (cfg->input.width / 2) + - ofl->unite_right_enlarge; - } - } else { - /** all right **/ - win->point[0].x = win->point[0].x - - (cfg->input.width / 2) + - ofl->unite_right_enlarge; - win->point[1].x = win->point[1].x - - (cfg->input.width / 2) + - ofl->unite_right_enlarge; - win->point[2].x = win->point[2].x - - (cfg->input.width / 2) + - ofl->unite_right_enlarge; - win->point[3].x = win->point[3].x - - (cfg->input.width / 2) + - ofl->unite_right_enlarge; - } - } - } - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - if (ch_win_en[i]) { - ctrl |= RKVPSS_CMSC_EN; - ctrl |= RKVPSS_CMSC_CHN_EN(i); - } - rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_WIN + i * 4, ch_win_en[i]); - rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_MODE + i * 4, ch_win_mode[i]); - hw_in_w = rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_WIDTH); - hw_in_h = rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_HEIGHT); - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - if (!(ch_win_en[i] & BIT(j))) - continue; - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - if (tmp_cfg.win[j].point[k].x > hw_in_w || - tmp_cfg.win[j].point[k].y > hw_in_h) { - v4l2_err(&ofl->v4l2_dev, - "%s cmsc coordinate error dev_id:%d unite:%u left:%u ch:%d win:%d point:%d x:%u y:%u hw_in_w:%u hw_in_h:%u\n", - __func__, cfg->dev_id, unite, left, - i, j, k, - tmp_cfg.win[j].point[k].x, - tmp_cfg.win[j].point[k].y, - hw_in_w, hw_in_w); - return -EINVAL; - } - val = RKVPSS_CMSC_WIN_VTX(tmp_cfg.win[j].point[k].x, - tmp_cfg.win[j].point[k].y); - rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_L0_VTX + k * 8 + j * 32, val); - rkvpss_cmsc_slop(&tmp_cfg.win[j].point[k], - (k + 1 == RKVPSS_CMSC_POINT_MAX) ? - &tmp_cfg.win[j].point[0] : &tmp_cfg.win[j].point[k + 1], - &slope, &hor); - val = RKVPSS_CMSC_WIN_SLP(slope, hor); - rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_L0_SLP + k * 8 + j * 32, val); - v4l2_dbg(4, rkvpss_debug, &ofl->v4l2_dev, - "%s dev_id:%d, unite:%d left:%d ch:%d win:%d point:%d x:%u y:%u", - __func__, cfg->dev_id, unite, left, i, j, k, - tmp_cfg.win[j].point[k].x, - tmp_cfg.win[j].point[k].y); - } - if ((ch_win_mode[i] & BIT(j))) - continue; - rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_PARA + j * 4, win_color[j]); - } - } - - ctrl |= RKVPSS_CMSC_BLK_SZIE(mosaic_block); - rkvpss_hw_write(hw, RKVPSS_CMSC_CTRL, ctrl); - - val = RKVPSS_CMSC_GEN_UPD | RKVPSS_CMSC_FORCE_UPD; - rkvpss_hw_write(hw, RKVPSS_CMSC_UPDATE, val); - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s dev_id:%d, unite:%d left:%d hw ctrl:0x%x update_val:0x%x", - __func__, cfg->dev_id, unite, left, ctrl, val); - - return 0; -} - -static void aspt_config(struct file *file, - struct rkvpss_frame_cfg *cfg) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - u32 reg_base, val; - int i; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - - switch (i) { - case RKVPSS_OUTPUT_CH0: - reg_base = RKVPSS_RATIO0_BASE; - break; - case RKVPSS_OUTPUT_CH1: - reg_base = RKVPSS_RATIO1_BASE; - break; - case RKVPSS_OUTPUT_CH2: - reg_base = RKVPSS_RATIO2_BASE; - break; - case RKVPSS_OUTPUT_CH3: - reg_base = RKVPSS_RATIO3_BASE; - break; - default: - return; - } - - if (!cfg->output[i].aspt.enable) { - rkvpss_hw_write(hw, reg_base, 0); - val = RKVPSS_RATIO_FORCE_UPD | RKVPSS_RATIO_GEN_UPD; - rkvpss_hw_write(hw, reg_base + 0x4, val); - continue; - } - val = cfg->output[i].scl_width | (cfg->output[i].scl_height << 16); - rkvpss_hw_write(hw, reg_base + 0x10, val); - val = cfg->output[i].aspt.width | (cfg->output[i].aspt.height << 16); - rkvpss_hw_write(hw, reg_base + 0x14, val); - val = cfg->output[i].aspt.h_offs | (cfg->output[i].aspt.v_offs << 16); - rkvpss_hw_write(hw, reg_base + 0x18, val); - val = cfg->output[i].aspt.color_y | - (cfg->output[i].aspt.color_u << 16) | - (cfg->output[i].aspt.color_v << 24); - rkvpss_hw_write(hw, reg_base + 0x1c, val); - rkvpss_hw_write(hw, reg_base, RKVPSS_RATIO_EN); - val = RKVPSS_RATIO_FORCE_UPD | RKVPSS_RATIO_GEN_UPD; - rkvpss_hw_write(hw, reg_base + 0x4, val); - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s hw ch:%d ctrl:0x%x in_size:0x%x out_size:0x%x offset:0x%x\n", - __func__, i, - rkvpss_hw_read(hw, reg_base), - rkvpss_hw_read(hw, reg_base + 0x10), - rkvpss_hw_read(hw, reg_base + 0x14), - rkvpss_hw_read(hw, reg_base + 0x18)); - } -} - -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++; - } - while (count >= rkvpss_cfginfo_num && count != 0) { - first_cfg = list_first_entry(&ofl->cfginfo_list, struct rkvpss_ofl_cfginfo, list); - list_del_init(&first_cfg->list); - kfree(first_cfg); - count--; - } - if (rkvpss_cfginfo_num) - list_add_tail(&new_cfg->list, &ofl->cfginfo_list); - else - kfree(new_cfg); - mutex_unlock(&ofl->ofl_lock); -} - - -static int read_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - const struct vb2_mem_ops *mem_ops = hw->mem_ops; - struct sg_table *sg_tbl; - struct rkvpss_offline_buf *buf; - u32 in_ctrl, in_size, in_c_offs, unite_r_offs, val, mask, unite_off = 0, enlarge = 0; - - in_c_offs = 0; - in_ctrl = 0; - switch (cfg->input.format) { - case V4L2_PIX_FMT_NV16: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = cfg->input.stride * cfg->input.height; - in_size = cfg->input.stride * cfg->input.height * 2; - in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; - unite_off = 8; - break; - case V4L2_PIX_FMT_NV12: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = cfg->input.stride * cfg->input.height; - in_size = cfg->input.stride * cfg->input.height * 3 / 2; - in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; - unite_off = 8; - break; - case V4L2_PIX_FMT_NV61: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = cfg->input.stride * cfg->input.height; - in_size = cfg->input.stride * cfg->input.height * 2; - in_ctrl |= RKVPSS_MI_RD_INPUT_422SP | RKVPSS_MI_RD_UV_SWAP; - unite_off = 8; - break; - case V4L2_PIX_FMT_NV21: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = cfg->input.stride * cfg->input.height; - in_size = cfg->input.stride * cfg->input.height * 3 / 2; - in_ctrl |= RKVPSS_MI_RD_INPUT_420SP | RKVPSS_MI_RD_UV_SWAP; - unite_off = 8; - break; - case V4L2_PIX_FMT_RGB565: - if (cfg->input.stride < ALIGN(cfg->input.width * 2, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 2, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_BGR565; - unite_off = 16; - break; - case V4L2_PIX_FMT_RGB565X: - if (cfg->input.stride < ALIGN(cfg->input.width * 2, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 2, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_BGR565 | RKVPSS_MI_RD_RB_SWAP; - unite_off = 16; - break; - case V4L2_PIX_FMT_RGB24: - if (cfg->input.stride < ALIGN(cfg->input.width * 3, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 3, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_BGR888; - unite_off = 24; - break; - case V4L2_PIX_FMT_BGR24: - if (cfg->input.stride < ALIGN(cfg->input.width * 3, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 3, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_BGR888 | RKVPSS_MI_RD_RB_SWAP; - unite_off = 24; - break; - case V4L2_PIX_FMT_XRGB32: - if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 4, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 | RKVPSS_MI_RD_RB_SWAP; - unite_off = 32; - break; - case V4L2_PIX_FMT_XBGR32: - if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 4, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 | RKVPSS_MI_RD_RB_SWAP; - unite_off = 32; - break; - case V4L2_PIX_FMT_RGBX32: - if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 4, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 - | RKVPSS_MI_RD_RB_SWAP - | RKVPSS_MI_RD_ALPHA_SWAP; - unite_off = 32; - break; - case V4L2_PIX_FMT_BGRX32: - if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 4, 16); - in_size = cfg->input.stride * cfg->input.height; - in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 - | RKVPSS_MI_RD_RB_SWAP - | RKVPSS_MI_RD_ALPHA_SWAP; - unite_off = 32; - break; - case V4L2_PIX_FMT_FBC0: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = 0; - in_size = cfg->input.stride * cfg->input.height * 3 / 2; - in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; - break; - case V4L2_PIX_FMT_FBC2: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = 0; - in_size = cfg->input.stride * cfg->input.height * 2; - in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; - break; - case V4L2_PIX_FMT_FBC4: - if (cfg->input.stride < ALIGN(cfg->input.width, 16)) - cfg->input.stride = ALIGN(cfg->input.width, 16); - in_c_offs = 0; - in_size = cfg->input.stride * cfg->input.height * 3; - in_ctrl |= RKVPSS_MI_RD_INPUT_422SP | RKVPSS_MI_RD_FBCD_YUV444_EN; - break; - case V4L2_PIX_FMT_TILE420: - if (cfg->input.stride < ALIGN(cfg->input.width * 6, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 6, 16); - in_c_offs = 0; - in_size = cfg->input.stride * (cfg->input.height / 4); - in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; - break; - case V4L2_PIX_FMT_TILE422: - if (cfg->input.stride < ALIGN(cfg->input.width * 8, 16)) - cfg->input.stride = ALIGN(cfg->input.width * 8, 16); - in_c_offs = 0; - in_size = cfg->input.stride * (cfg->input.height / 4); - in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; - break; - default: - v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support input format:%c%c%c%c\n", - cfg->dev_id, cfg->input.format, cfg->input.format >> 8, - cfg->input.format >> 16, cfg->input.format >> 24); - return -EINVAL; - } - - buf = buf_add(file, cfg->dev_id, cfg->input.buf_fd, in_size); - if (!buf) - return -ENOMEM; - - sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); - - if (!unite) { - val = cfg->input.width; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); - val = cfg->input.height; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_HEIGHT, val); - val = sg_dma_address(sg_tbl->sgl); - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); - val += in_c_offs; - rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); - } else { - val = cfg->input.height; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_HEIGHT, val); - ofl->unite_right_enlarge = ALIGN(cfg->input.width / 2, 16) - - (cfg->input.width / 2) + 16; - - if (left) { - if (!cfg->mirror) - enlarge = UNITE_LEFT_ENLARGE; - else - enlarge = ofl->unite_right_enlarge; - val = cfg->input.width / 2 + enlarge; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); - val = sg_dma_address(sg_tbl->sgl); - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); - val += in_c_offs; - rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); - } else { - if (!cfg->mirror) - enlarge = ofl->unite_right_enlarge; - else - enlarge = UNITE_LEFT_ENLARGE; - val = cfg->input.width / 2 + enlarge; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); - val = (cfg->input.width / 2 - enlarge) * unite_off; - unite_r_offs = ALIGN_DOWN(val / 8, 16); - val = sg_dma_address(sg_tbl->sgl) + unite_r_offs; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); - val += in_c_offs; - rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); - } - } - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s unite:%d left:%d hw width:%d height:%d y_base:0x%x\n", - __func__, unite, left, - rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_WIDTH), - rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_HEIGHT), - rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_BASE)); - - if (cfg->input.format == V4L2_PIX_FMT_FBC0 || - cfg->input.format == V4L2_PIX_FMT_FBC2 || - cfg->input.format == V4L2_PIX_FMT_FBC4) { - in_ctrl |= RKVPSS_MI_RD_MODE(2) | RKVPSS_MI_RD_FBCD_OPT_DIS; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, 0); - } else { - if (cfg->input.format == V4L2_PIX_FMT_TILE420 || - cfg->input.format == V4L2_PIX_FMT_TILE422) { - in_ctrl |= RKVPSS_MI_RD_MODE(1); - switch (cfg->input.rotate) { - case ROTATE_90: - in_ctrl |= RKVPSS_MI_RD_ROT_90; - break; - case ROTATE_180: - in_ctrl |= RKVPSS_MI_RD_ROT_180; - break; - case ROTATE_270: - in_ctrl |= RKVPSS_MI_RD_ROT_270; - break; - default: - in_ctrl |= RKVPSS_MI_RD_ROT_0; - break; - } - } - val = cfg->input.stride; - rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, val); - } - - mask = RKVPSS_MI_RD_GROUP_MODE(3) | RKVPSS_MI_RD_BURST16_LEN; - rkvpss_hw_set_bits(hw, RKVPSS_MI_RD_CTRL, ~mask, in_ctrl); - rkvpss_hw_write(hw, RKVPSS_MI_RD_INIT, RKVPSS_MI_RD_FORCE_UPD); - - return 0; -} - -static void crop_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - int i; - u32 reg, val, crop_en; - - crop_en = 0; - if (!unite) { - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - reg = RKVPSS_CROP0_0_H_OFFS; - val = cfg->output[i].crop_h_offs; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_OFFS; - val = cfg->output[i].crop_v_offs; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_H_SIZE; - val = cfg->output[i].crop_width; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_SIZE; - val = cfg->output[i].crop_height; - rkvpss_hw_write(hw, reg + i * 0x10, val); - crop_en |= RKVPSS_CROP_CHN_EN(i); - } - } else { - if (left) { - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - - reg = RKVPSS_CROP0_0_H_OFFS; - val = cfg->output[i].crop_h_offs; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_OFFS; - val = cfg->output[i].crop_v_offs; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_H_SIZE; - /*if no scale, left don't enlarge*/ - if (cfg->output[i].crop_width == cfg->output[i].scl_width) - val = cfg->output[i].crop_width / 2; - else - val = cfg->output[i].crop_width / 2 + UNITE_LEFT_ENLARGE; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_SIZE; - val = cfg->output[i].crop_height; - rkvpss_hw_write(hw, reg + i * 0x10, val); - crop_en |= RKVPSS_CROP_CHN_EN(i); - } - } else { - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - reg = RKVPSS_CROP0_0_H_OFFS; - val = ofl->unite_params[i].quad_crop_w; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_OFFS; - val = cfg->output[i].crop_v_offs; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_H_SIZE; - val = cfg->output[i].crop_width / 2 + - ofl->unite_right_enlarge - - ofl->unite_params[i].quad_crop_w; - rkvpss_hw_write(hw, reg + i * 0x10, val); - reg = RKVPSS_CROP0_0_V_SIZE; - val = cfg->output[i].crop_height; - rkvpss_hw_write(hw, reg + i * 0x10, val); - crop_en |= RKVPSS_CROP_CHN_EN(i); - } - } - } - rkvpss_hw_write(hw, RKVPSS_CROP0_CTRL, crop_en); - rkvpss_hw_write(hw, RKVPSS_CROP0_UPDATE, RKVPSS_CROP_FORCE_UPD); - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s unite:%d left:%d hw ch:%d h_offs:%d v_offs:%d width:%d height:%d\n", - __func__, unite, left, i, - rkvpss_hw_read(hw, RKVPSS_CROP0_0_H_OFFS + i * 0x10), - rkvpss_hw_read(hw, RKVPSS_CROP0_0_V_OFFS + i * 0x10), - rkvpss_hw_read(hw, RKVPSS_CROP0_0_H_SIZE + i * 0x10), - rkvpss_hw_read(hw, RKVPSS_CROP0_0_V_SIZE + i * 0x10)); - } -} - -static int write_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - const struct vb2_mem_ops *mem_ops = hw->mem_ops; - struct sg_table *sg_tbl; - struct rkvpss_offline_buf *buf; - struct rkvpss_output_ch out_ch[RKVPSS_OUTPUT_MAX] = { 0 }; - int i; - u32 w, h, val, reg, mask, mi_update, flip_en, unite_off = 0; - bool ch_en = false, wr_uv_swap = false; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!hw->is_ofl_ch[i] && cfg->output[i].enable) { - v4l2_err(&ofl->v4l2_dev, - "dev_id:%d ch%d no select for offline mode, set to disable\n", - cfg->dev_id, i); - cfg->output[i].enable = 0; - } - - if (!cfg->output[i].enable) - continue; - ch_en = true; - - if (cfg->output[i].aspt.enable) { - w = cfg->output[i].aspt.width; - h = cfg->output[i].aspt.height; - } else { - w = cfg->output[i].scl_width; - h = cfg->output[i].scl_height; - } - if (i == RKVPSS_OUTPUT_CH1) { - bool is_fmt_find = true; - - switch (cfg->output[i].format) { - case V4L2_PIX_FMT_RGB565: - if (cfg->output[i].stride < ALIGN(w * 2, 16)) - cfg->output[i].stride = ALIGN(w * 2, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB565 | RKVPSS_MI_CHN_WR_RB_SWAP; - break; - case V4L2_PIX_FMT_RGB24: - if (cfg->output[i].stride < ALIGN(w * 3, 16)) - cfg->output[i].stride = ALIGN(w * 3, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB888 | RKVPSS_MI_CHN_WR_RB_SWAP; - break; - case V4L2_PIX_FMT_RGB565X: - if (cfg->output[i].stride < ALIGN(w * 2, 16)) - cfg->output[i].stride = ALIGN(w * 2, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB565; - break; - case V4L2_PIX_FMT_BGR24: - if (cfg->output[i].stride < ALIGN(w * 3, 16)) - cfg->output[i].stride = ALIGN(w * 3, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB888; - break; - case V4L2_PIX_FMT_XBGR32: - if (cfg->output[i].stride < ALIGN(w * 4, 16)) - cfg->output[i].stride = ALIGN(w * 4, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_ARGB888; - break; - case V4L2_PIX_FMT_XRGB32: - if (cfg->output[i].stride < ALIGN(w * 4, 16)) - cfg->output[i].stride = ALIGN(w * 4, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_ARGB888 - | RKVPSS_MI_CHN_WR_RB_SWAP; - break; - default: - is_fmt_find = false; - } - if (is_fmt_find) { - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; - out_ch[i].size = cfg->output[i].stride * h; - continue; - } - } - switch (cfg->output[i].format) { - case V4L2_PIX_FMT_UYVY: - if (cfg->output[i].stride < ALIGN(w * 2, 16)) - cfg->output[i].stride = ALIGN(w * 2, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_422P | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; - out_ch[i].size = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_NV16: - if (cfg->output[i].stride < ALIGN(w, 16)) - cfg->output[i].stride = ALIGN(w, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; - out_ch[i].size = cfg->output[i].stride * h * 2; - out_ch[i].c_offs = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_NV12: - if (cfg->output[i].stride < ALIGN(w, 16)) - cfg->output[i].stride = ALIGN(w, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV420; - out_ch[i].size = cfg->output[i].stride * h * 3 / 2; - out_ch[i].c_offs = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_GREY: - if (cfg->output[i].stride < ALIGN(w, 16)) - cfg->output[i].stride = ALIGN(w, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV400; - out_ch[i].size = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_VYUY: - if (cfg->output[i].stride < ALIGN(w * 2, 16)) - cfg->output[i].stride = ALIGN(w * 2, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_422P | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; - out_ch[i].size = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_NV61: - if (cfg->output[i].stride < ALIGN(w, 16)) - cfg->output[i].stride = ALIGN(w, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; - out_ch[i].size = cfg->output[i].stride * h * 2; - out_ch[i].c_offs = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_NV21: - if (cfg->output[i].stride < ALIGN(w, 16)) - cfg->output[i].stride = ALIGN(w, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV420; - out_ch[i].size = cfg->output[i].stride * h * 3 / 2; - out_ch[i].c_offs = cfg->output[i].stride * h; - break; - case V4L2_PIX_FMT_TILE420: - if (cfg->output[i].stride < ALIGN(w * 6, 16)) - cfg->output[i].stride = ALIGN(w * 6, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_YUV420; - out_ch[i].size = cfg->output[i].stride * (h / 4); - out_ch[i].c_offs = 0; - break; - case V4L2_PIX_FMT_TILE422: - if (cfg->output[i].stride < ALIGN(w * 8, 16)) - cfg->output[i].stride = ALIGN(w * 8, 16); - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_YUV422; - out_ch[i].size = cfg->output[i].stride * (h / 4); - out_ch[i].c_offs = 0; - break; - default: - v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support output ch%d format:%c%c%c%c\n", - cfg->dev_id, i, - cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24); - return -EINVAL; - } - out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; - } - if (!ch_en) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d no output channel enable\n", cfg->dev_id); - return -EINVAL; - } - - mi_update = 0; - flip_en = 0; - mask = 0; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (hw->is_ofl_ch[i]) - mask |= RKVPSS_MI_CHN_V_FLIP(i); - if (!cfg->output[i].enable) - continue; - buf = buf_add(file, cfg->dev_id, cfg->output[i].buf_fd, out_ch[i].size); - if (!buf) - goto free_buf; - - if (unite && !left) - unite_off = (ALIGN_DOWN(cfg->output[i].scl_width / 2, 16) * 8) / 8; - - if (cfg->output[i].aspt.enable) - h = cfg->output[i].aspt.height; - else - h = cfg->output[i].scl_height; - - sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); - val = sg_dma_address(sg_tbl->sgl) + unite_off; - reg = RKVPSS_MI_CHN0_WR_Y_BASE; - rkvpss_hw_write(hw, reg + i * 0x100, val); - reg = RKVPSS_MI_CHN0_WR_CB_BASE; - val += out_ch[i].c_offs; - rkvpss_hw_write(hw, reg + i * 0x100, val); - - reg = RKVPSS_MI_CHN0_WR_Y_STRIDE; - val = cfg->output[i].stride; - rkvpss_hw_write(hw, reg + i * 0x100, val); - reg = RKVPSS_MI_CHN0_WR_Y_SIZE; - - if (cfg->output[i].format == V4L2_PIX_FMT_TILE420 || - cfg->output[i].format == V4L2_PIX_FMT_TILE422) - val = cfg->output[i].stride * (ALIGN(h, 4) / 4); - else - val = cfg->output[i].stride * h; - rkvpss_hw_write(hw, reg + i * 0x100, val); - reg = RKVPSS_MI_CHN0_WR_CB_SIZE; - val = out_ch[i].size - val; - rkvpss_hw_write(hw, reg + i * 0x100, val); - - reg = RKVPSS_MI_CHN0_WR_CTRL; - val = out_ch[i].ctrl; - rkvpss_hw_write(hw, reg + i * 0x100, val); - - if (cfg->output[i].flip && - !(cfg->output[i].format == V4L2_PIX_FMT_TILE420) && - !(cfg->output[i].format == V4L2_PIX_FMT_TILE422)) { - flip_en |= RKVPSS_MI_CHN_V_FLIP(i); - - switch (cfg->output[i].format) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - val = cfg->output[i].stride / 2; - break; - case V4L2_PIX_FMT_XBGR32: - case V4L2_PIX_FMT_XRGB32: - val = cfg->output[i].stride / 4; - break; - default: - val = cfg->output[i].stride; - break; - } - val = val * h; - reg = RKVPSS_MI_CHN0_WR_Y_PIC_SIZE; - rkvpss_hw_write(hw, reg + i * 0x100, val); - } - mi_update |= (RKVPSS_MI_CHN0_FORCE_UPD << i); - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s unite:%d left:%d hw ch:%d y_size:%d y_stride:%d y_pic_size:%d y_base:0x%x", - __func__, unite, left, i, - rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_SIZE + i * 100), - rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_STRIDE + i * 100), - rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_PIC_SIZE + i * 100), - rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_BASE + i * 100)); - } - - if (flip_en) - rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_VFLIP_CTRL, mask, flip_en); - - /* config output uv swap */ - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (cfg->output[i].enable && - (cfg->output[i].format == V4L2_PIX_FMT_VYUY || - cfg->output[i].format == V4L2_PIX_FMT_NV21 || - cfg->output[i].format == V4L2_PIX_FMT_NV61)) - wr_uv_swap = true; - } - if (wr_uv_swap) { - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (cfg->output[i].enable && (cfg->output[i].format == V4L2_PIX_FMT_UYVY || - cfg->output[i].format == V4L2_PIX_FMT_NV12 || - cfg->output[i].format == V4L2_PIX_FMT_NV16)) { - v4l2_err(&ofl->v4l2_dev, - "dev_id:%d wr_uv_swap need to be consistent\n", - cfg->dev_id); - return -EAGAIN; - } - } - } - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (cfg->output[i].format == V4L2_PIX_FMT_VYUY || - cfg->output[i].format == V4L2_PIX_FMT_NV21 || - cfg->output[i].format == V4L2_PIX_FMT_NV61) { - mask = RKVPSS_MI_WR_UV_SWAP; - val = RKVPSS_MI_WR_UV_SWAP; - rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_CTRL, mask, val); - break; - } - } - - for (i = 0; i <= RKVPSS_OUTPUT_CH1; i++) { - if (cfg->output[i].format == V4L2_PIX_FMT_TILE420 || - cfg->output[i].format == V4L2_PIX_FMT_TILE422) { - mask = RKVPSS_MI_WR_TILE_SEL(3); - val = RKVPSS_MI_WR_TILE_SEL(i + 1); - rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_CTRL, mask, val); - } - } - - /* need update two for online2 mode */ - rkvpss_hw_write(hw, RKVPSS_MI_WR_INIT, mi_update); - rkvpss_hw_write(hw, RKVPSS_MI_WR_INIT, mi_update); - - return 0; - -free_buf: - for (i -= 1; i >= 0; i--) { - if (!cfg->output[i].enable) - continue; - buf_del(file, cfg->dev_id, cfg->output[i].buf_fd, false, true); - } - buf_del(file, cfg->dev_id, cfg->input.buf_fd, false, true); - return -ENOMEM; -} - -static void calc_unite_scl_params(struct file *file, struct rkvpss_frame_cfg *cfg) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_unite_scl_params *params; - int i; - u32 right_scl_need_size_y, right_scl_need_size_c; - u32 left_in_used_size_y, left_in_used_size_c; - u32 right_fst_position_y, right_fst_position_c; - u32 right_y_crop_total; - u32 right_c_crop_total; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (cfg->output[i].enable == 0) - continue; - params = &ofl->unite_params[i]; - params->y_w_fac = (cfg->output[i].crop_width - 1) * 4096 / - (cfg->output[i].scl_width - 1); - params->c_w_fac = (cfg->output[i].crop_width / 2 - 1) * 4096 / - (cfg->output[i].scl_width / 2 - 1); - params->y_h_fac = (cfg->output[i].crop_height - 1) * 4096 / - (cfg->output[i].scl_height - 1); - params->c_h_fac = (cfg->output[i].crop_height - 1) * 4096 / - (cfg->output[i].scl_height - 1); - - right_fst_position_y = cfg->output[i].scl_width / 2 * - params->y_w_fac; - right_fst_position_c = cfg->output[i].scl_width / 2 / 2 * - params->c_w_fac; - - left_in_used_size_y = right_fst_position_y >> 12; - left_in_used_size_c = (right_fst_position_c >> 12) * 2; - - params->y_w_phase = right_fst_position_y & 0xfff; - params->c_w_phase = right_fst_position_c & 0xfff; - - right_scl_need_size_y = cfg->output[i].crop_width - - left_in_used_size_y; - params->right_scl_need_size_y = right_scl_need_size_y; - right_scl_need_size_c = cfg->output[i].crop_width - - left_in_used_size_c; - params->right_scl_need_size_c = right_scl_need_size_c; - - if (i == 0 && cfg->output[i].crop_width != cfg->output[i].scl_width) { - right_y_crop_total = cfg->output[i].crop_width / 2 + - ofl->unite_right_enlarge - - right_scl_need_size_y - 3; - right_c_crop_total = cfg->output[i].crop_width / 2 + - ofl->unite_right_enlarge - - right_scl_need_size_c - 6; - } else { - right_y_crop_total = cfg->output[i].crop_width / 2 + - ofl->unite_right_enlarge - - right_scl_need_size_y; - right_c_crop_total = cfg->output[i].crop_width / 2 + - ofl->unite_right_enlarge - - right_scl_need_size_c; - } - - params->quad_crop_w = ALIGN_DOWN(min(right_y_crop_total, right_c_crop_total), 2); - - params->scl_in_crop_w_y = right_y_crop_total - params->quad_crop_w; - params->scl_in_crop_w_c = right_c_crop_total - params->quad_crop_w; - - if (rkvpss_debug >= 4) { - v4l2_info(&ofl->v4l2_dev, - "%s dev_id:%d seq:%d ch:%d y_w_fac:%u c_w_fac:%u y_h_fac:%u c_h_fac:%u\n", - __func__, cfg->dev_id, cfg->sequence, i, params->y_w_fac, - params->c_w_fac, params->y_h_fac, params->c_h_fac); - v4l2_info(&ofl->v4l2_dev, - "\t\t\t\t\t\t unite_right_enlarge:%u", - ofl->unite_right_enlarge); - v4l2_info(&ofl->v4l2_dev, - "\t\t\t\t\t\t y_w_phase:%u c_w_phase:%u quad_crop_w:%u scl_in_crop_w_y:%u scl_in_crop_w_c:%u\n", - params->y_w_phase, params->c_w_phase, - params->quad_crop_w, - params->scl_in_crop_w_y, params->scl_in_crop_w_c); - v4l2_info(&ofl->v4l2_dev, - "\t\t\t\t\t\t right_scl_need_size_y:%u right_scl_need_size_c:%u\n", - params->right_scl_need_size_y, - params->right_scl_need_size_c); - } - } -} - -static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - u32 update, val, mask; - int ret, i; - bool left_tmp; - - if (!unite || left) - add_cfginfo(ofl, cfg); - - init_completion(&ofl->cmpl); - ofl->mode_sel_en = false; - - ret = read_config(file, cfg, unite, left); - if (ret < 0) - return ret; - - if (unite && left) - calc_unite_scl_params(file, cfg); - - if (unite && cfg->mirror) - left_tmp = !left; - else - left_tmp = left; - - ret = cmsc_config(ofl, cfg, unite, left_tmp); - if (ret) - return ret; - crop_config(file, cfg, unite, left_tmp); - scale_config(file, cfg, unite, left_tmp); - if (!unite) - aspt_config(file, cfg); - ret = write_config(file, cfg, unite, left_tmp); - if (ret < 0) - return ret; - - mask = 0; - val = 0; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!hw->is_ofl_ch[i]) - continue; - mask |= (RKVPSS_ISP2VPSS_CHN0_SEL(3) << i * 2); - if (hw->is_ofl_cmsc) - mask |= RKVPSS_ISP2VPSS_ONLINE2_CMSC_EN; - if (cfg->output[i].enable) - val |= (RKVPSS_ISP2VPSS_CHN0_SEL(1) << i * 2); - } - rkvpss_hw_set_bits(hw, RKVPSS_VPSS_ONLINE, mask, val); - - update = 0; - mask = hw->is_ofl_cmsc ? RKVPSS_MIR_EN : 0; - val = (mask && cfg->mirror) ? RKVPSS_MIR_EN : 0; - if (mask) { - rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CTRL, mask, val); - update |= RKVPSS_MIR_FORCE_UPD; - } - update |= RKVPSS_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD | RKVPSS_MIR_GEN_UPD; - rkvpss_hw_write(hw, RKVPSS_VPSS_UPDATE, update); - rkvpss_hw_set_bits(hw, RKVPSS_VPSS_IMSC, 0, RKVPSS_ALL_FRM_END); - - rkvpss_hw_write(hw, RKVPSS_MI_RD_START, RKVPSS_MI_RD_ST); - - ret = wait_for_completion_timeout(&ofl->cmpl, msecs_to_jiffies(500)); - if (!ret) { - v4l2_err(&ofl->v4l2_dev, "working timeout\n"); - ret = -EAGAIN; - } else { - ret = 0; - } - - return ret; -} - -static int rkvpss_module_get(struct file *file, - struct rkvpss_module_sel *get) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - int i, ret = 0; - - mutex_lock(&hw->dev_lock); - if (hw->is_ofl_cmsc) - get->mirror_cmsc_en = 1; - else - get->mirror_cmsc_en = 0; - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (hw->is_ofl_ch[i]) - get->ch_en[i] = 1; - else - get->ch_en[i] = 0; - } - mutex_unlock(&hw->dev_lock); - - return ret; -} - -static int rkvpss_module_sel(struct file *file, - struct rkvpss_module_sel *sel) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - struct rkvpss_hw_dev *hw = ofl->hw; - struct rkvpss_device *vpss; - int i, ret = 0; - - mutex_lock(&hw->dev_lock); - - if (!ofl->mode_sel_en) { - v4l2_err(&ofl->v4l2_dev, "already set module_sel\n"); - ret = -EINVAL; - goto unlock; - } - - for (i = 0; i < hw->dev_num; i++) { - vpss = hw->vpss[i]; - if (vpss && (vpss->vpss_sdev.state & VPSS_START)) { - v4l2_err(&ofl->v4l2_dev, "no support set mode when vpss working\n"); - ret = -EINVAL; - goto unlock; - } - } - - hw->is_ofl_cmsc = !!sel->mirror_cmsc_en; - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) - hw->is_ofl_ch[i] = !!sel->ch_en[i]; -unlock: - mutex_unlock(&hw->dev_lock); - return ret; -} - -static int rkvpss_check_params(struct file *file, struct rkvpss_frame_cfg *cfg, bool *unite) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - int i, ret = 0, tile_num = 0; - - /* check dev id out of range */ - if (cfg->dev_id >= DEV_NUM_MAX) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d is out of range. range[0, %d]\n", - cfg->dev_id, DEV_NUM_MAX); - ret = -EINVAL; - goto end; - } - - /* set unite mode */ - if (cfg->input.width > RKVPSS_MAX_WIDTH) - *unite = true; - else - *unite = false; - - /* check input format */ - switch (cfg->input.format) { - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_RGB24: - case V4L2_PIX_FMT_BGR24: - case V4L2_PIX_FMT_XRGB32: - case V4L2_PIX_FMT_XBGR32: - case V4L2_PIX_FMT_RGBX32: - case V4L2_PIX_FMT_BGRX32: - case V4L2_PIX_FMT_FBC0: - case V4L2_PIX_FMT_FBC2: - case V4L2_PIX_FMT_FBC4: - case V4L2_PIX_FMT_TILE420: - case V4L2_PIX_FMT_TILE422: - break; - default: - v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support input format:%c%c%c%c\n", - cfg->dev_id, cfg->input.format, cfg->input.format >> 8, - cfg->input.format >> 16, cfg->input.format >> 24); - ret = -EINVAL; - goto end; - } - - /* check input size */ - if (cfg->input.width > RKVPSS_UNITE_MAX_WIDTH || - cfg->input.height > RKVPSS_UNITE_MAX_HEIGHT || - cfg->input.width < RKVPSS_MIN_WIDTH || - cfg->input.height < RKVPSS_MIN_HEIGHT) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d input size not support width:%d height:%d\n", - cfg->dev_id, cfg->input.width, cfg->input.height); - ret = -EINVAL; - goto end; - } - - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - /* check output format */ - switch (cfg->output[i].format) { - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_VYUY: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_NV21: - break; - case V4L2_PIX_FMT_TILE420: - case V4L2_PIX_FMT_TILE422: - if (i == RKVPSS_OUTPUT_CH0 || i == RKVPSS_OUTPUT_CH1) { - tile_num++; - if (tile_num > 1) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d only ch0 or ch1 can tile write\n", - cfg->dev_id); - ret = -EINVAL; - goto end; - } - if (cfg->output[i].flip) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d tile write no support flip\n", - cfg->dev_id, i); - ret = -EINVAL; - goto end; - } - } else { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", - cfg->dev_id, i, cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24); - ret = -EINVAL; - goto end; - } - break; - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB24: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_BGR24: - case V4L2_PIX_FMT_XBGR32: - case V4L2_PIX_FMT_XRGB32: - if (i != RKVPSS_OUTPUT_CH1) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", - cfg->dev_id, i, cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24); - ret = -EINVAL; - goto end; - } - break; - default: - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", - cfg->dev_id, i, cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24); - ret = -EINVAL; - goto end; - } - - /* check output size */ - if (cfg->output[i].scl_width > RKVPSS_UNITE_MAX_WIDTH || - cfg->output[i].scl_height > RKVPSS_UNITE_MAX_HEIGHT || - cfg->output[i].scl_width < RKVPSS_MIN_WIDTH || - cfg->output[i].scl_height < RKVPSS_MIN_HEIGHT) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d output size not support width:%d height:%d\n", - cfg->dev_id, cfg->output[i].scl_width, cfg->output[i].scl_height); - ret = -EINVAL; - goto end; - } - - /* check crop */ - cfg->output[i].crop_h_offs = ALIGN(cfg->output[i].crop_h_offs, 2); - cfg->output[i].crop_v_offs = ALIGN(cfg->output[i].crop_v_offs, 2); - cfg->output[i].crop_width = ALIGN(cfg->output[i].crop_width, 2); - cfg->output[i].crop_height = ALIGN(cfg->output[i].crop_height, 2); - if (!cfg->input.rotate || cfg->input.rotate == 2) { - if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > cfg->input.width) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d inval crop(offs:%d w:%d) input width:%d\n", - i, cfg->dev_id, cfg->output[i].crop_h_offs, - cfg->output[i].crop_width, cfg->input.width); - ret = -EINVAL; - goto end; - } - if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > cfg->input.height) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d inval crop(offs:%d h:%d) input height:%d\n", - cfg->dev_id, i, cfg->output[i].crop_v_offs, - cfg->output[i].crop_height, cfg->input.height); - ret = -EINVAL; - goto end; - } - } else { - if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > cfg->input.height) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d rotate inval crop(offs:%d w:%d) input height:%d\n", - i, cfg->dev_id, cfg->output[i].crop_h_offs, - cfg->output[i].crop_width, cfg->input.height); - ret = -EINVAL; - goto end; - } - if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > cfg->input.width) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d rotate inval crop(offs:%d h:%d) input width:%d\n", - cfg->dev_id, i, cfg->output[i].crop_v_offs, - cfg->output[i].crop_height, cfg->input.width); - ret = -EINVAL; - goto end; - } - } - if (*unite) { - if (cfg->output[i].crop_h_offs != (cfg->input.width - - (cfg->output[i].crop_h_offs + - cfg->output[i].crop_width))) { - v4l2_err(&ofl->v4l2_dev, " dev_id:%d ch%d unite crop_v need centered crop(h_offs:%d w:%d) input width:%d\n", - cfg->dev_id, i, cfg->output[i].crop_h_offs, - cfg->output[i].crop_width, cfg->input.width); - ret = -EINVAL; - goto end; - } - } - - /* check scale */ - if (i == RKVPSS_OUTPUT_CH2 || i == RKVPSS_OUTPUT_CH3) { - if (cfg->output[i].crop_width != cfg->output[i].scl_width && - cfg->output[i].crop_height != cfg->output[i].scl_height) { - if ((!*unite && cfg->output[i].scl_width > 1920) || - (*unite && cfg->output[i].scl_width > 1920 * 2)) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d single scale max width 1920\n", - cfg->dev_id, i); - ret = -EINVAL; - goto end; - } - } - } - } - - /* check rotate */ - switch (cfg->input.rotate) { - case ROTATE_90: - case ROTATE_180: - case ROTATE_270: - if (cfg->input.format != V4L2_PIX_FMT_TILE420 && - cfg->input.format != V4L2_PIX_FMT_TILE422) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d input format:%c%c%c%c not support rotate\n", - cfg->dev_id, cfg->input.format, cfg->input.format >> 8, - cfg->input.format >> 16, cfg->input.format >> 24); - ret = -EINVAL; - goto end; - } - break; - default: - break; - } - - /** unite constraints **/ - if (*unite) { - if (cfg->input.format == V4L2_PIX_FMT_FBC0 || - cfg->input.format == V4L2_PIX_FMT_FBC2 || - cfg->input.format == V4L2_PIX_FMT_FBC4 || - cfg->input.format == V4L2_PIX_FMT_TILE420 || - cfg->input.format == V4L2_PIX_FMT_TILE422) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support input this format:%c%c%c%c\n", - cfg->dev_id, cfg->input.format, cfg->input.format >> 8, - cfg->input.format >> 16, cfg->input.format >> 24); - ret = -EINVAL; - goto end; - } - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - if (!cfg->output[i].enable) - continue; - if (cfg->output[i].format != V4L2_PIX_FMT_NV12 && - cfg->output[i].format != V4L2_PIX_FMT_NV16 && - cfg->output[i].format != V4L2_PIX_FMT_NV21 && - cfg->output[i].format != V4L2_PIX_FMT_NV61) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support output this format:%c%c%c%c\n", - cfg->dev_id, cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24); - ret = -EINVAL; - goto end; - } - if (cfg->output[i].scl_width > cfg->input.width) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite horizontal no support scale up\n", - cfg->dev_id); - ret = -EINVAL; - goto end; - } - if (cfg->output[i].aspt.enable) { - v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support aspt\n", - cfg->dev_id); - ret = -EINVAL; - goto end; - } - } - } - -end: - return ret; -} - -static int rkvpss_prepare_run(struct file *file, struct rkvpss_frame_cfg *cfg) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - int ret = 0; - bool unite; - int i, j, k; - s64 us = 0; - u64 ns; - ktime_t t = 0; - - 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; - - ret = rkvpss_check_params(file, cfg, &unite); - if (ret < 0) - goto end; - - /******** show cfg info ********/ - if (rkvpss_debug >= 2) { - t = ktime_get(); - - v4l2_info(&ofl->v4l2_dev, - "%s dev_id:%d seq:%d mirror:%d input:%dx%d buffd:%d format:%c%c%c%c stride:%d rotate:%d\n", - __func__, cfg->dev_id, cfg->sequence, cfg->mirror, - cfg->input.width, cfg->input.height, cfg->input.buf_fd, - cfg->input.format, cfg->input.format >> 8, - cfg->input.format >> 16, cfg->input.format >> 24, - cfg->input.stride, cfg->input.rotate); - for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) { - v4l2_info(&ofl->v4l2_dev, - "\t\t\tch%d enable:%d crop:(%d,%d)/%dx%d scl:%dx%d flip:%d buffd:%d format:%c%c%c%c stride:%d\n", - i, cfg->output[i].enable, - cfg->output[i].crop_h_offs, cfg->output[i].crop_v_offs, - cfg->output[i].crop_width, cfg->output[i].crop_height, - cfg->output[i].scl_width, cfg->output[i].scl_height, - cfg->output[i].flip, cfg->output[i].buf_fd, - cfg->output[i].format, cfg->output[i].format >> 8, - cfg->output[i].format >> 16, cfg->output[i].format >> 24, - cfg->output[i].stride); - if (rkvpss_debug < 4) - break; - if (!cfg->output[i].enable) - continue; - v4l2_info(&ofl->v4l2_dev, - "\t\t\tcmsc mosaic_block:%d width_ro:%d height_ro:%d\n", - cfg->output[i].cmsc.mosaic_block, - cfg->output[i].cmsc.width_ro, - cfg->output[i].cmsc.height_ro); - for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { - if (!cfg->output[i].cmsc.win[j].win_en) - continue; - v4l2_info(&ofl->v4l2_dev, - "\t\t\t\twin:%d win_en:%u mode:%u color_y:%u color_u:%u color_v:%u color_a:%u\n", - j, - cfg->output[i].cmsc.win[j].win_en, - cfg->output[i].cmsc.win[j].mode, - cfg->output[i].cmsc.win[j].cover_color_y, - cfg->output[i].cmsc.win[j].cover_color_u, - cfg->output[i].cmsc.win[j].cover_color_v, - cfg->output[i].cmsc.win[j].cover_color_a); - for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { - v4l2_info(&ofl->v4l2_dev, - "\t\t\t\t\tpoint:%d x:%d y:%d\n", - k, - cfg->output[i].cmsc.win[j].point[k].x, - cfg->output[i].cmsc.win[j].point[k].y); - } - } - v4l2_info(&ofl->v4l2_dev, - "\t\t\taspt_en:%d w:%d h:%d h_offs:%d v_offs:%d color_y:%d color_u:%d color_v:%d\n", - cfg->output[i].aspt.enable, - cfg->output[i].aspt.width, - cfg->output[i].aspt.height, - cfg->output[i].aspt.h_offs, - cfg->output[i].aspt.v_offs, - cfg->output[i].aspt.color_y, - cfg->output[i].aspt.color_u, - cfg->output[i].aspt.color_v); - } - } - - if (!unite) { - ret = rkvpss_ofl_run(file, cfg, false, false); - if (ret < 0) - goto end; - } else { - ret = rkvpss_ofl_run(file, cfg, true, true); - if (ret < 0) { - v4l2_err(&ofl->v4l2_dev, "unite left error\n"); - goto end; - } - ret = rkvpss_ofl_run(file, cfg, true, false); - if (ret < 0) { - v4l2_err(&ofl->v4l2_dev, "unite right error\n"); - goto end; - } - } - - if (rkvpss_debug >= 2) { - us = ktime_us_delta(ktime_get(), t); - v4l2_info(&ofl->v4l2_dev, - "%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; - -end: - return ret; -} - -static long rkvpss_ofl_ioctl(struct file *file, void *fh, - bool valid_prio, unsigned int cmd, void *arg) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - long ret = 0; - bool unite; - - ofl->pm_need_wait = true; - - if (!arg) { - ret = -EINVAL; - goto out; - } - - switch (cmd) { - case RKVPSS_CMD_MODULE_SEL: - ret = rkvpss_module_sel(file, arg); - break; - case RKVPSS_CMD_MODULE_GET: - ret = rkvpss_module_get(file, arg); - break; - case RKVPSS_CMD_FRAME_HANDLE: - ret = rkvpss_prepare_run(file, arg); - break; - case RKVPSS_CMD_BUF_ADD: - ret = rkvpss_ofl_buf_add(file, arg); - break; - case RKVPSS_CMD_BUF_DEL: - rkvpss_ofl_buf_del(file, arg); - break; - case RKVPSS_CMD_CHECKPARAMS: - ret = rkvpss_check_params(file, arg, &unite); - 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 = rkvpss_ofl_ioctl, -}; - -static int ofl_open(struct file *file) -{ - struct rkvpss_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, rkvpss_debug, &ofl->v4l2_dev, - "%s file:%p ret:%d\n", __func__, file, ret); - return (ret > 0) ? 0 : ret; -} - -static int ofl_release(struct file *file) -{ - struct rkvpss_offline_dev *ofl = video_drvdata(file); - - v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, - "%s file:%p\n", __func__, file); - - v4l2_fh_release(file); - buf_del(file, 0, 0, true, false); - 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 = "rkvpss-offline", - .vfl_dir = VFL_DIR_RX, - .fops = &offline_fops, - .ioctl_ops = &offline_ioctl_ops, - .minor = -1, - .release = video_device_release_empty, -}; void rkvpss_offline_irq(struct rkvpss_hw_dev *hw, u32 irq) { - struct rkvpss_offline_dev *ofl = &hw->ofl_dev; - - v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, - "%s 0x%x\n", __func__, irq); - - if (!completion_done(&ofl->cmpl)) - complete(&ofl->cmpl); + if (hw->vpss_ver == VPSS_V10) + rkvpss_offline_irq_v1(hw, irq); } int rkvpss_register_offline(struct rkvpss_hw_dev *hw) { - struct rkvpss_offline_dev *ofl = &hw->ofl_dev; - struct v4l2_device *v4l2_dev; - struct video_device *vfd; - int ret; + int ret = -EINVAL; - 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; + if (hw->vpss_ver == VPSS_V10) + ret = rkvpss_register_offline_v1(hw); - mutex_init(&ofl->apilock); - ofl->vfd = offline_videodev; - ofl->mode_sel_en = true; - vfd = &ofl->vfd; - vfd->device_caps = V4L2_CAP_STREAMING; - vfd->lock = &ofl->apilock; - 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); - INIT_LIST_HEAD(&ofl->cfginfo_list); - mutex_init(&ofl->ofl_lock); - rkvpss_offline_proc_init(ofl); - ofl->pm_need_wait = false; - init_completion(&ofl->pm_cmpl); - return 0; -unreg_v4l2: - mutex_destroy(&ofl->apilock); - v4l2_device_unregister(v4l2_dev); return ret; } 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); + if (hw->vpss_ver == VPSS_V10) + rkvpss_unregister_offline_v1(hw); } + diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.h b/drivers/media/platform/rockchip/vpss/vpss_offline.h index 78b7434d6a9c..ad858e9e63e9 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.h +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.h @@ -7,7 +7,7 @@ #define UNITE_ENLARGE 16 #define UNITE_LEFT_ENLARGE 16 -#include "hw.h" +extern char rkvpss_regfile[RKVPSS_REGFILE_LEN]; struct rkvpss_ofl_incfginfo { int width; @@ -82,5 +82,6 @@ struct rkvpss_offline_dev { int rkvpss_register_offline(struct rkvpss_hw_dev *hw); void rkvpss_unregister_offline(struct rkvpss_hw_dev *hw); void rkvpss_offline_irq(struct rkvpss_hw_dev *hw, u32 irq); +void rkvpss_dump_reg(struct rkvpss_offline_dev *ofl, int sequence, int size); #endif diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline_v1.c b/drivers/media/platform/rockchip/vpss/vpss_offline_v1.c new file mode 100644 index 000000000000..7ec40d3da13b --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_offline_v1.c @@ -0,0 +1,2271 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "vpss.h" +#include "common.h" +#include "stream.h" +#include "dev.h" +#include "vpss_offline.h" +#include "hw.h" +#include "regs_v1.h" + +#include "procfs.h" +#include "vpss_offline_v1.h" + +struct rkvpss_output_ch { + u32 ctrl; + u32 size; + u32 c_offs; +}; + +struct rkvpss_offline_buf { + struct list_head list; + struct vb2_buffer vb; + struct vb2_queue vb2_queue; + struct file *file; + struct dma_buf *dbuf; + void *mem; + int dev_id; + int fd; + bool alloc; +}; + +static void init_vb2(struct rkvpss_offline_dev *ofl, + struct rkvpss_offline_buf *buf) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + unsigned long attrs = DMA_ATTR_NO_KERNEL_MAPPING; + + if (!buf) + return; + memset(&buf->vb, 0, sizeof(buf->vb)); + memset(&buf->vb2_queue, 0, sizeof(buf->vb2_queue)); + buf->vb2_queue.gfp_flags = GFP_KERNEL | GFP_DMA32; + buf->vb2_queue.dma_dir = DMA_BIDIRECTIONAL; + if (hw->is_dma_contig) + attrs |= DMA_ATTR_FORCE_CONTIGUOUS; + buf->vb2_queue.dma_attrs = attrs; + buf->vb.vb2_queue = &buf->vb2_queue; +} + +static void buf_del(struct file *file, int id, int fd, bool is_all, bool running) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *ops = hw->mem_ops; + struct rkvpss_offline_buf *buf, *next; + + mutex_lock(&hw->dev_lock); + list_for_each_entry_safe(buf, next, &ofl->list, list) { + if (buf->file == file && (is_all || buf->fd == fd)) { + if (!is_all && running && buf->alloc) + break; + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p dev_id:%d fd:%d dbuf:%p\n", + __func__, file, buf->dev_id, buf->fd, buf->dbuf); + if (!buf->alloc) { + ops->unmap_dmabuf(buf->mem); + ops->detach_dmabuf(buf->mem); + dma_buf_put(buf->dbuf); + } else { + dma_buf_put(buf->dbuf); + ops->put(buf->mem); + } + 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 struct rkvpss_offline_buf *buf_add(struct file *file, int id, int fd, int size) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *ops = hw->mem_ops; + struct rkvpss_offline_buf *buf = NULL, *next = NULL; + struct dma_buf *dbuf; + void *mem = NULL; + bool is_add = true; + + dbuf = dma_buf_get(fd); + if (IS_ERR_OR_NULL(dbuf)) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d invalid dmabuf fd:%d", id, fd); + return buf; + } + if (size && dbuf->size < size) { + v4l2_err(&ofl->v4l2_dev, + "dev_id:%d input fd:%d size error:%zu < %u\n", + id, 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->file == file && buf->fd == fd && buf->dbuf == dbuf) { + is_add = false; + break; + } + } + + if (is_add) { + buf = kzalloc(sizeof(struct rkvpss_offline_buf), GFP_KERNEL); + if (!buf) + goto end; + init_vb2(ofl, buf); + mem = ops->attach_dmabuf(&buf->vb, hw->dev, dbuf, dbuf->size); + if (IS_ERR(mem)) { + v4l2_err(&ofl->v4l2_dev, "failed to attach dmabuf, fd:%d\n", fd); + dma_buf_put(dbuf); + kfree(buf); + buf = NULL; + goto end; + } + if (ops->map_dmabuf(mem)) { + v4l2_err(&ofl->v4l2_dev, "failed to map, fd:%d\n", fd); + ops->detach_dmabuf(mem); + dma_buf_put(dbuf); + mem = NULL; + kfree(buf); + buf = NULL; + goto end; + } + buf->dev_id = id; + buf->fd = fd; + buf->file = file; + buf->dbuf = dbuf; + buf->mem = mem; + buf->alloc = false; + list_add_tail(&buf->list, &ofl->list); + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p dev_id:%d fd:%d dbuf:%p size:%d\n", __func__, + file, id, fd, dbuf, size); + } else { + dma_buf_put(dbuf); + } +end: + mutex_unlock(&hw->dev_lock); + return buf; +} + +static int internal_buf_alloc(struct file *file, struct rkvpss_buf_info *info) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *ops = hw->mem_ops; + struct rkvpss_offline_buf *buf; + struct dma_buf *dbuf; + int fd, i, size; + void *mem; + + for (i = 0; i < info->buf_cnt; i++) { + info->buf_fd[i] = -1; + size = PAGE_ALIGN(info->buf_size[i]); + if (!size) + continue; + buf = kzalloc(sizeof(struct rkvpss_offline_buf), GFP_KERNEL); + if (!buf) + goto err; + init_vb2(ofl, buf); + mem = ops->alloc(&buf->vb, hw->dev, size); + if (IS_ERR_OR_NULL(mem)) { + kfree(buf); + goto err; + } + dbuf = ops->get_dmabuf(&buf->vb, mem, O_RDWR); + if (IS_ERR_OR_NULL(dbuf)) { + ops->put(mem); + kfree(buf); + goto err; + } + fd = dma_buf_fd(dbuf, O_CLOEXEC); + if (fd < 0) { + dma_buf_put(dbuf); + ops->put(mem); + kfree(buf); + goto err; + } + get_dma_buf(dbuf); + + info->buf_fd[i] = fd; + buf->fd = fd; + buf->file = file; + buf->dbuf = dbuf; + buf->mem = mem; + buf->alloc = true; + buf->dev_id = info->dev_id; + ops->prepare(buf->mem); + mutex_lock(&hw->dev_lock); + list_add_tail(&buf->list, &ofl->list); + mutex_unlock(&hw->dev_lock); + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p dev_id:%d fd:%d dbuf:%p\n", + __func__, file, buf->dev_id, fd, dbuf); + } + return 0; +err: + for (i -= 1; i >= 0; i--) + buf_del(file, info->dev_id, info->buf_fd[i], false, false); + return -ENOMEM; +} + +static int external_buf_add(struct file *file, struct rkvpss_buf_info *info) +{ + void *mem; + int i; + + for (i = 0; i < info->buf_cnt; i++) { + mem = buf_add(file, info->dev_id, info->buf_fd[i], info->buf_size[i]); + if (!mem) + goto err; + } + return 0; +err: + for (i -= 1; i >= 0; i--) + buf_del(file, info->dev_id, info->buf_fd[i], false, false); + return -ENOMEM; +} + +static int rkvpss_ofl_buf_add(struct file *file, struct rkvpss_buf_info *info) +{ + int ret; + + if (info->buf_alloc) + ret = internal_buf_alloc(file, info); + else + ret = external_buf_add(file, info); + return ret; +} + +static void rkvpss_ofl_buf_del(struct file *file, struct rkvpss_buf_info *info) +{ + int i; + + for (i = 0; i < info->buf_cnt; i++) + buf_del(file, info->dev_id, info->buf_fd[i], false, false); +} + +static void poly_phase_scale(struct rkvpss_frame_cfg *frame_cfg, + struct rkvpss_offline_dev *ofl, + struct rkvpss_output_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + u32 in_w = cfg->crop_width, in_h = cfg->crop_height; + u32 out_w = cfg->scl_width, out_h = cfg->scl_height; + u32 ctrl, y_xscl_fac, y_yscl_fac, uv_xscl_fac, uv_yscl_fac; + u32 i, j, idx, ratio, val, in_div, out_div, factor; + bool dering_en = false, yuv420_in = false, yuv422_to_420 = false; + + if (in_w == out_w && in_h == out_w) { + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SCL_CTRL, 0); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SCL_CTRL, 0); + rkvpss_hw_clear_bits(hw, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS); + goto end; + } else { + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS, + RKVPSS_SCL0_CKG_DIS); + } + + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + if (unite) { + if (left) { + if (in_w == out_w) + val = (cfg->crop_width / 2 - 1) | + ((cfg->crop_height - 1) << 16); + else + val = (cfg->crop_width / 2 + UNITE_ENLARGE - 1) | + ((cfg->crop_height - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); + } else { + val = (ALIGN(ofl->unite_params[0].right_scl_need_size_y + 3, 2) - 1) | + ((cfg->crop_height - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); + val = (ALIGN(ofl->unite_params[0].right_scl_need_size_c + 6, 2) - 1) | + ((cfg->crop_height - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); + } + val = (cfg->scl_width / 2 - 1) | ((cfg->scl_height - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_DST_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_DST_SIZE, val); + } else { + val = (in_w - 1) | ((in_h - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SRC_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SRC_SIZE, val); + val = (out_w - 1) | ((out_h - 1) << 16); + rkvpss_hw_write(hw, RKVPSS_ZME_Y_DST_SIZE, val); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_DST_SIZE, val); + } + + ctrl = RKVPSS_ZME_XSCL_MODE | RKVPSS_ZME_YSCL_MODE; + if (dering_en) { + ctrl |= RKVPSS_ZME_DERING_EN; + rkvpss_hw_write(hw, RKVPSS_ZME_Y_DERING_PARA, 0xd10410); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_DERING_PARA, 0xd10410); + } + if (in_w != out_w) { + if (in_w > out_w) { + factor = 4096; + ctrl |= RKVPSS_ZME_XSD_EN; + } else { + factor = 65536; + ctrl |= RKVPSS_ZME_XSU_EN; + } + y_xscl_fac = (in_w - 1) * factor / (out_w - 1); + uv_xscl_fac = (in_w / 2 - 1) * factor / (out_w / 2 - 1); + + ratio = y_xscl_fac * 10000 / factor; + idx = rkvpss_get_zme_tap_coe_index(ratio); + for (i = 0; i < 17; i++) { + for (j = 0; j < 8; j += 2) { + val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap8_coe[idx][i][j], + rkvpss_zme_tap8_coe[idx][i][j + 1]); + rkvpss_hw_write(hw, + RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, val); + rkvpss_hw_write(hw, + RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, val); + } + } + } else { + y_xscl_fac = 0; + uv_xscl_fac = 0; + } + if (unite && !left) { + rkvpss_hw_write(hw, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac | + (ofl->unite_params[0].y_w_phase << 16)); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac | + (ofl->unite_params[0].c_w_phase << 16)); + } else { + rkvpss_hw_write(hw, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac); + } + + if (in_h != out_h) { + if (in_h > out_h) { + factor = 4096; + ctrl |= RKVPSS_ZME_YSD_EN; + } else { + factor = 65536; + ctrl |= RKVPSS_ZME_YSU_EN; + } + y_yscl_fac = (in_h - 1) * factor / (out_h - 1); + uv_yscl_fac = (in_h / in_div - 1) * factor / (out_h / out_div - 1); + + ratio = y_yscl_fac * 10000 / factor; + idx = rkvpss_get_zme_tap_coe_index(ratio); + for (i = 0; i < 17; i++) { + for (j = 0; j < 8; j += 2) { + val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap6_coe[idx][i][j], + rkvpss_zme_tap6_coe[idx][i][j + 1]); + rkvpss_hw_write(hw, + RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, val); + rkvpss_hw_write(hw, + RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, val); + } + } + } else { + y_yscl_fac = 0; + uv_yscl_fac = 0; + } + rkvpss_hw_write(hw, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac); + + rkvpss_hw_write(hw, RKVPSS_ZME_Y_SCL_CTRL, ctrl); + rkvpss_hw_write(hw, RKVPSS_ZME_UV_SCL_CTRL, ctrl); + + if (unite) { + val = cfg->scl_width / 2; + rkvpss_hw_write(hw, RKVPSS_ZME_H_SIZE, (val << 16) | val); + rkvpss_hw_write(hw, RKVPSS_ZME_H_OFFS, 0); + if (!left) { + val = cfg->scl_width / 2 - ALIGN_DOWN(cfg->scl_width / 2, 16); + rkvpss_hw_write(hw, RKVPSS_ZME_H_OFFS, (3 << 20) | (3 << 16) | + (ofl->unite_params[0].scl_in_crop_w_y << 8) | + (ofl->unite_params[0].scl_in_crop_w_c << 12) | + (val << 4) | val); + } + } + + ctrl = RKVPSS_ZME_GATING_EN; + if (yuv420_in) + ctrl |= RKVPSS_ZME_SCL_YUV420_REAL_EN; + if (yuv422_to_420) + ctrl |= RKVPSS_ZME_422TO420_EN; + if (unite) { + if (left) + ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_8K_EN; + else + ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_IN_CLIP_EN | RKVPSS_ZME_8K_EN; + } + rkvpss_hw_write(hw, RKVPSS_ZME_CTRL, ctrl); +end: + val = RKVPSS_ZME_GEN_UPD | RKVPSS_ZME_FORCE_UPD; + rkvpss_hw_write(hw, RKVPSS_ZME_UPDATE, val); + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d hw ctrl:0x%x y_src_size:0x%x y_dst_size:0x%x\n", + __func__, unite, left, + rkvpss_hw_read(hw, RKVPSS_ZME_CTRL), + rkvpss_hw_read(hw, RKVPSS_ZME_Y_SRC_SIZE), + rkvpss_hw_read(hw, RKVPSS_ZME_Y_DST_SIZE)); +} + +static void bilinear_scale(struct rkvpss_frame_cfg *frame_cfg, + struct rkvpss_offline_dev *ofl, + struct rkvpss_output_cfg *cfg, int idx, bool unite, bool left) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + u32 in_w = cfg->crop_width, in_h = cfg->crop_height; + u32 out_w = cfg->scl_width, out_h = cfg->scl_height; + u32 reg_base, in_div, out_div, val, ctrl = 0, clk_mask = 0; + bool yuv420_in = false, yuv422_to_420 = false; + + switch (idx) { + case RKVPSS_OUTPUT_CH1: + reg_base = RKVPSS_SCALE1_BASE; + clk_mask = RKVPSS_SCL1_CKG_DIS; + break; + case RKVPSS_OUTPUT_CH2: + reg_base = RKVPSS_SCALE2_BASE; + clk_mask = RKVPSS_SCL2_CKG_DIS; + break; + case RKVPSS_OUTPUT_CH3: + reg_base = RKVPSS_SCALE3_BASE; + clk_mask = RKVPSS_SCL3_CKG_DIS; + break; + default: + return; + } + + /*config scl clk gate*/ + if (in_w == out_w && in_h == out_h) + rkvpss_hw_clear_bits(hw, RKVPSS_VPSS_CLK_GATE, clk_mask); + else + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CLK_GATE, clk_mask, clk_mask); + + if (!unite) { + if (in_w == out_w && in_h == out_w) + goto end; + + /* TODO diff for input and output format */ + if (yuv420_in) { + in_div = 2; + out_div = 2; + } else if (yuv422_to_420) { + in_div = 1; + out_div = 2; + } else { + in_div = 1; + out_div = 1; + } + + val = in_w | (in_h << 16); + rkvpss_hw_write(hw, reg_base + 0x8, val); + val = out_w | (out_h << 16); + rkvpss_hw_write(hw, reg_base + 0xc, val); + + if (in_w != out_w) { + val = (in_w - 1) * 4096 / (out_w - 1); + rkvpss_hw_write(hw, reg_base + 0x10, val); + val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1); + rkvpss_hw_write(hw, reg_base + 0x14, val); + + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + if (in_h != out_h) { + val = (in_h - 1) * 4096 / (out_h - 1); + rkvpss_hw_write(hw, reg_base + 0x18, val); + val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1); + rkvpss_hw_write(hw, reg_base + 0x1c, val); + + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } + } else { + if (left) { + rkvpss_hw_write(hw, reg_base + 0x50, 0); + rkvpss_hw_write(hw, reg_base + 0x20, 0); + rkvpss_hw_write(hw, reg_base + 0x24, 0); + rkvpss_hw_write(hw, reg_base + 0x48, 0); + rkvpss_hw_write(hw, reg_base + 0x4c, 0); + if (in_w == out_w) + val = (cfg->crop_width / 2) | (cfg->crop_height << 16); + else + val = (cfg->crop_width / 2 + UNITE_ENLARGE) | + (cfg->crop_height << 16); + rkvpss_hw_write(hw, reg_base + 0x8, val); + val = cfg->scl_width / 2 | (cfg->scl_height << 16); + rkvpss_hw_write(hw, reg_base + 0xc, val); + ctrl |= RKVPSS_SCL_CLIP_EN; + } else { + val = ofl->unite_params[idx].scl_in_crop_w_y | + (ofl->unite_params[idx].scl_in_crop_w_c << 4); + rkvpss_hw_write(hw, reg_base + 0x50, val); + rkvpss_hw_write(hw, reg_base + 0x20, ofl->unite_params[idx].y_w_phase); + rkvpss_hw_write(hw, reg_base + 0x24, ofl->unite_params[idx].c_w_phase); + val = cfg->scl_width / 2 - ALIGN_DOWN(cfg->scl_width / 2, 16); + rkvpss_hw_write(hw, reg_base + 0x48, val); + rkvpss_hw_write(hw, reg_base + 0x4c, val); + val = (cfg->crop_width / 2 + ofl->unite_right_enlarge) | + (cfg->crop_height << 16); + rkvpss_hw_write(hw, reg_base + 0x8, val); + val = cfg->scl_width / 2 | (cfg->scl_height << 16); + rkvpss_hw_write(hw, reg_base + 0xc, val); + ctrl |= RKVPSS_SCL_CLIP_EN | RKVPSS_SCL_IN_CLIP_EN; + } + if (cfg->scl_width != frame_cfg->input.width) { + rkvpss_hw_write(hw, reg_base + 0x10, ofl->unite_params[idx].y_w_fac); + rkvpss_hw_write(hw, reg_base + 0x14, ofl->unite_params[idx].c_w_fac); + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN; + } + if (cfg->scl_height != frame_cfg->input.height) { + rkvpss_hw_write(hw, reg_base + 0x18, ofl->unite_params[idx].y_h_fac); + rkvpss_hw_write(hw, reg_base + 0x1c, ofl->unite_params[idx].c_h_fac); + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN; + } + } + +end: + rkvpss_hw_write(hw, reg_base, ctrl); + val = RKVPSS_SCL_GEN_UPD | RKVPSS_SCL_FORCE_UPD; + rkvpss_hw_write(hw, reg_base + 0x4, val); + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d hw ch:%d ctrl:0x%x y_src_size:0x%x y_dst_size:0x%x\n", + __func__, unite, left, idx, + rkvpss_hw_read(hw, reg_base), + rkvpss_hw_read(hw, reg_base + 0x8), + rkvpss_hw_read(hw, reg_base + 0xc)); +} + +static void scale_config(struct file *file, + struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + int i; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + + if (i == RKVPSS_OUTPUT_CH0) + poly_phase_scale(cfg, ofl, &cfg->output[i], unite, left); + else + bilinear_scale(cfg, ofl, &cfg->output[i], i, unite, left); + } +} + +static int cmsc_config(struct rkvpss_offline_dev *ofl, + struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + struct rkvpss_cmsc_cfg *cmsc_cfg, tmp_cfg = {0}; + struct rkvpss_cmsc_win *win; + struct rkvpss_cmsc_point *point; + int i, j, k; + u32 ch_win_en[RKVPSS_OUT_V1_MAX]; + u32 ch_win_mode[RKVPSS_OUT_V1_MAX]; + u32 win_color[RKVPSS_CMSC_WIN_MAX]; + u32 val, slope, hor, mask, mosaic_block = 0, ctrl = 0; + u32 hw_in_w, hw_in_h; + + if (!hw->is_ofl_cmsc) + return 0; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + ch_win_en[i] = 0; + ch_win_mode[i] = 0; + cmsc_cfg = &cfg->output[i].cmsc; + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + if (i == 0) + win_color[j] = 0; + if (!cmsc_cfg->win[j].win_en) + continue; + ch_win_en[i] |= BIT(j); + ch_win_mode[i] |= cmsc_cfg->win[j].mode ? BIT(j) : 0; + /** mosaic_block use the last channel **/ + if (cmsc_cfg->win[j].mode) + mosaic_block = cfg->output[i].cmsc.mosaic_block; + /** window cover all channel consistent **/ + if (!cfg->output[i].cmsc.win[j].mode) { + win_color[j] = RKVPSS_CMSK_WIN_YUV( + cfg->output[i].cmsc.win[j].cover_color_y, + cfg->output[i].cmsc.win[j].cover_color_u, + cfg->output[i].cmsc.win[j].cover_color_v); + if (cfg->output[i].cmsc.win[j].cover_color_a > 15) + cfg->output[i].cmsc.win[j].cover_color_a = 15; + win_color[j] |= RKVPSS_CMSC_WIN_ALPHA( + cfg->output[i].cmsc.win[j].cover_color_a); + } + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) + tmp_cfg.win[j].point[k] = cmsc_cfg->win[j].point[k]; + } + } + + /* deal unite left params */ + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!unite || !left) + break; + if (!cfg->output[i].enable) + continue; + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + win = &tmp_cfg.win[j]; + if (!(ch_win_en[i] & BIT(j))) + continue; + mask = 0; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + point = &win->point[k]; + if (point->x >= cfg->input.width / 2) + mask |= BIT(k); + else + mask &= ~BIT(k); + } + if (mask == 0xf) { + /** all right **/ + ch_win_en[i] &= ~BIT(j); + } else if (mask != 0) { + /** middle need avoid pentagon **/ + if (win->point[0].x != win->point[3].x || + win->point[1].x != win->point[2].x) { + ch_win_en[i] &= ~BIT(j); + } else { + win->point[1].x = cfg->input.width / 2; + win->point[2].x = cfg->input.width / 2; + } + } + } + } + + /* deal unite right params */ + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!unite || left) + break; + if (!cfg->output[i].enable) + continue; + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + win = &tmp_cfg.win[j]; + if (!(ch_win_en[i] & BIT(j))) + continue; + mask = 0; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + point = &win->point[k]; + if (point->x <= cfg->input.width / 2) + mask |= BIT(k); + else + mask &= ~BIT(k); + } + if (mask == 0xf) { + /** all left **/ + ch_win_en[i] &= ~BIT(j); + } else if (mask != 0) { + /** middle need avoid pentagon **/ + if (win->point[0].x != win->point[3].x || + win->point[1].x != win->point[2].x) { + ch_win_en[i] &= ~BIT(j); + } else { + win->point[0].x = ofl->unite_right_enlarge; + win->point[3].x = ofl->unite_right_enlarge; + win->point[1].x = win->point[1].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + win->point[2].x = win->point[2].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + } + } else { + /** all right **/ + win->point[0].x = win->point[0].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + win->point[1].x = win->point[1].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + win->point[2].x = win->point[2].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + win->point[3].x = win->point[3].x - + (cfg->input.width / 2) + + ofl->unite_right_enlarge; + } + } + } + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + if (ch_win_en[i]) { + ctrl |= RKVPSS_CMSC_EN; + ctrl |= RKVPSS_CMSC_CHN_EN(i); + } + rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_WIN + i * 4, ch_win_en[i]); + rkvpss_hw_write(hw, RKVPSS_CMSC_CHN0_MODE + i * 4, ch_win_mode[i]); + hw_in_w = rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_WIDTH); + hw_in_h = rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_HEIGHT); + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + if (!(ch_win_en[i] & BIT(j))) + continue; + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + if (tmp_cfg.win[j].point[k].x > hw_in_w || + tmp_cfg.win[j].point[k].y > hw_in_h) { + v4l2_err(&ofl->v4l2_dev, + "%s cmsc coordinate error dev_id:%d unite:%u left:%u ch:%d win:%d point:%d x:%u y:%u hw_in_w:%u hw_in_h:%u\n", + __func__, cfg->dev_id, unite, left, + i, j, k, + tmp_cfg.win[j].point[k].x, + tmp_cfg.win[j].point[k].y, + hw_in_w, hw_in_w); + return -EINVAL; + } + val = RKVPSS_CMSC_WIN_VTX(tmp_cfg.win[j].point[k].x, + tmp_cfg.win[j].point[k].y); + rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_L0_VTX + k * 8 + j * 32, val); + rkvpss_cmsc_slop(&tmp_cfg.win[j].point[k], + (k + 1 == RKVPSS_CMSC_POINT_MAX) ? + &tmp_cfg.win[j].point[0] : + &tmp_cfg.win[j].point[k + 1], + &slope, &hor); + val = RKVPSS_CMSC_WIN_SLP(slope, hor); + rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_L0_SLP + k * 8 + j * 32, val); + v4l2_dbg(4, rkvpss_debug, &ofl->v4l2_dev, + "%s dev_id:%d, unite:%d left:%d ch:%d win:%d point:%d x:%u y:%u", + __func__, cfg->dev_id, unite, left, i, j, k, + tmp_cfg.win[j].point[k].x, + tmp_cfg.win[j].point[k].y); + } + if ((ch_win_mode[i] & BIT(j))) + continue; + rkvpss_hw_write(hw, RKVPSS_CMSC_WIN0_PARA + j * 4, win_color[j]); + } + } + + ctrl |= RKVPSS_CMSC_BLK_SZIE(mosaic_block); + rkvpss_hw_write(hw, RKVPSS_CMSC_CTRL, ctrl); + + val = RKVPSS_CMSC_GEN_UPD | RKVPSS_CMSC_FORCE_UPD; + rkvpss_hw_write(hw, RKVPSS_CMSC_UPDATE, val); + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s dev_id:%d, unite:%d left:%d hw ctrl:0x%x update_val:0x%x", + __func__, cfg->dev_id, unite, left, ctrl, val); + + return 0; +} + +static void aspt_config(struct file *file, + struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + u32 reg_base, val; + int i; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + + switch (i) { + case RKVPSS_OUTPUT_CH0: + reg_base = RKVPSS_RATIO0_BASE; + break; + case RKVPSS_OUTPUT_CH1: + reg_base = RKVPSS_RATIO1_BASE; + break; + case RKVPSS_OUTPUT_CH2: + reg_base = RKVPSS_RATIO2_BASE; + break; + case RKVPSS_OUTPUT_CH3: + reg_base = RKVPSS_RATIO3_BASE; + break; + default: + return; + } + + if (!cfg->output[i].aspt.enable) { + rkvpss_hw_write(hw, reg_base, 0); + val = RKVPSS_RATIO_FORCE_UPD | RKVPSS_RATIO_GEN_UPD; + rkvpss_hw_write(hw, reg_base + 0x4, val); + continue; + } + val = cfg->output[i].scl_width | (cfg->output[i].scl_height << 16); + rkvpss_hw_write(hw, reg_base + 0x10, val); + val = cfg->output[i].aspt.width | (cfg->output[i].aspt.height << 16); + rkvpss_hw_write(hw, reg_base + 0x14, val); + val = cfg->output[i].aspt.h_offs | (cfg->output[i].aspt.v_offs << 16); + rkvpss_hw_write(hw, reg_base + 0x18, val); + val = cfg->output[i].aspt.color_y | + (cfg->output[i].aspt.color_u << 16) | + (cfg->output[i].aspt.color_v << 24); + rkvpss_hw_write(hw, reg_base + 0x1c, val); + rkvpss_hw_write(hw, reg_base, RKVPSS_RATIO_EN); + val = RKVPSS_RATIO_FORCE_UPD | RKVPSS_RATIO_GEN_UPD; + rkvpss_hw_write(hw, reg_base + 0x4, val); + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s hw ch:%d ctrl:0x%x in_size:0x%x out_size:0x%x offset:0x%x\n", + __func__, i, + rkvpss_hw_read(hw, reg_base), + rkvpss_hw_read(hw, reg_base + 0x10), + rkvpss_hw_read(hw, reg_base + 0x14), + rkvpss_hw_read(hw, reg_base + 0x18)); + } +} + +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_OUT_V1_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++; + } + while (count >= rkvpss_cfginfo_num && count != 0) { + first_cfg = list_first_entry(&ofl->cfginfo_list, struct rkvpss_ofl_cfginfo, list); + list_del_init(&first_cfg->list); + kfree(first_cfg); + count--; + } + if (rkvpss_cfginfo_num) + list_add_tail(&new_cfg->list, &ofl->cfginfo_list); + else + kfree(new_cfg); + mutex_unlock(&ofl->ofl_lock); +} + + +static int read_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *mem_ops = hw->mem_ops; + struct sg_table *sg_tbl; + struct rkvpss_offline_buf *buf; + u32 in_ctrl, in_size, in_c_offs, unite_r_offs, val, mask, unite_off = 0, enlarge = 0; + + in_c_offs = 0; + in_ctrl = 0; + switch (cfg->input.format) { + case V4L2_PIX_FMT_NV16: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = cfg->input.stride * cfg->input.height; + in_size = cfg->input.stride * cfg->input.height * 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; + unite_off = 8; + break; + case V4L2_PIX_FMT_NV12: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = cfg->input.stride * cfg->input.height; + in_size = cfg->input.stride * cfg->input.height * 3 / 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; + unite_off = 8; + break; + case V4L2_PIX_FMT_NV61: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = cfg->input.stride * cfg->input.height; + in_size = cfg->input.stride * cfg->input.height * 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_422SP | RKVPSS_MI_RD_UV_SWAP; + unite_off = 8; + break; + case V4L2_PIX_FMT_NV21: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = cfg->input.stride * cfg->input.height; + in_size = cfg->input.stride * cfg->input.height * 3 / 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_420SP | RKVPSS_MI_RD_UV_SWAP; + unite_off = 8; + break; + case V4L2_PIX_FMT_RGB565: + if (cfg->input.stride < ALIGN(cfg->input.width * 2, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 2, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_BGR565; + unite_off = 16; + break; + case V4L2_PIX_FMT_RGB565X: + if (cfg->input.stride < ALIGN(cfg->input.width * 2, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 2, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_BGR565 | RKVPSS_MI_RD_RB_SWAP; + unite_off = 16; + break; + case V4L2_PIX_FMT_RGB24: + if (cfg->input.stride < ALIGN(cfg->input.width * 3, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 3, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_BGR888; + unite_off = 24; + break; + case V4L2_PIX_FMT_BGR24: + if (cfg->input.stride < ALIGN(cfg->input.width * 3, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 3, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_BGR888 | RKVPSS_MI_RD_RB_SWAP; + unite_off = 24; + break; + case V4L2_PIX_FMT_XRGB32: + if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 4, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888; + unite_off = 32; + break; + case V4L2_PIX_FMT_XBGR32: + if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 4, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 | RKVPSS_MI_RD_RB_SWAP; + unite_off = 32; + break; + case V4L2_PIX_FMT_RGBX32: + if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 4, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 + | RKVPSS_MI_RD_ALPHA_SWAP; + unite_off = 32; + break; + case V4L2_PIX_FMT_BGRX32: + if (cfg->input.stride < ALIGN(cfg->input.width * 4, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 4, 16); + in_size = cfg->input.stride * cfg->input.height; + in_ctrl |= RKVPSS_MI_RD_INPUT_ABGR888 + | RKVPSS_MI_RD_RB_SWAP + | RKVPSS_MI_RD_ALPHA_SWAP; + unite_off = 32; + break; + case V4L2_PIX_FMT_FBC0: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = 0; + in_size = cfg->input.stride * cfg->input.height * 3 / 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; + break; + case V4L2_PIX_FMT_FBC2: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = 0; + in_size = cfg->input.stride * cfg->input.height * 2; + in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; + break; + case V4L2_PIX_FMT_FBC4: + if (cfg->input.stride < ALIGN(cfg->input.width, 16)) + cfg->input.stride = ALIGN(cfg->input.width, 16); + in_c_offs = 0; + in_size = cfg->input.stride * cfg->input.height * 3; + in_ctrl |= RKVPSS_MI_RD_INPUT_422SP | RKVPSS_MI_RD_FBCD_YUV444_EN; + break; + case V4L2_PIX_FMT_TILE420: + if (cfg->input.stride < ALIGN(cfg->input.width * 6, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 6, 16); + in_c_offs = 0; + in_size = cfg->input.stride * (cfg->input.height / 4); + in_ctrl |= RKVPSS_MI_RD_INPUT_420SP; + break; + case V4L2_PIX_FMT_TILE422: + if (cfg->input.stride < ALIGN(cfg->input.width * 8, 16)) + cfg->input.stride = ALIGN(cfg->input.width * 8, 16); + in_c_offs = 0; + in_size = cfg->input.stride * (cfg->input.height / 4); + in_ctrl |= RKVPSS_MI_RD_INPUT_422SP; + break; + default: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support input format:%c%c%c%c\n", + cfg->dev_id, cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24); + return -EINVAL; + } + + buf = buf_add(file, cfg->dev_id, cfg->input.buf_fd, in_size); + if (!buf) + return -ENOMEM; + + sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); + + if (!unite) { + val = cfg->input.width; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); + val = cfg->input.height; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_HEIGHT, val); + val = sg_dma_address(sg_tbl->sgl); + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); + val += in_c_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); + } else { + val = cfg->input.height; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_HEIGHT, val); + ofl->unite_right_enlarge = ALIGN(cfg->input.width / 2, 16) - + (cfg->input.width / 2) + 16; + + if (left) { + if (!cfg->mirror) + enlarge = UNITE_LEFT_ENLARGE; + else + enlarge = ofl->unite_right_enlarge; + val = cfg->input.width / 2 + enlarge; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); + val = sg_dma_address(sg_tbl->sgl); + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); + val += in_c_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); + } else { + if (!cfg->mirror) + enlarge = ofl->unite_right_enlarge; + else + enlarge = UNITE_LEFT_ENLARGE; + val = cfg->input.width / 2 + enlarge; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_WIDTH, val); + val = (cfg->input.width / 2 - enlarge) * unite_off; + unite_r_offs = ALIGN_DOWN(val / 8, 16); + val = sg_dma_address(sg_tbl->sgl) + unite_r_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_BASE, val); + val += in_c_offs; + rkvpss_hw_write(hw, RKVPSS_MI_RD_C_BASE, val); + } + } + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d hw width:%d height:%d y_base:0x%x\n", + __func__, unite, left, + rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_WIDTH), + rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_HEIGHT), + rkvpss_hw_read(hw, RKVPSS_MI_RD_Y_BASE)); + + if (cfg->input.format == V4L2_PIX_FMT_FBC0 || + cfg->input.format == V4L2_PIX_FMT_FBC2 || + cfg->input.format == V4L2_PIX_FMT_FBC4) { + in_ctrl |= RKVPSS_MI_RD_MODE(2) | RKVPSS_MI_RD_FBCD_OPT_DIS; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, 0); + } else { + if (cfg->input.format == V4L2_PIX_FMT_TILE420 || + cfg->input.format == V4L2_PIX_FMT_TILE422) { + in_ctrl |= RKVPSS_MI_RD_MODE(1); + switch (cfg->input.rotate) { + case ROTATE_90: + in_ctrl |= RKVPSS_MI_RD_ROT_90; + break; + case ROTATE_180: + in_ctrl |= RKVPSS_MI_RD_ROT_180; + break; + case ROTATE_270: + in_ctrl |= RKVPSS_MI_RD_ROT_270; + break; + default: + in_ctrl |= RKVPSS_MI_RD_ROT_0; + break; + } + } + val = cfg->input.stride; + rkvpss_hw_write(hw, RKVPSS_MI_RD_Y_STRIDE, val); + } + + mask = RKVPSS_MI_RD_GROUP_MODE(3) | RKVPSS_MI_RD_BURST16_LEN; + rkvpss_hw_set_bits(hw, RKVPSS_MI_RD_CTRL, ~mask, in_ctrl); + rkvpss_hw_write(hw, RKVPSS_MI_RD_INIT, RKVPSS_MI_RD_FORCE_UPD); + + return 0; +} + +static void crop_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + int i; + u32 reg, val, crop_en; + + crop_en = 0; + if (!unite) { + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + reg = RKVPSS_CROP0_0_H_OFFS; + val = cfg->output[i].crop_h_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_OFFS; + val = cfg->output[i].crop_v_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_H_SIZE; + val = cfg->output[i].crop_width; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_SIZE; + val = cfg->output[i].crop_height; + rkvpss_hw_write(hw, reg + i * 0x10, val); + crop_en |= RKVPSS_CROP_CHN_EN(i); + } + } else { + if (left) { + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + + reg = RKVPSS_CROP0_0_H_OFFS; + val = cfg->output[i].crop_h_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_OFFS; + val = cfg->output[i].crop_v_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_H_SIZE; + /*if no scale, left don't enlarge*/ + if (cfg->output[i].crop_width == cfg->output[i].scl_width) + val = cfg->output[i].crop_width / 2; + else + val = cfg->output[i].crop_width / 2 + UNITE_LEFT_ENLARGE; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_SIZE; + val = cfg->output[i].crop_height; + rkvpss_hw_write(hw, reg + i * 0x10, val); + crop_en |= RKVPSS_CROP_CHN_EN(i); + } + } else { + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + reg = RKVPSS_CROP0_0_H_OFFS; + val = ofl->unite_params[i].quad_crop_w; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_OFFS; + val = cfg->output[i].crop_v_offs; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_H_SIZE; + val = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + ofl->unite_params[i].quad_crop_w; + rkvpss_hw_write(hw, reg + i * 0x10, val); + reg = RKVPSS_CROP0_0_V_SIZE; + val = cfg->output[i].crop_height; + rkvpss_hw_write(hw, reg + i * 0x10, val); + crop_en |= RKVPSS_CROP_CHN_EN(i); + } + } + } + rkvpss_hw_write(hw, RKVPSS_CROP0_CTRL, crop_en); + rkvpss_hw_write(hw, RKVPSS_CROP0_UPDATE, RKVPSS_CROP_FORCE_UPD); + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d hw ch:%d h_offs:%d v_offs:%d width:%d height:%d\n", + __func__, unite, left, i, + rkvpss_hw_read(hw, RKVPSS_CROP0_0_H_OFFS + i * 0x10), + rkvpss_hw_read(hw, RKVPSS_CROP0_0_V_OFFS + i * 0x10), + rkvpss_hw_read(hw, RKVPSS_CROP0_0_H_SIZE + i * 0x10), + rkvpss_hw_read(hw, RKVPSS_CROP0_0_V_SIZE + i * 0x10)); + } +} + +static int write_config(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + const struct vb2_mem_ops *mem_ops = hw->mem_ops; + struct sg_table *sg_tbl; + struct rkvpss_offline_buf *buf; + struct rkvpss_output_ch out_ch[RKVPSS_OUT_V1_MAX] = { 0 }; + int i; + u32 w, h, val, reg, mask, mi_update, flip_en, unite_off = 0; + bool ch_en = false, wr_uv_swap = false; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!hw->is_ofl_ch[i] && cfg->output[i].enable) { + v4l2_err(&ofl->v4l2_dev, + "dev_id:%d ch%d no select for offline mode, set to disable\n", + cfg->dev_id, i); + cfg->output[i].enable = 0; + } + + if (!cfg->output[i].enable) + continue; + ch_en = true; + + if (cfg->output[i].aspt.enable) { + w = cfg->output[i].aspt.width; + h = cfg->output[i].aspt.height; + } else { + w = cfg->output[i].scl_width; + h = cfg->output[i].scl_height; + } + if (i == RKVPSS_OUTPUT_CH1) { + bool is_fmt_find = true; + + switch (cfg->output[i].format) { + case V4L2_PIX_FMT_RGB565: + if (cfg->output[i].stride < ALIGN(w * 2, 16)) + cfg->output[i].stride = ALIGN(w * 2, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB565 | + RKVPSS_MI_CHN_WR_RB_SWAP; + break; + case V4L2_PIX_FMT_RGB24: + if (cfg->output[i].stride < ALIGN(w * 3, 16)) + cfg->output[i].stride = ALIGN(w * 3, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB888 | + RKVPSS_MI_CHN_WR_RB_SWAP; + break; + case V4L2_PIX_FMT_RGB565X: + if (cfg->output[i].stride < ALIGN(w * 2, 16)) + cfg->output[i].stride = ALIGN(w * 2, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB565; + break; + case V4L2_PIX_FMT_BGR24: + if (cfg->output[i].stride < ALIGN(w * 3, 16)) + cfg->output[i].stride = ALIGN(w * 3, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_RGB888; + break; + case V4L2_PIX_FMT_XBGR32: + if (cfg->output[i].stride < ALIGN(w * 4, 16)) + cfg->output[i].stride = ALIGN(w * 4, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_ARGB888; + break; + case V4L2_PIX_FMT_XRGB32: + if (cfg->output[i].stride < ALIGN(w * 4, 16)) + cfg->output[i].stride = ALIGN(w * 4, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_ARGB888 + | RKVPSS_MI_CHN_WR_RB_SWAP; + break; + default: + is_fmt_find = false; + } + if (is_fmt_find) { + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; + out_ch[i].size = cfg->output[i].stride * h; + continue; + } + } + switch (cfg->output[i].format) { + case V4L2_PIX_FMT_UYVY: + if (cfg->output[i].stride < ALIGN(w * 2, 16)) + cfg->output[i].stride = ALIGN(w * 2, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_422P | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_NV16: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * h * 2; + out_ch[i].c_offs = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_NV12: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV420; + out_ch[i].size = cfg->output[i].stride * h * 3 / 2; + out_ch[i].c_offs = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_GREY: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV400; + out_ch[i].size = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_VYUY: + if (cfg->output[i].stride < ALIGN(w * 2, 16)) + cfg->output[i].stride = ALIGN(w * 2, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_422P | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_NV61: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * h * 2; + out_ch[i].c_offs = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_NV21: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_42XSP | RKVPSS_MI_CHN_WR_OUTPUT_YUV420; + out_ch[i].size = cfg->output[i].stride * h * 3 / 2; + out_ch[i].c_offs = cfg->output[i].stride * h; + break; + case V4L2_PIX_FMT_TILE420: + if (cfg->output[i].stride < ALIGN(w * 6, 16)) + cfg->output[i].stride = ALIGN(w * 6, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_YUV420; + out_ch[i].size = cfg->output[i].stride * (h / 4); + out_ch[i].c_offs = 0; + break; + case V4L2_PIX_FMT_TILE422: + if (cfg->output[i].stride < ALIGN(w * 8, 16)) + cfg->output[i].stride = ALIGN(w * 8, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * (h / 4); + out_ch[i].c_offs = 0; + break; + default: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support output ch%d format:%c%c%c%c\n", + cfg->dev_id, i, + cfg->output[i].format, cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24); + return -EINVAL; + } + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD; + } + if (!ch_en) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d no output channel enable\n", cfg->dev_id); + return -EINVAL; + } + + mi_update = 0; + flip_en = 0; + mask = 0; + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (hw->is_ofl_ch[i]) + mask |= RKVPSS_MI_CHN_V_FLIP(i); + if (!cfg->output[i].enable) + continue; + buf = buf_add(file, cfg->dev_id, cfg->output[i].buf_fd, out_ch[i].size); + if (!buf) + goto free_buf; + + if (unite && !left) + unite_off = (ALIGN_DOWN(cfg->output[i].scl_width / 2, 16) * 8) / 8; + + if (cfg->output[i].aspt.enable) + h = cfg->output[i].aspt.height; + else + h = cfg->output[i].scl_height; + + sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); + val = sg_dma_address(sg_tbl->sgl) + unite_off; + reg = RKVPSS_MI_CHN0_WR_Y_BASE; + rkvpss_hw_write(hw, reg + i * 0x100, val); + reg = RKVPSS_MI_CHN0_WR_CB_BASE; + val += out_ch[i].c_offs; + rkvpss_hw_write(hw, reg + i * 0x100, val); + + reg = RKVPSS_MI_CHN0_WR_Y_STRIDE; + val = cfg->output[i].stride; + rkvpss_hw_write(hw, reg + i * 0x100, val); + reg = RKVPSS_MI_CHN0_WR_Y_SIZE; + + if (cfg->output[i].format == V4L2_PIX_FMT_TILE420 || + cfg->output[i].format == V4L2_PIX_FMT_TILE422) + val = cfg->output[i].stride * (ALIGN(h, 4) / 4); + else + val = cfg->output[i].stride * h; + rkvpss_hw_write(hw, reg + i * 0x100, val); + reg = RKVPSS_MI_CHN0_WR_CB_SIZE; + val = out_ch[i].size - val; + rkvpss_hw_write(hw, reg + i * 0x100, val); + + reg = RKVPSS_MI_CHN0_WR_CTRL; + val = out_ch[i].ctrl; + rkvpss_hw_write(hw, reg + i * 0x100, val); + + if (cfg->output[i].flip && + !(cfg->output[i].format == V4L2_PIX_FMT_TILE420) && + !(cfg->output[i].format == V4L2_PIX_FMT_TILE422)) { + flip_en |= RKVPSS_MI_CHN_V_FLIP(i); + + switch (cfg->output[i].format) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + val = cfg->output[i].stride / 2; + break; + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_XRGB32: + val = cfg->output[i].stride / 4; + break; + default: + val = cfg->output[i].stride; + break; + } + val = val * h; + reg = RKVPSS_MI_CHN0_WR_Y_PIC_SIZE; + rkvpss_hw_write(hw, reg + i * 0x100, val); + } + mi_update |= (RKVPSS_MI_CHN0_FORCE_UPD << i); + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s unite:%d left:%d hw ch:%d y_size:%d y_stride:%d y_pic_size:%d y_base:0x%x", + __func__, unite, left, i, + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_SIZE + i * 100), + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_STRIDE + i * 100), + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_PIC_SIZE + i * 100), + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_BASE + i * 100)); + } + + + rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_VFLIP_CTRL, mask, flip_en); + + /* config output uv swap */ + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (cfg->output[i].enable && + (cfg->output[i].format == V4L2_PIX_FMT_VYUY || + cfg->output[i].format == V4L2_PIX_FMT_NV21 || + cfg->output[i].format == V4L2_PIX_FMT_NV61)) + wr_uv_swap = true; + } + if (wr_uv_swap) { + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (cfg->output[i].enable && (cfg->output[i].format == V4L2_PIX_FMT_UYVY || + cfg->output[i].format == V4L2_PIX_FMT_NV12 || + cfg->output[i].format == V4L2_PIX_FMT_NV16)) { + v4l2_err(&ofl->v4l2_dev, + "dev_id:%d wr_uv_swap need to be consistent\n", + cfg->dev_id); + return -EAGAIN; + } + } + } + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (cfg->output[i].format == V4L2_PIX_FMT_VYUY || + cfg->output[i].format == V4L2_PIX_FMT_NV21 || + cfg->output[i].format == V4L2_PIX_FMT_NV61) { + mask = RKVPSS_MI_WR_UV_SWAP; + val = RKVPSS_MI_WR_UV_SWAP; + rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_CTRL, mask, val); + break; + } + } + + for (i = 0; i <= RKVPSS_OUTPUT_CH1; i++) { + if (cfg->output[i].format == V4L2_PIX_FMT_TILE420 || + cfg->output[i].format == V4L2_PIX_FMT_TILE422) { + mask = RKVPSS_MI_WR_TILE_SEL(3); + val = RKVPSS_MI_WR_TILE_SEL(i + 1); + rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_CTRL, mask, val); + } + } + + /* need update two for online2 mode */ + rkvpss_hw_write(hw, RKVPSS_MI_WR_INIT, mi_update); + rkvpss_hw_write(hw, RKVPSS_MI_WR_INIT, mi_update); + + return 0; + +free_buf: + for (i -= 1; i >= 0; i--) { + if (!cfg->output[i].enable) + continue; + buf_del(file, cfg->dev_id, cfg->output[i].buf_fd, false, true); + } + buf_del(file, cfg->dev_id, cfg->input.buf_fd, false, true); + return -ENOMEM; +} + +static void calc_unite_scl_params(struct file *file, struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_unite_scl_params *params; + int i; + u32 right_scl_need_size_y, right_scl_need_size_c; + u32 left_in_used_size_y, left_in_used_size_c; + u32 right_fst_position_y, right_fst_position_c; + u32 right_y_crop_total; + u32 right_c_crop_total; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (cfg->output[i].enable == 0) + continue; + params = &ofl->unite_params[i]; + params->y_w_fac = (cfg->output[i].crop_width - 1) * 4096 / + (cfg->output[i].scl_width - 1); + params->c_w_fac = (cfg->output[i].crop_width / 2 - 1) * 4096 / + (cfg->output[i].scl_width / 2 - 1); + params->y_h_fac = (cfg->output[i].crop_height - 1) * 4096 / + (cfg->output[i].scl_height - 1); + params->c_h_fac = (cfg->output[i].crop_height - 1) * 4096 / + (cfg->output[i].scl_height - 1); + + right_fst_position_y = cfg->output[i].scl_width / 2 * + params->y_w_fac; + right_fst_position_c = cfg->output[i].scl_width / 2 / 2 * + params->c_w_fac; + + left_in_used_size_y = right_fst_position_y >> 12; + left_in_used_size_c = (right_fst_position_c >> 12) * 2; + + params->y_w_phase = right_fst_position_y & 0xfff; + params->c_w_phase = right_fst_position_c & 0xfff; + + right_scl_need_size_y = cfg->output[i].crop_width - + left_in_used_size_y; + params->right_scl_need_size_y = right_scl_need_size_y; + right_scl_need_size_c = cfg->output[i].crop_width - + left_in_used_size_c; + params->right_scl_need_size_c = right_scl_need_size_c; + + if (i == 0 && cfg->output[i].crop_width != cfg->output[i].scl_width) { + right_y_crop_total = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + right_scl_need_size_y - 3; + right_c_crop_total = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + right_scl_need_size_c - 6; + } else { + right_y_crop_total = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + right_scl_need_size_y; + right_c_crop_total = cfg->output[i].crop_width / 2 + + ofl->unite_right_enlarge - + right_scl_need_size_c; + } + + params->quad_crop_w = ALIGN_DOWN(min(right_y_crop_total, right_c_crop_total), 2); + + params->scl_in_crop_w_y = right_y_crop_total - params->quad_crop_w; + params->scl_in_crop_w_c = right_c_crop_total - params->quad_crop_w; + + if (rkvpss_debug >= 4) { + v4l2_info(&ofl->v4l2_dev, + "%s dev_id:%d seq:%d ch:%d y_w_fac:%u c_w_fac:%u y_h_fac:%u c_h_fac:%u\n", + __func__, cfg->dev_id, cfg->sequence, i, params->y_w_fac, + params->c_w_fac, params->y_h_fac, params->c_h_fac); + v4l2_info(&ofl->v4l2_dev, + "\t\t\t\t\t\t unite_right_enlarge:%u", + ofl->unite_right_enlarge); + v4l2_info(&ofl->v4l2_dev, + "\t\t\t\t\t\t y_w_phase:%u c_w_phase:%u quad_crop_w:%u scl_in_crop_w_y:%u scl_in_crop_w_c:%u\n", + params->y_w_phase, params->c_w_phase, + params->quad_crop_w, + params->scl_in_crop_w_y, params->scl_in_crop_w_c); + v4l2_info(&ofl->v4l2_dev, + "\t\t\t\t\t\t right_scl_need_size_y:%u right_scl_need_size_c:%u\n", + params->right_scl_need_size_y, + params->right_scl_need_size_c); + } + } +} + +static int rkvpss_ofl_run(struct file *file, struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + u32 update, val, mask; + int ret, i; + bool left_tmp; + + if (!unite || left) + add_cfginfo(ofl, cfg); + + init_completion(&ofl->cmpl); + ofl->mode_sel_en = false; + + ret = read_config(file, cfg, unite, left); + if (ret < 0) + return ret; + + if (unite && left) + calc_unite_scl_params(file, cfg); + + if (unite && cfg->mirror) + left_tmp = !left; + else + left_tmp = left; + + ret = cmsc_config(ofl, cfg, unite, left_tmp); + if (ret) + return ret; + crop_config(file, cfg, unite, left_tmp); + scale_config(file, cfg, unite, left_tmp); + if (!unite) + aspt_config(file, cfg); + ret = write_config(file, cfg, unite, left_tmp); + if (ret < 0) + return ret; + + mask = 0; + val = 0; + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!hw->is_ofl_ch[i]) + continue; + mask |= (RKVPSS_ISP2VPSS_CHN0_SEL(3) << i * 2); + if (hw->is_ofl_cmsc) + mask |= RKVPSS_ISP2VPSS_ONLINE2_CMSC_EN; + if (cfg->output[i].enable) + val |= (RKVPSS_ISP2VPSS_CHN0_SEL(1) << i * 2); + } + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_ONLINE, mask, val); + + update = 0; + mask = hw->is_ofl_cmsc ? RKVPSS_MIR_EN : 0; + val = (mask && cfg->mirror) ? RKVPSS_MIR_EN : 0; + if (mask) { + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CTRL, mask, val); + update |= RKVPSS_MIR_FORCE_UPD; + } + update |= RKVPSS_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD | RKVPSS_MIR_GEN_UPD; + rkvpss_hw_write(hw, RKVPSS_VPSS_UPDATE, update); + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_IMSC, 0, RKVPSS_ALL_FRM_END); + + if (rkvpss_debug == 6) + rkvpss_dump_reg(ofl, cfg->sequence, 0x3f24); + + rkvpss_hw_write(hw, RKVPSS_MI_RD_START, RKVPSS_MI_RD_ST); + + ret = wait_for_completion_timeout(&ofl->cmpl, msecs_to_jiffies(500)); + if (!ret) { + v4l2_err(&ofl->v4l2_dev, "working timeout\n"); + ret = -EAGAIN; + } else { + ret = 0; + } + + return ret; +} + +static int rkvpss_module_get(struct file *file, + struct rkvpss_module_sel *get) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + int i, ret = 0; + + mutex_lock(&hw->dev_lock); + if (hw->is_ofl_cmsc) + get->mirror_cmsc_en = 1; + else + get->mirror_cmsc_en = 0; + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (hw->is_ofl_ch[i]) + get->ch_en[i] = 1; + else + get->ch_en[i] = 0; + } + mutex_unlock(&hw->dev_lock); + + return ret; +} + +static int rkvpss_module_sel(struct file *file, + struct rkvpss_module_sel *sel) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + struct rkvpss_hw_dev *hw = ofl->hw; + struct rkvpss_device *vpss; + int i, ret = 0; + + mutex_lock(&hw->dev_lock); + + if (!ofl->mode_sel_en) { + v4l2_err(&ofl->v4l2_dev, "already set module_sel\n"); + ret = -EINVAL; + goto unlock; + } + + for (i = 0; i < hw->dev_num; i++) { + vpss = hw->vpss[i]; + if (vpss && (vpss->vpss_sdev.state & VPSS_START)) { + v4l2_err(&ofl->v4l2_dev, "no support set mode when vpss working\n"); + ret = -EINVAL; + goto unlock; + } + } + + hw->is_ofl_cmsc = !!sel->mirror_cmsc_en; + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) + hw->is_ofl_ch[i] = !!sel->ch_en[i]; +unlock: + mutex_unlock(&hw->dev_lock); + return ret; +} + +static int rkvpss_check_params(struct file *file, struct rkvpss_frame_cfg *cfg, bool *unite) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + int i, ret = 0, tile_num = 0; + + /* check dev id out of range */ + if (cfg->dev_id >= DEV_NUM_MAX) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d is out of range. range[0, %d]\n", + cfg->dev_id, DEV_NUM_MAX); + ret = -EINVAL; + goto end; + } + + /* set unite mode */ + if (cfg->input.width > RKVPSS_MAX_WIDTH) + *unite = true; + else + *unite = false; + + /* check input format */ + switch (cfg->input.format) { + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_XRGB32: + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_RGBX32: + case V4L2_PIX_FMT_BGRX32: + case V4L2_PIX_FMT_FBC0: + case V4L2_PIX_FMT_FBC2: + case V4L2_PIX_FMT_FBC4: + case V4L2_PIX_FMT_TILE420: + case V4L2_PIX_FMT_TILE422: + break; + default: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d no support input format:%c%c%c%c\n", + cfg->dev_id, cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24); + ret = -EINVAL; + goto end; + } + + /* check input size */ + if (cfg->input.width > RKVPSS_UNITE_MAX_WIDTH || + cfg->input.height > RKVPSS_UNITE_MAX_HEIGHT || + cfg->input.width < RKVPSS_MIN_WIDTH || + cfg->input.height < RKVPSS_MIN_HEIGHT) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d input size not support width:%d height:%d\n", + cfg->dev_id, cfg->input.width, cfg->input.height); + ret = -EINVAL; + goto end; + } + + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + /* check output format */ + switch (cfg->output[i].format) { + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV21: + break; + case V4L2_PIX_FMT_TILE420: + case V4L2_PIX_FMT_TILE422: + if (i == RKVPSS_OUTPUT_CH0 || i == RKVPSS_OUTPUT_CH1) { + tile_num++; + if (tile_num > 1) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d only ch0 or ch1 can tile write\n", + cfg->dev_id); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].flip) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d tile write no support flip\n", + cfg->dev_id, i); + ret = -EINVAL; + goto end; + } + } else { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", + cfg->dev_id, + i, + cfg->output[i].format, + cfg->output[i].format >> 8, + cfg->output[i].format >> 16, + cfg->output[i].format >> 24); + ret = -EINVAL; + goto end; + } + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_XRGB32: + if (i != RKVPSS_OUTPUT_CH1) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", + cfg->dev_id, + i, + cfg->output[i].format, + cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24); + ret = -EINVAL; + goto end; + } + break; + default: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d no support output format:%c%c%c%c\n", + cfg->dev_id, + i, cfg->output[i].format, + cfg->output[i].format >> 8, + cfg->output[i].format >> 16, + cfg->output[i].format >> 24); + ret = -EINVAL; + goto end; + } + + /* check output size */ + if (cfg->output[i].scl_width > RKVPSS_UNITE_MAX_WIDTH || + cfg->output[i].scl_height > RKVPSS_UNITE_MAX_HEIGHT || + cfg->output[i].scl_width < RKVPSS_MIN_WIDTH || + cfg->output[i].scl_height < RKVPSS_MIN_HEIGHT) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d output size not support width:%d height:%d\n", + cfg->dev_id, + cfg->output[i].scl_width, + cfg->output[i].scl_height); + ret = -EINVAL; + goto end; + } + + /* check crop */ + cfg->output[i].crop_h_offs = ALIGN(cfg->output[i].crop_h_offs, 2); + cfg->output[i].crop_v_offs = ALIGN(cfg->output[i].crop_v_offs, 2); + cfg->output[i].crop_width = ALIGN(cfg->output[i].crop_width, 2); + cfg->output[i].crop_height = ALIGN(cfg->output[i].crop_height, 2); + if (!cfg->input.rotate || cfg->input.rotate == 2) { + if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > + cfg->input.width) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d inval crop(offs:%d w:%d) input width:%d\n", + i, cfg->dev_id, cfg->output[i].crop_h_offs, + cfg->output[i].crop_width, cfg->input.width); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > + cfg->input.height) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d inval crop(offs:%d h:%d) input height:%d\n", + cfg->dev_id, i, cfg->output[i].crop_v_offs, + cfg->output[i].crop_height, cfg->input.height); + ret = -EINVAL; + goto end; + } + } else { + if (cfg->output[i].crop_width + cfg->output[i].crop_h_offs > + cfg->input.height) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d rotate inval crop(offs:%d w:%d) input height:%d\n", + i, cfg->dev_id, cfg->output[i].crop_h_offs, + cfg->output[i].crop_width, cfg->input.height); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].crop_height + cfg->output[i].crop_v_offs > + cfg->input.width) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d rotate inval crop(offs:%d h:%d) input width:%d\n", + cfg->dev_id, i, cfg->output[i].crop_v_offs, + cfg->output[i].crop_height, cfg->input.width); + ret = -EINVAL; + goto end; + } + } + if (*unite) { + if (cfg->output[i].crop_h_offs != (cfg->input.width - + (cfg->output[i].crop_h_offs + + cfg->output[i].crop_width))) { + v4l2_err(&ofl->v4l2_dev, " dev_id:%d ch%d unite crop_v need centered crop(h_offs:%d w:%d) input width:%d\n", + cfg->dev_id, i, cfg->output[i].crop_h_offs, + cfg->output[i].crop_width, cfg->input.width); + ret = -EINVAL; + goto end; + } + } + + /* check scale */ + if (i == RKVPSS_OUTPUT_CH2 || i == RKVPSS_OUTPUT_CH3) { + if (cfg->output[i].crop_width != cfg->output[i].scl_width && + cfg->output[i].crop_height != cfg->output[i].scl_height) { + if ((!*unite && cfg->output[i].scl_width > 1920) || + (*unite && cfg->output[i].scl_width > 1920 * 2)) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d single scale max width 1920\n", + cfg->dev_id, i); + ret = -EINVAL; + goto end; + } + } + } + } + + /* check rotate */ + switch (cfg->input.rotate) { + case ROTATE_90: + case ROTATE_180: + case ROTATE_270: + if (cfg->input.format != V4L2_PIX_FMT_TILE420 && + cfg->input.format != V4L2_PIX_FMT_TILE422) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d input format:%c%c%c%c not support rotate\n", + cfg->dev_id, cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24); + ret = -EINVAL; + goto end; + } + break; + default: + break; + } + + /** unite constraints **/ + if (*unite) { + if (cfg->input.format == V4L2_PIX_FMT_FBC0 || + cfg->input.format == V4L2_PIX_FMT_FBC2 || + cfg->input.format == V4L2_PIX_FMT_FBC4 || + cfg->input.format == V4L2_PIX_FMT_TILE420 || + cfg->input.format == V4L2_PIX_FMT_TILE422) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support input this format:%c%c%c%c\n", + cfg->dev_id, + cfg->input.format, + cfg->input.format >> 8, + cfg->input.format >> 16, + cfg->input.format >> 24); + ret = -EINVAL; + goto end; + } + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + if (!cfg->output[i].enable) + continue; + if (cfg->output[i].format != V4L2_PIX_FMT_NV12 && + cfg->output[i].format != V4L2_PIX_FMT_NV16 && + cfg->output[i].format != V4L2_PIX_FMT_NV21 && + cfg->output[i].format != V4L2_PIX_FMT_NV61) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support output this format:%c%c%c%c\n", + cfg->dev_id, + cfg->output[i].format, + cfg->output[i].format >> 8, + cfg->output[i].format >> 16, + cfg->output[i].format >> 24); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].scl_width > cfg->input.width) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite horizontal no support scale up\n", + cfg->dev_id); + ret = -EINVAL; + goto end; + } + if (cfg->output[i].aspt.enable) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support aspt\n", + cfg->dev_id); + ret = -EINVAL; + goto end; + } + } + } + +end: + return ret; +} + +static int rkvpss_prepare_run(struct file *file, struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + int ret = 0; + bool unite; + int i, j, k; + s64 us = 0; + u64 ns; + ktime_t t = 0; + + 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; + + ret = rkvpss_check_params(file, cfg, &unite); + if (ret < 0) + goto end; + + /******** show cfg info ********/ + if (rkvpss_debug >= 2) { + t = ktime_get(); + + v4l2_info(&ofl->v4l2_dev, + "%s dev_id:%d seq:%d mirror:%d input:%dx%d buffd:%d format:%c%c%c%c stride:%d rotate:%d\n", + __func__, cfg->dev_id, cfg->sequence, cfg->mirror, + cfg->input.width, cfg->input.height, cfg->input.buf_fd, + cfg->input.format, cfg->input.format >> 8, + cfg->input.format >> 16, cfg->input.format >> 24, + cfg->input.stride, cfg->input.rotate); + for (i = 0; i < RKVPSS_OUT_V1_MAX; i++) { + v4l2_info(&ofl->v4l2_dev, + "\t\t\tch%d enable:%d crop:(%d,%d)/%dx%d scl:%dx%d flip:%d buffd:%d format:%c%c%c%c stride:%d\n", + i, cfg->output[i].enable, + cfg->output[i].crop_h_offs, cfg->output[i].crop_v_offs, + cfg->output[i].crop_width, cfg->output[i].crop_height, + cfg->output[i].scl_width, cfg->output[i].scl_height, + cfg->output[i].flip, cfg->output[i].buf_fd, + cfg->output[i].format, cfg->output[i].format >> 8, + cfg->output[i].format >> 16, cfg->output[i].format >> 24, + cfg->output[i].stride); + if (rkvpss_debug < 4) + break; + if (!cfg->output[i].enable) + continue; + v4l2_info(&ofl->v4l2_dev, + "\t\t\tcmsc mosaic_block:%d width_ro:%d height_ro:%d\n", + cfg->output[i].cmsc.mosaic_block, + cfg->output[i].cmsc.width_ro, + cfg->output[i].cmsc.height_ro); + for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) { + if (!cfg->output[i].cmsc.win[j].win_en) + continue; + v4l2_info(&ofl->v4l2_dev, + "\t\t\t\twin:%d win_en:%u mode:%u color_y:%u color_u:%u color_v:%u color_a:%u\n", + j, + cfg->output[i].cmsc.win[j].win_en, + cfg->output[i].cmsc.win[j].mode, + cfg->output[i].cmsc.win[j].cover_color_y, + cfg->output[i].cmsc.win[j].cover_color_u, + cfg->output[i].cmsc.win[j].cover_color_v, + cfg->output[i].cmsc.win[j].cover_color_a); + for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) { + v4l2_info(&ofl->v4l2_dev, + "\t\t\t\t\tpoint:%d x:%d y:%d\n", + k, + cfg->output[i].cmsc.win[j].point[k].x, + cfg->output[i].cmsc.win[j].point[k].y); + } + } + v4l2_info(&ofl->v4l2_dev, + "\t\t\taspt_en:%d w:%d h:%d h_offs:%d v_offs:%d color_y:%d color_u:%d color_v:%d\n", + cfg->output[i].aspt.enable, + cfg->output[i].aspt.width, + cfg->output[i].aspt.height, + cfg->output[i].aspt.h_offs, + cfg->output[i].aspt.v_offs, + cfg->output[i].aspt.color_y, + cfg->output[i].aspt.color_u, + cfg->output[i].aspt.color_v); + } + } + + if (!unite) { + ret = rkvpss_ofl_run(file, cfg, false, false); + if (ret < 0) + goto end; + } else { + ret = rkvpss_ofl_run(file, cfg, true, true); + if (ret < 0) { + v4l2_err(&ofl->v4l2_dev, "unite left error\n"); + goto end; + } + ret = rkvpss_ofl_run(file, cfg, true, false); + if (ret < 0) { + v4l2_err(&ofl->v4l2_dev, "unite right error\n"); + goto end; + } + } + + if (rkvpss_debug >= 2) { + us = ktime_us_delta(ktime_get(), t); + v4l2_info(&ofl->v4l2_dev, + "%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; + +end: + return ret; +} + +static long rkvpss_ofl_ioctl(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + long ret = 0; + bool unite; + + ofl->pm_need_wait = true; + + if (!arg) { + ret = -EINVAL; + goto out; + } + + switch (cmd) { + case RKVPSS_CMD_MODULE_SEL: + ret = rkvpss_module_sel(file, arg); + break; + case RKVPSS_CMD_MODULE_GET: + ret = rkvpss_module_get(file, arg); + break; + case RKVPSS_CMD_FRAME_HANDLE: + ret = rkvpss_prepare_run(file, arg); + break; + case RKVPSS_CMD_BUF_ADD: + ret = rkvpss_ofl_buf_add(file, arg); + break; + case RKVPSS_CMD_BUF_DEL: + rkvpss_ofl_buf_del(file, arg); + break; + case RKVPSS_CMD_CHECKPARAMS: + ret = rkvpss_check_params(file, arg, &unite); + 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 = rkvpss_ofl_ioctl, +}; + +static int ofl_open(struct file *file) +{ + struct rkvpss_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, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p ret:%d\n", __func__, file, ret); + return (ret > 0) ? 0 : ret; +} + +static int ofl_release(struct file *file) +{ + struct rkvpss_offline_dev *ofl = video_drvdata(file); + + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p\n", __func__, file); + + v4l2_fh_release(file); + buf_del(file, 0, 0, true, false); + 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 = "rkvpss-offline", + .vfl_dir = VFL_DIR_RX, + .fops = &offline_fops, + .ioctl_ops = &offline_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, +}; + +void rkvpss_offline_irq_v1(struct rkvpss_hw_dev *hw, u32 irq) +{ + struct rkvpss_offline_dev *ofl = &hw->ofl_dev; + + v4l2_dbg(3, rkvpss_debug, &ofl->v4l2_dev, + "%s 0x%x\n", __func__, irq); + + if (!completion_done(&ofl->cmpl)) + complete(&ofl->cmpl); +} + +int rkvpss_register_offline_v1(struct rkvpss_hw_dev *hw) +{ + struct rkvpss_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; + ofl->mode_sel_en = true; + vfd = &ofl->vfd; + vfd->device_caps = V4L2_CAP_STREAMING; + vfd->lock = &ofl->apilock; + 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); + INIT_LIST_HEAD(&ofl->cfginfo_list); + mutex_init(&ofl->ofl_lock); + rkvpss_offline_proc_init(ofl); + ofl->pm_need_wait = false; + init_completion(&ofl->pm_cmpl); + return 0; +unreg_v4l2: + mutex_destroy(&ofl->apilock); + v4l2_device_unregister(v4l2_dev); + return ret; +} + +void rkvpss_unregister_offline_v1(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_v1.h b/drivers/media/platform/rockchip/vpss/vpss_offline_v1.h new file mode 100644 index 000000000000..877daf8e1fea --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_offline_v1.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKVPSS_OFFLINE_V1_H +#define _RKVPSS_OFFLINE_V1_H +#define DEV_NUM_MAX 256 +#define UNITE_ENLARGE 16 +#define UNITE_LEFT_ENLARGE 16 + + +#if IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_VPSS_V1) +int rkvpss_register_offline_v1(struct rkvpss_hw_dev *hw); +void rkvpss_unregister_offline_v1(struct rkvpss_hw_dev *hw); +void rkvpss_offline_irq_v1(struct rkvpss_hw_dev *hw, u32 irq); +#else +static inline int rkvpss_register_offline_v1(struct rkvpss_hw_dev *hw) {return -EINVAL; } +static inline void rkvpss_unregister_offline_v1(struct rkvpss_hw_dev *hw) {} +static inline void rkvpss_offline_irq_v1(struct rkvpss_hw_dev *hw, u32 irq) {} +#endif + +#endif + diff --git a/include/uapi/linux/rk-vpss-config.h b/include/uapi/linux/rk-vpss-config.h index f243cac7ebf6..00f69fe72a77 100644 --- a/include/uapi/linux/rk-vpss-config.h +++ b/include/uapi/linux/rk-vpss-config.h @@ -27,6 +27,8 @@ * ioctl RKVPSS_CMD_MODULE_SEL to select function using */ +#define RKVPSS_OUT_V1_MAX 4 + /******vpss(online mode) v4l2 ioctl***************************/ /* set before VIDIOC_S_FMT if dynamically changing output resolution */ #define RKVPSS_CMD_SET_STREAM_MAX_SIZE \