diff --git a/drivers/media/platform/rockchip/vpss/Kconfig b/drivers/media/platform/rockchip/vpss/Kconfig index dd2f3bba2cc2..2826e4b0e5b5 100644 --- a/drivers/media/platform/rockchip/vpss/Kconfig +++ b/drivers/media/platform/rockchip/vpss/Kconfig @@ -4,7 +4,7 @@ config VIDEO_ROCKCHIP_VPSS depends on V4L_PLATFORM_DRIVERS depends on VIDEO_DEV depends on ARCH_ROCKCHIP || COMPILE_TEST - depends on CPU_RK3576 + depends on CPU_RK3576 || CPU_RV1126B select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_CMA_SG default n @@ -18,3 +18,11 @@ config VIDEO_ROCKCHIP_VPSS_V10 default y help Support for rk3576 + +config VIDEO_ROCKCHIP_VPSS_V20 + bool "vpss2 for rv1126b" + depends on CPU_RV1126B + depends on VIDEO_ROCKCHIP_VPSS + default y + help + Support for rv1126b diff --git a/drivers/media/platform/rockchip/vpss/Makefile b/drivers/media/platform/rockchip/vpss/Makefile index 91cf5f13ef65..35b36539ea8c 100644 --- a/drivers/media/platform/rockchip/vpss/Makefile +++ b/drivers/media/platform/rockchip/vpss/Makefile @@ -12,3 +12,13 @@ video_rkvpss-objs += hw.o \ video_rkvpss-$(CONFIG_VIDEO_ROCKCHIP_VPSS_V10) += \ stream_v10.o \ vpss_offline_v10.o + +video_rkvpss-$(CONFIG_VIDEO_ROCKCHIP_VPSS_V20) += \ + stream_v20.o \ + vpss_offline_v20.o \ + vpss_offline_rockit.o \ + vpss_rockit.o + +video_rkvpss-$(CONFIG_ROCKCHIP_DVBM) += \ + vpss_dvbm.o \ + vpss_offline_dvbm.o diff --git a/drivers/media/platform/rockchip/vpss/common.c b/drivers/media/platform/rockchip/vpss/common.c index d5f43571b4c9..ea5c21fe01d8 100644 --- a/drivers/media/platform/rockchip/vpss/common.c +++ b/drivers/media/platform/rockchip/vpss/common.c @@ -76,6 +76,7 @@ void rkvpss_unite_clear_bits(struct rkvpss_device *dev, u32 reg, u32 mask) rkvpss_idx_clear_bits(dev, reg, mask, VPSS_UNITE_RIGHT); } +// 1126b unite or multi sensor todo void rkvpss_update_regs(struct rkvpss_device *dev, u32 start, u32 end) { struct rkvpss_hw_dev *hw = dev->hw_dev; diff --git a/drivers/media/platform/rockchip/vpss/common.h b/drivers/media/platform/rockchip/vpss/common.h index 0bad5eb6aa3f..fb99bf0a05c4 100644 --- a/drivers/media/platform/rockchip/vpss/common.h +++ b/drivers/media/platform/rockchip/vpss/common.h @@ -26,6 +26,8 @@ #define RKVPSS_DEFAULT_HEIGHT 1080 #define RKVPSS_MAX_WIDTH 4672 #define RKVPSS_MAX_HEIGHT 3504 +#define RKVPSS_MAX_WIDTH_V20 4096 +#define RKVPSS_MAX_HEIGHT_V20 3072 #define RKVPSS_MIN_WIDTH 32 #define RKVPSS_MIN_HEIGHT 32 #define RKVPSS_UNITE_MAX_WIDTH 8192 @@ -34,13 +36,19 @@ #define RKVPSS_REG_CACHE_SYNC 0xeeeeeeee #define RKVPSS_REG_CACHE 0xffffffff -#define RKVPSS_SW_REG_SIZE 0x35c0 +#define RKVPSS_SW_REG_SIZE 0x37c0 #define RKVPSS_SW_REG_SIZE_MAX (RKVPSS_SW_REG_SIZE * 2) struct rkvpss_device; enum rkvpss_ver { VPSS_V10 = 0x00, + VPSS_V20 = 0x20, +}; + +enum { + ROCKIT_DVBM_END, + ROCKIT_DVBM_START, }; enum rkvpss_fmt_pix_type { @@ -95,6 +103,8 @@ static inline int vpss_outchn_max(int version) { if (version == VPSS_V10) return 4; + else if (version == VPSS_V20) + return 6; return 0; } diff --git a/drivers/media/platform/rockchip/vpss/dev.c b/drivers/media/platform/rockchip/vpss/dev.c index d4de40a09975..d1be6bb1d865 100644 --- a/drivers/media/platform/rockchip/vpss/dev.c +++ b/drivers/media/platform/rockchip/vpss/dev.c @@ -372,6 +372,7 @@ static int __maybe_unused rkvpss_dev_runtime_resume(struct device *dev) struct rkvpss_device *vpss_dev = dev_get_drvdata(dev); int ret; + vpss_dev->stream_vdev.wrap_line = rkvpss_wrap_line; mutex_lock(&vpss_dev->hw_dev->dev_lock); ret = pm_runtime_get_sync(vpss_dev->hw_dev->dev); mutex_unlock(&vpss_dev->hw_dev->dev_lock); diff --git a/drivers/media/platform/rockchip/vpss/dev.h b/drivers/media/platform/rockchip/vpss/dev.h index eaab883d823e..a91f844dcad8 100644 --- a/drivers/media/platform/rockchip/vpss/dev.h +++ b/drivers/media/platform/rockchip/vpss/dev.h @@ -24,6 +24,8 @@ #define S1_VDEV_NAME DRIVER_NAME "_scale1" #define S2_VDEV_NAME DRIVER_NAME "_scale2" #define S3_VDEV_NAME DRIVER_NAME "_scale3" +#define S4_VDEV_NAME DRIVER_NAME "_scale4" +#define S5_VDEV_NAME DRIVER_NAME "_scale5" #define RKVPSS_REGFILE_LEN 50 @@ -50,6 +52,11 @@ struct rkvpss_rdbk_info { u64 seq; }; +struct rkvpss_wrap_buf { + struct dma_buf *dbuf; + dma_addr_t dma_addr; +}; + struct rkvpss_device { char name[128]; struct device *dev; @@ -64,6 +71,7 @@ struct rkvpss_device { struct rkvpss_subdev vpss_sdev; struct rkvpss_stream_vdev stream_vdev; struct proc_dir_entry *procfs; + struct rkvpss_wrap_buf wrap_buf; atomic_t pipe_power_cnt; atomic_t pipe_stream_cnt; diff --git a/drivers/media/platform/rockchip/vpss/hw.c b/drivers/media/platform/rockchip/vpss/hw.c index c2362267468e..61e16950656b 100644 --- a/drivers/media/platform/rockchip/vpss/hw.c +++ b/drivers/media/platform/rockchip/vpss/hw.c @@ -655,6 +655,10 @@ static irqreturn_t mi_irq_hdl(int irq, void *ctx) if (mis_val) { if (mis_val & RKVPSS_MI_BUS_ERR) dev_err(dev, "axi bus error\n"); + if (is_vpss_v20(hw_dev) && + mis_val & RKVPSS2X_MI_RD_FBCD_ERR) + dev_err(dev, "rd_fbcd_err\n"); + rkvpss_hw_write(hw_dev, RKVPSS_MI_ICR, mis_val); if (vpss) rkvpss_mi_isr(vpss, mis_val); @@ -681,12 +685,26 @@ static const struct vpss_match_data rk3576_vpss_match_data = { .vpss_ver = VPSS_V10, }; +static const struct vpss_match_data rv1126b_vpss_match_data = { + .irqs = vpss_irqs, + .num_irqs = ARRAY_SIZE(vpss_irqs), + .clks = vpss_clks, + .clks_num = ARRAY_SIZE(vpss_clks), + .vpss_ver = VPSS_V20, +}; + static const struct of_device_id rkvpss_hw_of_match[] = { #ifdef CONFIG_VIDEO_ROCKCHIP_VPSS_V10 { .compatible = "rockchip,rk3576-rkvpss", .data = &rk3576_vpss_match_data, }, +#endif +#ifdef CONFIG_VIDEO_ROCKCHIP_VPSS_V20 + { + .compatible = "rockchip,rv1126b-rkvpss", + .data = &rv1126b_vpss_match_data, + }, #endif {}, }; @@ -703,7 +721,7 @@ void rkvpss_hw_reg_restore(struct rkvpss_hw_dev *dev) { void __iomem *base = dev->base_addr; void *reg_buf = dev->sw_reg; - u32 val, *reg, i; + u32 val, *reg, i, range; dev_info(dev->dev, "%s\n", __func__); @@ -718,7 +736,9 @@ void rkvpss_hw_reg_restore(struct rkvpss_hw_dev *dev) writel(val, base + RKVPSS_CMSC_UPDATE); /* crop1 */ - for (i = RKVPSS_CROP1_CTRL; i <= RKVPSS_CROP1_3_V_SIZE; i += 4) { + range = is_vpss_v10(dev) ? RKVPSS_CROP1_3_V_SIZE : + RKVPSS2X_CROP1_5_SIZE; + for (i = RKVPSS_CROP1_CTRL; i <= range; i += 4) { if (i == RKVPSS_CROP1_UPDATE) continue; reg = reg_buf + i; @@ -765,8 +785,30 @@ void rkvpss_hw_reg_restore(struct rkvpss_hw_dev *dev) val = RKVPSS_SCL_GEN_UPD | RKVPSS_SCL_FORCE_UPD; writel(val, base + RKVPSS_SCALE3_UPDATE); + if (is_vpss_v20(dev)) { + for (i = RKVPSS2X_SCALE4_BASE; i <= RKVPSS2X_SCALE4_IN_CROP_OFFSET; i += 4) { + if (i == RKVPSS2X_SCALE4_UPDATE) + continue; + reg = reg_buf + i; + writel(*reg, base + i); + } + val = RKVPSS_SCL_GEN_UPD | RKVPSS_SCL_FORCE_UPD; + writel(val, base + RKVPSS2X_SCALE4_UPDATE); + + for (i = RKVPSS2X_SCALE5_BASE; i <= RKVPSS2X_SCALE5_IN_CROP_OFFSET; i += 4) { + if (i == RKVPSS2X_SCALE4_UPDATE) + continue; + reg = reg_buf + i; + writel(*reg, base + i); + } + val = RKVPSS_SCL_GEN_UPD | RKVPSS_SCL_FORCE_UPD; + writel(val, base + RKVPSS2X_SCALE5_UPDATE); + } + /* mi */ - for (i = RKVPSS_MI_BASE; i <= RKVPSS_MI_CHN3_WR_LINE_CNT; i += 4) { + range = is_vpss_v10(dev) ? RKVPSS_MI_CHN3_WR_LINE_CNT : + RKVPSS2X_MI_CHN5_WR_LINE_CNT; + for (i = RKVPSS_MI_BASE; i <= range; i += 4) { if (i >= RKVPSS_MI_RD_CTRL && i <= RKVPSS_MI_RD_Y_HEIGHT_SHD) continue; if (i == RKVPSS_MI_WR_INIT) @@ -787,6 +829,8 @@ void rkvpss_hw_reg_restore(struct rkvpss_hw_dev *dev) } val = RKVPSS_CFG_FORCE_UPD | RKVPSS_CFG_GEN_UPD | RKVPSS_MIR_GEN_UPD | RKVPSS_ONLINE2_CHN_FORCE_UPD; + if (is_vpss_v20(dev)) + val |= RKVPSS_MIR_FORCE_UPD; if (!dev->is_ofl_cmsc) val |= RKVPSS_MIR_FORCE_UPD; writel(val, base + RKVPSS_VPSS_UPDATE); diff --git a/drivers/media/platform/rockchip/vpss/hw.h b/drivers/media/platform/rockchip/vpss/hw.h index 5f57892c14e2..f694f7a3b31f 100644 --- a/drivers/media/platform/rockchip/vpss/hw.h +++ b/drivers/media/platform/rockchip/vpss/hw.h @@ -76,6 +76,12 @@ static inline bool is_vpss_v10(const struct rkvpss_hw_dev *hw_dev) { return hw_d static inline bool is_vpss_v10(const struct rkvpss_hw_dev *hw_dev) { return false; } #endif +#ifdef CONFIG_VIDEO_ROCKCHIP_VPSS_V20 +static inline bool is_vpss_v20(const struct rkvpss_hw_dev *hw_dev) { return hw_dev->vpss_ver == VPSS_V20; } +#else +static inline bool is_vpss_v20(const struct rkvpss_hw_dev *hw_dev) { return false; } +#endif + #define RKVPSS_ZME_TAP_COE(x, y) (((x) & 0x3ff) | (((y) & 0x3ff) << 16)) extern const s16 rkvpss_zme_tap8_coe[11][17][8]; extern const s16 rkvpss_zme_tap6_coe[11][17][8]; diff --git a/drivers/media/platform/rockchip/vpss/regs.h b/drivers/media/platform/rockchip/vpss/regs.h index 8ec27134d2c1..d79cc94d94a6 100644 --- a/drivers/media/platform/rockchip/vpss/regs.h +++ b/drivers/media/platform/rockchip/vpss/regs.h @@ -5,5 +5,6 @@ #define _RKVPSS_REGS_H #include "regs_v10.h" +#include "regs_v20.h" #endif diff --git a/drivers/media/platform/rockchip/vpss/regs_v20.h b/drivers/media/platform/rockchip/vpss/regs_v20.h new file mode 100644 index 000000000000..c214ebb3f47f --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/regs_v20.h @@ -0,0 +1,322 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKVPSS_REGS_V20_H +#define _RKVPSS_REGS_V20_H + +/* VPSS */ +#define RKVPSS2X_VPSS_BASE 0x0000 +#define RKVPSS2X_VPSS_PIPE_ACK2 (RKVPSS2X_VPSS_BASE + 0xb8) + +/* CMSC identical */ + +/* CROP */ +#define RKVPSS2X_CROP0_BASE 0x0700 +#define RKVPSS2X_CROP0_4_OFFS (RKVPSS2X_CROP0_BASE + 0x50) +#define RKVPSS2X_CROP0_4_SIZE (RKVPSS2X_CROP0_BASE + 0x54) +#define RKVPSS2X_CROP0_5_OFFS (RKVPSS2X_CROP0_BASE + 0x58) +#define RKVPSS2X_CROP0_5_SIZE (RKVPSS2X_CROP0_BASE + 0x5c) +#define RKVPSS2X_CROP0_4_OFFS_SHD (RKVPSS2X_CROP0_BASE + 0xb0) +#define RKVPSS2X_CROP0_4_SIZE_SHD (RKVPSS2X_CROP0_BASE + 0xb4) +#define RKVPSS2X_CROP0_5_OFFS_SHD (RKVPSS2X_CROP0_BASE + 0xb8) +#define RKVPSS2X_CROP0_5_SIZE_SHD (RKVPSS2X_CROP0_BASE + 0xbc) + +#define RKVPSS2X_CROP1_BASE 0x0800 +#define RKVPSS2X_CROP1_4_OFFS (RKVPSS2X_CROP1_BASE + 0x50) +#define RKVPSS2X_CROP1_4_SIZE (RKVPSS2X_CROP1_BASE + 0x54) +#define RKVPSS2X_CROP1_5_OFFS (RKVPSS2X_CROP1_BASE + 0x58) +#define RKVPSS2X_CROP1_5_SIZE (RKVPSS2X_CROP1_BASE + 0x5c) +#define RKVPSS2X_CROP1_4_OFFS_SHD (RKVPSS2X_CROP1_BASE + 0xb0) +#define RKVPSS2X_CROP1_4_SIZE_SHD (RKVPSS2X_CROP1_BASE + 0xb4) +#define RKVPSS2X_CROP1_5_OFFS_SHD (RKVPSS2X_CROP1_BASE + 0xb8) +#define RKVPSS2X_CROP1_5_SIZE_SHD (RKVPSS2X_CROP1_BASE + 0xbc) + +/* Bilinear scale channel0 */ +#define RKVPSS2X_SCALE0_BASE 0x1000 +#define RKVPSS2X_SCALE0_CTRL (RKVPSS2X_SCALE0_BASE + 0x00) +#define RKVPSS2X_SCALE0_UPDATE (RKVPSS2X_SCALE0_BASE + 0x04) +#define RKVPSS2X_SCALE0_SRC_SIZE (RKVPSS2X_SCALE0_BASE + 0x08) +#define RKVPSS2X_SCALE0_DST_SIZE (RKVPSS2X_SCALE0_BASE + 0x0c) +#define RKVPSS2X_SCALE0_HY_FAC (RKVPSS2X_SCALE0_BASE + 0x10) +#define RKVPSS2X_SCALE0_HC_FAC (RKVPSS2X_SCALE0_BASE + 0x14) +#define RKVPSS2X_SCALE0_VY_FAC (RKVPSS2X_SCALE0_BASE + 0x18) +#define RKVPSS2X_SCALE0_VC_FAC (RKVPSS2X_SCALE0_BASE + 0x1c) +#define RKVPSS2X_SCALE0_HY_OFFS (RKVPSS2X_SCALE0_BASE + 0x20) +#define RKVPSS2X_SCALE0_HC_OFFS (RKVPSS2X_SCALE0_BASE + 0x24) +#define RKVPSS2X_SCALE0_VY_OFFS (RKVPSS2X_SCALE0_BASE + 0x28) +#define RKVPSS2X_SCALE0_VC_OFFS (RKVPSS2X_SCALE0_BASE + 0x2c) +#define RKVPSS2X_SCALE0_HY_SIZE (RKVPSS2X_SCALE0_BASE + 0x40) +#define RKVPSS2X_SCALE0_HC_SIZE (RKVPSS2X_SCALE0_BASE + 0x44) +#define RKVPSS2X_SCALE0_HY_OFFS_MI (RKVPSS2X_SCALE0_BASE + 0x48) +#define RKVPSS2X_SCALE0_HC_OFFS_MI (RKVPSS2X_SCALE0_BASE + 0x4c) +#define RKVPSS2X_SCALE0_IN_CROP_OFFSET (RKVPSS2X_SCALE0_BASE + 0x50) +#define RKVPSS2X_SCALE0_CTRL_SHD (RKVPSS2X_SCALE0_BASE + 0x80) +#define RKVPSS2X_SCALE0_SRC_SIZE_SHD (RKVPSS2X_SCALE0_BASE + 0x88) +#define RKVPSS2X_SCALE0_DST_SIZE_SHD (RKVPSS2X_SCALE0_BASE + 0x8c) +#define RKVPSS2X_SCALE0_HY_FAC_SHD (RKVPSS2X_SCALE0_BASE + 0x90) +#define RKVPSS2X_SCALE0_HC_FAC_SHD (RKVPSS2X_SCALE0_BASE + 0x94) +#define RKVPSS2X_SCALE0_VY_FAC_SHD (RKVPSS2X_SCALE0_BASE + 0x98) +#define RKVPSS2X_SCALE0_VC_FAC_SHD (RKVPSS2X_SCALE0_BASE + 0x9c) +#define RKVPSS2X_SCALE0_HY_OFFS_SHD (RKVPSS2X_SCALE0_BASE + 0xa0) +#define RKVPSS2X_SCALE0_HC_OFFS_SHD (RKVPSS2X_SCALE0_BASE + 0xa4) +#define RKVPSS2X_SCALE0_VY_OFFS_SHD (RKVPSS2X_SCALE0_BASE + 0xa8) +#define RKVPSS2X_SCALE0_VC_OFFS_SHD (RKVPSS2X_SCALE0_BASE + 0xac) +#define RKVPSS2X_SCALE0_HY_SIZE_SHD (RKVPSS2X_SCALE0_BASE + 0xc0) +#define RKVPSS2X_SCALE0_HC_SIZE_SHD (RKVPSS2X_SCALE0_BASE + 0xc4) +#define RKVPSS2X_SCALE0_HY_OFFS_MI_SHD (RKVPSS2X_SCALE0_BASE + 0xc8) +#define RKVPSS2X_SCALE0_HC_OFFS_MI_SHD (RKVPSS2X_SCALE0_BASE + 0xcc) +#define RKVPSS2X_SCALE0_IN_CROP_OFFSET_SHD (RKVPSS2X_SCALE0_BASE + 0xd0) + +/* Bilinear scale channel4 */ +#define RKVPSS2X_SCALE4_BASE 0x0a00 +#define RKVPSS2X_SCALE4_CTRL (RKVPSS2X_SCALE4_BASE + 0x00) +#define RKVPSS2X_SCALE4_UPDATE (RKVPSS2X_SCALE4_BASE + 0x04) +#define RKVPSS2X_SCALE4_SRC_SIZE (RKVPSS2X_SCALE4_BASE + 0x08) +#define RKVPSS2X_SCALE4_DST_SIZE (RKVPSS2X_SCALE4_BASE + 0x0c) +#define RKVPSS2X_SCALE4_HY_FAC (RKVPSS2X_SCALE4_BASE + 0x10) +#define RKVPSS2X_SCALE4_HC_FAC (RKVPSS2X_SCALE4_BASE + 0x14) +#define RKVPSS2X_SCALE4_VY_FAC (RKVPSS2X_SCALE4_BASE + 0x18) +#define RKVPSS2X_SCALE4_VC_FAC (RKVPSS2X_SCALE4_BASE + 0x1c) +#define RKVPSS2X_SCALE4_HY_OFFS (RKVPSS2X_SCALE4_BASE + 0x20) +#define RKVPSS2X_SCALE4_HC_OFFS (RKVPSS2X_SCALE4_BASE + 0x24) +#define RKVPSS2X_SCALE4_VY_OFFS (RKVPSS2X_SCALE4_BASE + 0x28) +#define RKVPSS2X_SCALE4_VC_OFFS (RKVPSS2X_SCALE4_BASE + 0x2c) +#define RKVPSS2X_SCALE4_HY_SIZE (RKVPSS2X_SCALE4_BASE + 0x40) +#define RKVPSS2X_SCALE4_HC_SIZE (RKVPSS2X_SCALE4_BASE + 0x44) +#define RKVPSS2X_SCALE4_HY_OFFS_MI (RKVPSS2X_SCALE4_BASE + 0x48) +#define RKVPSS2X_SCALE4_HC_OFFS_MI (RKVPSS2X_SCALE4_BASE + 0x4c) +#define RKVPSS2X_SCALE4_IN_CROP_OFFSET (RKVPSS2X_SCALE4_BASE + 0x50) +#define RKVPSS2X_SCALE4_CTRL_SHD (RKVPSS2X_SCALE4_BASE + 0x80) +#define RKVPSS2X_SCALE4_SRC_SIZE_SHD (RKVPSS2X_SCALE4_BASE + 0x88) +#define RKVPSS2X_SCALE4_DST_SIZE_SHD (RKVPSS2X_SCALE4_BASE + 0x8c) +#define RKVPSS2X_SCALE4_HY_FAC_SHD (RKVPSS2X_SCALE4_BASE + 0x90) +#define RKVPSS2X_SCALE4_HC_FAC_SHD (RKVPSS2X_SCALE4_BASE + 0x94) +#define RKVPSS2X_SCALE4_VY_FAC_SHD (RKVPSS2X_SCALE4_BASE + 0x98) +#define RKVPSS2X_SCALE4_VC_FAC_SHD (RKVPSS2X_SCALE4_BASE + 0x9c) +#define RKVPSS2X_SCALE4_HY_OFFS_SHD (RKVPSS2X_SCALE4_BASE + 0xa0) +#define RKVPSS2X_SCALE4_HC_OFFS_SHD (RKVPSS2X_SCALE4_BASE + 0xa4) +#define RKVPSS2X_SCALE4_VY_OFFS_SHD (RKVPSS2X_SCALE4_BASE + 0xa8) +#define RKVPSS2X_SCALE4_VC_OFFS_SHD (RKVPSS2X_SCALE4_BASE + 0xac) +#define RKVPSS2X_SCALE4_HY_SIZE_SHD (RKVPSS2X_SCALE4_BASE + 0xc0) +#define RKVPSS2X_SCALE4_HC_SIZE_SHD (RKVPSS2X_SCALE4_BASE + 0xc4) +#define RKVPSS2X_SCALE4_HY_OFFS_MI_SHD (RKVPSS2X_SCALE4_BASE + 0xc8) +#define RKVPSS2X_SCALE4_HC_OFFS_MI_SHD (RKVPSS2X_SCALE4_BASE + 0xcc) +#define RKVPSS2X_SCALE4_IN_CROP_OFFSET_SHD (RKVPSS2X_SCALE4_BASE + 0xd0) + +/* Bilinear scale channel5 */ +#define RKVPSS2X_SCALE5_BASE 0x0c00 +#define RKVPSS2X_SCALE5_CTRL (RKVPSS2X_SCALE5_BASE + 0x00) +#define RKVPSS2X_SCALE5_UPDATE (RKVPSS2X_SCALE5_BASE + 0x04) +#define RKVPSS2X_SCALE5_SRC_SIZE (RKVPSS2X_SCALE5_BASE + 0x08) +#define RKVPSS2X_SCALE5_DST_SIZE (RKVPSS2X_SCALE5_BASE + 0x0c) +#define RKVPSS2X_SCALE5_HY_FAC (RKVPSS2X_SCALE5_BASE + 0x10) +#define RKVPSS2X_SCALE5_HC_FAC (RKVPSS2X_SCALE5_BASE + 0x14) +#define RKVPSS2X_SCALE5_VY_FAC (RKVPSS2X_SCALE5_BASE + 0x18) +#define RKVPSS2X_SCALE5_VC_FAC (RKVPSS2X_SCALE5_BASE + 0x1c) +#define RKVPSS2X_SCALE5_HY_OFFS (RKVPSS2X_SCALE5_BASE + 0x20) +#define RKVPSS2X_SCALE5_HC_OFFS (RKVPSS2X_SCALE5_BASE + 0x24) +#define RKVPSS2X_SCALE5_VY_OFFS (RKVPSS2X_SCALE5_BASE + 0x28) +#define RKVPSS2X_SCALE5_VC_OFFS (RKVPSS2X_SCALE5_BASE + 0x2c) +#define RKVPSS2X_SCALE5_HY_SIZE (RKVPSS2X_SCALE5_BASE + 0x40) +#define RKVPSS2X_SCALE5_HC_SIZE (RKVPSS2X_SCALE5_BASE + 0x44) +#define RKVPSS2X_SCALE5_HY_OFFS_MI (RKVPSS2X_SCALE5_BASE + 0x48) +#define RKVPSS2X_SCALE5_HC_OFFS_MI (RKVPSS2X_SCALE5_BASE + 0x4c) +#define RKVPSS2X_SCALE5_IN_CROP_OFFSET (RKVPSS2X_SCALE5_BASE + 0x50) +#define RKVPSS2X_SCALE5_CTRL_SHD (RKVPSS2X_SCALE5_BASE + 0x80) +#define RKVPSS2X_SCALE5_SRC_SIZE_SHD (RKVPSS2X_SCALE5_BASE + 0x88) +#define RKVPSS2X_SCALE5_DST_SIZE_SHD (RKVPSS2X_SCALE5_BASE + 0x8c) +#define RKVPSS2X_SCALE5_HY_FAC_SHD (RKVPSS2X_SCALE5_BASE + 0x90) +#define RKVPSS2X_SCALE5_HC_FAC_SHD (RKVPSS2X_SCALE5_BASE + 0x94) +#define RKVPSS2X_SCALE5_VY_FAC_SHD (RKVPSS2X_SCALE5_BASE + 0x98) +#define RKVPSS2X_SCALE5_VC_FAC_SHD (RKVPSS2X_SCALE5_BASE + 0x9c) +#define RKVPSS2X_SCALE5_HY_OFFS_SHD (RKVPSS2X_SCALE5_BASE + 0xa0) +#define RKVPSS2X_SCALE5_HC_OFFS_SHD (RKVPSS2X_SCALE5_BASE + 0xa4) +#define RKVPSS2X_SCALE5_VY_OFFS_SHD (RKVPSS2X_SCALE5_BASE + 0xa8) +#define RKVPSS2X_SCALE5_VC_OFFS_SHD (RKVPSS2X_SCALE5_BASE + 0xac) +#define RKVPSS2X_SCALE5_HY_SIZE_SHD (RKVPSS2X_SCALE5_BASE + 0xc0) +#define RKVPSS2X_SCALE5_HC_SIZE_SHD (RKVPSS2X_SCALE5_BASE + 0xc4) +#define RKVPSS2X_SCALE5_HY_OFFS_MI_SHD (RKVPSS2X_SCALE5_BASE + 0xc8) +#define RKVPSS2X_SCALE5_HC_OFFS_MI_SHD (RKVPSS2X_SCALE5_BASE + 0xcc) +#define RKVPSS2X_SCALE5_IN_CROP_OFFSET_SHD (RKVPSS2X_SCALE5_BASE + 0xd0) + +/* ASPTO4 */ +#define RKVPSS2X_RATIO4_BASE 0x0b00 +#define RKVPSS2X_RATIO4_CTRL (RKVPSS2X_RATIO4_BASE + 0x00) +#define RKVPSS2X_RATIO4_UPDATE (RKVPSS2X_RATIO4_BASE + 0x04) +#define RKVPSS2X_RATIO4_ACT_SIZE (RKVPSS2X_RATIO4_BASE + 0x10) +#define RKVPSS2X_RATIO4_VIR_SIZE (RKVPSS2X_RATIO4_BASE + 0x14) +#define RKVPSS2X_RATIO4_OFFS (RKVPSS2X_RATIO4_BASE + 0x18) +#define RKVPSS2X_RATIO4_COLOR (RKVPSS2X_RATIO4_BASE + 0x1c) +#define RKVPSS2X_RATIO4_ACT_SIZE_SHD (RKVPSS2X_RATIO4_BASE + 0x30) +#define RKVPSS2X_RATIO4_VIR_SIZE_SHD (RKVPSS2X_RATIO4_BASE + 0x34) +#define RKVPSS2X_RATIO4_OFFS_SHD (RKVPSS2X_RATIO4_BASE + 0x38) +#define RKVPSS2X_RATIO4_COLOR_SHD (RKVPSS2X_RATIO4_BASE + 0x3c) + +/* ASPTO5 */ +#define RKVPSS2X_RATIO5_BASE 0x0d00 +#define RKVPSS2X_RATIO5_CTRL (RKVPSS2X_RATIO5_BASE + 0x00) +#define RKVPSS2X_RATIO5_UPDATE (RKVPSS2X_RATIO5_BASE + 0x04) +#define RKVPSS2X_RATIO5_ACT_SIZE (RKVPSS2X_RATIO5_BASE + 0x10) +#define RKVPSS2X_RATIO5_VIR_SIZE (RKVPSS2X_RATIO5_BASE + 0x14) +#define RKVPSS2X_RATIO5_OFFS (RKVPSS2X_RATIO5_BASE + 0x18) +#define RKVPSS2X_RATIO5_COLOR (RKVPSS2X_RATIO5_BASE + 0x1c) +#define RKVPSS2X_RATIO5_ACT_SIZE_SHD (RKVPSS2X_RATIO5_BASE + 0x30) +#define RKVPSS2X_RATIO5_VIR_SIZE_SHD (RKVPSS2X_RATIO5_BASE + 0x34) +#define RKVPSS2X_RATIO5_OFFS_SHD (RKVPSS2X_RATIO5_BASE + 0x38) +#define RKVPSS2X_RATIO5_COLOR_SHD (RKVPSS2X_RATIO5_BASE + 0x3c) + +/* MI */ +#define RKVPSS2X_MI_BASE 0x3000 +#define RKVPSS2X_MI_WR_FBCE_CTRL (RKVPSS2X_MI_BASE + 0x20) +#define RKVPSS2X_MI_WR_FBCE_SIZE (RKVPSS2X_MI_BASE + 0x24) +#define RKVPSS2X_MI_WR_FBCE_OFFSET (RKVPSS2X_MI_BASE + 0x28) +#define RKVPSS2X_MI_CHN0_WR_CB_STRIDE (RKVPSS2X_MI_BASE + 0x240) +#define RKVPSS2X_MI_CHN0_WR_CB_PIC_SIZE (RKVPSS2X_MI_BASE + 0x24c) +#define RKVPSS2X_MI_CHN0_WR_Y_STRIDE_SHD (RKVPSS2X_MI_BASE + 0x26c) +#define RKVPSS2X_MI_CHN0_WR_CB_STRIDE_SHD (RKVPSS2X_MI_BASE + 0x27c) +#define RKVPSS2X_MI_CHN1_WR_CB_STRIDE (RKVPSS2X_MI_BASE + 0x340) +#define RKVPSS2X_MI_CHN1_WR_CB_PIC_SIZE (RKVPSS2X_MI_BASE + 0x34c) +#define RKVPSS2X_MI_CHN1_WR_Y_STRIDE_SHD (RKVPSS2X_MI_BASE + 0x36c) +#define RKVPSS2X_MI_CHN1_WR_CB_STRIDE_SHD (RKVPSS2X_MI_BASE + 0x37c) +#define RKVPSS2X_MI_CHN2_WR_Y_STRIDE_SHD (RKVPSS2X_MI_BASE + 0x46c) +#define RKVPSS2X_MI_CHN3_WR_Y_STRIDE_SHD (RKVPSS2X_MI_BASE + 0x56c) +#define RKVPSS2X_MI_CHN4_WR_CTRL (RKVPSS2X_MI_BASE + 0x600) +#define RKVPSS2X_MI_CHN4_WR_Y_BASE (RKVPSS2X_MI_BASE + 0x610) +#define RKVPSS2X_MI_CHN4_WR_Y_SIZE (RKVPSS2X_MI_BASE + 0x614) +#define RKVPSS2X_MI_CHN4_WR_Y_OFFS_CNT (RKVPSS2X_MI_BASE + 0x618) +#define RKVPSS2X_MI_CHN4_WR_Y_OFFS_CNT_START (RKVPSS2X_MI_BASE + 0x61c) +#define RKVPSS2X_MI_CHN4_WR_CB_BASE (RKVPSS2X_MI_BASE + 0x620) +#define RKVPSS2X_MI_CHN4_WR_CB_SIZE (RKVPSS2X_MI_BASE + 0x624) +#define RKVPSS2X_MI_CHN4_WR_CB_OFFS_CNT (RKVPSS2X_MI_BASE + 0x628) +#define RKVPSS2X_MI_CHN4_WR_CB_OFFS_CNT_START (RKVPSS2X_MI_BASE + 0x62c) +#define RKVPSS2X_MI_CHN4_WR_Y_STRIDE (RKVPSS2X_MI_BASE + 0x630) +#define RKVPSS2X_MI_CHN4_WR_Y_PIC_WIDTH (RKVPSS2X_MI_BASE + 0x634) +#define RKVPSS2X_MI_CHN4_WR_Y_PIC_SIZE (RKVPSS2X_MI_BASE + 0x63c) +#define RKVPSS2X_MI_CHN4_WR_CTRL_SHD (RKVPSS2X_MI_BASE + 0x650) +#define RKVPSS2X_MI_CHN4_WR_Y_BASE_SHD (RKVPSS2X_MI_BASE + 0x660) +#define RKVPSS2X_MI_CHN4_WR_Y_SIZE_SHD (RKVPSS2X_MI_BASE + 0x664) +#define RKVPSS2X_MI_CHN4_WR_Y_OFFS_CNT_SHD (RKVPSS2X_MI_BASE + 0x668) +#define RKVPSS2X_MI_CHN4_WR_Y_STRDE_SHD (RKVPSS2X_MI_BASE + 0x66c) +#define RKVPSS2X_MI_CHN4_WR_CB_BASE_SHD (RKVPSS2X_MI_BASE + 0x670) +#define RKVPSS2X_MI_CHN4_WR_CB_SIZE_SHD (RKVPSS2X_MI_BASE + 0x674) +#define RKVPSS2X_MI_CHN4_WR_CB_OFFS_CNT_SHD (RKVPSS2X_MI_BASE + 0x678) +#define RKVPSS2X_MI_CHN4_WR_Y_END_ADDR (RKVPSS2X_MI_BASE + 0x680) +#define RKVPSS2X_MI_CHN4_WR_CB_END_ADDR (RKVPSS2X_MI_BASE + 0x684) +#define RKVPSS2X_MI_CHN4_WR_LINE_CNT (RKVPSS2X_MI_BASE + 0x688) +#define RKVPSS2X_MI_CHN5_WR_CTRL (RKVPSS2X_MI_BASE + 0x700) +#define RKVPSS2X_MI_CHN5_WR_Y_BASE (RKVPSS2X_MI_BASE + 0x710) +#define RKVPSS2X_MI_CHN5_WR_Y_SIZE (RKVPSS2X_MI_BASE + 0x714) +#define RKVPSS2X_MI_CHN5_WR_Y_OFFS_CNT (RKVPSS2X_MI_BASE + 0x718) +#define RKVPSS2X_MI_CHN5_WR_Y_OFFS_CNT_START (RKVPSS2X_MI_BASE + 0x71c) +#define RKVPSS2X_MI_CHN5_WR_CB_BASE (RKVPSS2X_MI_BASE + 0x720) +#define RKVPSS2X_MI_CHN5_WR_CB_SIZE (RKVPSS2X_MI_BASE + 0x724) +#define RKVPSS2X_MI_CHN5_WR_CB_OFFS_CNT (RKVPSS2X_MI_BASE + 0x728) +#define RKVPSS2X_MI_CHN5_WR_CB_OFFS_CNT_START (RKVPSS2X_MI_BASE + 0x72c) +#define RKVPSS2X_MI_CHN5_WR_Y_STRIDE (RKVPSS2X_MI_BASE + 0x730) +#define RKVPSS2X_MI_CHN5_WR_Y_PIC_WIDTH (RKVPSS2X_MI_BASE + 0x734) +#define RKVPSS2X_MI_CHN5_WR_Y_PIC_SIZE (RKVPSS2X_MI_BASE + 0x73c) +#define RKVPSS2X_MI_CHN5_WR_CTRL_SHD (RKVPSS2X_MI_BASE + 0x750) +#define RKVPSS2X_MI_CHN5_WR_Y_BASE_SHD (RKVPSS2X_MI_BASE + 0x760) +#define RKVPSS2X_MI_CHN5_WR_Y_SIZE_SHD (RKVPSS2X_MI_BASE + 0x764) +#define RKVPSS2X_MI_CHN5_WR_Y_OFFS_CNT_SHD (RKVPSS2X_MI_BASE + 0x768) +#define RKVPSS2X_MI_CHN5_WR_Y_STRDE_SHD (RKVPSS2X_MI_BASE + 0x76c) +#define RKVPSS2X_MI_CHN5_WR_CB_BASE_SHD (RKVPSS2X_MI_BASE + 0x770) +#define RKVPSS2X_MI_CHN5_WR_CB_SIZE_SHD (RKVPSS2X_MI_BASE + 0x774) +#define RKVPSS2X_MI_CHN5_WR_CB_OFFS_CNT_SHD (RKVPSS2X_MI_BASE + 0x778) +#define RKVPSS2X_MI_CHN5_WR_Y_END_ADDR (RKVPSS2X_MI_BASE + 0x780) +#define RKVPSS2X_MI_CHN5_WR_CB_END_ADDR (RKVPSS2X_MI_BASE + 0x784) +#define RKVPSS2X_MI_CHN5_WR_LINE_CNT (RKVPSS2X_MI_BASE + 0x788) + +/********* BIT **********/ + +/* VPSS_CTRL */ +#define RKVPSS2X_VPSS2ENC_PATH_EN BIT(11) +#define RKVPSS2X_SENSOR_ID(x) (((x) & 0x7) << 12) +#define RKVPSS2X_CHN4_CMSC_SEL(x) (((x) & 0x3) << 16) +#define RKVPSS2X_CHN5_CMSC_SEL(x) (((x) & 0x3) << 18) + +/* VPSS_ONLINE */ +#define RKVPSS2X_ISP2VPSS_CHN4_SEL(x) (((x) & 0x3) << 8) +#define RKVPSS2X_ISP2VPSS_CHN5_SEL(x) (((x) & 0x3) << 10) + +/* VPSS_CLK_EN */ +#define RKVPSS2X_SCL4_CLK_EN BIT(12) +#define RKVPSS2X_SCL5_CLK_EN BIT(13) +#define RKVPSS2X_RATIO4_CLK_EN BIT(14) +#define RKVPSS2X_RATIO5_CLK_EN BIT(15) + +/* VPSS_CLK_GATE */ +#define RKVPSS2X_SCL4_CKG_DIS BIT(12) +#define RKVPSS2X_SCL5_CKG_DIS BIT(13) +#define RKVPSS2X_RATIO4_CKG_DIS BIT(14) +#define RKVPSS2X_RATI05_CKG_DIS BIT(15) +#define RKVPSS2X_MI_CHN4_CKG_DIS BIT(20) +#define RKVPSS2X_MI_CHN5_CKG_DIS BIT(21) + +/* VPSS_IMSC */ +#define RKVPSS2X_SCL4_IN_FRM_END BIT(16) +#define RKVPSS2X_SCL5_IN_FRM_END BIT(17) +#define RKVPSS2X_RATIO4_IN_FRM_END BIT(18) +#define RKVPSS2X_RATIO5_IN_FRM_END BIT(19) + +/* VPSS_CTRL_SHD */ +#define RKVPSS2X_VPSS2ENC_PATH_EN_SHD BIT(11) + +/* VPSS_ONLINE_SHD */ +#define RKVPSS2X_CHN4_EN_SHD BIT(8) +#define RKVPSS2X_ISP_CHN4_EN_SHD BIT(9) +#define RKVPSS2X_CHN5_EN_SHD BIT(10) +#define RKVPSS2X_ISP_CHN5_EN_SHD BIT(11) +#define RKVPSS2X_ISP_USE_CMSC_EN_SHD BIT(18) +#define RKVPSS2X_VPSS_USE_CMSC_EN_SHD BIT(19) + +/* VPSS_WORKING */ +#define RKVPSS2X_SCL4_WORKING BIT(12) +#define RKVPSS2X_SCL5_WORKING BIT(13) +#define RKVPSS2X_RATIO4_WORKING BIT(14) +#define RKVPSS2X_RATIO5_WORKING BIT(15) + +/* CMSC_CTRL */ +#define RKVPSS2X_CMSC_BLK_SIZE(x) (((x) & 0x7) << 8) + +/* CROP_CTRL_SHD */ +#define RKVPSS2X_CROP_CHN4_EN_SHD BIT(4) +#define RKVPSS2X_CROP_CHN5_EN_SHD BIT(5) + +/* CROP_CH4_5 */ +#define RKVPSS2X_CROP_OFFS(v, h) (((v & 0x3fff) << 16) | (h & 0x3fff)) +#define RKVPSS2X_CROP_SIZE(v, h) (((v & 0x3fff) << 16) | (h & 0x3fff)) + + +/* SCALE_CTRL */ +#define RKVPSS2X_SW_AVG_SCALE_H_EN BIT(8) +#define RKVPSS2X_SW_AVG_SCALE_V_EN BIT(9) + +/* MI_FORCE_UPDATE */ +#define RKVPSS2X_MI_CHN4_FORCE_UPD BIT(8) +#define RKVPSS2X_MI_CHN5_FORCE_UPD BIT(9) + +/* MI_WR_FBCE_SET */ +#define RKVPSS2X_MI_WR_FBCE_SEL(x) (BIT(0) << (x)) +#define RKVPSS2X_WR_FBCE_FORCE_UN_EN BIT(2) +#define RKVPSS2X_WR_FBCE_PAYL_ALIGN_EN BIT(3) +#define RKVPSS2X_MI_WR_FBCE_SET_SHD(x) (BIT(28) << (x)) + +/* MI_IMSC */ +#define RKVPSS2X_MI_CHN4_FRM_END BIT(16) +#define RKVPSS2X_MI_CHN5_FRM_END BIT(17) +#define RKVPSS2X_MI_RD_FBCD_ERR BIT(29) + +/* MI_RD_CTRL */ +#define RKVPSS2X_MI_RD_INPUT_UYVY 3 + +/* MI_CHN1_WR_CTRL */ +#define RKVPSS2X_CH1_WR_RGB888_ALPHA(x) ((x & 0xff) << 16) + +/* MI_WR_FBCE_CTRL */ +#define RKVPSS2X_SW_MI_WR_FBCE_SEL(x) ((x) & 0x3) + +/* MI_WR_FBCE_SIZE */ +#define RKVPSS2X_SW_WR_FBCE_SIZE(w, h) ((w & 0x7fff) | (h & 0x7fff) << 16) +#endif + diff --git a/drivers/media/platform/rockchip/vpss/stream.c b/drivers/media/platform/rockchip/vpss/stream.c index 63288431bc22..df4ac7fb419c 100644 --- a/drivers/media/platform/rockchip/vpss/stream.c +++ b/drivers/media/platform/rockchip/vpss/stream.c @@ -11,11 +11,14 @@ #include "regs.h" #include "stream_v10.h" +#include "stream_v20.h" void rkvpss_cmsc_config(struct rkvpss_device *dev, bool sync) { if (is_vpss_v10(dev->hw_dev)) rkvpss_cmsc_config_v10(dev, sync); + else if (is_vpss_v20(dev->hw_dev)) + rkvpss_cmsc_config_v20(dev, sync); } int rkvpss_stream_buf_cnt(struct rkvpss_stream *stream) @@ -25,6 +28,8 @@ int rkvpss_stream_buf_cnt(struct rkvpss_stream *stream) if (is_vpss_v10(vpss->hw_dev)) ret = rkvpss_stream_buf_cnt_v10(stream); + else if (is_vpss_v20(vpss->hw_dev)) + ret = rkvpss_stream_buf_cnt_v20(stream); return ret; } @@ -35,6 +40,8 @@ int rkvpss_register_stream_vdevs(struct rkvpss_device *dev) if (is_vpss_v10(dev->hw_dev)) ret = rkvpss_register_stream_vdevs_v10(dev); + else if (is_vpss_v20(dev->hw_dev)) + ret = rkvpss_register_stream_vdevs_v20(dev); return ret; } @@ -43,6 +50,8 @@ void rkvpss_unregister_stream_vdevs(struct rkvpss_device *dev) { if (is_vpss_v10(dev->hw_dev)) rkvpss_unregister_stream_vdevs_v10(dev); + else if (is_vpss_v20(dev->hw_dev)) + rkvpss_unregister_stream_vdevs_v20(dev); } void rkvpss_stream_default_fmt(struct rkvpss_device *dev, u32 id, @@ -50,16 +59,22 @@ void rkvpss_stream_default_fmt(struct rkvpss_device *dev, u32 id, { if (is_vpss_v10(dev->hw_dev)) rkvpss_stream_default_fmt_v10(dev, id, width, height, pixelformat); + else if (is_vpss_v20(dev->hw_dev)) + rkvpss_stream_default_fmt_v20(dev, id, width, height, pixelformat); } void rkvpss_isr(struct rkvpss_device *dev, u32 mis_val) { if (is_vpss_v10(dev->hw_dev)) rkvpss_isr_v10(dev, mis_val); + else if (is_vpss_v20(dev->hw_dev)) + rkvpss_isr_v20(dev, mis_val); } void rkvpss_mi_isr(struct rkvpss_device *dev, u32 mis_val) { if (is_vpss_v10(dev->hw_dev)) rkvpss_mi_isr_v10(dev, mis_val); + else if (is_vpss_v20(dev->hw_dev)) + rkvpss_mi_isr_v20(dev, mis_val); } diff --git a/drivers/media/platform/rockchip/vpss/stream.h b/drivers/media/platform/rockchip/vpss/stream.h index d8bee7c96a8b..71418622d306 100644 --- a/drivers/media/platform/rockchip/vpss/stream.h +++ b/drivers/media/platform/rockchip/vpss/stream.h @@ -96,6 +96,10 @@ struct stream_config { u32 v_offs_shd; u32 h_size_shd; u32 v_size_shd; + u32 ch_4_5_offs; + u32 ch_4_5_size; + u32 ch_4_5_offs_shd; + u32 ch_4_5_size_shd; } crop; struct { u32 ctrl; @@ -162,6 +166,8 @@ struct rkvpss_online_unite_params { * streaming: stream start flag * stopping: stream stop flag * linked: link enable flag + * alpha: argb a value + * avg_scl_down : CH0 and CH2 can use average scale down, 1-16 scale range */ struct rkvpss_stream { struct rkvpss_device *dev; @@ -186,6 +192,7 @@ struct rkvpss_stream { struct rkvpss_online_unite_params unite_params; int id; + u32 alpha; bool streaming; bool stopping; bool linked; @@ -194,12 +201,15 @@ struct rkvpss_stream { bool is_mf_upd; bool is_pause; bool is_attach_info; + bool rockit_on; + bool avg_scl_down; }; /* rkvpss stream device */ struct rkvpss_stream_vdev { struct rkvpss_stream stream[RKVPSS_OUTPUT_MAX]; atomic_t refcnt; + u32 wrap_line; }; void rkvpss_cmsc_config(struct rkvpss_device *dev, bool sync); diff --git a/drivers/media/platform/rockchip/vpss/stream_v10.h b/drivers/media/platform/rockchip/vpss/stream_v10.h index a841da037cae..0cf8b28d050c 100644 --- a/drivers/media/platform/rockchip/vpss/stream_v10.h +++ b/drivers/media/platform/rockchip/vpss/stream_v10.h @@ -35,7 +35,7 @@ static inline void rkvpss_stream_default_fmt_v10(struct rkvpss_device *dev, u32 static inline void rkvpss_isr_v10(struct rkvpss_device *dev, u32 mis_val) {} static inline void rkvpss_mi_isr_v10(struct rkvpss_device *dev, u32 mis_val) {} static inline void rkvpss_cmsc_config_v10(struct rkvpss_device *dev, bool sync) {} -static inline int rkvpss_stream_buf_cnt_v10(struct rkvpss_stream *stream) {return -EINVAL; } +static inline int rkvpss_stream_buf_cnt_v10(struct rkvpss_stream *stream) {return 0; } #endif diff --git a/drivers/media/platform/rockchip/vpss/stream_v20.c b/drivers/media/platform/rockchip/vpss/stream_v20.c new file mode 100644 index 000000000000..d1daf3df03b2 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/stream_v20.c @@ -0,0 +1,3030 @@ +// 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.h" + +#include "stream_v20.h" +#include "vpss_rockit.h" +#include "vpss_dvbm.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 int rkvpss_stream_crop_ch4_5(struct rkvpss_stream *stream, bool on, bool sync); + + +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_4_5_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, + }, + .scale = { + .ctrl = RKVPSS2X_SCALE0_CTRL, + .update = RKVPSS2X_SCALE0_UPDATE, + .src_size = RKVPSS2X_SCALE0_SRC_SIZE, + .dst_size = RKVPSS2X_SCALE0_DST_SIZE, + .hy_fac = RKVPSS2X_SCALE0_HY_FAC, + .hc_fac = RKVPSS2X_SCALE0_HC_FAC, + .vy_fac = RKVPSS2X_SCALE0_VY_FAC, + .vc_fac = RKVPSS2X_SCALE0_VC_FAC, + .hy_offs = RKVPSS2X_SCALE0_HY_OFFS, + .hc_offs = RKVPSS2X_SCALE0_HC_OFFS, + .vy_offs = RKVPSS2X_SCALE0_VY_OFFS, + .vc_offs = RKVPSS2X_SCALE0_VC_OFFS, + .hy_size = RKVPSS2X_SCALE0_HY_SIZE, + .hc_size = RKVPSS2X_SCALE0_HC_SIZE, + .hy_offs_mi = RKVPSS2X_SCALE0_HY_OFFS_MI, + .hc_offs_mi = RKVPSS2X_SCALE0_HC_OFFS_MI, + .in_crop_offs = RKVPSS2X_SCALE0_IN_CROP_OFFSET, + .ctrl_shd = RKVPSS2X_SCALE0_CTRL_SHD, + .src_size_shd = RKVPSS2X_SCALE0_SRC_SIZE_SHD, + .dst_size_shd = RKVPSS2X_SCALE0_DST_SIZE_SHD, + .hy_fac_shd = RKVPSS2X_SCALE0_HY_FAC_SHD, + .hc_fac_shd = RKVPSS2X_SCALE0_HC_FAC_SHD, + .vy_fac_shd = RKVPSS2X_SCALE0_VY_FAC_SHD, + .vc_fac_shd = RKVPSS2X_SCALE0_VC_FAC_SHD, + .hy_offs_shd = RKVPSS2X_SCALE0_HY_OFFS_SHD, + .hc_offs_shd = RKVPSS2X_SCALE0_HC_OFFS_SHD, + .vy_offs_shd = RKVPSS2X_SCALE0_VY_OFFS_SHD, + .vc_offs_shd = RKVPSS2X_SCALE0_VC_OFFS_SHD, + .hy_size_shd = RKVPSS2X_SCALE0_HY_SIZE_SHD, + .hc_size_shd = RKVPSS2X_SCALE0_HC_SIZE_SHD, + .hy_offs_mi_shd = RKVPSS2X_SCALE0_HY_OFFS_MI_SHD, + .hc_offs_mi_shd = RKVPSS2X_SCALE0_HC_OFFS_MI_SHD, + .in_crop_offs_shd = RKVPSS2X_SCALE0_IN_CROP_OFFSET_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_4_5_fmts, + .fmt_size = ARRAY_SIZE(scl2_3_4_5_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_4_5_fmts, + .fmt_size = ARRAY_SIZE(scl2_3_4_5_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 struct stream_config scl4_config = { + .fmts = scl2_3_4_5_fmts, + .fmt_size = ARRAY_SIZE(scl2_3_4_5_fmts), + .frame_end_id = RKVPSS2X_MI_CHN4_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .ch_4_5_offs = RKVPSS2X_CROP1_4_OFFS, + .ch_4_5_size = RKVPSS2X_CROP1_4_SIZE, + .ch_4_5_offs_shd = RKVPSS2X_CROP1_4_OFFS_SHD, + .ch_4_5_size_shd = RKVPSS2X_CROP1_4_SIZE_SHD, + }, + .scale = { + .ctrl = RKVPSS2X_SCALE4_CTRL, + .update = RKVPSS2X_SCALE4_UPDATE, + .src_size = RKVPSS2X_SCALE4_SRC_SIZE, + .dst_size = RKVPSS2X_SCALE4_DST_SIZE, + .hy_fac = RKVPSS2X_SCALE4_HY_FAC, + .hc_fac = RKVPSS2X_SCALE4_HC_FAC, + .vy_fac = RKVPSS2X_SCALE4_VY_FAC, + .vc_fac = RKVPSS2X_SCALE4_VC_FAC, + .hy_offs = RKVPSS2X_SCALE4_HY_OFFS, + .hc_offs = RKVPSS2X_SCALE4_HC_OFFS, + .vy_offs = RKVPSS2X_SCALE4_VY_OFFS, + .vc_offs = RKVPSS2X_SCALE4_VC_OFFS, + .hy_size = RKVPSS2X_SCALE4_HY_SIZE, + .hc_size = RKVPSS2X_SCALE4_HC_SIZE, + .hy_offs_mi = RKVPSS2X_SCALE4_HY_OFFS_MI, + .hc_offs_mi = RKVPSS2X_SCALE4_HC_OFFS_MI, + .in_crop_offs = RKVPSS2X_SCALE4_IN_CROP_OFFSET, + .ctrl_shd = RKVPSS2X_SCALE4_CTRL_SHD, + .src_size_shd = RKVPSS2X_SCALE4_SRC_SIZE_SHD, + .dst_size_shd = RKVPSS2X_SCALE4_DST_SIZE_SHD, + .hy_fac_shd = RKVPSS2X_SCALE4_HY_FAC_SHD, + .hc_fac_shd = RKVPSS2X_SCALE4_HC_FAC_SHD, + .vy_fac_shd = RKVPSS2X_SCALE4_VY_FAC_SHD, + .vc_fac_shd = RKVPSS2X_SCALE4_VC_FAC_SHD, + .hy_offs_shd = RKVPSS2X_SCALE4_HY_OFFS_SHD, + .hc_offs_shd = RKVPSS2X_SCALE4_HC_OFFS_SHD, + .vy_offs_shd = RKVPSS2X_SCALE4_VY_OFFS_SHD, + .vc_offs_shd = RKVPSS2X_SCALE4_VC_OFFS_SHD, + .hy_size_shd = RKVPSS2X_SCALE4_HY_SIZE_SHD, + .hc_size_shd = RKVPSS2X_SCALE4_HC_SIZE_SHD, + .hy_offs_mi_shd = RKVPSS2X_SCALE4_HY_OFFS_MI_SHD, + .hc_offs_mi_shd = RKVPSS2X_SCALE4_HC_OFFS_MI_SHD, + .in_crop_offs_shd = RKVPSS2X_SCALE4_IN_CROP_OFFSET_SHD, + }, + .mi = { + .ctrl = RKVPSS2X_MI_CHN4_WR_CTRL, + .stride = RKVPSS2X_MI_CHN4_WR_Y_STRIDE, + .y_base = RKVPSS2X_MI_CHN4_WR_Y_BASE, + .uv_base = RKVPSS2X_MI_CHN4_WR_CB_BASE, + .y_size = RKVPSS2X_MI_CHN4_WR_Y_SIZE, + .uv_size = RKVPSS2X_MI_CHN4_WR_CB_SIZE, + .y_offs_cnt = RKVPSS2X_MI_CHN4_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS2X_MI_CHN4_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS2X_MI_CHN4_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS2X_MI_CHN4_WR_Y_PIC_SIZE, + .ctrl_shd = RKVPSS2X_MI_CHN4_WR_CTRL_SHD, + .y_shd = RKVPSS2X_MI_CHN4_WR_Y_BASE_SHD, + .uv_shd = RKVPSS2X_MI_CHN4_WR_CB_BASE_SHD, + }, +}; + +static struct stream_config scl5_config = { + .fmts = scl2_3_4_5_fmts, + .fmt_size = ARRAY_SIZE(scl2_3_4_5_fmts), + .frame_end_id = RKVPSS2X_MI_CHN5_FRM_END, + .crop = { + .ctrl = RKVPSS_CROP1_CTRL, + .update = RKVPSS_CROP1_UPDATE, + .ch_4_5_offs = RKVPSS2X_CROP1_5_OFFS, + .ch_4_5_size = RKVPSS2X_CROP1_5_SIZE, + .ch_4_5_offs_shd = RKVPSS2X_CROP1_5_OFFS_SHD, + .ch_4_5_size_shd = RKVPSS2X_CROP1_5_SIZE_SHD, + }, + .scale = { + .ctrl = RKVPSS2X_SCALE5_CTRL, + .update = RKVPSS2X_SCALE5_UPDATE, + .src_size = RKVPSS2X_SCALE5_SRC_SIZE, + .dst_size = RKVPSS2X_SCALE5_DST_SIZE, + .hy_fac = RKVPSS2X_SCALE5_HY_FAC, + .hc_fac = RKVPSS2X_SCALE5_HC_FAC, + .vy_fac = RKVPSS2X_SCALE5_VY_FAC, + .vc_fac = RKVPSS2X_SCALE5_VC_FAC, + .hy_offs = RKVPSS2X_SCALE5_HY_OFFS, + .hc_offs = RKVPSS2X_SCALE5_HC_OFFS, + .vy_offs = RKVPSS2X_SCALE5_VY_OFFS, + .vc_offs = RKVPSS2X_SCALE5_VC_OFFS, + .hy_size = RKVPSS2X_SCALE5_HY_SIZE, + .hc_size = RKVPSS2X_SCALE5_HC_SIZE, + .hy_offs_mi = RKVPSS2X_SCALE5_HY_OFFS_MI, + .hc_offs_mi = RKVPSS2X_SCALE5_HC_OFFS_MI, + .in_crop_offs = RKVPSS2X_SCALE5_IN_CROP_OFFSET, + .ctrl_shd = RKVPSS2X_SCALE5_CTRL_SHD, + .src_size_shd = RKVPSS2X_SCALE5_SRC_SIZE_SHD, + .dst_size_shd = RKVPSS2X_SCALE5_DST_SIZE_SHD, + .hy_fac_shd = RKVPSS2X_SCALE5_HY_FAC_SHD, + .hc_fac_shd = RKVPSS2X_SCALE5_HC_FAC_SHD, + .vy_fac_shd = RKVPSS2X_SCALE5_VY_FAC_SHD, + .vc_fac_shd = RKVPSS2X_SCALE5_VC_FAC_SHD, + .hy_offs_shd = RKVPSS2X_SCALE5_HY_OFFS_SHD, + .hc_offs_shd = RKVPSS2X_SCALE5_HC_OFFS_SHD, + .vy_offs_shd = RKVPSS2X_SCALE5_VY_OFFS_SHD, + .vc_offs_shd = RKVPSS2X_SCALE5_VC_OFFS_SHD, + .hy_size_shd = RKVPSS2X_SCALE5_HY_SIZE_SHD, + .hc_size_shd = RKVPSS2X_SCALE5_HC_SIZE_SHD, + .hy_offs_mi_shd = RKVPSS2X_SCALE5_HY_OFFS_MI_SHD, + .hc_offs_mi_shd = RKVPSS2X_SCALE5_HC_OFFS_MI_SHD, + .in_crop_offs_shd = RKVPSS2X_SCALE5_IN_CROP_OFFSET_SHD, + }, + .mi = { + .ctrl = RKVPSS2X_MI_CHN5_WR_CTRL, + .stride = RKVPSS2X_MI_CHN5_WR_Y_STRIDE, + .y_base = RKVPSS2X_MI_CHN5_WR_Y_BASE, + .uv_base = RKVPSS2X_MI_CHN5_WR_CB_BASE, + .y_size = RKVPSS2X_MI_CHN5_WR_Y_SIZE, + .uv_size = RKVPSS2X_MI_CHN5_WR_CB_SIZE, + .y_offs_cnt = RKVPSS2X_MI_CHN5_WR_Y_OFFS_CNT, + .uv_offs_cnt = RKVPSS2X_MI_CHN5_WR_CB_OFFS_CNT, + .y_pic_width = RKVPSS2X_MI_CHN5_WR_Y_PIC_WIDTH, + .y_pic_size = RKVPSS2X_MI_CHN5_WR_Y_PIC_SIZE, + .ctrl_shd = RKVPSS2X_MI_CHN4_WR_CTRL_SHD, + .y_shd = RKVPSS2X_MI_CHN4_WR_Y_BASE_SHD, + .uv_shd = RKVPSS2X_MI_CHN4_WR_CB_BASE_SHD, + }, +}; + +//1126b todo +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_v20(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); + + if (dev->stream_vdev.wrap_line) { + rkvpss_rockit_frame_start(dev); + rkvpss_dvbm_event(dev, ROCKIT_DVBM_START); + } +} + +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; + case RKVPSS_OUTPUT_CH4: + val = RKVPSS2X_MI_CHN4_FORCE_UPD; + break; + case RKVPSS_OUTPUT_CH5: + val = RKVPSS2X_MI_CHN4_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) { + /* wrap mode don't disable mi */ + if (stream->id != RKVPSS_OUTPUT_CH0 || !dev->stream_vdev.wrap_line) { + 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); + + if (dev->stream_vdev.wrap_line && stream->id == RKVPSS_OUTPUT_CH0) + val = out_fmt->plane_fmt[0].bytesperline * dev->stream_vdev.wrap_line; + else + 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; + /* argb alpha value */ + if (stream->id == RKVPSS_OUTPUT_CH1 && + (fmt->fourcc == V4L2_PIX_FMT_XBGR32 || fmt->fourcc == V4L2_PIX_FMT_XRGB32)) + val |= RKVPSS2X_CH1_WR_RGB888_ALPHA(stream->alpha); + 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; + case RKVPSS_OUTPUT_CH4: + val = RKVPSS2X_ISP2VPSS_CHN4_SEL(2); + mask = RKVPSS2X_ISP2VPSS_CHN4_SEL(3); + break; + case RKVPSS_OUTPUT_CH5: + val = RKVPSS2X_ISP2VPSS_CHN5_SEL(2); + mask = RKVPSS2X_ISP2VPSS_CHN5_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; + case RKVPSS_OUTPUT_CH4: + val = RKVPSS2X_ISP2VPSS_CHN4_SEL(3); + break; + case RKVPSS_OUTPUT_CH5: + val = RKVPSS2X_ISP2VPSS_CHN4_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; + /* wrap mode use one buffer */ + if (stream->curr_buf->vb.vb2_buf.memory || !dev->stream_vdev.wrap_line) + 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; + if (vb2_buf->memory) + rkvpss_stream_buf_done(stream, buf); + else { + rkvpss_rockit_buf_done(stream, ROCKIT_DVBM_END, buf); + rkvpss_dvbm_event(dev, ROCKIT_DVBM_END); + } + } + + 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); + if (buf->vb.vb2_buf.memory) { + 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); + if (buf->vb.vb2_buf.memory) { + buf->vb.vb2_buf.synced = false; + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + } + + rkvpss_rockit_buf_state_clear(stream); + rkvpss_rockit_buf_free(stream); +} + +static int rkvpss_stream_crop_ch4_5(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_ch4_5_offs = stream->config->crop.ch_4_5_offs; + u32 reg_ch4_5_size = stream->config->crop.ch_4_5_size; + u32 val; + u32 h_offs, v_offs, h_size, v_size; + + 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_ch4_5_offs, + RKVPSS2X_CROP_OFFS(crop->top, crop->left)); + rkvpss_unite_write(dev, + reg_ch4_5_size, + RKVPSS2X_CROP_SIZE(crop->height, crop->width)); + 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_ch4_5_offs, + RKVPSS2X_CROP_OFFS(crop->top, crop->left), + VPSS_UNITE_LEFT); + /* if no scale left don't enlarge */ + if (crop->width == stream->out_fmt.width) + h_size = crop->width / 2; + else + h_size = crop->width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL; + + v_size = crop->height; + rkvpss_idx_write(dev, reg_ch4_5_size, + RKVPSS2X_CROP_SIZE(v_size, h_size), 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_ch4_5_offs, VPSS_UNITE_LEFT) & 0x3fff, + rkvpss_idx_read(dev, reg_ch4_5_offs, VPSS_UNITE_LEFT) >> 16, + rkvpss_idx_read(dev, reg_ch4_5_size, VPSS_UNITE_LEFT) & 0x3fff, + rkvpss_idx_read(dev, reg_ch4_5_size, VPSS_UNITE_LEFT) >> 16); + + /* unite right */ + rkvpss_idx_set_bits(dev, reg_ctrl, 0, val, VPSS_UNITE_RIGHT); + h_offs = stream->unite_params.quad_crop_w; + v_offs = crop->top; + h_size = crop->width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL - + stream->unite_params.quad_crop_w; + v_size = crop->height; + rkvpss_idx_write(dev, reg_ch4_5_offs, + RKVPSS2X_CROP_OFFS(v_offs, h_offs), VPSS_UNITE_RIGHT); + rkvpss_idx_write(dev, reg_ch4_5_offs, + RKVPSS2X_CROP_SIZE(v_size, h_size), 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_ch4_5_offs, VPSS_UNITE_RIGHT) & 0x3fff, + rkvpss_idx_read(dev, reg_ch4_5_offs, VPSS_UNITE_RIGHT) >> 16, + rkvpss_idx_read(dev, reg_ch4_5_size, VPSS_UNITE_RIGHT) & 0x3fff, + rkvpss_idx_read(dev, reg_ch4_5_size, VPSS_UNITE_RIGHT) >> 16); + } 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 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; + + if (stream->id > RKVPSS_OUTPUT_CH3) + return rkvpss_stream_crop_ch4_5(stream, on, sync); + + 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 average_scale_down(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 reg, val, ctrl = 0, clk_mask = 0; + + /*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) { + 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 || !sync) { + val = (out_w - 1) * 65536 / (in_w - 1) + 1; + reg = stream->config->scale.hy_fac; + rkvpss_unite_write(dev, reg, val); + + val = (out_w / 2 - 1) * 65536 / (in_w / 2 - 1) + 1; + reg = stream->config->scale.hc_fac; + rkvpss_unite_write(dev, reg, val); + + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN | RKVPSS2X_SW_AVG_SCALE_H_EN; + } + if (in_h != out_h || !sync) { + val = (out_h - 1) * 65536 / (out_h - 1) + 1; + reg = stream->config->scale.vy_fac; + rkvpss_unite_write(dev, reg, val); + + val = (out_h - 1) * 4096 / (in_h - 1) + 1; + reg = stream->config->scale.vc_fac; + rkvpss_unite_write(dev, reg, val); + + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN | RKVPSS2X_SW_AVG_SCALE_V_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); + } + //unite todo +} + +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; + } + + 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 * 32 < out_w)) || + ((in_w > out_w) && (out_w * 32 < in_w)) || + ((out_h >= in_h) && (in_h * 32 < out_h)) || + ((in_h > out_h) && (out_h * 32 < in_h))) { + v4l2_err(&dev->v4l2_dev, "stream:%d scale size error\n", stream->id); + 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 ch4 ch5 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 ch4 ch5 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 ch4 ch5 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 == 0 || stream->id == 2) && stream->avg_scl_down) + average_scale_down(stream, on, sync); + + 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); + stream->rockit_on = false; + 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_V20_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->rockit_on = true; + 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; + + if (dev->stream_vdev.wrap_line && stream->id == RKVPSS_OUTPUT_CH0) + h = dev->stream_vdev.wrap_line; + else + 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; +} + +struct rockit_vpss_ops rockit_vpss_ops = { + .rkvpss_stream_start = rkvpss_stream_start, + .rkvpss_stream_stop = rkvpss_stream_stop, + .rkvpss_set_fmt = rkvpss_set_fmt, +}; + +/************************* 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 < 4; 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 |= RKVPSS2X_CMSC_BLK_SIZE(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); +} + +//todo +void rkvpss_cmsc_config_v20(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_V20_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_V20_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_V20_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_V20_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 int rkvpss_get_wrap_line(struct rkvpss_stream *stream, int *wrap_line) +{ + struct rkvpss_device *vpss_dev = stream->dev; + + if (stream->id != RKVPSS_OUTPUT_CH0) + return -EINVAL; + + *wrap_line = vpss_dev->stream_vdev.wrap_line; + return 0; +} + +static int rkvpss_set_wrap_line(struct rkvpss_stream *stream, int *wrap_line) +{ + struct rkvpss_device *vpss_dev = stream->dev; + + if (stream->id != RKVPSS_OUTPUT_CH0) + return -EINVAL; + + vpss_dev->stream_vdev.wrap_line = *wrap_line; + + //set_wrap todo + + return 0; +} + +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; + case RKVPSS_CMD_GET_WRAP_LINE: + ret = rkvpss_get_wrap_line(stream, arg); + break; + case RKVPSS_CMD_SET_WRAP_LINE: + ret = rkvpss_set_wrap_line(stream, arg); + break; + case RKVPSS_CMD_GET_ALPHA: + *(int *)arg = stream->alpha; + break; + case RKVPSS_CMD_SET_ALPHA: + stream->alpha = *(int *)arg; + break; + case RKVPSS_CMD_GET_AVG_SCL_DOWN: + if (stream->avg_scl_down) + *(int *)arg = 1; + else + *(int *)arg = 0; + break; + case RKVPSS_CMD_SET_AVG_SCL_DOWN: + stream->avg_scl_down = !!*(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_v20(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_v20(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_V20_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; + case RKVPSS_OUTPUT_CH4: + vdev_name = S4_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl4_config; + break; + case RKVPSS_OUTPUT_CH5: + vdev_name = S5_VDEV_NAME; + stream->ops = &scl_stream_ops; + stream->config = &scl5_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; + } + rkvpss_dvbm_get(dev); + rkvpss_rockit_dev_init(dev); + 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_v20(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_V20_MAX; i++) { + stream = &stream_vdev->stream[i]; + rkvpss_unregister_stream_video(stream); + } + rkvpss_rockit_dev_deinit(); +} + +void rkvpss_isr_v20(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_v20(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_V20_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)) | ((ris >> 9) & 0x180)); +} + diff --git a/drivers/media/platform/rockchip/vpss/stream_v20.h b/drivers/media/platform/rockchip/vpss/stream_v20.h new file mode 100644 index 000000000000..154bc68d37a0 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/stream_v20.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */ + + +#ifndef _RKVPSS_STREAM_V20_H +#define _RKVPSS_STREAM_V20_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rockit_vpss_ops { + int (*rkvpss_stream_start)(struct rkvpss_stream *stream); + void (*rkvpss_stream_stop)(struct rkvpss_stream *stream); + int (*rkvpss_set_fmt)(struct rkvpss_stream *stream, + struct v4l2_pix_format_mplane *pixm, + bool try); +}; + +int rkvpss_rockit_buf_done(struct rkvpss_stream *stream, int cmd, struct rkvpss_buffer *curr_buf); +int rkvpss_rockit_buf_free(struct rkvpss_stream *stream); +void rkvpss_rockit_buf_state_clear(struct rkvpss_stream *stream); +void rkvpss_rockit_frame_start(struct rkvpss_device *dev); + + +#if IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_VPSS_V20) +int rkvpss_register_stream_vdevs_v20(struct rkvpss_device *dev); +void rkvpss_unregister_stream_vdevs_v20(struct rkvpss_device *dev); +void rkvpss_stream_default_fmt_v20(struct rkvpss_device *dev, u32 id, + u32 width, u32 height, u32 pixelformat); +void rkvpss_isr_v20(struct rkvpss_device *dev, u32 mis_val); +void rkvpss_mi_isr_v20(struct rkvpss_device *dev, u32 mis_val); +void rkvpss_cmsc_config_v20(struct rkvpss_device *dev, bool sync); +int rkvpss_stream_buf_cnt_v20(struct rkvpss_stream *stream); + +#else +static inline int rkvpss_register_stream_vdevs_v20(struct rkvpss_device *dev) {return -EINVAL; } +static inline void rkvpss_unregister_stream_vdevs_v20(struct rkvpss_device *dev) {} +static inline void rkvpss_stream_default_fmt_v20(struct rkvpss_device *dev, u32 id, u32 width, u32 height, u32 pixelformat) {} +static inline void rkvpss_isr_v20(struct rkvpss_device *dev, u32 mis_val) {} +static inline void rkvpss_mi_isr_v20(struct rkvpss_device *dev, u32 mis_val) {} +static inline void rkvpss_cmsc_config_v20(struct rkvpss_device *dev, bool sync) {} +static inline int rkvpss_stream_buf_cnt_v20(struct rkvpss_stream *stream) {return 0; } + +#endif + +#endif + diff --git a/drivers/media/platform/rockchip/vpss/vpss.c b/drivers/media/platform/rockchip/vpss/vpss.c index 97a3d59f3f3d..b623b1bcdce4 100644 --- a/drivers/media/platform/rockchip/vpss/vpss.c +++ b/drivers/media/platform/rockchip/vpss/vpss.c @@ -28,6 +28,10 @@ static inline const char *s_dev_name(int i) return S2_VDEV_NAME; case 3: return S3_VDEV_NAME; + case 4: + return S4_VDEV_NAME; + case 5: + return S5_VDEV_NAME; default: return S0_VDEV_NAME; } @@ -185,6 +189,8 @@ static int rkvpss_sd_s_stream(struct v4l2_subdev *sd, int on) rkvpss_unite_write(dev, RKVPSS_VPSS_ONLINE2_SIZE, h << 16 | w); val = RKVPSS_CFG_FORCE_UPD | RKVPSS_CFG_GEN_UPD | RKVPSS_MIR_GEN_UPD; + if (dev->hw_dev->vpss_ver == VPSS_V20) + val |= RKVPSS_MIR_FORCE_UPD; if (!dev->hw_dev->is_ofl_cmsc) val |= RKVPSS_MIR_FORCE_UPD; @@ -278,6 +284,7 @@ static int rkvpss_sof(struct rkvpss_subdev *sdev, struct rkisp_vpss_sof *info) } if (!info->irq && (!hw->is_single || dev->unite_mode)) { + // 1126b unite or multi sensor todo hw->cur_dev_id = dev->dev_id; rkvpss_update_regs(dev, RKVPSS_VPSS_CTRL, RKVPSS_VPSS_ONLINE2_SIZE); rkvpss_update_regs(dev, RKVPSS_VPSS_IRQ_CFG, RKVPSS_VPSS_IMSC); diff --git a/drivers/media/platform/rockchip/vpss/vpss.h b/drivers/media/platform/rockchip/vpss/vpss.h index da8037c72e63..68ac7e9f8d36 100644 --- a/drivers/media/platform/rockchip/vpss/vpss.h +++ b/drivers/media/platform/rockchip/vpss/vpss.h @@ -47,9 +47,11 @@ enum rkvpss_state { VPSS_FRAME_SCL1 = BIT(4), VPSS_FRAME_SCL2 = BIT(5), VPSS_FRAME_SCL3 = BIT(6), + VPSS_FRAME_SCL4 = BIT(7), + VPSS_FRAME_SCL5 = BIT(8), - VPSS_START = BIT(8), - VPSS_RX_START = BIT(9), + VPSS_START = BIT(9), + VPSS_RX_START = BIT(10), }; struct vpsssd_fmt { diff --git a/drivers/media/platform/rockchip/vpss/vpss_dvbm.c b/drivers/media/platform/rockchip/vpss/vpss_dvbm.c new file mode 100644 index 000000000000..fa24d5163b60 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_dvbm.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + + +#define pr_fmt(fmt) "vpss_dvbm: %s:%d " fmt, __func__, __LINE__ + +#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 "vpss_dvbm.h" +#include "stream_v20.h" + +static struct dvbm_port *g_dvbm; + +int rkvpss_dvbm_get(struct rkvpss_device *vpss_dev) +{ + struct device_node *np = vpss_dev->dev->of_node; + struct device_node *np_dvbm = of_parse_phandle(np, "dvbm", 0); + int ret = 0; + + g_dvbm = NULL; + if (!np_dvbm || !of_device_is_available(np_dvbm)) { + dev_warn(vpss_dev->dev, "failed to get dvbm node\n"); + } else { + struct platform_device *p_dvbm = of_find_device_by_node(np_dvbm); + + g_dvbm = rk_dvbm_get_port(p_dvbm, DVBM_ISP_PORT); + put_device(&p_dvbm->dev); + } + + of_node_put(np_dvbm); + + return ret; +} + +int rkvpss_dvbm_init(struct rkvpss_stream *stream) +{ + struct rkvpss_device *vpss_dev = stream->dev; + struct dvbm_isp_cfg_t dvbm_cfg; + u32 width, height, wrap_line; + + if (!g_dvbm) + return -EINVAL; + + width = stream->out_fmt.plane_fmt[0].bytesperline; + height = stream->out_fmt.height; + wrap_line = vpss_dev->stream_vdev.wrap_line; + dvbm_cfg.dma_addr = vpss_dev->wrap_buf.dma_addr; + dvbm_cfg.buf = vpss_dev->wrap_buf.dbuf; + dvbm_cfg.ybuf_bot = 0; + dvbm_cfg.ybuf_top = width * wrap_line; + dvbm_cfg.ybuf_lstd = width; + dvbm_cfg.ybuf_fstd = width * height; + dvbm_cfg.cbuf_bot = dvbm_cfg.ybuf_top; + dvbm_cfg.cbuf_top = dvbm_cfg.cbuf_bot + (width * wrap_line / 2); + dvbm_cfg.cbuf_lstd = width; + dvbm_cfg.cbuf_fstd = dvbm_cfg.ybuf_fstd / 2; + + rk_dvbm_ctrl(g_dvbm, DVBM_ISP_SET_CFG, &dvbm_cfg); + rk_dvbm_link(g_dvbm, 0); + return 0; +} + +void rkvpss_dvbm_deinit(struct rkvpss_device *vpss_dev) +{ + if (!g_dvbm || !vpss_dev) { + pr_err("g_dvbm %p or vpss_dev %p is NULL\n", g_dvbm, vpss_dev); + return; + } + rk_dvbm_unlink(g_dvbm, 0); +} + +int rkvpss_dvbm_event(struct rkvpss_device *vpss_dev, u32 event) +{ + enum dvbm_cmd cmd; + u32 seq; + + if (!g_dvbm || !vpss_dev->stream_vdev.wrap_line) + return -EINVAL; + + seq = vpss_dev->vpss_sdev.frame_seq; + + switch (event) { + case ROCKIT_DVBM_START: + cmd = DVBM_ISP_FRM_START; + break; + case ROCKIT_DVBM_END: + cmd = DVBM_ISP_FRM_END; + break; + default: + return -EINVAL; + } + + return rk_dvbm_ctrl(g_dvbm, cmd, &seq); +} + diff --git a/drivers/media/platform/rockchip/vpss/vpss_dvbm.h b/drivers/media/platform/rockchip/vpss/vpss_dvbm.h new file mode 100644 index 000000000000..34ed0f5305c1 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_dvbm.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKVPSS_DVBM_H +#define _RKVPSS_DVBM_H + +#include +#include + +#include + + +#if IS_ENABLED(CONFIG_ROCKCHIP_DVBM) +int rkvpss_dvbm_get(struct rkvpss_device *vpss_dev); +int rkvpss_dvbm_init(struct rkvpss_stream *stream); +void rkvpss_dvbm_deinit(struct rkvpss_device *vpss_dev); +int rkvpss_dvbm_event(struct rkvpss_device *vpss_dev, u32 event); +#else +static inline int rkvpss_dvbm_get(struct rkvpss_device *vpss_dev) {return -EINVAL; } +static inline int rkvpss_dvbm_init(struct rkvpss_stream *stream) {return -EINVAL; } +static inline void rkvpss_dvbm_deinit(struct rkvpss_device *vpss_dev) {} +static inline int rkvpss_dvbm_event(struct rkvpss_device *vpss_dev, u32 event) {return -EINVAL; } +#endif + +#endif + diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.c b/drivers/media/platform/rockchip/vpss/vpss_offline.c index 8f7fc294858f..c9d152138259 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.c +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.c @@ -14,6 +14,7 @@ #include "regs.h" #include "vpss_offline_v10.h" +#include "vpss_offline_v20.h" void rkvpss_dump_reg(struct rkvpss_offline_dev *ofl, int sequence, int size) { @@ -72,6 +73,8 @@ void rkvpss_offline_irq(struct rkvpss_hw_dev *hw, u32 irq) { if (is_vpss_v10(hw)) rkvpss_offline_irq_v10(hw, irq); + else if (is_vpss_v20(hw)) + rkvpss_offline_irq_v20(hw, irq); } int rkvpss_register_offline(struct rkvpss_hw_dev *hw) @@ -80,6 +83,8 @@ int rkvpss_register_offline(struct rkvpss_hw_dev *hw) if (is_vpss_v10(hw)) ret = rkvpss_register_offline_v10(hw); + else if (is_vpss_v20(hw)) + ret = rkvpss_register_offline_v20(hw); return ret; } @@ -88,5 +93,7 @@ void rkvpss_unregister_offline(struct rkvpss_hw_dev *hw) { if (is_vpss_v10(hw)) rkvpss_unregister_offline_v10(hw); + else if (is_vpss_v20(hw)) + rkvpss_unregister_offline_v20(hw); } diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline.h b/drivers/media/platform/rockchip/vpss/vpss_offline.h index ad858e9e63e9..297cd2fdf87a 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline.h +++ b/drivers/media/platform/rockchip/vpss/vpss_offline.h @@ -75,6 +75,9 @@ struct rkvpss_offline_dev { struct rkvpss_unite_scl_params unite_params[RKVPSS_OUTPUT_MAX]; struct completion pm_cmpl; u32 unite_right_enlarge; + struct idr file_idr; + struct mutex idr_lock; + struct mutex handle_lock; bool mode_sel_en; bool pm_need_wait; }; diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline_dvbm.c b/drivers/media/platform/rockchip/vpss/vpss_offline_dvbm.c new file mode 100644 index 000000000000..bbe322911af8 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_offline_dvbm.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + + +#define pr_fmt(fmt) "vpss_dvbm: %s:%d " fmt, __func__, __LINE__ + +#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 "vpss_offline_dvbm.h" + +static struct dvbm_port *g_ofl_dvbm; + +int rkvpss_ofl_dvbm_get(struct rkvpss_offline_dev *ofl) +{ + struct device_node *np = ofl->hw->dev->of_node; + struct device_node *np_dvbm = of_parse_phandle(np, "dvbm", 0); + int ret = 0; + + g_ofl_dvbm = NULL; + if (!np_dvbm || !of_device_is_available(np_dvbm)) { + dev_warn(ofl->hw->dev, "failed to get dvbm node\n"); + } else { + struct platform_device *p_dvbm = of_find_device_by_node(np_dvbm); + + g_ofl_dvbm = rk_dvbm_get_port(p_dvbm, DVBM_ISP_PORT); + put_device(&p_dvbm->dev); + } + + of_node_put(np_dvbm); + + return ret; +} + +int rkvpss_ofl_dvbm_init(struct dma_buf *dbuf, u32 dma_addr, u32 wrap_line, + int width, int height, int id) +{ + struct dvbm_isp_cfg_t dvbm_cfg; + + if (!g_ofl_dvbm) + return -EINVAL; + + dvbm_cfg.dma_addr = dma_addr; + dvbm_cfg.buf = dbuf; + dvbm_cfg.ybuf_bot = 0; + dvbm_cfg.ybuf_top = width * wrap_line; + dvbm_cfg.ybuf_lstd = width; + dvbm_cfg.ybuf_fstd = width * height; + dvbm_cfg.cbuf_bot = dvbm_cfg.ybuf_top; + dvbm_cfg.cbuf_top = dvbm_cfg.cbuf_bot + (width * wrap_line / 2); + dvbm_cfg.cbuf_lstd = width; + dvbm_cfg.cbuf_fstd = dvbm_cfg.ybuf_fstd / 2; + dvbm_cfg.chan_id = id; + + rk_dvbm_ctrl(g_ofl_dvbm, DVBM_ISP_SET_CFG, &dvbm_cfg); + rk_dvbm_link(g_ofl_dvbm, id); + + return 0; +} + +void rkvpss_ofl_dvbm_deinit(struct rkvpss_offline_dev *ofl, int id) +{ + if (!g_ofl_dvbm || !ofl) { + pr_err("g_dvbm %p or vpss_dev %p is NULL\n", g_ofl_dvbm, ofl); + return; + } + rk_dvbm_unlink(g_ofl_dvbm, id); +} + +int rkvpss_ofl_dvbm_event(u32 event, u32 seq) +{ + enum dvbm_cmd cmd; + + if (!g_ofl_dvbm) + return -EINVAL; + + switch (event) { + case ROCKIT_DVBM_START: + cmd = DVBM_ISP_FRM_START; + break; + case ROCKIT_DVBM_END: + cmd = DVBM_ISP_FRM_END; + break; + default: + return -EINVAL; + } + + return rk_dvbm_ctrl(g_ofl_dvbm, cmd, &seq); +} + diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline_dvbm.h b/drivers/media/platform/rockchip/vpss/vpss_offline_dvbm.h new file mode 100644 index 000000000000..90b22bd72b4c --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_offline_dvbm.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKVPSS_OFFLINE_DVBM_H +#define _RKVPSS_OFFLINE_DVBM_H + +#include +#include + +#include + + +#if IS_ENABLED(CONFIG_ROCKCHIP_DVBM) +int rkvpss_ofl_dvbm_get(struct rkvpss_offline_dev *ofl); +int rkvpss_ofl_dvbm_init(struct dma_buf *dbuf, u32 dma_addr, u32 wrap_line, int width, int height, int id); +void rkvpss_ofl_dvbm_deinit(struct rkvpss_offline_dev *ofl, int id); +int rkvpss_ofl_dvbm_event(u32 event, u32 seq); +#else +static inline int rkvpss_ofl_dvbm_get(struct rkvpss_offline_dev *ofl) {return -EINVAL; } +static inline int rkvpss_ofl_dvbm_init(struct dma_buf *dbuf, u32 dma_addr, u32 wrap_line, int width, int height, int id) {return -EINVAL; } +static inline void rkvpss_ofl_dvbm_deinit(struct rkvpss_offline_dev *ofl, int id) {} +static inline int rkvpss_ofl_dvbm_event(u32 event, u32 seq) {return -EINVAL; } +#endif + +#endif + diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline_rockit.c b/drivers/media/platform/rockchip/vpss/vpss_offline_rockit.c new file mode 100644 index 000000000000..2296dd2c367d --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_offline_rockit.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 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.h" + +#include "vpss_offline_rockit.h" +#include "vpss_offline_v20.h" + +static struct rkvpss_offline_dev *global_ofl; + +void rkvpss_ofl_rockit_init(struct rkvpss_offline_dev *ofl) +{ + global_ofl = ofl; +} + +static long rkvpss_ofl_rockit_open(int *file_id) +{ + void *temp_file = kmalloc(sizeof(void *), GFP_KERNEL); + long ret = 0; + + *file_id = rkvpss_ofl_add_file_id(global_ofl, temp_file); + if (*file_id <= 0) { + ret = -EINVAL; + kfree(temp_file); + goto out; + } + + mutex_lock(&global_ofl->hw->dev_lock); + pm_runtime_get_sync(global_ofl->hw->dev); + mutex_unlock(&global_ofl->hw->dev_lock); + + v4l2_dbg(1, rkvpss_debug, &global_ofl->v4l2_dev, + "%s file_id:%d\n", __func__, *file_id); +out: + return ret; +} + +static long rkvpss_ofl_rockit_release(int *file_id) +{ + void *idr_entity = NULL; + long ret = 0; + + rkvpss_ofl_buf_del_by_file(global_ofl, *file_id); + idr_entity = idr_remove(&global_ofl->file_idr, *file_id); + if (!idr_entity) + goto out; + + kfree(idr_entity); + + mutex_lock(&global_ofl->hw->dev_lock); + pm_runtime_put_sync(global_ofl->hw->dev); + mutex_unlock(&global_ofl->hw->dev_lock); + + v4l2_dbg(1, rkvpss_debug, &global_ofl->v4l2_dev, + "%s file_id:%d\n", __func__, *file_id); +out: + return ret; +} + +static void *rkvpss_ofl_check_file_id(struct rkvpss_offline_dev *ofl, + int file_id) +{ + void *idr_entity = NULL; + + mutex_lock(&ofl->idr_lock); + idr_entity = idr_find(&ofl->file_idr, file_id); + mutex_unlock(&ofl->idr_lock); + + return idr_entity; +} + +long vpss_rockit_action(int *file_id, unsigned int cmd, void *arg) +{ + long ret = 0; + + switch (cmd) { + case RKVPSS_CMD_OPEN: + ret = rkvpss_ofl_rockit_open(file_id); + break; + case RKVPSS_CMD_RELEASE: + ret = rkvpss_ofl_rockit_release(file_id); + break; + case RKVPSS_CMD_MODULE_SEL: + case RKVPSS_CMD_MODULE_GET: + case RKVPSS_CMD_BUF_ADD: + case RKVPSS_CMD_BUF_DEL: + case RKVPSS_CMD_FRAME_HANDLE: + case RKVPSS_CMD_CHECKPARAMS: + case RKVPSS_CMD_WRAP_DVBM_INIT: + case RKVPSS_CMD_WRAP_DVBM_DEINIT: + if (!rkvpss_ofl_check_file_id(global_ofl, *file_id)) { + v4l2_err(&global_ofl->v4l2_dev, "file_id error\n"); + ret = -EINVAL; + goto out; + } + ret = rkvpss_ofl_action(global_ofl, *file_id, cmd, arg); + break; + default: + break; + } + +out: + return ret; +} +EXPORT_SYMBOL(vpss_rockit_action); + diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline_rockit.h b/drivers/media/platform/rockchip/vpss/vpss_offline_rockit.h new file mode 100644 index 000000000000..c4f4ea5050eb --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_offline_rockit.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKVPSS_OFFLINE_ROCKIT_H +#define _RKVPSS_OFFLINE_ROCKIT_H + +#include + +struct rkvpss_offline_dev; + +void rkvpss_ofl_rockit_init(struct rkvpss_offline_dev *ofl); + +#endif diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline_v20.c b/drivers/media/platform/rockchip/vpss/vpss_offline_v20.c new file mode 100644 index 000000000000..1660484f9b1a --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_offline_v20.c @@ -0,0 +1,2650 @@ +// 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.h" + +#include "procfs.h" + +#include "vpss_offline_v20.h" +#include "vpss_offline_rockit.h" +#include "vpss_offline_dvbm.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; + int file_id; + 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 rkvpss_offline_dev *ofl, + struct dma_buf *dmabuf, + int file_id, int id, int fd, + bool is_all, bool running) +{ + 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_id == file_id && (is_all || buf->fd == fd || buf->dbuf == dmabuf)) { + if (!is_all && running && buf->alloc) + break; + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file_id:%d dev_id:%d fd:%d dbuf:%p\n", + __func__, file_id, 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_id = 0; + 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 rkvpss_offline_dev *ofl, + struct dma_buf *dmabuf, + int file_id, int id, + int fd, int size) +{ + 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; + + if (dmabuf) { + get_dma_buf(dmabuf); + dbuf = dmabuf; + } else { + 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_id == file_id && 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_id = file_id; + 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_id:%d dev_id:%d fd:%d dbuf:%p size:%d\n", __func__, + file_id, id, fd, dbuf, size); + } else { + dma_buf_put(dbuf); + } +end: + mutex_unlock(&hw->dev_lock); + return buf; +} + +static int internal_buf_alloc(struct rkvpss_offline_dev *ofl, + int file_id, + struct rkvpss_buf_info *info) +{ + 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_id = file_id; + 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_id:%d dev_id:%d fd:%d dbuf:%p size:%d\n", + __func__, file_id, buf->dev_id, fd, dbuf, size); + } + return 0; +err: + for (i -= 1; i >= 0; i--) + buf_del(ofl, info->dmabufs[i], file_id, info->dev_id, info->buf_fd[i], + false, false); + return -ENOMEM; +} + +static int external_buf_add(struct rkvpss_offline_dev *ofl, + int file_id, + struct rkvpss_buf_info *info) +{ + void *mem; + int i; + + for (i = 0; i < info->buf_cnt; i++) { + mem = buf_add(ofl, info->dmabufs[i], file_id, 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(ofl, info->dmabufs[i], file_id, info->dev_id, info->buf_fd[i], + false, false); + return -ENOMEM; +} + + +int rkvpss_ofl_buf_add(struct rkvpss_offline_dev *ofl, + int file_id, + struct rkvpss_buf_info *info) +{ + int ret; + + if (info->buf_alloc) + ret = internal_buf_alloc(ofl, file_id, info); + else + ret = external_buf_add(ofl, file_id, info); + return ret; +} + +void rkvpss_ofl_buf_del(struct rkvpss_offline_dev *ofl, + int file_id, + struct rkvpss_buf_info *info) +{ + int i; + + for (i = 0; i < info->buf_cnt; i++) + buf_del(ofl, info->dmabufs[i], file_id, info->dev_id, info->buf_fd[i], + false, false); +} + +void rkvpss_ofl_buf_del_by_file(struct rkvpss_offline_dev *ofl, + int file_id) +{ + buf_del(ofl, NULL, file_id, 0, 0, true, false); +} + +static void average_scale_down(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, val, ctrl = 0, clk_mask = 0; + + switch (idx) { + case RKVPSS_OUTPUT_CH0: + reg_base = RKVPSS2X_SCALE0_BASE; + clk_mask = RKVPSS_SCL0_CKG_DIS; + break; + case RKVPSS_OUTPUT_CH2: + reg_base = RKVPSS_SCALE2_BASE; + clk_mask = RKVPSS_SCL2_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; + + 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 = (out_w - 1) * 65536 / (in_w - 1) + 1; + rkvpss_hw_write(hw, reg_base + 0x10, val); + val = (out_w / 2 - 1) * 65536 / (in_w / 2 - 1) + 1; + rkvpss_hw_write(hw, reg_base + 0x14, val); + + ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN | RKVPSS2X_SW_AVG_SCALE_H_EN; + } + if (in_h != out_h) { + val = (out_h - 1) * 65536 / (in_h - 1) + 1; + rkvpss_hw_write(hw, reg_base + 0x18, val); + val = (out_h - 1) * 65536 / (in_h - 1) + 1; + rkvpss_hw_write(hw, reg_base + 0x1c, val); + + ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN | RKVPSS2X_SW_AVG_SCALE_V_EN; + } + } + //unite todo + +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 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_CH0: + reg_base = RKVPSS2X_SCALE0_BASE; + clk_mask = RKVPSS_SCL0_CKG_DIS; + break; + 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; + case RKVPSS_OUTPUT_CH4: + reg_base = RKVPSS2X_SCALE4_BASE; + clk_mask = RKVPSS2X_SCL4_CKG_DIS; + break; + case RKVPSS_OUTPUT_CH5: + reg_base = RKVPSS2X_SCALE5_BASE; + clk_mask = RKVPSS2X_SCL5_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 rkvpss_offline_dev *ofl, + struct rkvpss_frame_cfg *cfg, bool unite, bool left) +{ + int i; + + for (i = 0; i < RKVPSS_OUT_V20_MAX; i++) { + if (!cfg->output[i].enable) + continue; + if ((i == 0 || i == 2) && cfg->output[i].avg_scl_down) + average_scale_down(cfg, ofl, &cfg->output[i], 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_V20_MAX]; + u32 ch_win_mode[RKVPSS_OUT_V20_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_V20_MAX; i++) { + 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 && cfg->output[i].enable) + 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_V20_MAX; i++) { + if (!unite || !left) + break; + 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_V20_MAX; i++) { + if (!unite || left) + break; + 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_CH4; i++) { + 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]); + } + } + + /* chn4 cmsc */ + if (cfg->output[RKVPSS_OUTPUT_CH4].cmsc.reuse_ch) { + mask = RKVPSS2X_CHN4_CMSC_SEL(3); + val = RKVPSS2X_CHN4_CMSC_SEL(cfg->output[RKVPSS_OUTPUT_CH4].cmsc.reuse_ch); + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CTRL, mask, val); + } + + /* chn5 cmsc */ + if (cfg->output[RKVPSS_OUTPUT_CH5].cmsc.reuse_ch) { + mask = RKVPSS2X_CHN5_CMSC_SEL(3); + val = RKVPSS2X_CHN5_CMSC_SEL(cfg->output[RKVPSS_OUTPUT_CH5].cmsc.reuse_ch); + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CTRL, mask, val); + } + + ctrl |= RKVPSS2X_CMSC_BLK_SIZE(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 rkvpss_offline_dev *ofl, + struct rkvpss_frame_cfg *cfg) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + u32 reg_base, val; + int i; + + for (i = 0; i < RKVPSS_OUT_V20_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; + case RKVPSS_OUTPUT_CH4: + reg_base = RKVPSS2X_RATIO4_BASE; + break; + case RKVPSS_OUTPUT_CH5: + reg_base = RKVPSS2X_RATIO5_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_V20_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 rkvpss_offline_dev *ofl, + int file_id, + struct rkvpss_frame_cfg *cfg, + bool unite, bool left) +{ + 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; + case V4L2_PIX_FMT_UYVY: + 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 |= RKVPSS2X_MI_RD_INPUT_UYVY; + //unite_off todo + break; + case V4L2_PIX_FMT_VYUY: + 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 |= RKVPSS2X_MI_RD_INPUT_UYVY | RKVPSS_MI_RD_UV_SWAP; + //unite_off todo + break; + case V4L2_PIX_FMT_YUYV: + 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 |= RKVPSS2X_MI_RD_INPUT_UYVY | RKVPSS_MI_RD_RB_SWAP; + //unite_off todo + break; + case V4L2_PIX_FMT_YVYU: + 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 |= RKVPSS2X_MI_RD_INPUT_UYVY | RKVPSS_MI_RD_RB_SWAP | RKVPSS_MI_RD_UV_SWAP; + //unite_off todo + 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(ofl, cfg->input.dmabuf, file_id, 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); + } + + /* raster rotate 90 */ + switch (cfg->input.format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + if (cfg->input.rotate_90) + in_ctrl |= RKVPSS_MI_RD_MODE(3); + break; + default: + break; + } + + 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_ch4_5(struct rkvpss_offline_dev *ofl, + struct rkvpss_frame_cfg *cfg, + u32 *crop_en, + bool unite, bool left) +{ + struct rkvpss_hw_dev *hw = ofl->hw; + int i; + u32 off_reg, size_reg; + + if (!unite) { + for (i = RKVPSS_OUTPUT_CH4; i < RKVPSS_OUT_V20_MAX; i++) { + if (!cfg->output[i].enable) + continue; + if (i == RKVPSS_OUTPUT_CH4) { + off_reg = RKVPSS2X_CROP0_4_OFFS; + size_reg = RKVPSS2X_CROP0_4_SIZE; + } else { + off_reg = RKVPSS2X_CROP0_5_OFFS; + size_reg = RKVPSS2X_CROP0_5_SIZE; + } + rkvpss_hw_write(hw, off_reg, + RKVPSS2X_CROP_OFFS(cfg->output[i].crop_v_offs, + cfg->output[i].crop_h_offs)); + rkvpss_hw_write(hw, size_reg, + RKVPSS2X_CROP_SIZE(cfg->output[i].crop_height, + cfg->output[i].crop_width)); + *crop_en |= RKVPSS_CROP_CHN_EN(i); + } + } else { + //1126b todo + } +} + +static void crop_config(struct rkvpss_offline_dev *ofl, + struct rkvpss_frame_cfg *cfg, + bool unite, bool left) +{ + 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_CH3; 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_CH3; 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_CH3; 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); + } + } + } + + crop_ch4_5(ofl, cfg, &crop_en, unite, left); + + 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_CH3; 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 rkvpss_offline_dev *ofl, + int file_id, + struct rkvpss_frame_cfg *cfg, + bool unite, bool left) +{ + 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_V20_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_V20_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 | + RKVPSS2X_CH1_WR_RGB888_ALPHA(cfg->output[i].alpha); + 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 | + RKVPSS2X_CH1_WR_RGB888_ALPHA(cfg->output[i].alpha); + 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; + case V4L2_PIX_FMT_FBC0: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_YUV420; + out_ch[i].size = cfg->output[i].stride * h * 3 / 2; + break; + case V4L2_PIX_FMT_FBC2: + if (cfg->output[i].stride < ALIGN(w, 16)) + cfg->output[i].stride = ALIGN(w, 16); + out_ch[i].ctrl |= RKVPSS_MI_CHN_WR_OUTPUT_YUV422; + out_ch[i].size = cfg->output[i].stride * h * 2; + 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; + + /* for wrap recalculate size and c_offs*/ + if (cfg->output[i].wrap.enable) { + out_ch[i].size = (out_ch[i].size / h) * cfg->output[i].wrap.wrap_line; + if (out_ch[i].c_offs) + out_ch[i].c_offs = (out_ch[i].c_offs / h) * + cfg->output[i].wrap.wrap_line; + } + } + 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_V20_MAX; i++) { + if (hw->is_ofl_ch[i]) + mask |= RKVPSS_MI_CHN_V_FLIP(i); + if (!cfg->output[i].enable) + continue; + buf = buf_add(ofl, cfg->output[i].dmabuf, file_id, 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); + + if (cfg->output[i].format == V4L2_PIX_FMT_FBC0 || + cfg->output[i].format == V4L2_PIX_FMT_FBC2) { + val = sg_dma_address(sg_tbl->sgl); + reg = RKVPSS_MI_CHN0_WR_CB_BASE; + rkvpss_hw_write(hw, reg + i * 0x100, val); + val += (ALIGN(w, 64) / 64) * (ALIGN(h, 4) / 4) * 16; + reg = RKVPSS_MI_CHN0_WR_Y_BASE; + rkvpss_hw_write(hw, reg + i * 0x100, val); + + cfg->output[i].stride = 0; //stride is 0 + } else { + 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 if (cfg->output[i].format == V4L2_PIX_FMT_FBC0 || + cfg->output[i].format == V4L2_PIX_FMT_FBC2) + val = 0x1f000000; + else + val = cfg->output[i].stride * h; + + /* for wrap recalutate y_size*/ + if (cfg->output[i].wrap.enable) + val = (val / h) * cfg->output[i].wrap.wrap_line; + + 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 * 0x100), + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_STRIDE + i * 0x100), + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_PIC_SIZE + i * 0x100), + rkvpss_hw_read(hw, RKVPSS_MI_CHN0_WR_Y_BASE + i * 0x100)); + } + + + rkvpss_hw_set_bits(hw, RKVPSS_MI_WR_VFLIP_CTRL, mask, flip_en); + + /* config output uv swap */ + for (i = 0; i < RKVPSS_OUT_V20_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_V20_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_V20_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); + } else if (cfg->output[i].format == V4L2_PIX_FMT_FBC0 || + cfg->output[i].format == V4L2_PIX_FMT_FBC2) { + mask = RKVPSS2X_SW_MI_WR_FBCE_SEL(3); + val = RKVPSS2X_SW_MI_WR_FBCE_SEL(i + 1); + rkvpss_hw_set_bits(hw, RKVPSS2X_MI_WR_FBCE_CTRL, mask, val); + rkvpss_hw_write(hw, RKVPSS2X_MI_WR_FBCE_SIZE, + RKVPSS2X_SW_WR_FBCE_SIZE(w, h)); + rkvpss_hw_write(hw, RKVPSS2X_MI_WR_FBCE_OFFSET, + (ALIGN(w, 64) / 64) * (ALIGN(h, 4) / 4) * 16); + } + } + + /* 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(ofl, cfg->output[i].dmabuf, file_id, cfg->dev_id, cfg->output[i].buf_fd, + false, true); + } + buf_del(ofl, cfg->input.dmabuf, file_id, cfg->dev_id, cfg->input.buf_fd, + false, true); + return -ENOMEM; +} + +//1126b todo +static void calc_unite_scl_params(struct rkvpss_offline_dev *ofl, + struct rkvpss_frame_cfg *cfg) +{ + 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_V20_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 bool is_wrap(struct rkvpss_frame_cfg *cfg) +{ + bool is_wrap = false; + int i; + + for (i = 0; i <= RKVPSS_OUTPUT_CH1; i++) { + if (cfg->output[i].wrap.enable) { + is_wrap = true; + break; + } + } + + return is_wrap; +} + +static int rkvpss_ofl_run(struct rkvpss_offline_dev *ofl, + int file_id, + struct rkvpss_frame_cfg *cfg, + bool unite, bool left) +{ + 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(ofl, file_id, cfg, unite, left); + if (ret < 0) + return ret; + + if (unite && left) + calc_unite_scl_params(ofl, 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(ofl, cfg, unite, left_tmp); + scale_config(ofl, cfg, unite, left_tmp); + if (!unite) + aspt_config(ofl, cfg); + ret = write_config(ofl, file_id, cfg, unite, left_tmp); + if (ret < 0) + return ret; + + mask = 0; + val = 0; + for (i = 0; i < RKVPSS_OUT_V20_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); + + /* for wrap */ + for (i = 0; i <= RKVPSS_OUTPUT_CH1; i++) { + if (!cfg->output[i].wrap.enable) + continue; + mask |= RKVPSS_VPSS2ENC_SEL | RKVPSS_VPSS2ENC_PIPE_EN | + RKVPSS2X_VPSS2ENC_PATH_EN | RKVPSS2X_SENSOR_ID(7); + val |= RKVPSS_VPSS2ENC_SEL | RKVPSS_VPSS2ENC_PIPE_EN | + RKVPSS2X_VPSS2ENC_PATH_EN | RKVPSS2X_SENSOR_ID(cfg->dev_id); + if (i == 1) { + mask |= RKVPSS_VPSS2ENC_CNT_SEL; + val |= RKVPSS_VPSS2ENC_CNT_SEL; + } + } + rkvpss_hw_set_bits(hw, RKVPSS_VPSS_CTRL, mask, val); + + //1126b add RKVPSS_MIR_FORCE_UPD + update |= RKVPSS_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD | RKVPSS_MIR_GEN_UPD | + RKVPSS_MIR_FORCE_UPD; + if (is_wrap(cfg)) + update |= RKVPSS_CFG_FORCE_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); + + if (is_wrap(cfg)) + rkvpss_ofl_dvbm_event(ROCKIT_DVBM_START, cfg->sequence); + + 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; + } + + if (is_wrap(cfg)) + rkvpss_ofl_dvbm_event(ROCKIT_DVBM_END, cfg->sequence); + + return ret; +} + +int rkvpss_module_get(struct rkvpss_offline_dev *ofl, + struct rkvpss_module_sel *get) +{ + 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_V20_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; +} + +int rkvpss_module_sel(struct rkvpss_offline_dev *ofl, + struct rkvpss_module_sel *sel) +{ + 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_V20_MAX; i++) + hw->is_ofl_ch[i] = !!sel->ch_en[i]; +unlock: + mutex_unlock(&hw->dev_lock); + return ret; +} + +int rkvpss_check_params(struct rkvpss_offline_dev *ofl, + struct rkvpss_frame_cfg *cfg, + bool *unite) +{ + int i, ret = 0, tile_num = 0; + int scale_w_factor, scale_h_factor; + int out_width, out_height; + + /* 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_V20) + *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: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + 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_V20_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; + case V4L2_PIX_FMT_FBC0: + case V4L2_PIX_FMT_FBC2: + 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].aspt.enable) { + out_width = cfg->output[i].aspt.width; + out_height = cfg->output[i].aspt.height; + } else { + out_width = cfg->output[i].scl_width; + out_height = cfg->output[i].scl_height; + } + if (out_width > RKVPSS_UNITE_MAX_WIDTH || + out_height > RKVPSS_UNITE_MAX_HEIGHT || + out_width < RKVPSS_MIN_WIDTH || + out_height < RKVPSS_MIN_HEIGHT) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d output size not support width:%d height:%d\n", + cfg->dev_id, i, out_width, out_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->output[i].crop_width == 0 || cfg->output[i].crop_height == 0) + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch%d crop output width:%d height:%d can't be zero\n", + cfg->dev_id, i, + cfg->output[i].crop_width, cfg->output[i].crop_height); + if ((!cfg->input.rotate || cfg->input.rotate == 2) + && !cfg->input.rotate_90) { + 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", + cfg->dev_id, i, 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", + cfg->dev_id, i, 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 */ + scale_w_factor = 32; + scale_h_factor = 32; + + if (((cfg->output[i].scl_width >= cfg->output[i].crop_width) && + (cfg->output[i].crop_width * scale_w_factor < cfg->output[i].scl_width)) || + ((cfg->output[i].crop_width > cfg->output[i].scl_width) && + (cfg->output[i].scl_width * scale_w_factor < cfg->output[i].crop_width)) || + ((cfg->output[i].scl_height >= cfg->output[i].crop_height) && + (cfg->output[i].crop_height * scale_h_factor < cfg->output[i].scl_height)) || + ((cfg->output[i].crop_height > cfg->output[i].scl_height) && + (cfg->output[i].scl_height * scale_h_factor < cfg->output[i].crop_height))) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d scale size error\n", + cfg->dev_id, i); + ret = -EINVAL; + goto end; + } + + if (i >= RKVPSS_OUTPUT_CH2) { + 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; + } + + /* check wrap */ + if (((cfg->output[0].enable && cfg->output[0].wrap.enable) && + (cfg->output[1].enable && cfg->output[1].wrap.enable)) || + (cfg->output[2].enable && cfg->output[2].wrap.enable) || + (cfg->output[2].enable && cfg->output[2].wrap.enable)) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d only ch0 or ch1 support wrap\n", + cfg->dev_id); + ret = -EINVAL; + goto end; + } + for (i = 0; i <= RKVPSS_OUTPUT_CH1; i++) { + struct rkvpss_output_cfg *output = &cfg->output[i]; + + if (output->aspt.enable) + out_height = output->aspt.height; + else + out_height = output->scl_height; + if (output->enable && output->wrap.enable) { + if (output->wrap.wrap_line <= 0 || + output->wrap.wrap_line > out_height) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d wrap_line:%d error\n", + cfg->dev_id, i, + output->wrap.wrap_line); + ret = -EINVAL; + goto end; + } + if (output->flip) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d wrap no support flip\n", + cfg->dev_id, i); + ret = -EINVAL; + goto end; + } + switch (output->format) { + case V4L2_PIX_FMT_TILE420: + case V4L2_PIX_FMT_TILE422: + 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: + v4l2_err(&ofl->v4l2_dev, "dev_id:%d ch:%d wrap no support output format:%c%c%c%c\n", + cfg->dev_id, i, output->format, + output->format >> 8, + output->format >> 16, output->format >> 24); + ret = -EINVAL; + 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_V20_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; + } + if (cfg->output[i].wrap.enable) { + v4l2_err(&ofl->v4l2_dev, "dev_id:%d unite no support wrap\n", + cfg->dev_id); + ret = -EINVAL; + goto end; + } + } + } + +end: + return ret; +} + +int rkvpss_prepare_run(struct rkvpss_offline_dev *ofl, + int file_id, + struct rkvpss_frame_cfg *cfg) +{ + 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(ofl, 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_V20_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(ofl, file_id, cfg, false, false); + if (ret < 0) + goto end; + } else { + ret = rkvpss_ofl_run(ofl, file_id, cfg, true, true); + if (ret < 0) { + v4l2_err(&ofl->v4l2_dev, "unite left error\n"); + goto end; + } + ret = rkvpss_ofl_run(ofl, file_id, 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 int ofl_get_file_id(struct rkvpss_offline_dev *ofl, + struct file *file) +{ + void *idr_entity_temp; + int file_id = 0; + bool in_idr = false; + int ret; + + mutex_lock(&ofl->idr_lock); + idr_for_each_entry(&ofl->file_idr, idr_entity_temp, file_id) { + if ((void *)file == idr_entity_temp) { + in_idr = true; + break; + } + } + mutex_unlock(&ofl->idr_lock); + + if (in_idr) + ret = file_id; + else + ret = 0; + + return ret; +} + +static long rkvpss_ofl_wrap_dvbm_init(struct rkvpss_offline_dev *ofl, + int file_id, struct rkvpss_frame_cfg *cfg) +{ + const struct vb2_mem_ops *mem_ops = ofl->hw->mem_ops; + struct dma_buf *dbuf; + struct rkvpss_offline_buf *buf; + struct sg_table *sg_tbl; + int width, height, wrap_line, i; + u32 dma_addr; + + for (i = 0; i <= RKVPSS_OUTPUT_CH1; i++) { + v4l2_dbg(4, rkvpss_debug, &ofl->v4l2_dev, "%s chn:%d enable:%d sequence:%d\n", + __func__, i, cfg->output[i].wrap.enable, cfg->sequence); + + if (!cfg->output[i].wrap.enable) + continue; + + buf = buf_add(ofl, cfg->output[i].dmabuf, file_id, cfg->dev_id, + cfg->output[i].buf_fd, 0); + if (!buf) { + v4l2_err(&ofl->v4l2_dev, "get wrap_buf fail"); + return -EFAULT; + } + + sg_tbl = mem_ops->cookie(&buf->vb, buf->mem); + dma_addr = sg_dma_address(sg_tbl->sgl); + + dbuf = cfg->output[i].dmabuf; + + if (cfg->output[i].aspt.enable) + height = cfg->output[i].aspt.height; + else + height = cfg->output[i].scl_height; + + width = cfg->output[i].stride; + + wrap_line = cfg->output[i].wrap.wrap_line; + + rkvpss_ofl_dvbm_init(dbuf, dma_addr, wrap_line, width, height, cfg->dev_id); + + v4l2_dbg(4, rkvpss_debug, &ofl->v4l2_dev, "%s file_id:%d dev_id:%d wrap_chn:%d\n", + __func__, file_id, cfg->dev_id, i); + } + + return 0; +} + +static void rkvpss_ofl_wrap_dvbm_deinit(struct rkvpss_offline_dev *ofl, int *id) +{ + rkvpss_ofl_dvbm_deinit(ofl, *id); +} + +long rkvpss_ofl_action(struct rkvpss_offline_dev *ofl, + int file_id, unsigned int cmd, void *arg) +{ + long ret = 0; + bool unite; + + v4l2_dbg(4, rkvpss_debug, &ofl->v4l2_dev, "%s cmd:%d", __func__, cmd); + mutex_lock(&ofl->handle_lock); + ofl->pm_need_wait = true; + + switch (cmd) { + case RKVPSS_CMD_MODULE_SEL: + ret = rkvpss_module_sel(ofl, arg); + break; + case RKVPSS_CMD_MODULE_GET: + ret = rkvpss_module_get(ofl, arg); + break; + case RKVPSS_CMD_FRAME_HANDLE: + ret = rkvpss_prepare_run(ofl, file_id, arg); + break; + case RKVPSS_CMD_BUF_ADD: + ret = rkvpss_ofl_buf_add(ofl, file_id, arg); + break; + case RKVPSS_CMD_BUF_DEL: + rkvpss_ofl_buf_del(ofl, file_id, arg); + break; + case RKVPSS_CMD_CHECKPARAMS: + ret = rkvpss_check_params(ofl, arg, &unite); + break; + case RKVPSS_CMD_WRAP_DVBM_INIT: + ret = rkvpss_ofl_wrap_dvbm_init(ofl, file_id, arg); + break; + case RKVPSS_CMD_WRAP_DVBM_DEINIT: + rkvpss_ofl_wrap_dvbm_deinit(ofl, arg); + break; + default: + ret = -EFAULT; + } + + ofl->pm_need_wait = false; + mutex_unlock(&ofl->handle_lock); + + 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; + int file_id = 0; + + if (!arg) { + ret = -EINVAL; + goto out; + } + + file_id = ofl_get_file_id(ofl, file); + if (file_id <= 0) { + ret = -EINVAL; + goto out; + } + + ret = rkvpss_ofl_action(ofl, file_id, cmd, arg); + +out: + /* notify hw suspend */ + if (ofl->hw->is_suspend) + complete(&ofl->pm_cmpl); + + return ret; +} + + +static const struct v4l2_ioctl_ops offline_ioctl_ops = { + .vidioc_default = rkvpss_ofl_ioctl, +}; + +int rkvpss_ofl_add_file_id(struct rkvpss_offline_dev *ofl, void *idr_entity) +{ + void *idr_entity_temp; + int idr_id = 0, ret = 0; + bool in_idr = false; + + mutex_lock(&ofl->idr_lock); + + idr_for_each_entry(&ofl->file_idr, idr_entity_temp, idr_id) { + if (idr_entity == idr_entity_temp) { + in_idr = true; + break; + } + } + if (!in_idr) + ret = idr_alloc(&ofl->file_idr, idr_entity, 1, 0, GFP_KERNEL); + + mutex_unlock(&ofl->idr_lock); + + return ret; +} + +void *rkvpss_ofl_del_file_id(struct rkvpss_offline_dev *ofl, struct file *file) +{ + void *ret = NULL; + int idr_id = 0; + + idr_id = ofl_get_file_id(ofl, file); + + mutex_lock(&ofl->idr_lock); + + if (idr_id) + ret = idr_remove(&ofl->file_idr, idr_id); + + mutex_unlock(&ofl->idr_lock); + + return ret; +} + +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); + else + ret = rkvpss_ofl_add_file_id(ofl, (void *)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); + int ret = 0; + int file_id = 0; + + v4l2_dbg(1, rkvpss_debug, &ofl->v4l2_dev, + "%s file:%p\n", __func__, file); + + v4l2_fh_release(file); + file_id = ofl_get_file_id(ofl, file); + if (file_id) + buf_del(ofl, NULL, file_id, 0, 0, true, false); + + mutex_lock(&ofl->hw->dev_lock); + pm_runtime_put_sync(ofl->hw->dev); + mutex_unlock(&ofl->hw->dev_lock); + + if (rkvpss_ofl_del_file_id(ofl, file) != (void *)file) + ret = -EINVAL; + + return ret; +} + +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_v20(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_v20(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->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); + mutex_init(&ofl->idr_lock); + mutex_init(&ofl->handle_lock); + idr_init(&ofl->file_idr); + rkvpss_offline_proc_init(ofl); + ofl->pm_need_wait = false; + init_completion(&ofl->pm_cmpl); + rkvpss_ofl_rockit_init(ofl); + rkvpss_ofl_dvbm_get(ofl); + return 0; +unreg_v4l2: + mutex_destroy(&ofl->apilock); + v4l2_device_unregister(v4l2_dev); + return ret; +} + +void rkvpss_unregister_offline_v20(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); + mutex_destroy(&hw->ofl_dev.idr_lock); + mutex_destroy(&hw->ofl_dev.handle_lock); + idr_destroy(&hw->ofl_dev.file_idr); + rkvpss_offline_proc_cleanup(&hw->ofl_dev); +} + diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline_v20.h b/drivers/media/platform/rockchip/vpss/vpss_offline_v20.h new file mode 100644 index 000000000000..fa9acb57dcc5 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_offline_v20.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKVPSS_OFFLINE_V20_H +#define _RKVPSS_OFFLINE_V20_H + +struct rkvpss_offline_dev; + +//void buf_del(struct rkvpss_offline_dev *ofl, +// struct dma_buf *dmabuf, +// int file_id, int id, int fd, bool is_all, +// bool running); +int rkvpss_module_sel(struct rkvpss_offline_dev *ofl, + struct rkvpss_module_sel *sel); +int rkvpss_module_get(struct rkvpss_offline_dev *ofl, + struct rkvpss_module_sel *get); +int rkvpss_ofl_buf_add(struct rkvpss_offline_dev *ofl, int file_id, struct rkvpss_buf_info *info); +void rkvpss_ofl_buf_del(struct rkvpss_offline_dev *ofl, int file_id, struct rkvpss_buf_info *info); +void rkvpss_ofl_buf_del_by_file(struct rkvpss_offline_dev *ofl, int file_id); +int rkvpss_prepare_run(struct rkvpss_offline_dev *ofl, int file_id, struct rkvpss_frame_cfg *cfg); +int rkvpss_check_params(struct rkvpss_offline_dev *ofl, struct rkvpss_frame_cfg *cfg, bool *unite); +long rkvpss_ofl_action(struct rkvpss_offline_dev *ofl, int file_id, unsigned int cmd, void *arg); +int rkvpss_ofl_add_file_id(struct rkvpss_offline_dev *ofl, void *idr_entity); +void *rkvpss_ofl_del_file_id(struct rkvpss_offline_dev *ofl, struct file *file); + + + +#if IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_VPSS_V20) +int rkvpss_register_offline_v20(struct rkvpss_hw_dev *hw); +void rkvpss_unregister_offline_v20(struct rkvpss_hw_dev *hw); +void rkvpss_offline_irq_v20(struct rkvpss_hw_dev *hw, u32 irq); +#else +static inline int rkvpss_register_offline_v20(struct rkvpss_hw_dev *hw) {return -EINVAL; } +static inline void rkvpss_unregister_offline_v20(struct rkvpss_hw_dev *hw) {} +static inline void rkvpss_offline_irq_v20(struct rkvpss_hw_dev *hw, u32 irq) {} +#endif + + +#endif + diff --git a/drivers/media/platform/rockchip/vpss/vpss_rockit.c b/drivers/media/platform/rockchip/vpss/vpss_rockit.c new file mode 100644 index 000000000000..5d1f831cb318 --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_rockit.c @@ -0,0 +1,617 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + +#define pr_fmt(fmt) "vpss_rockit: %s:%d " fmt, __func__, __LINE__ + + +#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 "stream_v20.h" +#include "vpss_rockit.h" +#include "vpss_dvbm.h" + + +static struct rockit_rkvpss_cfg *rockit_vpss_cfg; + +struct rkvpss_rockit_buffer { + struct rkvpss_buffer vpss_buffer; + struct dma_buf *dmabuf; + struct dma_buf_attachment *dba; + struct sg_table *sgt; + void *mpi_buf; + struct list_head queue; + int buf_id; + u32 buff_addr; + void *vaddr; +}; + +static struct rkvpss_stream *rkvpss_rockit_get_stream(struct rockit_rkvpss_cfg *input_cfg) +{ + struct rkvpss_device *vpss_dev = NULL; + struct rkvpss_stream *stream = NULL; + u8 i; + + if (!rockit_vpss_cfg) { + pr_err("rockit_vpss_cfg is null, get stream failed\n"); + return NULL; + } + if (!input_cfg) { + pr_err("input_cfg is null, get stream failed\n"); + return NULL; + } + + for (i = 0; i < rockit_vpss_cfg->vpss_num; i++) { + if (!strcmp(rockit_vpss_cfg->rkvpss_dev_cfg[i].vpss_name, + input_cfg->current_name)) { + vpss_dev = rockit_vpss_cfg->rkvpss_dev_cfg[i].vpss_dev; + break; + } + } + + if (vpss_dev == NULL) { + pr_err("can not find vpss_dev!\n"); + return NULL; + } + + switch (input_cfg->nick_id) { + case 0: + stream = &vpss_dev->stream_vdev.stream[RKVPSS_OUTPUT_CH0]; + break; + case 1: + stream = &vpss_dev->stream_vdev.stream[RKVPSS_OUTPUT_CH1]; + break; + case 2: + stream = &vpss_dev->stream_vdev.stream[RKVPSS_OUTPUT_CH2]; + break; + case 3: + stream = &vpss_dev->stream_vdev.stream[RKVPSS_OUTPUT_CH3]; + break; + default: + stream = NULL; + break; + } + + return stream; +} + +static void rkvpss_rockit_cfg_stream_buffer(struct rkvpss_stream *stream, + struct rkvpss_rockit_buffer *vpssrk_buf, + struct rockit_rkvpss_cfg *input_cfg) +{ + u32 y_offs = 0, uv_offs = 0; + u32 dma_addr = vpssrk_buf->buff_addr; + struct capture_fmt *fmt; + + fmt = &stream->out_cap_fmt; + + if (input_cfg->vir_width) { + stream->out_fmt.plane_fmt[0].bytesperline = input_cfg->vir_width * + DIV_ROUND_UP(fmt->bpp[0], 8); + y_offs = input_cfg->y_offset; + uv_offs = input_cfg->uv_offset; + + stream->out_fmt.plane_fmt[1].bytesperline = input_cfg->vir_width * + DIV_ROUND_UP(fmt->bpp[0], 8); + stream->out_fmt.plane_fmt[1].sizeimage = stream->out_fmt.plane_fmt[1].bytesperline * + stream->out_fmt.height; + } else { + y_offs = 0; + if (stream->dev->stream_vdev.wrap_line && stream->id == RKVPSS_OUTPUT_CH0) { + uv_offs = stream->out_fmt.plane_fmt[0].bytesperline * + stream->dev->stream_vdev.wrap_line; + stream->dev->wrap_buf.dbuf = vpssrk_buf->dmabuf; + stream->dev->wrap_buf.dma_addr = dma_addr; + } else { + uv_offs = stream->out_fmt.plane_fmt[0].bytesperline * stream->out_fmt.height; + } + } + + vpssrk_buf->vpss_buffer.dma[0] = dma_addr + y_offs; + vpssrk_buf->vpss_buffer.dma[1] = dma_addr + uv_offs; + vpssrk_buf->vpss_buffer.vaddr[0] = NULL; + vpssrk_buf->vpss_buffer.vaddr[1] = NULL; + vpssrk_buf->vpss_buffer.vb.vb2_buf.planes[0].mem_priv = NULL; + if (vpssrk_buf->vaddr) { + vpssrk_buf->vpss_buffer.vaddr[0] = vpssrk_buf->vaddr + y_offs; + vpssrk_buf->vpss_buffer.vaddr[1] = vpssrk_buf->vaddr + uv_offs; + vpssrk_buf->vpss_buffer.vb.vb2_buf.planes[0].mem_priv = vpssrk_buf->sgt; + } +} + +int rkvpss_rockit_buf_queue(struct rockit_rkvpss_cfg *input_cfg) +{ + struct rkvpss_stream *stream = NULL; + struct rkvpss_rockit_buffer *vpssrk_buf = NULL; + struct rkvpss_device *vpss_dev = NULL; + struct rkvpss_stream_cfg *stream_cfg = NULL; + unsigned long lock_flags = 0; + int dev_id, i; + + if (!input_cfg) { + pr_err("input_cfg is null\n"); + return -EINVAL; + } + + stream = rkvpss_rockit_get_stream(input_cfg); + if (!stream || stream->id >= ROCKIT_STREAM_NUM_MAX) { + pr_err("inval stream"); + return -EINVAL; + } + + dev_id = stream->dev->dev_id; + vpss_dev = stream->dev; + + if (!rockit_vpss_cfg) { + pr_err("rockit_vpss_cfg is null\n"); + return -EINVAL; + } + stream_cfg = &rockit_vpss_cfg->rkvpss_dev_cfg[dev_id].rkvpss_stream_cfg[stream->id]; + stream_cfg->node = input_cfg->node; + + if (!input_cfg->buf) + return -EINVAL; + + for (i = 0; i < ROCKIT_BUF_NUM_MAX; i++) { + if (stream_cfg->buff_id[i] == input_cfg->mpi_id) { + input_cfg->is_alloc = 0; + break; + } + } + + if (input_cfg->is_alloc) { + struct dma_buf_attachment *dba; + struct sg_table *sgt; + struct iosys_map map; + + for (i = 0; i < ROCKIT_BUF_NUM_MAX; i++) { + if (!stream_cfg->buff_id[i] && !stream_cfg->rkvpss_buff[i]) { + stream_cfg->buff_id[i] = input_cfg->mpi_id; + vpssrk_buf = kzalloc(sizeof(struct rkvpss_rockit_buffer), + GFP_KERNEL); + if (!vpssrk_buf) { + stream_cfg->buff_id[i] = 0; + pr_err("vpssrk_buf alloc failed\n"); + return -ENOMEM; + } + break; + } + } + if (i == ROCKIT_BUF_NUM_MAX) + return -EINVAL; + + dba = dma_buf_attach(input_cfg->buf, vpss_dev->hw_dev->dev); + if (IS_ERR(dba)) { + kfree(vpssrk_buf); + stream_cfg->buff_id[i] = 0; + return PTR_ERR(dba); + } + + sgt = dma_buf_map_attachment(dba, DMA_BIDIRECTIONAL); + if (IS_ERR(sgt)) { + dma_buf_detach(input_cfg->buf, dba); + kfree(vpssrk_buf); + stream_cfg->buff_id[i] = 0; + return PTR_ERR(sgt); + } + + vpssrk_buf->vaddr = NULL; + if (dma_buf_vmap(input_cfg->buf, &map) == 0) + vpssrk_buf->vaddr = map.vaddr; + + vpssrk_buf->buff_addr = sg_dma_address(sgt->sgl); + get_dma_buf(input_cfg->buf); + vpssrk_buf->mpi_buf = input_cfg->mpibuf; + vpssrk_buf->dmabuf = input_cfg->buf; + vpssrk_buf->dba = dba; + vpssrk_buf->sgt = sgt; + stream_cfg->rkvpss_buff[i] = vpssrk_buf; + } + + /*no todo, vpss use update config mi addr */ + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + vpssrk_buf = NULL; + for (i = 0; i < ROCKIT_BUF_NUM_MAX; i++) { + if (stream_cfg->buff_id[i] == input_cfg->mpi_id) { + vpssrk_buf = stream_cfg->rkvpss_buff[i]; + break; + } + } + if (!vpssrk_buf) { + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + return -EINVAL; + } + + if (stream->out_cap_fmt.mplanes == 1) + rkvpss_rockit_cfg_stream_buffer(stream, vpssrk_buf, input_cfg); + + v4l2_dbg(2, rkvpss_debug, &vpss_dev->v4l2_dev, + "stream:%d rockit_queue buf:%p y:0x%x uv:0x%x\n", + stream->id, vpssrk_buf, + vpssrk_buf->vpss_buffer.dma[0], vpssrk_buf->vpss_buffer.dma[1]); + + list_add_tail(&vpssrk_buf->vpss_buffer.queue, &stream->buf_queue); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + if (stream->is_pause) + stream->ops->update_mi(stream); + + return 0; +} + +int rkvpss_rockit_buf_done(struct rkvpss_stream *stream, int cmd, struct rkvpss_buffer *curr_buf) +{ + struct rkvpss_device *vpss_dev = stream->dev; + struct rkvpss_rockit_buffer *vpssrk_buf = NULL; + struct rkvpss_stream_cfg *stream_cfg = NULL; + unsigned long lock_flags = 0; + u32 dev_id = vpss_dev->dev_id; + + if (!rockit_vpss_cfg || + !rockit_vpss_cfg->rkvpss_rockit_mpibuf_done || + stream->id >= ROCKIT_STREAM_NUM_MAX) + return -EINVAL; + + v4l2_dbg(4, rkvpss_debug, &vpss_dev->v4l2_dev, "%s enter cmd:%d\n", __func__, cmd); + + if (!stream->rockit_on) { + if (!vpss_dev->stream_vdev.wrap_line) { + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + list_add_tail(&curr_buf->queue, &stream->buf_queue); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + } + v4l2_dbg(2, rkvpss_debug, &vpss_dev->v4l2_dev, "%s already stop\n", __func__); + return 0; + } + + stream_cfg = &rockit_vpss_cfg->rkvpss_dev_cfg[dev_id].rkvpss_stream_cfg[stream->id]; + + if (cmd == ROCKIT_DVBM_END) { + vpssrk_buf = container_of(curr_buf, struct rkvpss_rockit_buffer, vpss_buffer); + + rockit_vpss_cfg->mpibuf = vpssrk_buf->mpi_buf; + rockit_vpss_cfg->frame.u64PTS = curr_buf->vb.vb2_buf.timestamp; + rockit_vpss_cfg->frame.u32TimeRef = curr_buf->vb.sequence; + + v4l2_dbg(2, rkvpss_debug, &vpss_dev->v4l2_dev, + "stream:%d seq:%d rockit buf done:0x%x\n", + stream->id, + curr_buf->vb.sequence, + curr_buf->dma[0]); + } else { + //tosee + if (!(stream->dev->stream_vdev.wrap_line && stream->id == RKVPSS_OUTPUT_CH0)) + return 0; + + rockit_vpss_cfg->frame.u64PTS = vpss_dev->vpss_sdev.frame_timestamp; + rockit_vpss_cfg->frame.u32TimeRef = vpss_dev->vpss_sdev.frame_seq; + } + + rockit_vpss_cfg->frame.u32Height = stream->out_fmt.height; + rockit_vpss_cfg->frame.u32Width = stream->out_fmt.width; + rockit_vpss_cfg->frame.enPixelFormat = stream->out_fmt.pixelformat; + rockit_vpss_cfg->frame.u32VirWidth = stream->out_fmt.width; + rockit_vpss_cfg->frame.u32VirHeight = stream->out_fmt.height; + rockit_vpss_cfg->current_name = vpss_dev->name; + rockit_vpss_cfg->node = stream_cfg->node; + rockit_vpss_cfg->event = cmd; + + if (list_empty(&stream->buf_queue)) + rockit_vpss_cfg->is_empty = true; + else + rockit_vpss_cfg->is_empty = false; + + rockit_vpss_cfg->rkvpss_rockit_mpibuf_done(rockit_vpss_cfg); + + return 0; +} + +/* dynamic refmt */ +int rkvpss_rockit_pause_stream(struct rockit_rkvpss_cfg *input_cfg) +{ + struct rkvpss_stream *stream = NULL; + + stream = rkvpss_rockit_get_stream(input_cfg); + + if (stream == NULL) { + pr_err("the stream is null\n"); + return -EINVAL; + } + + v4l2_dbg(1, rkvpss_debug, &stream->dev->v4l2_dev, + "%s stream:%d\n", __func__, stream->id); + + rockit_vpss_ops.rkvpss_stream_stop(stream); + + if (stream->dev->stream_vdev.wrap_line && stream->id == RKVPSS_OUTPUT_CH0) + rkvpss_dvbm_deinit(stream->dev); + return 0; +} +EXPORT_SYMBOL(rkvpss_rockit_pause_stream); + +/* dynamic refmt */ +int rkvpss_rockit_config_stream(struct rockit_rkvpss_cfg *input_cfg, + int width, int height, int wrap_line) +{ + struct rkvpss_stream *stream = NULL; + struct rkvpss_buffer *vpss_buf, *buf_temp; + int ret; + unsigned long lock_flags = 0; + + + stream = rkvpss_rockit_get_stream(input_cfg); + if (stream == NULL) { + pr_err("the stream is null\n"); + return -EINVAL; + } + + v4l2_dbg(1, rkvpss_debug, &stream->dev->v4l2_dev, + "%s stream:%d %dx%d wrap_line:%d\n", + __func__, stream->id, width, height, wrap_line); + + stream->dev->stream_vdev.wrap_line = wrap_line; + stream->out_fmt.width = width; + stream->out_fmt.height = height; + stream->out_fmt.plane_fmt[0].bytesperline = 0; + ret = rockit_vpss_ops.rkvpss_set_fmt(stream, &stream->out_fmt, false); + if (ret < 0) { + pr_err("stream id %d config failed\n", stream->id); + return -EINVAL; + } + + /* wrap mode */ + if (stream->dev->stream_vdev.wrap_line && stream->id == RKVPSS_OUTPUT_CH0) + rkvpss_dvbm_init(stream); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + + if (stream->curr_buf) { + list_add_tail(&stream->curr_buf->queue, &stream->buf_queue); + if (stream->curr_buf == stream->next_buf) + stream->next_buf = NULL; + stream->curr_buf = NULL; + } + if (stream->next_buf) { + list_add_tail(&stream->next_buf->queue, &stream->buf_queue); + stream->next_buf = NULL; + } + + list_for_each_entry_safe(vpss_buf, buf_temp, &stream->buf_queue, queue) { + struct rkvpss_rockit_buffer *vpssrk_buf = container_of(vpss_buf, + struct rkvpss_rockit_buffer, vpss_buffer); + + if (stream->out_cap_fmt.mplanes == 1) + rkvpss_rockit_cfg_stream_buffer(stream, vpssrk_buf, input_cfg); + } + + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + return 0; +} +EXPORT_SYMBOL(rkvpss_rockit_config_stream); + +/* dynamic refmt */ +int rkvpss_rockit_resume_stream(struct rockit_rkvpss_cfg *input_cfg) +{ + struct rkvpss_stream *stream = NULL; + int ret = 0; + + stream = rkvpss_rockit_get_stream(input_cfg); + if (stream == NULL) { + pr_err("the stream is NULL"); + return -EINVAL; + } + + v4l2_dbg(1, rkvpss_debug, &stream->dev->v4l2_dev, + "%s stream:%d\n", __func__, stream->id); + + ret = rockit_vpss_ops.rkvpss_stream_start(stream); + if (ret < 0) { + pr_err("stream id:%d start failed\n", stream->id); + return -EINVAL; + } + + //tosee + + return 0; +} +EXPORT_SYMBOL(rkvpss_rockit_resume_stream); + +void rkvpss_rockit_buf_state_clear(struct rkvpss_stream *stream) +{ + struct rkvpss_stream_cfg *stream_cfg; + u32 i = 0, dev_id = stream->dev->dev_id; + + if (!rockit_vpss_cfg || stream->id >= ROCKIT_STREAM_NUM_MAX) + return; + + stream_cfg = &rockit_vpss_cfg->rkvpss_dev_cfg[dev_id].rkvpss_stream_cfg[stream->id]; + + for (i = 0; i < ROCKIT_BUF_NUM_MAX; i++) + stream_cfg->buff_id[i] = 0; +} + +int rkvpss_rockit_buf_free(struct rkvpss_stream *stream) +{ + struct rkvpss_rockit_buffer *vpssrk_buf; + struct rkvpss_stream_cfg *stream_cfg; + u32 i = 0, dev_id = stream->dev->dev_id; + + if (!rockit_vpss_cfg || stream->id >= ROCKIT_STREAM_NUM_MAX) + return -EINVAL; + + stream_cfg = &rockit_vpss_cfg->rkvpss_dev_cfg[dev_id].rkvpss_stream_cfg[stream->id]; + + mutex_lock(&stream_cfg->freebuf_lock); + for (i = 0; i < ROCKIT_BUF_NUM_MAX; i++) { + if (!stream_cfg->rkvpss_buff[i]) + continue; + + vpssrk_buf = (struct rkvpss_rockit_buffer *)stream_cfg->rkvpss_buff[i]; + if (vpssrk_buf->dba) { + if (vpssrk_buf->vaddr) { + struct iosys_map map = IOSYS_MAP_INIT_VADDR(vpssrk_buf->vaddr); + + dma_buf_vunmap(vpssrk_buf->dmabuf, &map); + vpssrk_buf->vaddr = NULL; + } + if (vpssrk_buf->sgt) { + dma_buf_unmap_attachment(vpssrk_buf->dba, + vpssrk_buf->sgt, DMA_BIDIRECTIONAL); + vpssrk_buf->sgt = NULL; + } + dma_buf_detach(vpssrk_buf->dmabuf, vpssrk_buf->dba); + dma_buf_put(vpssrk_buf->dmabuf); + vpssrk_buf->dba = NULL; + } + kfree(stream_cfg->rkvpss_buff[i]); + stream_cfg->rkvpss_buff[i] = NULL; + } + mutex_unlock(&stream_cfg->freebuf_lock); + + return 0; +} + +/* when rockit stream off call */ +int rkvpss_rockit_free_stream_buf(struct rockit_rkvpss_cfg *input_cfg) +{ + struct rkvpss_stream *stream; + struct rkvpss_buffer *buf; + unsigned long lock_flags = 0; + + if (!input_cfg) + return -EINVAL; + stream = rkvpss_rockit_get_stream(input_cfg); + if (!stream) + return -EINVAL; + if (stream->streaming) + return 0; + + v4l2_dbg(1, rkvpss_debug, &stream->dev->v4l2_dev, + "%s stream:%d\n", __func__, stream->id); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + + if (stream->curr_buf) { + list_add_tail(&stream->curr_buf->queue, &stream->buf_queue); + if (stream->curr_buf == stream->next_buf) + stream->next_buf = NULL; + stream->curr_buf = NULL; + } + + if (stream->next_buf) { + list_add_tail(&stream->next_buf->queue, &stream->buf_queue); + stream->next_buf = NULL; + } + + while (!list_empty(&stream->buf_queue)) { + buf = list_first_entry(&stream->buf_queue, struct rkvpss_buffer, queue); + list_del(&buf->queue); + } + rkvpss_rockit_buf_state_clear(stream); + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + rkvpss_rockit_buf_free(stream); + + return 0; +} +EXPORT_SYMBOL(rkvpss_rockit_free_stream_buf); + +void rkvpss_rockit_dev_init(struct rkvpss_device *dev) +{ + struct rkvpss_stream_cfg *stream_cfg; + int i, j; + + if (rockit_vpss_cfg == NULL) { + rockit_vpss_cfg = kzalloc(sizeof(struct rockit_rkvpss_cfg), GFP_KERNEL); + if (rockit_vpss_cfg == NULL) + return; + } + rockit_vpss_cfg->vpss_num = dev->hw_dev->dev_num; + for (i = 0; i < rockit_vpss_cfg->vpss_num; i++) { + if (dev->hw_dev->vpss[i]) { + rockit_vpss_cfg->rkvpss_dev_cfg[i].vpss_name = dev->hw_dev->vpss[i]->name; + rockit_vpss_cfg->rkvpss_dev_cfg[i].vpss_dev = dev->hw_dev->vpss[i]; + + for (j = 0; j < vpss_outchn_max(dev->hw_dev->vpss_ver); j++) { + stream_cfg = &rockit_vpss_cfg->rkvpss_dev_cfg[i].rkvpss_stream_cfg[j]; + mutex_init(&stream_cfg->freebuf_lock); + } + } + } +} + +void rkvpss_rockit_dev_deinit(void) +{ + kfree(rockit_vpss_cfg); + rockit_vpss_cfg = NULL; +} + +void rkvpss_rockit_frame_start(struct rkvpss_device *dev) +{ + struct rkvpss_stream *stream; + int i; + + if (rockit_vpss_cfg == NULL) + return; + + for (i = 0; i <= RKVPSS_OUTPUT_CH1; i++) { + stream = &dev->stream_vdev.stream[i]; + if (!stream->streaming) + continue; + rkvpss_rockit_buf_done(stream, ROCKIT_DVBM_START, stream->curr_buf); + } +} + +void *rkvpss_rockit_function_register(void *function, int cmd) +{ + if (rockit_vpss_cfg == NULL) { + pr_err("rockit_vpss_cfg is null, function register failed\n"); + return NULL; + } + + switch (cmd) { + case ROCKIT_BUF_QUE: + function = rkvpss_rockit_buf_queue; + break; + case ROCKIT_MPIBUF_DONE: + rockit_vpss_cfg->rkvpss_rockit_mpibuf_done = function; + if (!rockit_vpss_cfg->rkvpss_rockit_mpibuf_done) + pr_err("get rkvpss_rockit_mpibuf_done failed\n"); + break; + default: + break; + } + return function; +} +EXPORT_SYMBOL(rkvpss_rockit_function_register); + +int rkvpss_rockit_get_vpssdev(char **name) +{ + int i = 0; + + if (rockit_vpss_cfg == NULL) { + pr_err("rockit_vpss_cfg is null\n"); + return -EINVAL; + } + + if (name == NULL) { + pr_err("the name is null\n"); + return -EINVAL; + } + + for (i = 0; i < rockit_vpss_cfg->vpss_num; i++) + name[i] = (char *)rockit_vpss_cfg->rkvpss_dev_cfg[i].vpss_name; + if (name[0] == NULL) + return -EINVAL; + else + return 0; +} +EXPORT_SYMBOL(rkvpss_rockit_get_vpssdev); + diff --git a/drivers/media/platform/rockchip/vpss/vpss_rockit.h b/drivers/media/platform/rockchip/vpss/vpss_rockit.h new file mode 100644 index 000000000000..98b0bf0dcaea --- /dev/null +++ b/drivers/media/platform/rockchip/vpss/vpss_rockit.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKVPSS_ROCKIT_H +#define _RKVPSS_ROCKIT_H + +#include +#include +#include + +extern struct rockit_vpss_ops rockit_vpss_ops; + +void rkvpss_rockit_dev_init(struct rkvpss_device *dev); +void rkvpss_rockit_dev_deinit(void); + +#endif + diff --git a/include/soc/rockchip/rockchip_rockit.h b/include/soc/rockchip/rockchip_rockit.h index f960615771ac..ab4a9b7b5387 100644 --- a/include/soc/rockchip/rockchip_rockit.h +++ b/include/soc/rockchip/rockchip_rockit.h @@ -13,6 +13,7 @@ #define ROCKIT_STREAM_NUM_MAX 12 #define ROCKIT_VICAP_NUM_MAX 6 +#define ROCKIT_VPSS_NUM_MAX 3 enum { RKISP_NORMAL_ONLINE, @@ -125,6 +126,46 @@ struct rockit_rkcif_cfg { int (*rkcif_rockit_mpibuf_done)(struct rockit_rkcif_cfg *rockit_cif_cfg); }; +struct rkvpss_stream_cfg { + struct rkvpss_rockit_buffer *rkvpss_buff[ROCKIT_BUF_NUM_MAX]; + int buff_id[ROCKIT_BUF_NUM_MAX]; + void *node; + int fps_cnt; + int dst_fps; + int cur_fps; + u64 old_time; + bool is_discard; + struct mutex freebuf_lock; +}; + +struct rkvpss_dev_cfg { + const char *vpss_name; + void *vpss_dev; + struct rkvpss_stream_cfg rkvpss_stream_cfg[ROCKIT_STREAM_NUM_MAX]; +}; + +struct rockit_rkvpss_cfg { + bool is_alloc; + bool is_empty; + bool is_qbuf; + char *current_name; + int *buff_id; + int mpi_id; + u32 nick_id; + u32 event; + int vpss_num; + u32 y_offset; + u32 uv_offset; + u32 vir_width; + void *node; + void *mpibuf; + void *vvi_dev[ROCKIT_VPSS_NUM_MAX]; + struct dma_buf *buf; + struct ISP_VIDEO_FRAMES frame; + struct rkvpss_dev_cfg rkvpss_dev_cfg[ROCKIT_VPSS_NUM_MAX]; + int (*rkvpss_rockit_mpibuf_done)(struct rockit_rkvpss_cfg *rockit_vpss_cfg); +}; + #if IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V32) || \ IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V33) || \ IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V35) @@ -191,4 +232,45 @@ static inline int rkisp_rockit_free_stream_buf(struct rockit_cfg *input_rockit_c #endif +#if IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_VPSS_V20) +void *rkvpss_rockit_function_register(void *function, int cmd); +int rkvpss_rockit_get_vpssdev(char **name); +int rkvpss_rockit_buf_queue(struct rockit_rkvpss_cfg *input_cfg); +int rkvpss_rockit_pause_stream(struct rockit_rkvpss_cfg *input_cfg); +int rkvpss_rockit_config_stream(struct rockit_rkvpss_cfg *input_cfg, + int width, int height, int wrap_line); +int rkvpss_rockit_resume_stream(struct rockit_rkvpss_cfg *input_cfg); +int rkvpss_rockit_free_stream_buf(struct rockit_rkvpss_cfg *input_cfg); +#else +static inline void *rkvpss_rockit_function_register(void *function, int cmd) +{ + return NULL; +} +static inline int rkvpss_rockit_get_vpssdev(char **name) +{ + return -EINVAL; +} +static inline int rkvpss_rockit_buf_queue(struct rockit_rkvpss_cfg *input_cfg) +{ + return -EINVAL; +} +static inline int rkvpss_rockit_pause_stream(struct rockit_rkvpss_cfg *input_cfg) +{ + return -EINVAL; +} +static inline int rkvpss_rockit_config_stream(struct rockit_rkvpss_cfg *input_cfg, + int width, int height, int wrap_line) +{ + return -EINVAL; +} +static inline int rkvpss_rockit_resume_stream(struct rockit_rkvpss_cfg *input_cfg) +{ + return -EINVAL; +} +static inline int rkvpss_rockit_free_stream_buf(struct rockit_rkvpss_cfg *input_cfg) +{ + return -EINVAL; +} +#endif + #endif diff --git a/include/uapi/linux/rk-vpss-config.h b/include/uapi/linux/rk-vpss-config.h index 42ec77848186..530197b17c7c 100644 --- a/include/uapi/linux/rk-vpss-config.h +++ b/include/uapi/linux/rk-vpss-config.h @@ -28,6 +28,7 @@ */ #define RKVPSS_OUT_V10_MAX 4 +#define RKVPSS_OUT_V20_MAX 6 /******vpss(online mode) v4l2 ioctl***************************/ /* set before VIDIOC_S_FMT if dynamically changing output resolution */ @@ -50,6 +51,20 @@ _IOR('V', BASE_VIDIOC_PRIVATE + 5, struct rkvpss_cmsc_cfg) #define RKVPSS_CMD_SET_CMSC \ _IOW('V', BASE_VIDIOC_PRIVATE + 6, struct rkvpss_cmsc_cfg) +#define RKVPSS_CMD_GET_WRAP_LINE \ + _IOR('V', BASE_VIDIOC_PRIVATE + 7, int *) +#define RKVPSS_CMD_SET_WRAP_LINE \ + _IOW('V', BASE_VIDIOC_PRIVATE + 8, int *) + +#define RKVPSS_CMD_GET_ALPHA \ + _IOR('V', BASE_VIDIOC_PRIVATE + 9, int *) +#define RKVPSS_CMD_SET_ALPHA \ + _IOW('V', BASE_VIDIOC_PRIVATE + 10, int *) + +#define RKVPSS_CMD_GET_AVG_SCL_DOWN \ + _IOR('V', BASE_VIDIOC_PRIVATE + 11, int *) +#define RKVPSS_CMD_SET_AVG_SCL_DOWN \ + _IOW('V', BASE_VIDIOC_PRIVATE + 12, int *) /******vpss(offline mode) independent video ioctl****************/ #define RKVPSS_CMD_MODULE_SEL \ @@ -72,6 +87,20 @@ #define RKVPSS_CMD_STREAM_ATTACH_INFO \ _IOW('V', BASE_VIDIOC_PRIVATE + 56, int) + +/****** vpss(offline mode rockit) independent ioctl*************/ +#define RKVPSS_CMD_OPEN \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 101, int *) + +#define RKVPSS_CMD_RELEASE \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 102, int *) + +#define RKVPSS_CMD_WRAP_DVBM_INIT \ + _IOW('V', BASE_VIDIOC_PRIVATE + 103, struct rkvpss_frame_cfg) + +#define RKVPSS_CMD_WRAP_DVBM_DEINIT \ + _IOW('V', BASE_VIDIOC_PRIVATE + 104, int *) + /********************************************************************/ /* struct rkvpss_mirror_flip @@ -128,15 +157,19 @@ struct rkvpss_cmsc_win { /* struct rkvpss_cmsc_cfg * cover and mosaic configure * win: priacy mask window - * mosaic_block: Mosaic block size, 0:8x8 1:16x16 2:32x32 3:64x64, share for all windows + * mosaic_block: Mosaic block size, 0:8x8 1:16x16 2:32x32 3:64x64, + * 4: 128x128 (only for rv1126b) share for all windows * width_ro: vpss full resolution. * height_ro: vpss full resolution. + * reuse_ch : (only for rv1126b) ch4 and ch5 use ch0 or ch1 or ch2 or ch3 params, + * -1:disable、 0:ch0、 1:ch1、 2:ch2、3:ch3 */ struct rkvpss_cmsc_cfg { struct rkvpss_cmsc_win win[RKVPSS_CMSC_WIN_MAX]; unsigned int mosaic_block; unsigned int width_ro; unsigned int height_ro; + int reuse_ch; } __attribute__ ((packed)); /* struct rkisp_aspt_cfg _____background____ @@ -164,13 +197,30 @@ struct rkvpss_aspt_cfg { unsigned char enable; } __attribute__ ((packed)); +/* struct rkvpss_wrap_cfg + * vpss to encoder wrap + * only channel0 or channel1 support wrap + * vpss online wrap_line need to bo greater than or equal to 1/4 out height + */ +struct rkvpss_wrap_cfg { + int enable; + int wrap_line; + int buffer_size; +} __attribute__ ((packed)); + /********************************************************************/ +/* + * V1: ch0 - ch3 + * V2: ch0 - ch5 + */ enum { RKVPSS_OUTPUT_CH0 = 0, RKVPSS_OUTPUT_CH1, RKVPSS_OUTPUT_CH2, RKVPSS_OUTPUT_CH3, + RKVPSS_OUTPUT_CH4, + RKVPSS_OUTPUT_CH5, RKVPSS_OUTPUT_MAX, }; @@ -187,16 +237,20 @@ struct rkvpss_module_sel { /* struct rkvpss_input_cfg * input configuration of image * - * width: width of input image, range: 32~4672 - * height: height of input image, range: 32~3504 - * stride: virtual width of input image, 16 align. auto calculate according to width and format if 0. - * format: V4L2_PIX_FMT_NV12/V4L2_PIX_FMT_NV16/V4L2_PIX_FMT_RGB565/V4L2_PIX_FMT_RGB24/V4L2_PIX_FMT_XBGR32/ - * V4L2_PIX_FMT_NV61/V4L2_PIX_FMT_NV21/V4L2_PIX_FMT_RGB565X/V4L2_PIX_FMT_BGR24/V4L2_PIX_FMT_XRGB32/ - * V4L2_PIX_FMT_RGBX32/V4L2_PIX_FMT_BGRX32 + * width: width of input image, range: 32~4672(rk3576) 32~4096(rv1126b) + * height: height of input image, range: 32~3504(rk3576) 32~3072(rv1126b) + * stride: virtual width of input image, 16 align. auto calculate according to width and + * format if 0. + * format: V4L2_PIX_FMT_NV12/V4L2_PIX_FMT_NV16/V4L2_PIX_FMT_RGB565/V4L2_PIX_FMT_RGB24/ + * V4L2_PIX_FMT_XBGR32/ + * V4L2_PIX_FMT_NV61/V4L2_PIX_FMT_NV21/V4L2_PIX_FMT_RGB565X/V4L2_PIX_FMT_BGR24/ + * V4L2_PIX_FMT_XRGB32/ V4L2_PIX_FMT_RGBX32/V4L2_PIX_FMT_BGRX32 * V4L2_PIX_FMT_FBC0/V4L2_PIX_FMT_FBC2/V4L2_PIX_FMT_FBC4 for rkfbcd * V4L2_PIX_FMT_TILE420/V4L2_PIX_FMT_TILE422 for tile * buf_fd: dmabuf fd of input image buf * rotate: 0:rotate0 1:rotate90 2:rotate180; 3:rotate270, note:only tile input support rotate + * rotate_90: (only for rv1126b) 1:raster rotate90; + * onte: only support input fmt: NV12/NV21/NV16/NV61/UYVY/YUYV */ struct rkvpss_input_cfg { int width; @@ -205,6 +259,8 @@ struct rkvpss_input_cfg { int format; int buf_fd; int rotate; + int rotate_90; + struct dma_buf *dmabuf; } __attribute__ ((packed)); /* struct rkvpss_output_cfg __________________ @@ -215,19 +271,24 @@ struct rkvpss_input_cfg { * crop_v_offs: vertical offset of crop, 2align | | |scl___| | | * crop_width: crop output width, 2align | |crop______| | * crop_height: crop output height, 2align |input_____________| - * scl_width: scale width. CH0 1~8 scale range. CH1/CH2/CH3 1~32 scale range. CH2/CH3 max 1080p with scale. - * scl_height: scale height. CH0 1~6 scale range. CH1/CH2/CH3 1~32 scale range. CH2/CH3 max 1080p with scale. - * stride: virtual width of output image, 16 align. auto calculate according to width and format if 0. + * scl_width: scale width. CH0 1~8 scale range. CH1/CH2/CH3 1~32 scale range. + * CH2/CH3 max 1080p with scale. + * scl_height: scale height. CH0 1~6 scale range. CH1/CH2/CH3 1~32 scale range. + * CH2/CH3 max 1080p with scale. + * stride: virtual width of output image, 16 align. + * auto calculate according to width and format if 0. * format: V4L2_PIX_FMT_NV12/V4L2_PIX_FMT_NV16/V4L2_PIX_FMT_GREY/V4L2_PIX_FMT_UYVY/ * V4L2_PIX_FMT_VYUY/V4L2_PIX_FMT_NV21/V4L2_PIX_FMT_NV61 for all channel. * NOTE:V,LSB is for all channel - * V4L2_PIX_FMT_RGB565/V4L2_PIX_FMT_RGB24/V4L2_PIX_FMT_XBGR32/V4L2_PIX_FMT_RGB565X/V4L2_PIX_FMT_BGR24/ - * V4L2_PIX_FMT_XRGB32 only for RKVPSS_OUTPUT_CH1. + * V4L2_PIX_FMT_RGB565/V4L2_PIX_FMT_RGB24/V4L2_PIX_FMT_XBGR32/V4L2_PIX_FMT_RGB565X/ + * V4L2_PIX_FMT_BGR24/V4L2_PIX_FMT_XRGB32 only for RKVPSS_OUTPUT_CH1. * V4L2_PIX_FMT_TILE420/V4L2_PIX_FMT_TILE422 for tile, ch0 or ch1 support tile * flip: flip enable * buf_fd: dmabuf fd of output image buf * cmsc: cover and mosaic configure * aspt: aspective ratio for image background color filling + * avg_scl_down: (only for rv1126b) CH0 and CH2 can use average scale down, 1-16 scale range + * alpha: (only for rv1126b) only use for V4L2_PIX_FMT_XBGR32 and V4L2_PIX_FMT_XRGB32 (0-0xff) */ struct rkvpss_output_cfg { int enable; @@ -243,9 +304,13 @@ struct rkvpss_output_cfg { int format; int flip; int buf_fd; + int avg_scl_down; + unsigned int alpha; struct rkvpss_cmsc_cfg cmsc; struct rkvpss_aspt_cfg aspt; + struct dma_buf *dmabuf; + struct rkvpss_wrap_cfg wrap; } __attribute__ ((packed)); #define RKVPSS_DEV_ID_MAX 128 @@ -284,6 +349,7 @@ struct rkvpss_buf_info { int buf_cnt; int buf_size[RKVPSS_BUF_MAX]; int buf_fd[RKVPSS_BUF_MAX]; + struct dma_buf *dmabufs[RKVPSS_BUF_MAX]; } __attribute__ ((packed)); struct rkvpss_frame_info { @@ -309,4 +375,6 @@ struct rkvpss_frame_info { __u32 isp_reg[6144]; } __attribute__ ((packed)); +long vpss_rockit_action(int *file_id, unsigned int cmd, void *arg); + #endif