From d7466155ba7a25b145b006c2c782d64f533cf753 Mon Sep 17 00:00:00 2001 From: Hu Kejun Date: Wed, 18 Dec 2024 09:31:18 +0800 Subject: [PATCH] media: rockchip: add aiisp driver Signed-off-by: Hu Kejun Change-Id: I11459fd2862411528c1fce4eb6fdd174c42325d8 --- drivers/media/platform/rockchip/Kconfig | 1 + drivers/media/platform/rockchip/Makefile | 1 + drivers/media/platform/rockchip/aiisp/Kconfig | 11 + .../media/platform/rockchip/aiisp/Makefile | 4 + drivers/media/platform/rockchip/aiisp/aiisp.c | 1685 +++++++++++++++++ drivers/media/platform/rockchip/aiisp/aiisp.h | 179 ++ drivers/media/platform/rockchip/aiisp/dev.c | 236 +++ drivers/media/platform/rockchip/aiisp/hw.c | 446 +++++ drivers/media/platform/rockchip/aiisp/hw.h | 80 + drivers/media/platform/rockchip/aiisp/regs.h | 136 ++ .../media/platform/rockchip/aiisp/version.h | 18 + include/uapi/linux/rk-aiisp-config.h | 140 ++ 12 files changed, 2937 insertions(+) create mode 100644 drivers/media/platform/rockchip/aiisp/Kconfig create mode 100644 drivers/media/platform/rockchip/aiisp/Makefile create mode 100644 drivers/media/platform/rockchip/aiisp/aiisp.c create mode 100644 drivers/media/platform/rockchip/aiisp/aiisp.h create mode 100644 drivers/media/platform/rockchip/aiisp/dev.c create mode 100644 drivers/media/platform/rockchip/aiisp/hw.c create mode 100644 drivers/media/platform/rockchip/aiisp/hw.h create mode 100644 drivers/media/platform/rockchip/aiisp/regs.h create mode 100644 drivers/media/platform/rockchip/aiisp/version.h create mode 100644 include/uapi/linux/rk-aiisp-config.h diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig index 87ecf195e4fb..51db7d94216f 100644 --- a/drivers/media/platform/rockchip/Kconfig +++ b/drivers/media/platform/rockchip/Kconfig @@ -2,6 +2,7 @@ comment "Rockchip media platform drivers" +source "drivers/media/platform/rockchip/aiisp/Kconfig" source "drivers/media/platform/rockchip/cif/Kconfig" source "drivers/media/platform/rockchip/fec/Kconfig" source "drivers/media/platform/rockchip/flexbus_cif/Kconfig" diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile index d3f3fcfe4f6c..9aec3025aa45 100644 --- a/drivers/media/platform/rockchip/Makefile +++ b/drivers/media/platform/rockchip/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-y += aiisp/ obj-y += cif/ obj-y += fec/ obj-y += flexbus_cif/ diff --git a/drivers/media/platform/rockchip/aiisp/Kconfig b/drivers/media/platform/rockchip/aiisp/Kconfig new file mode 100644 index 000000000000..bbb7c1d93c06 --- /dev/null +++ b/drivers/media/platform/rockchip/aiisp/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +config VIDEO_ROCKCHIP_AIISP + tristate "Rockchip AIISP driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_ROCKCHIP || COMPILE_TEST + select VIDEOBUF2_CMA_SG + select V4L2_FWNODE + default n + help + Support for AIISP on the rockchip SoC. diff --git a/drivers/media/platform/rockchip/aiisp/Makefile b/drivers/media/platform/rockchip/aiisp/Makefile new file mode 100644 index 000000000000..7948872241bf --- /dev/null +++ b/drivers/media/platform/rockchip/aiisp/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_ROCKCHIP_AIISP) += video_rkaiisp.o + +video_rkaiisp-objs += hw.o dev.o aiisp.o diff --git a/drivers/media/platform/rockchip/aiisp/aiisp.c b/drivers/media/platform/rockchip/aiisp/aiisp.c new file mode 100644 index 000000000000..7c84934ec6e5 --- /dev/null +++ b/drivers/media/platform/rockchip/aiisp/aiisp.c @@ -0,0 +1,1685 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */ + +#include +#include +#include "regs.h" +#include "aiisp.h" + +#define RKAIISP_REQ_BUFS_MIN 2 +#define RKAIISP_REQ_BUFS_MAX 8 + +#define CEIL_DOWN(x, y) (((x) + ((y) - 1)) / (y)) +#define FLOOR_BY(v, r) (((v) / (r)) * (r)) +#define CEIL_BY(v, r) FLOOR_BY(((v) + (r) - 1), (r)) + +enum AIISP_OP_MODE { + AIISP_RUN_MODE_SINGLE, + AIISP_RUN_MODE_COMBO +}; + +enum AIISP_MODE { + AIISP_MODE_MODE0, + AIISP_MODE_MODE1 +}; + +enum AIISP_LEVEL_MODE0 { + AIISP_LEVEL_MODE_18x18x1x1, + AIISP_LEVEL_MODE_18x18x3x3G18, + AIISP_LEVEL_MODE_18x8x3x3, + AIISP_LEVEL_MODE_18x4x3x3 +}; + +enum AIISP_LEVEL_MODE1 { + AIISP_LEVEL_MODE_24x24x1x1, + AIISP_LEVEL_MODE_24x24x3x3G12, + AIISP_LEVEL_MODE_24x15x3x3, + AIISP_LEVEL_MODE_24x4x3x3, + AIISP_LEVEL_MODE_24x24x3x3G8 +}; + +enum AIISP_RD_CHN_DATA_MODE { + AIISP_CHN_DATA_MODE_0_8BITS, + AIISP_CHN_DATA_MODE_1_11BITS, + AIISP_CHN_DATA_MODE_2_16BITS_NAR +}; + +enum AIISP_OUT_MODE { + AIISP_OUT_MODE_BYPASS, + AIISP_OUT_MODE_ADD_MERGE, + AIISP_OUT_MODE_DIFF_MERGE +}; + +enum AIISP_M0_MERGE_MODE { + AIISP_M0_MERGE_MODE_ADD_MERGE, + AIISP_M0_MERGE_MODE_DIFF_MERGE +}; + +enum AIISP_SLICE_MODE { + AIISP_SLICE_MODE_344, + AIISP_SLICE_MODE_256, + AIISP_SLICE_MODE_320 +}; + +enum AIISP_CHN_MODE { + AIISP_CHN_MODE_BYPASS, + AIISP_CHN_MODE_UPSAMPLE, + AIISP_CHN_MODE_SPACE2DEPTH +}; + +enum AIISP_CHN_NUMBER { + AIISP_CHN_NUMBER_8, + AIISP_CHN_NUMBER_15 +}; + +// mi chn data mode +static int bits_tab[3] = {8, 11, 16}; + +// mi channels mode bypass, upsample, s2d +static int num_tab[3] = {1, 2, 1}; +static int den_tab[3] = {1, 1, 2}; + +static int ext_tab[2][3] = { + {8, 10, 8}, + {8, 16, 8} +}; + +// dim 0, mode; dim 1, level mode +static int channels_lut[2][5] = { + {18, 18, 8, 4, -1}, + {24, 24, 15, 4, 24} +}; + +// mode and op_mode +static int lst_slice_align_tab[2][2] = { + {8, 8}, + {6, 12} +}; + +// dim 0: mode; dim 1: mi chn +static int mi_chns_tab[2][7] = { + {8, 1, 1, 1, 1, 1, 1}, + {15, 1, 1, 1, 1, 1, 1} +}; + +static void rkaiisp_update_regs(struct rkaiisp_device *aidev, u32 start, u32 end) +{ + struct rkaiisp_hw_dev *hw_dev = aidev->hw_dev; + void __iomem *base = hw_dev->base_addr; + u32 i; + + if (end > RKAIISP_SW_REG_SIZE - 4) { + dev_err(aidev->dev, "%s out of range\n", __func__); + return; + } + for (i = start; i <= end; i += 4) { + u32 *val = aidev->sw_base_addr + i; + u32 *flag = aidev->sw_base_addr + i + RKAIISP_SW_REG_SIZE; + + if (*flag == SW_REG_CACHE) + writel(*val, base + i); + } +} + +void rkaiisp_update_list_reg(struct rkaiisp_device *aidev) +{ + rkaiisp_update_regs(aidev, AIISP_MI_CTRL, AIISP_MI_CTRL); + rkaiisp_update_regs(aidev, AIISP_MI_SLICE_CTRL, AIISP_MI_MANUAL_CTRL); + rkaiisp_update_regs(aidev, AIISP_MI_CORE_WIDTH, AIISP_MI_CORE_HEIGHT); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH0_CTRL, AIISP_MI_RD_CH0_BASE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH0_HEIGHT, AIISP_MI_RD_CH0_STRIDE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH1_CTRL, AIISP_MI_RD_CH1_BASE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH1_HEIGHT, AIISP_MI_RD_CH1_STRIDE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH2_CTRL, AIISP_MI_RD_CH2_BASE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH2_HEIGHT, AIISP_MI_RD_CH2_STRIDE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH3_CTRL, AIISP_MI_RD_CH3_BASE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH3_HEIGHT, AIISP_MI_RD_CH3_STRIDE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH4_CTRL, AIISP_MI_RD_CH4_BASE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH4_HEIGHT, AIISP_MI_RD_CH4_STRIDE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH5_CTRL, AIISP_MI_RD_CH5_BASE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH5_HEIGHT, AIISP_MI_RD_CH5_STRIDE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH6_CTRL, AIISP_MI_RD_CH6_BASE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_CH6_HEIGHT, AIISP_MI_RD_CH6_STRIDE); + rkaiisp_update_regs(aidev, AIISP_MI_RD_KWT_CTRL, AIISP_MI_RD_KWT_STRIDE); + rkaiisp_update_regs(aidev, AIISP_MI_WR_CTRL, AIISP_MI_WR_CTRL); + rkaiisp_update_regs(aidev, AIISP_MI_CHN0_WR_CRTL, AIISP_MI_CHN0_WR_STRIDE); + + rkaiisp_update_regs(aidev, AIISP_CORE_CTRL, AIISP_CORE_CTRL); + rkaiisp_update_regs(aidev, AIISP_CORE_LEVEL_CTRL0, AIISP_CORE_LEVEL_CTRL3); + rkaiisp_update_regs(aidev, AIISP_CORE_OUT_CTRL, AIISP_CORE_NOISE_LMT); + rkaiisp_update_regs(aidev, AIISP_CORE_COMP0, AIISP_CORE_COMP16); + rkaiisp_update_regs(aidev, AIISP_CORE_DECOMP0, AIISP_CORE_DECOMP16); + + rkaiisp_write(aidev, AIISP_MI_IMSC, AIISP_MI_ISR_ALL, true); + rkaiisp_write(aidev, AIISP_MI_WR_INIT, AIISP_MI_CHN0SELF_FORCE_UPD, true); + rkaiisp_write(aidev, AIISP_MI_RD_START, AIISP_MI_RD_START_EN, true); +} + +static void rkaiisp_dumpreg(struct rkaiisp_device *aidev, u32 start, u32 end) +{ + u32 i, val; + + if (end > RKAIISP_SW_REG_SIZE - 4) { + dev_err(aidev->dev, "%s out of range\n", __func__); + return; + } + + for (i = start; i <= end; i += 4) { + val = rkaiisp_read(aidev, i, false); + dev_info(aidev->dev, "%08x: %08x\n", i, val); + } +} + +static void rkaiisp_dump_list_reg(struct rkaiisp_device *aidev) +{ + dev_info(aidev->dev, "frame_id: %d, run_idx: %d\n", + aidev->frame_id, aidev->run_idx); + + rkaiisp_dumpreg(aidev, AIISP_CORE_CTRL, AIISP_CORE_NOISE_LMT); + rkaiisp_dumpreg(aidev, AIISP_CORE_COMP0, AIISP_CORE_DECOMP16); + rkaiisp_dumpreg(aidev, AIISP_MI_HURRY_CTRL, AIISP_MI_ISR); + rkaiisp_dumpreg(aidev, AIISP_MI_RD_START, AIISP_MI_CORE_HEIGHT); + rkaiisp_dumpreg(aidev, AIISP_MI_RD_CH0_CTRL, AIISP_MI_RD_CH0_STRIDE); + rkaiisp_dumpreg(aidev, AIISP_MI_RD_CH1_CTRL, AIISP_MI_RD_CH1_STRIDE); + rkaiisp_dumpreg(aidev, AIISP_MI_RD_CH2_CTRL, AIISP_MI_RD_CH2_STRIDE); + rkaiisp_dumpreg(aidev, AIISP_MI_RD_CH3_CTRL, AIISP_MI_RD_CH3_STRIDE); + rkaiisp_dumpreg(aidev, AIISP_MI_RD_CH4_CTRL, AIISP_MI_RD_CH4_STRIDE); + rkaiisp_dumpreg(aidev, AIISP_MI_RD_CH5_CTRL, AIISP_MI_RD_CH5_STRIDE); + rkaiisp_dumpreg(aidev, AIISP_MI_RD_CH6_CTRL, AIISP_MI_RD_CH6_STRIDE); + rkaiisp_dumpreg(aidev, AIISP_MI_RD_KWT_CTRL, AIISP_MI_RD_KWT_STRIDE); + rkaiisp_dumpreg(aidev, AIISP_MI_WR_CTRL, AIISP_MI_CHN0_WR_STRIDE); +} + +#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE +static void rkaiisp_init_dummy_vb2(struct rkaiisp_device *dev, + struct rkaiisp_dummy_buffer *buf) +{ + unsigned long attrs = 0; + + memset(&buf->vb2_queue, 0, sizeof(buf->vb2_queue)); + memset(&buf->vb, 0, sizeof(buf->vb)); + buf->vb2_queue.gfp_flags = GFP_KERNEL | GFP_DMA32; + buf->vb2_queue.dma_dir = DMA_BIDIRECTIONAL; + if (dev->hw_dev->is_dma_contig) + attrs |= DMA_ATTR_FORCE_CONTIGUOUS; + buf->vb2_queue.dma_attrs = attrs; + buf->vb.vb2_queue = &buf->vb2_queue; +} + +static int rkaiisp_allow_buffer(struct rkaiisp_device *aidev, + struct rkaiisp_dummy_buffer *buf) +{ + const struct vb2_mem_ops *mem_ops = aidev->mem_ops; + struct sg_table *sg_tbl; + void *mem_priv; + int ret = 0; + + if (!buf->size) { + ret = -EINVAL; + goto err; + } + + rkaiisp_init_dummy_vb2(aidev, buf); + buf->size = PAGE_ALIGN(buf->size); + mem_priv = mem_ops->alloc(&buf->vb, aidev->hw_dev->dev, buf->size); + if (IS_ERR_OR_NULL(mem_priv)) { + ret = -ENOMEM; + goto err; + } + + buf->mem_priv = mem_priv; + sg_tbl = (struct sg_table *)mem_ops->cookie(&buf->vb, mem_priv); + buf->dma_addr = sg_dma_address(sg_tbl->sgl); + mem_ops->prepare(mem_priv); + + buf->vaddr = mem_ops->vaddr(&buf->vb, mem_priv); + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "%s buf:%pad size:%d\n", __func__, + &buf->dma_addr, buf->size); + + return ret; +err: + dev_err(aidev->dev, "%s failed ret:%d\n", __func__, ret); + return ret; +} +#else +static int rkaiisp_allow_buffer(struct rkaiisp_device *aidev, + struct rkaiisp_dummy_buffer *buf) +{ + const struct vb2_mem_ops *mem_ops = aidev->mem_ops; + struct sg_table *sg_tbl; + unsigned long attrs = 0; + void *mem_priv; + int ret = 0; + + if (!buf->size) { + ret = -EINVAL; + goto err; + } + + if (aidev->hw_dev->is_dma_contig) + attrs |= DMA_ATTR_FORCE_CONTIGUOUS; + buf->size = PAGE_ALIGN(buf->size); + mem_priv = mem_ops->alloc(aidev->hw_dev->dev, attrs, buf->size, + DMA_BIDIRECTIONAL, GFP_KERNEL | GFP_DMA32); + if (IS_ERR_OR_NULL(mem_priv)) { + ret = -ENOMEM; + goto err; + } + + buf->mem_priv = mem_priv; + sg_tbl = (struct sg_table *)mem_ops->cookie(mem_priv); + buf->dma_addr = sg_dma_address(sg_tbl->sgl); + mem_ops->prepare(mem_priv); + + buf->vaddr = mem_ops->vaddr(mem_priv); + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "%s buf:%pad size:%d\n", __func__, + &buf->dma_addr, buf->size); + + return ret; +err: + dev_err(aidev->dev, "%s failed ret:%d\n", __func__, ret); + return ret; +} +#endif + +static void rkaiisp_free_buffer(struct rkaiisp_device *aidev, + struct rkaiisp_dummy_buffer *buf) +{ + const struct vb2_mem_ops *mem_ops = aidev->mem_ops; + + if (buf && buf->mem_priv) { + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "%s buf:%pad size:%d\n", __func__, + &buf->dma_addr, buf->size); + + mem_ops->put(buf->mem_priv); + buf->size = 0; + buf->vaddr = NULL; + buf->mem_priv = NULL; + } +} + +static void rkaiisp_detach_dmabuf(struct rkaiisp_device *aidev, + struct rkaiisp_dummy_buffer *buffer) +{ + if (buffer->dma_fd >= 0) { + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "%s buf:%pad size:%d\n", __func__, + &buffer->dma_addr, buffer->size); + dma_buf_unmap_attachment(buffer->dba, buffer->sgt, DMA_BIDIRECTIONAL); + dma_buf_detach(buffer->dmabuf, buffer->dba); + dma_buf_put(buffer->dmabuf); + memset(buffer, 0, sizeof(struct rkaiisp_dummy_buffer)); + buffer->dma_fd = -1; + } +} + +static void rkaiisp_free_tempbuf(struct rkaiisp_device *aidev) +{ + rkaiisp_free_buffer(aidev, &aidev->temp_buf[0]); + rkaiisp_free_buffer(aidev, &aidev->temp_buf[1]); +} + +static int rkaiisp_free_pool(struct rkaiisp_device *aidev) +{ + struct rkaiisp_ispbuf_info *ispbuf = &aidev->ispbuf; + int i; + + for (i = 0; i < ispbuf->bnr_buf.iir.buf_cnt; i++) + rkaiisp_detach_dmabuf(aidev, &aidev->iirbuf[i]); + + for (i = 0; i < ispbuf->bnr_buf.u.v35.aipre_gain.buf_cnt; i++) + rkaiisp_detach_dmabuf(aidev, &aidev->aiprebuf[i]); + + for (i = 0; i < ispbuf->bnr_buf.u.v35.vpsl.buf_cnt; i++) + rkaiisp_detach_dmabuf(aidev, &aidev->vpslbuf[i]); + + for (i = 0; i < ispbuf->bnr_buf.u.v35.aiisp.buf_cnt; i++) + rkaiisp_detach_dmabuf(aidev, &aidev->aiispbuf[i]); + + rkaiisp_free_tempbuf(aidev); + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "free buf poll\n"); + return 0; +} + +static int rkaiisp_attach_dmabuf(struct rkaiisp_device *aidev, + struct rkaiisp_dummy_buffer *buffer) +{ + struct dma_buf_attachment *dba; + struct dma_buf *dmabuf; + struct sg_table *sgt; + int ret = 0; + + dmabuf = dma_buf_get(buffer->dma_fd); + if (IS_ERR_OR_NULL(dmabuf)) { + v4l2_err(&aidev->v4l2_dev, "invalid dmabuf fd:%d", buffer->dma_fd); + return -EINVAL; + } + buffer->dmabuf = dmabuf; + dba = dma_buf_attach(dmabuf, aidev->hw_dev->dev); + if (IS_ERR(dba)) { + dma_buf_put(dmabuf); + ret = PTR_ERR(dba); + return ret; + } + buffer->dba = dba; + sgt = dma_buf_map_attachment(dba, DMA_BIDIRECTIONAL); + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + dma_buf_detach(dmabuf, dba); + dma_buf_put(dmabuf); + return ret; + } + buffer->sgt = sgt; + buffer->dma_addr = sg_dma_address(sgt->sgl); + buffer->size = sg_dma_len(sgt->sgl); + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "%s buf:%pad size:%d\n", __func__, + &buffer->dma_addr, buffer->size); + + return ret; +} + +static void rkaiisp_calc_outbuf_size(struct rkaiisp_device *aidev, u32 raw_hgt, u32 raw_wid) +{ + int i; + + if (aidev->model_mode == SINGLEX2_MODE) { + for (i = 0; i < RKAIISP_PYRAMID_LAYER_NUM; i++) { + if (i == 0) { + aidev->outbuf_size[i * 2 + 0].height = raw_hgt; + aidev->outbuf_size[i * 2 + 0].width = raw_wid; + aidev->outbuf_size[i * 2 + 0].channel = 1; + aidev->outbuf_size[i * 2 + 0].stride = raw_wid; + aidev->outbuf_size[i * 2 + 1].height = raw_hgt / 2; + aidev->outbuf_size[i * 2 + 1].width = raw_wid / 2; + aidev->outbuf_size[i * 2 + 1].channel = 15; + aidev->outbuf_size[i * 2 + 1].stride = raw_wid * 15; + } else { + aidev->outbuf_size[i * 2 + 0].height = raw_hgt / 2; + aidev->outbuf_size[i * 2 + 0].width = raw_wid / 2; + aidev->outbuf_size[i * 2 + 0].channel = 15; + aidev->outbuf_size[i * 2 + 0].stride = raw_wid * 15; + aidev->outbuf_size[i * 2 + 1].height = raw_hgt / 2; + aidev->outbuf_size[i * 2 + 1].width = raw_wid / 2; + aidev->outbuf_size[i * 2 + 1].channel = 15; + aidev->outbuf_size[i * 2 + 1].stride = raw_wid * 15; + } + + raw_hgt = CEIL_BY(CEIL_DOWN(raw_hgt, 2), 2); + raw_wid = CEIL_BY(CEIL_DOWN(raw_wid, 2), 2); + } + } else { + for (i = 0; i < RKAIISP_PYRAMID_LAYER_NUM; i++) { + if (i == 0) { + aidev->outbuf_size[i].height = raw_hgt; + aidev->outbuf_size[i].width = raw_wid; + aidev->outbuf_size[i].channel = 1; + aidev->outbuf_size[i].stride = raw_wid; + } else { + aidev->outbuf_size[i].height = raw_hgt / 2; + aidev->outbuf_size[i].width = raw_wid / 2; + aidev->outbuf_size[i].channel = 15; + aidev->outbuf_size[i].stride = raw_wid * 15; + } + + raw_hgt = CEIL_BY(CEIL_DOWN(raw_hgt, 2), 2); + raw_wid = CEIL_BY(CEIL_DOWN(raw_wid, 2), 2); + } + } +} + +static int rkaiisp_init_pool(struct rkaiisp_device *aidev, struct rkaiisp_ispbuf_info *ispbuf) +{ + int i, ret = 0; + u32 stride; + + for (i = 0; i < ispbuf->bnr_buf.iir.buf_cnt; i++) { + aidev->iirbuf[i].dma_fd = ispbuf->bnr_buf.iir.buf_fd[i]; + ret = rkaiisp_attach_dmabuf(aidev, &aidev->iirbuf[i]); + + if (ret) { + rkaiisp_free_pool(aidev); + v4l2_err(&aidev->v4l2_dev, "attach iirbuf failed: %d\n", ret); + return -EINVAL; + } + } + for (i = 0; i < ispbuf->bnr_buf.u.v35.aipre_gain.buf_cnt; i++) { + aidev->aiprebuf[i].dma_fd = ispbuf->bnr_buf.u.v35.aipre_gain.buf_fd[i]; + ret = rkaiisp_attach_dmabuf(aidev, &aidev->aiprebuf[i]); + + if (ret) { + rkaiisp_free_pool(aidev); + v4l2_err(&aidev->v4l2_dev, "attach aiprebuf failed: %d\n", ret); + return -EINVAL; + } + } + for (i = 0; i < ispbuf->bnr_buf.u.v35.vpsl.buf_cnt; i++) { + aidev->vpslbuf[i].dma_fd = ispbuf->bnr_buf.u.v35.vpsl.buf_fd[i]; + ret = rkaiisp_attach_dmabuf(aidev, &aidev->vpslbuf[i]); + + if (ret) { + rkaiisp_free_pool(aidev); + v4l2_err(&aidev->v4l2_dev, "attach vpslbuf failed: %d\n", ret); + return -EINVAL; + } + } + for (i = 0; i < ispbuf->bnr_buf.u.v35.aiisp.buf_cnt; i++) { + aidev->aiispbuf[i].dma_fd = ispbuf->bnr_buf.u.v35.aiisp.buf_fd[i]; + ret = rkaiisp_attach_dmabuf(aidev, &aidev->aiispbuf[i]); + + if (ret) { + rkaiisp_free_pool(aidev); + v4l2_err(&aidev->v4l2_dev, "attach dmabuf failed: %d\n", ret); + return -EINVAL; + } + } + + stride = ((ispbuf->iir_width + 1) / 2 * 15 * 11 + 7) >> 3; + aidev->temp_buf[0].size = stride * (ispbuf->iir_height + 1) / 2; + aidev->temp_buf[1].size = aidev->temp_buf[0].size; + ret = rkaiisp_allow_buffer(aidev, &aidev->temp_buf[0]); + ret |= rkaiisp_allow_buffer(aidev, &aidev->temp_buf[1]); + if (ret) + rkaiisp_free_pool(aidev); + + aidev->ispbuf = *ispbuf; + aidev->outbuf_idx = 0; + + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, "init buf poll\n"); + return ret; +} + +int rkaiisp_queue_ispbuf(struct rkaiisp_device *aidev, struct rkisp_aiisp_st *idxbuf) +{ + struct kfifo *fifo = &aidev->idxbuf_kfifo; + struct rkaiisp_hw_dev *hw_dev = aidev->hw_dev; + unsigned long flags = 0; + int ret = 0; + + spin_lock_irqsave(&hw_dev->hw_lock, flags); + if (!aidev->streamon) { + spin_unlock_irqrestore(&hw_dev->hw_lock, flags); + v4l2_err(&aidev->v4l2_dev, + "rkaiisp device is not stream on\n"); + return -EINVAL; + } + + if (!kfifo_is_full(fifo)) + kfifo_in(fifo, idxbuf, sizeof(struct rkisp_aiisp_st)); + else + v4l2_err(&aidev->v4l2_dev, "fifo is full\n"); + + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "idxbuf fifo in: %d\n", idxbuf->sequence); + + if (hw_dev->is_idle) { + hw_dev->cur_dev_id = aidev->dev_id; + hw_dev->is_idle = false; + spin_unlock_irqrestore(&hw_dev->hw_lock, flags); + rkaiisp_trigger(aidev); + } else { + spin_unlock_irqrestore(&hw_dev->hw_lock, flags); + } + + return ret; +} + +static void rkaiisp_gen_slice_param(struct rkaiisp_device *aidev, + struct rkaiisp_model_cfg *model_cfg, int width) +{ + int left = width; + int slice_num = 0; + int slice_idx = 0; + int slice_mode[8] = {0}; + int lst_slice_len = 0; + int mi_lst_exp_num = 0; + int lext_num_sel; + int slice_align; + int least_rexp; + int align_len; + u32 value; + + if (model_cfg->sw_aiisp_op_mode == AIISP_RUN_MODE_COMBO) { + while (left > 0) { + if (slice_idx == 0) { + if (left >= 408) { + slice_mode[slice_idx] = AIISP_SLICE_MODE_344; + left -= 344; + } else if (left <= 344) { + lst_slice_len = left; + slice_mode[slice_idx] = 0; + left = 0; + } else { + slice_mode[slice_idx] = AIISP_SLICE_MODE_256; + left -= 256; + } + } else { + if (left >= 384) { + slice_mode[slice_idx] = AIISP_SLICE_MODE_320; + left -= 320; + } else if (left <= 344) { + lst_slice_len = left; + slice_mode[slice_idx] = 0; + left = 0; + } else { + slice_mode[slice_idx] = AIISP_SLICE_MODE_256; + left -= 256; + } + } + slice_idx++; + } + } else { + while (left > 0) { + if (model_cfg->sw_aiisp_mode == AIISP_MODE_MODE0) { + if (left >= 384) { + slice_mode[slice_idx] = AIISP_SLICE_MODE_320; + left -= 320; + } else if (left <= 344) { + slice_mode[slice_idx] = 0; + lst_slice_len = left; + left = 0; + } else { + slice_mode[slice_idx] = AIISP_SLICE_MODE_256; + left -= 256; + } + } else { + if (left > 344) { + slice_mode[slice_idx] = AIISP_SLICE_MODE_256; + left -= 256; + } else { + lst_slice_len = left; + slice_mode[slice_idx] = 0; + left = 0; + } + } + slice_idx++; + } + } + + if (slice_idx >= 1) + slice_num = slice_idx - 1; + value = slice_mode[0] | + slice_mode[1] << 2 | + slice_mode[2] << 4 | + slice_mode[3] << 6 | + slice_mode[4] << 8 | + slice_mode[5] << 10 | + slice_mode[6] << 12 | + slice_mode[7] << 14 | + slice_num << 24 | + AIISP_MODE_MODE1 << 30; + rkaiisp_write(aidev, AIISP_MI_SLICE_CTRL, value, false); + + lext_num_sel = ext_tab[model_cfg->sw_aiisp_op_mode][slice_mode[slice_num]]; + slice_align = lst_slice_align_tab[model_cfg->sw_aiisp_mode][model_cfg->sw_aiisp_op_mode]; + least_rexp = 8; + + align_len = CEIL_BY(lst_slice_len + lext_num_sel + least_rexp, slice_align); + mi_lst_exp_num = align_len - lext_num_sel - lst_slice_len; + + value = lst_slice_len << 4 | mi_lst_exp_num << 24; + rkaiisp_write(aidev, AIISP_MI_MANUAL_CTRL, value, false); +} + +static int rkaiisp_determine_size(struct rkaiisp_device *aidev, + struct rkaiisp_model_cfg *model_cfg) +{ + int i, type, width, height, den, num; + int n, dex, odd, bits, cols, chns; + int sw_mi_chn_height_odd[7] = {0}; + int sw_mi_chn_stride[7] = {0}; + int sw_layer_s2d_flag; + int out_ch_stride; + int tmp_cols = 0, tmp_rows = 0; + int base_cols = 0, base_rows = 0; + int last_lv_mode, dma_wr_width; + u32 value; + + for (i = 0; i < RKAIISP_MAX_CHANNEL; i++) { + if (model_cfg->sw_mi_chn_en[i]) { + if (model_cfg->sw_aiisp_op_mode == AIISP_RUN_MODE_COMBO) { + if ((i == 1 && model_cfg->sw_mi_chn1_sel) || + (i == 2) || + (i == 3 && model_cfg->sw_mi_chn3_sel)) { + type = model_cfg->sw_mi_chn_mode[i]; + width = aidev->chn_size[i].width; + height = aidev->chn_size[i].height; + den = den_tab[type]; + num = num_tab[type]; + + tmp_cols = width * num / den; + tmp_rows = height * num / den; + } + } else { + if ((i == 0) || + (i == 1 && model_cfg->sw_mi_chn1_sel) || + (i == 2) || + (i == 3 && model_cfg->sw_mi_chn3_sel) || + (i == 4 && !model_cfg->sw_mi_chn1_sel) || + (i == 6 && !model_cfg->sw_mi_chn3_sel)) { + type = model_cfg->sw_mi_chn_mode[i]; + width = aidev->chn_size[i].width; + height = aidev->chn_size[i].height; + den = den_tab[type]; + num = num_tab[type]; + + tmp_cols = width * num / den; + tmp_rows = height * num / den; + } + } + } + } + + if ((model_cfg->sw_mi_chn_en[1] && + model_cfg->sw_mi_chn_mode[1] == AIISP_CHN_MODE_SPACE2DEPTH) || + (model_cfg->sw_mi_chn_en[2] && + model_cfg->sw_mi_chn_mode[2] == AIISP_CHN_MODE_SPACE2DEPTH)) + sw_layer_s2d_flag = 1; + else + sw_layer_s2d_flag = 0; + + base_cols = tmp_cols; + base_rows = tmp_rows; + for (n = 0; n < RKAIISP_MAX_CHANNEL; n++) { + if (model_cfg->sw_mi_chn_en[n] == 0) + continue; + + dex = 1; + + if (model_cfg->sw_aiisp_op_mode == AIISP_RUN_MODE_COMBO && + model_cfg->sw_aiisp_mode == AIISP_MODE_MODE1 && + (n == 0 || n == 4 || n == 5 || n == 6)) { + dex = 2; + } + + type = model_cfg->sw_mi_chn_mode[n]; + den = den_tab[type]; + num = num_tab[type]; + odd = base_rows - aidev->chn_size[n].height * num * dex / den != 0 ? 1 : 0; + + sw_mi_chn_height_odd[n] = odd; + + bits = bits_tab[model_cfg->sw_mi_chn_data_mode[n]]; + cols = aidev->chn_size[n].width; + chns = mi_chns_tab[model_cfg->sw_aiisp_mode][n]; + + if (n == 3 && model_cfg->sw_mi_chn3_sel == 0) + bits = 8; + + sw_mi_chn_stride[n] = CEIL_BY(cols * chns * bits, 16 * 8) / 32; + } + + last_lv_mode = model_cfg->sw_aiisp_lv_mode[model_cfg->sw_aiisp_level_num - 1]; + dma_wr_width = 0; + + if (model_cfg->sw_aiisp_mode == AIISP_MODE_MODE1) { + if (last_lv_mode == AIISP_LEVEL_MODE_24x15x3x3) + dma_wr_width = (base_cols * 15 * 11 + 7) >> 3; + else if (last_lv_mode == AIISP_LEVEL_MODE_24x4x3x3) + dma_wr_width = (base_cols * 2 * 16 + 7) >> 3; + } else { + if (last_lv_mode == AIISP_LEVEL_MODE_18x8x3x3) + dma_wr_width = (base_cols * 8 * 11 + 7) >> 3; + else if (last_lv_mode == AIISP_LEVEL_MODE_18x4x3x3) { + if (model_cfg->sw_out_mode == AIISP_OUT_MODE_BYPASS) + dma_wr_width = (base_cols * 2 * 16 + 7) >> 3; + else + dma_wr_width = (base_cols * 2 * 8 + 7) >> 3; + } + } + out_ch_stride = CEIL_DOWN(dma_wr_width, 16) * 4; + + // write to hardware + rkaiisp_write(aidev, AIISP_MI_CORE_HEIGHT, base_rows, false); + rkaiisp_write(aidev, AIISP_MI_CORE_WIDTH, base_cols * (sw_layer_s2d_flag + 1), false); + + for (i = 0; i < RKAIISP_MAX_CHANNEL; ++i) { + if (model_cfg->sw_mi_chn_en[i]) + rkaiisp_write(aidev, AIISP_MI_RD_CH0_STRIDE + 0x100 * i, + sw_mi_chn_stride[i], false); + } + rkaiisp_write(aidev, AIISP_MI_CHN0_WR_STRIDE, out_ch_stride, false); + + if (model_cfg->sw_mi_chn_en[0]) { + value = AIISP_MI_RD_CH_EN | + (sw_mi_chn_height_odd[0] << 2) | + (model_cfg->sw_mi_chn_mode[0] << 4) | + (AIISP_MI_RD_CH0_GROUP_MODE << 8); + rkaiisp_write(aidev, AIISP_MI_RD_CH0_CTRL, value, false); + } else { + rkaiisp_write(aidev, AIISP_MI_RD_CH0_CTRL, 0, false); + } + if (model_cfg->sw_mi_chn_en[1]) { + value = AIISP_MI_RD_CH_EN | + (model_cfg->sw_mi_chn1_sel << 1) | + (sw_mi_chn_height_odd[1] << 2) | + (model_cfg->sw_mi_chn_mode[1] << 4) | + (model_cfg->sw_mi_chn_data_mode[1] << 6) | + (AIISP_MI_RD_CH1_GROUP_MODE << 8); + rkaiisp_write(aidev, AIISP_MI_RD_CH1_CTRL, value, false); + } else { + rkaiisp_write(aidev, AIISP_MI_RD_CH1_CTRL, 0, false); + } + if (model_cfg->sw_mi_chn_en[2]) { + value = AIISP_MI_RD_CH_EN | + (sw_mi_chn_height_odd[2] << 2) | + (model_cfg->sw_mi_chn_mode[2] << 4) | + (model_cfg->sw_mi_chn_data_mode[2] << 6) | + (AIISP_MI_RD_CH2_GROUP_MODE << 8); + rkaiisp_write(aidev, AIISP_MI_RD_CH2_CTRL, value, false); + } else { + rkaiisp_write(aidev, AIISP_MI_RD_CH2_CTRL, 0, false); + } + if (model_cfg->sw_mi_chn_en[3]) { + value = AIISP_MI_RD_CH_EN | + (model_cfg->sw_mi_chn3_sel << 1) | + (sw_mi_chn_height_odd[3] << 2) | + (model_cfg->sw_mi_chn_data_mode[3] << 6) | + (AIISP_MI_RD_CH3_GROUP_MODE << 8); + rkaiisp_write(aidev, AIISP_MI_RD_CH3_CTRL, value, false); + } else { + rkaiisp_write(aidev, AIISP_MI_RD_CH3_CTRL, 0, false); + } + if (model_cfg->sw_mi_chn_en[4]) { + value = AIISP_MI_RD_CH_EN | + (sw_mi_chn_height_odd[4] << 2) | + (model_cfg->sw_mi_chn_mode[4] << 4) | + (model_cfg->sw_mi_chn_data_mode[4] << 6) | + (AIISP_MI_RD_CH4_GROUP_MODE << 8); + rkaiisp_write(aidev, AIISP_MI_RD_CH4_CTRL, value, false); + } else { + rkaiisp_write(aidev, AIISP_MI_RD_CH4_CTRL, 0, false); + } + if (model_cfg->sw_mi_chn_en[5]) { + value = AIISP_MI_RD_CH_EN | + (sw_mi_chn_height_odd[5] << 2) | + (model_cfg->sw_mi_chn_mode[5] << 4) | + (model_cfg->sw_mi_chn_data_mode[5] << 6) | + (AIISP_MI_RD_CH5_GROUP_MODE << 8); + rkaiisp_write(aidev, AIISP_MI_RD_CH5_CTRL, value, false); + } else { + rkaiisp_write(aidev, AIISP_MI_RD_CH5_CTRL, 0, false); + } + if (model_cfg->sw_mi_chn_en[6]) { + value = AIISP_MI_RD_CH_EN | + (model_cfg->sw_mi_chn_data_mode[6] << 6) | + (AIISP_MI_RD_CH6_GROUP_MODE << 8); + rkaiisp_write(aidev, AIISP_MI_RD_CH6_CTRL, value, false); + } else { + rkaiisp_write(aidev, AIISP_MI_RD_CH6_CTRL, 0, false); + } + + value = (AIISP_MI_WR_GROUP_MODE << 16) | + AIISP_MI_WR_INIT_BASE_EN | AIISP_MI_WR_INIT_OFFSET_EN; + rkaiisp_write(aidev, AIISP_MI_WR_CTRL, value, false); + value = AIISP_MI_CHN0_WR_EN | AIISP_MI_CHN0_WR_AUTOUPD; + rkaiisp_write(aidev, AIISP_MI_CHN0_WR_CRTL, value, false); + + return tmp_cols; +} + +static void rkaiisp_cfg_other_iqparam(struct rkaiisp_device *aidev, + struct rkaiisp_other_cfg *other_cfg) +{ + u32 val; + int i; + + val = other_cfg->sw_neg_noiselimit | other_cfg->sw_pos_noiselimit << 16; + rkaiisp_write(aidev, AIISP_CORE_NOISE_LMT, val, false); + for (i = 0; i < 32; i += 2) { + val = other_cfg->sw_in_comp_y[i] | other_cfg->sw_in_comp_y[i+1] << 16; + rkaiisp_write(aidev, AIISP_CORE_COMP0 + 2*i, val, false); + } + val = other_cfg->sw_in_comp_y[32] | other_cfg->sw_prev_blacklvl << 16; + rkaiisp_write(aidev, AIISP_CORE_COMP16, val, false); + + for (i = 0; i < 32; i += 2) { + val = other_cfg->sw_out_decomp_y[i] | other_cfg->sw_out_decomp_y[i+1] << 16; + rkaiisp_write(aidev, AIISP_CORE_DECOMP0 + 2*i, val, false); + } + val = other_cfg->sw_out_decomp_y[32] | other_cfg->sw_post_blacklvl << 16; + rkaiisp_write(aidev, AIISP_CORE_DECOMP16, val, false); +} + +static u32 rkaiisp_config_rdchannel(struct rkaiisp_device *aidev, + struct rkaiisp_model_cfg *model_cfg, u32 run_idx) +{ + struct rkaiisp_ispbuf_info *ispbuf = &aidev->ispbuf; + struct rkaiisp_dummy_buffer *vpsl_buf; + dma_addr_t dma_addr; + u32 width, height; + u32 sig_width = 0; + int i; + + vpsl_buf = &aidev->vpslbuf[aidev->curr_idxbuf.vpsl_index]; + for (i = 0; i < 7; i++) { + if (model_cfg->sw_mi_chn_en[i] == 0) + continue; + + switch (model_cfg->mi_chn_src[i]) { + case ISP_IIR: + width = CEIL_BY(ispbuf->iir_width, 16); + width = CEIL_BY(width * 9 / 4, 16); + width = width >> 1; + height = ispbuf->iir_height; + dma_addr = aidev->iirbuf[aidev->curr_idxbuf.iir_index].dma_addr; + break; + case VPSL_YRAW_CHN0: + width = ispbuf->raw_width[0]; + height = ispbuf->raw_height[0]; + dma_addr = vpsl_buf->dma_addr + ispbuf->bnr_buf.u.v35.vpsl_yraw_offs[0]; + break; + case VPSL_YRAW_CHN1: + width = ispbuf->raw_width[1]; + height = ispbuf->raw_height[1]; + dma_addr = vpsl_buf->dma_addr + ispbuf->bnr_buf.u.v35.vpsl_yraw_offs[1]; + break; + case VPSL_YRAW_CHN2: + width = ispbuf->raw_width[2]; + height = ispbuf->raw_height[2]; + dma_addr = vpsl_buf->dma_addr + ispbuf->bnr_buf.u.v35.vpsl_yraw_offs[2]; + break; + case VPSL_YRAW_CHN3: + width = ispbuf->raw_width[3]; + height = ispbuf->raw_height[3]; + dma_addr = vpsl_buf->dma_addr + ispbuf->bnr_buf.u.v35.vpsl_yraw_offs[3]; + break; + case VPSL_YRAW_CHN4: + width = ispbuf->raw_width[4]; + height = ispbuf->raw_height[4]; + dma_addr = vpsl_buf->dma_addr + ispbuf->bnr_buf.u.v35.vpsl_yraw_offs[4]; + break; + case VPSL_YRAW_CHN5: + width = ispbuf->raw_width[5]; + height = ispbuf->raw_height[5]; + dma_addr = vpsl_buf->dma_addr + ispbuf->bnr_buf.u.v35.vpsl_yraw_offs[5]; + break; + case VPSL_SIG_CHN0: + width = ispbuf->sig_width[0]; + height = ispbuf->sig_height[0]; + dma_addr = vpsl_buf->dma_addr + ispbuf->bnr_buf.u.v35.vpsl_sig_offs[0]; + sig_width = width; + break; + case VPSL_SIG_CHN1: + width = ispbuf->sig_width[1]; + height = ispbuf->sig_height[1]; + dma_addr = vpsl_buf->dma_addr + ispbuf->bnr_buf.u.v35.vpsl_sig_offs[1]; + sig_width = width; + break; + case VPSL_SIG_CHN2: + width = ispbuf->sig_width[2]; + height = ispbuf->sig_height[2]; + dma_addr = vpsl_buf->dma_addr + ispbuf->bnr_buf.u.v35.vpsl_sig_offs[2]; + sig_width = width; + break; + case VPSL_SIG_CHN3: + width = ispbuf->sig_width[3]; + height = ispbuf->sig_height[3]; + dma_addr = vpsl_buf->dma_addr + ispbuf->bnr_buf.u.v35.vpsl_sig_offs[3]; + sig_width = width; + break; + case VPSL_SIG_CHN4: + width = ispbuf->sig_width[4]; + height = ispbuf->sig_height[4]; + dma_addr = vpsl_buf->dma_addr + ispbuf->bnr_buf.u.v35.vpsl_sig_offs[4]; + sig_width = width; + break; + case ISP_AIPRE_NARMAP: + width = ispbuf->narmap_width; + height = ispbuf->narmap_height; + dma_addr = aidev->aiprebuf[aidev->curr_idxbuf.aipre_gain_index].dma_addr; + break; + case AIISP_LAST_OUT: + if (aidev->model_mode == COMBO_MODE) { + width = aidev->outbuf_size[aidev->model_runcnt-run_idx+1].width; + height = aidev->outbuf_size[aidev->model_runcnt-run_idx+1].height; + } else { + width = aidev->outbuf_size[aidev->model_runcnt-run_idx].width; + height = aidev->outbuf_size[aidev->model_runcnt-run_idx].height; + } + dma_addr = aidev->temp_buf[aidev->outbuf_idx].dma_addr; + break; + default: + width = 0; + height = 0; + dma_addr = 0; + break; + } + + if (width > 0) { + aidev->chn_size[i].width = width; + aidev->chn_size[i].height = height; + rkaiisp_write(aidev, AIISP_MI_RD_CH0_BASE + 0x100 * i, dma_addr, false); + rkaiisp_write(aidev, AIISP_MI_RD_CH0_HEIGHT + 0x100 * i, height, false); + + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "configure channel %d, width %d, height %d, dma_addr %pad\n", + i, aidev->chn_size[i].width, aidev->chn_size[i].height, &dma_addr); + } + } + + return sig_width; +} + +static void rkaiisp_run_cfg(struct rkaiisp_device *aidev, u32 run_idx) +{ + struct rkaiisp_ispbuf_info *ispbuf = &aidev->ispbuf; + struct rkaiisp_params *cur_params; + struct rkaiisp_model_cfg *model_cfg; + int lastlv, lv_mode, out_chns, i; + u32 outbuf_idx, val; + u32 sw_lastlv_bypass = 0; + u32 sw_m0_diff_merge = 0; + u32 iir_stride; + u32 sig_width; + dma_addr_t dma_addr; + + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "run frame id: %d, run_idx: %d\n", + aidev->curr_idxbuf.sequence, run_idx); + + cur_params = (struct rkaiisp_params *)aidev->cur_params->vaddr[0]; + model_cfg = &cur_params->model_cfg[run_idx]; + + lastlv = model_cfg->sw_aiisp_level_num - 1; + lv_mode = model_cfg->sw_aiisp_lv_mode[lastlv]; + out_chns = channels_lut[model_cfg->sw_aiisp_mode][lv_mode]; + if (aidev->model_mode == SINGLEX2_MODE) { + if (run_idx == 0) { + sig_width = rkaiisp_config_rdchannel(aidev, model_cfg, run_idx); + + outbuf_idx = 0; + aidev->outbuf_idx = outbuf_idx; + rkaiisp_write(aidev, AIISP_MI_CHN0_WR_BASE, + aidev->temp_buf[outbuf_idx].dma_addr, false); + + rkaiisp_gen_slice_param(aidev, model_cfg, sig_width); + rkaiisp_determine_size(aidev, model_cfg); + } else if (run_idx < aidev->model_runcnt-1) { + outbuf_idx = aidev->outbuf_idx; + sig_width = rkaiisp_config_rdchannel(aidev, model_cfg, run_idx); + rkaiisp_gen_slice_param(aidev, model_cfg, sig_width); + rkaiisp_determine_size(aidev, model_cfg); + + outbuf_idx = (outbuf_idx + 1) % 2; + aidev->outbuf_idx = outbuf_idx; + rkaiisp_write(aidev, AIISP_MI_CHN0_WR_BASE, + aidev->temp_buf[outbuf_idx].dma_addr, false); + } else { + sig_width = rkaiisp_config_rdchannel(aidev, model_cfg, run_idx); + + dma_addr = aidev->aiispbuf[aidev->curr_idxbuf.aiisp_index].dma_addr; + rkaiisp_write(aidev, AIISP_MI_CHN0_WR_BASE, dma_addr, false); + + rkaiisp_gen_slice_param(aidev, model_cfg, sig_width); + rkaiisp_determine_size(aidev, model_cfg); + + iir_stride = CEIL_BY(ispbuf->iir_width, 16); + iir_stride = CEIL_BY(iir_stride * 9 / 4, 16); + iir_stride = iir_stride >> 1; + rkaiisp_write(aidev, AIISP_MI_CHN0_WR_STRIDE, iir_stride / 2, false); + } + } else { + if (run_idx == 0) { + rkaiisp_config_rdchannel(aidev, model_cfg, run_idx); + + outbuf_idx = 0; + aidev->outbuf_idx = outbuf_idx; + rkaiisp_write(aidev, AIISP_MI_CHN0_WR_BASE, + aidev->temp_buf[outbuf_idx].dma_addr, false); + + rkaiisp_gen_slice_param(aidev, model_cfg, ispbuf->sig_width[3]); + rkaiisp_determine_size(aidev, model_cfg); + } else if (run_idx < aidev->model_runcnt-1) { + outbuf_idx = aidev->outbuf_idx; + rkaiisp_config_rdchannel(aidev, model_cfg, run_idx); + if (run_idx == 1) + rkaiisp_gen_slice_param(aidev, model_cfg, ispbuf->sig_width[2]); + else + rkaiisp_gen_slice_param(aidev, model_cfg, ispbuf->sig_width[1]); + rkaiisp_determine_size(aidev, model_cfg); + + outbuf_idx = (outbuf_idx + 1) % 2; + aidev->outbuf_idx = outbuf_idx; + rkaiisp_write(aidev, AIISP_MI_CHN0_WR_BASE, + aidev->temp_buf[outbuf_idx].dma_addr, false); + } else { + rkaiisp_config_rdchannel(aidev, model_cfg, run_idx); + + dma_addr = aidev->aiispbuf[aidev->curr_idxbuf.aiisp_index].dma_addr; + rkaiisp_write(aidev, AIISP_MI_CHN0_WR_BASE, dma_addr, false); + + rkaiisp_gen_slice_param(aidev, model_cfg, ispbuf->sig_width[0]); + rkaiisp_determine_size(aidev, model_cfg); + + iir_stride = CEIL_BY(ispbuf->iir_width, 16); + iir_stride = CEIL_BY(iir_stride * 9 / 4, 16); + iir_stride = iir_stride >> 1; + rkaiisp_write(aidev, AIISP_MI_CHN0_WR_STRIDE, iir_stride / 2, false); + } + } + + cur_params = (struct rkaiisp_params *)aidev->cur_params->vaddr[0]; + val = aidev->cur_params->buff_addr[0] + cur_params->kwt_cfg.kwt_offet[run_idx]; + rkaiisp_write(aidev, AIISP_MI_RD_KWT_BASE, val, false); + rkaiisp_write(aidev, AIISP_MI_RD_KWT_WIDTH, + cur_params->kwt_cfg.kwt_size[run_idx], false); + rkaiisp_write(aidev, AIISP_MI_RD_KWT_HEIGHT, 1, false); + rkaiisp_write(aidev, AIISP_MI_RD_KWT_STRIDE, + CEIL_DOWN(cur_params->kwt_cfg.kwt_size[run_idx], 4), false); + rkaiisp_write(aidev, AIISP_MI_RD_KWT_CTRL, AIISP_MI_RD_KWT_EN, false); + + val = (model_cfg->sw_aiisp_mode << 2) | + (model_cfg->sw_aiisp_op_mode << 6) | + (model_cfg->sw_aiisp_drop_en << 7) | + (model_cfg->sw_aiisp_level_num << 8) | + (model_cfg->sw_aiisp_l1_level_num << 16); + rkaiisp_write(aidev, AIISP_CORE_CTRL, val, false); + + for (i = 0; i < 4; ++i) { + val = (model_cfg->sw_aiisp_lv_active[i * 4 + 0] << 2) | + (model_cfg->sw_aiisp_lv_mode[i * 4 + 0] << 4) | + (model_cfg->sw_aiisp_lv_active[i * 4 + 1] << 10) | + (model_cfg->sw_aiisp_lv_mode[i * 4 + 1] << 12) | + (model_cfg->sw_aiisp_lv_active[i * 4 + 2] << 18) | + (model_cfg->sw_aiisp_lv_mode[i * 4 + 2] << 20) | + (model_cfg->sw_aiisp_lv_active[i * 4 + 3] << 26) | + (model_cfg->sw_aiisp_lv_mode[i * 4 + 3] << 28); + + rkaiisp_write(aidev, AIISP_CORE_LEVEL_CTRL0 + i * 4, val, false); + } + + if ((out_chns == 4 && model_cfg->sw_out_d2s_en == 0)) + sw_lastlv_bypass = 1; + if (model_cfg->sw_aiisp_mode == 0 && model_cfg->sw_out_mode == AIISP_OUT_MODE_DIFF_MERGE) + sw_m0_diff_merge = 1; + + val = sw_lastlv_bypass | + (sw_m0_diff_merge << 1) | + (model_cfg->sw_lastlvlm1_clip8bit << 2); + rkaiisp_write(aidev, AIISP_CORE_OUT_CTRL, val, false); + + /* rkaiisp_dump_list_reg(aidev); */ +} + +static int rkaiisp_update_buf(struct rkaiisp_device *aidev) +{ + struct kfifo *fifo = &aidev->idxbuf_kfifo; + struct rkaiisp_hw_dev *hw_dev = aidev->hw_dev; + struct rkisp_aiisp_st idxbuf = {0}; + unsigned long flags = 0; + int ret = -1; + + spin_lock_irqsave(&hw_dev->hw_lock, flags); + if (!kfifo_is_empty(fifo)) + ret = kfifo_out(fifo, &idxbuf, sizeof(struct rkisp_aiisp_st)); + if (!ret) { + ret = -EINVAL; + } else { + ret = 0; + aidev->curr_idxbuf = idxbuf; + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "idxbuf fifo out: %d\n", idxbuf.sequence); + } + spin_unlock_irqrestore(&hw_dev->hw_lock, flags); + + return ret; +} + +static void rkaiisp_run_start(struct rkaiisp_device *aidev) +{ + struct rkaiisp_hw_dev *hw_dev = aidev->hw_dev; + + rkaiisp_write(aidev, AIISP_MI_IMSC, AIISP_MI_ISR_ALL, false); + rkaiisp_write(aidev, AIISP_MI_WR_INIT, AIISP_MI_CHN0SELF_FORCE_UPD, false); + + if ((aidev->run_idx == 0) && (rkaiisp_showreg != 0)) + aidev->showreg = true; + + if (aidev->showreg) + rkaiisp_dump_list_reg(aidev); + + if ((aidev->run_idx == aidev->model_runcnt - 1) && aidev->showreg) { + aidev->showreg = false; + rkaiisp_showreg = 0; + } + + rkaiisp_write(aidev, AIISP_MI_RD_START, AIISP_MI_RD_START_EN, false); + + if (!hw_dev->is_single) + rkaiisp_update_list_reg(aidev); +} + +static void rkaiisp_get_new_iqparam(struct rkaiisp_device *aidev) +{ + struct rkaiisp_params *iq_params, *old_params; + struct rkaiisp_buffer *cur_buf = NULL; + struct rkaiisp_buffer *done_buf = NULL; + unsigned int cur_frame_id = aidev->frame_id; + unsigned long flags = 0; + + spin_lock_irqsave(&aidev->config_lock, flags); + if (!list_empty(&aidev->params)) + cur_buf = list_first_entry(&aidev->params, + struct rkaiisp_buffer, queue); + if (!cur_buf) { + spin_unlock_irqrestore(&aidev->config_lock, flags); + return; + } + + list_del(&cur_buf->queue); + iq_params = (struct rkaiisp_params *)cur_buf->vaddr[0]; + if ((!(iq_params->module_update & RKAIISP_MODEL_UPDATE) && + (iq_params->module_update & RKAIISP_OTHER_UPDATE)) && aidev->cur_params) { + old_params = (struct rkaiisp_params *)aidev->cur_params->vaddr[0]; + old_params->frame_id = iq_params->frame_id; + old_params->module_update = iq_params->module_update; + old_params->other_cfg = iq_params->other_cfg; + done_buf = cur_buf; + } else { + done_buf = aidev->cur_params; + aidev->cur_params = cur_buf; + } + + if (done_buf) { + done_buf->vb.sequence = cur_frame_id; + vb2_buffer_done(&done_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + spin_unlock_irqrestore(&aidev->config_lock, flags); + + // configure other params + if (aidev->cur_params) { + iq_params = (struct rkaiisp_params *)aidev->cur_params->vaddr[0]; + + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "update iq param: %d, module: 0x%x\n", + iq_params->frame_id, iq_params->module_update); + + aidev->model_mode = iq_params->model_mode; + aidev->model_runcnt = iq_params->model_runcnt; + rkaiisp_cfg_other_iqparam(aidev, &iq_params->other_cfg); + } +} + + +void rkaiisp_trigger(struct rkaiisp_device *aidev) +{ + struct rkaiisp_ispbuf_info *ispbuf = &aidev->ispbuf; + + if (!rkaiisp_update_buf(aidev)) { + aidev->run_idx = 0; + aidev->frame_id = aidev->curr_idxbuf.sequence; + rkaiisp_get_new_iqparam(aidev); + rkaiisp_calc_outbuf_size(aidev, ispbuf->iir_height, ispbuf->iir_width); + rkaiisp_run_cfg(aidev, aidev->run_idx); + aidev->hwstate = HW_RUNNING; + rkaiisp_run_start(aidev); + } +} + +static void rkaiisp_event_queue(struct rkaiisp_device *aidev, struct rkisp_aiisp_st *idxbuf) +{ + struct rkisp_aiisp_st *rundone; + struct v4l2_event event = {0}; + + if (aidev->subdev.is_subs_evt && aidev->exemode != BOTHEVENT_IN_KERNEL) { + event.type = RKAIISP_V4L2_EVENT_AIISP_DONE; + rundone = (struct rkisp_aiisp_st *)&event.u.data[0]; + *rundone = *idxbuf; + v4l2_event_queue(aidev->subdev.sd.devnode, &event); + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "aiisp done: %d\n", rundone->sequence); + } else { + // call isp api to inform + } +} + +int rkaiisp_get_idxbuf_len(struct rkaiisp_device *aidev) +{ + struct kfifo *fifo = &aidev->idxbuf_kfifo; + int len = 0; + + len = kfifo_len(fifo) / sizeof(struct rkisp_aiisp_st); + return len; +} + +enum rkaiisp_irqhdl_ret rkaiisp_irq_hdl(struct rkaiisp_device *aidev, u32 mi_mis) +{ + struct rkisp_aiisp_st *idxbuf = NULL; + + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "irq val: 0x%x, run_idx %d, model_runcnt %d\n", + mi_mis, aidev->run_idx, aidev->model_runcnt); + + if (mi_mis & AIISP_MI_ISR_BUSERR) { + v4l2_err(&aidev->v4l2_dev, "buserr 0x%x\n", mi_mis); + rkaiisp_write(aidev, AIISP_MI_ICR, AIISP_MI_ISR_BUSERR, true); + } + + if (!(mi_mis & AIISP_MI_ISR_WREND)) + return NOT_WREND; + rkaiisp_write(aidev, AIISP_MI_ICR, AIISP_MI_ISR_WREND, true); + + if (aidev->run_idx < aidev->model_runcnt - 1) { + aidev->run_idx++; + rkaiisp_run_cfg(aidev, aidev->run_idx); + rkaiisp_run_start(aidev); + return CONTINUE_RUN; + } + + idxbuf = &aidev->curr_idxbuf; + if (idxbuf) + rkaiisp_event_queue(aidev, idxbuf); + + aidev->hwstate = HW_STOP; + if (!aidev->streamon) + wake_up(&aidev->sync_onoff); + + return RUN_COMPLETE; +} + +static inline struct rkaiisp_device *sd_to_aiispdev(struct v4l2_subdev *sd) +{ + return container_of(sd->v4l2_dev, struct rkaiisp_device, v4l2_dev); +} + +static int rkaiisp_sd_subs_evt(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct rkaiisp_device *aidev = sd_to_aiispdev(sd); + + if (sub->type != RKAIISP_V4L2_EVENT_AIISP_DONE) + return -EINVAL; + + aidev->subdev.is_subs_evt = true; + return v4l2_event_subscribe(fh, sub, RKAIISP_V4L2_EVENT_ELEMS, NULL); +} + +static int rkaiisp_sd_unsubs_evt(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct rkaiisp_device *aidev = sd_to_aiispdev(sd); + + if (sub->type != RKAIISP_V4L2_EVENT_AIISP_DONE) + return -EINVAL; + + aidev->subdev.is_subs_evt = false; + return v4l2_event_subdev_unsubscribe(sd, fh, sub); +} + +static const struct v4l2_subdev_core_ops rkaiisp_core_ops = { + .subscribe_event = rkaiisp_sd_subs_evt, + .unsubscribe_event = rkaiisp_sd_unsubs_evt, +}; + +static const struct v4l2_subdev_ops rkaiisp_sd_ops = { + .core = &rkaiisp_core_ops, +}; + +static int rkaiisp_register_subdev(struct rkaiisp_device *aidev, + struct v4l2_device *v4l2_dev) +{ + struct rkaiiisp_subdev *subdev = &aidev->subdev; + struct v4l2_subdev *sd = &subdev->sd; + int ret; + + v4l2_subdev_init(sd, &rkaiisp_sd_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; + snprintf(sd->name, sizeof(sd->name), RKAIISP_SUBDEV_NAME); + + sd->owner = THIS_MODULE; + v4l2_set_subdevdata(sd, aidev); + + sd->grp_id = 0; + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) { + v4l2_err(sd, "Failed to register subdev\n"); + return ret; + } + + ret = v4l2_device_register_subdev_nodes(v4l2_dev); + if (ret < 0) { + v4l2_err(sd, "Failed to register subdev nodes\n"); + return ret; + } + + return 0; +} + +static void rkaiisp_unregister_subdev(struct rkaiisp_device *aidev) +{ + struct v4l2_subdev *sd = &aidev->subdev.sd; + + v4l2_device_unregister_subdev(sd); +} + +static int rkaiisp_enum_fmt_meta_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct video_device *video = video_devdata(file); + struct rkaiisp_device *aidev = video_get_drvdata(video); + + if (f->index > 0 || f->type != video->queue->type) + return -EINVAL; + + f->pixelformat = aidev->vdev_fmt.fmt.meta.dataformat; + + return 0; +} + +static int rkaiisp_g_fmt_meta_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct video_device *video = video_devdata(file); + struct rkaiisp_device *aidev = video_get_drvdata(video); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (f->type != video->queue->type) + return -EINVAL; + + memset(meta, 0, sizeof(*meta)); + meta->dataformat = aidev->vdev_fmt.fmt.meta.dataformat; + meta->buffersize = aidev->vdev_fmt.fmt.meta.buffersize; + + return 0; +} + +static int rkaiisp_querycap(struct file *file, + void *priv, struct v4l2_capability *cap) +{ + snprintf(cap->driver, sizeof(cap->driver), "%s", DRIVER_NAME); + snprintf(cap->card, sizeof(cap->card), "%s", DRIVER_NAME); + strscpy(cap->bus_info, "platform: " DRIVER_NAME, sizeof(cap->bus_info)); + + return 0; +} + +static long rkaiisp_ioctl_default(struct file *file, void *fh, + bool valid_prio, unsigned int cmd, void *arg) +{ + struct rkaiisp_device *aidev = video_drvdata(file); + struct rkaiisp_param_info *param_info = (struct rkaiisp_param_info *)arg; + long ret = -EINVAL; + + switch (cmd) { + case RKAIISP_CMD_SET_PARAM_INFO: + if ((param_info->para_size > 0) && + (param_info->max_runcnt > 0) && + (param_info->max_runcnt <= RKAIISP_MAX_RUNCNT)) { + aidev->exemode = param_info->exemode; + aidev->para_size = param_info->para_size; + aidev->max_runcnt = param_info->max_runcnt; + ret = 0; + } else { + v4l2_err(&aidev->v4l2_dev, + "wrong params in set param info, para_size %d, max_runcnt %d\n", + aidev->para_size, aidev->max_runcnt); + } + break; + case RKAIISP_CMD_INIT_BUFPOOL: + ret = rkaiisp_init_pool(aidev, arg); + break; + case RKAIISP_CMD_FREE_BUFPOOL: + ret = rkaiisp_free_pool(aidev); + break; + case RKAIISP_CMD_QUEUE_BUF: + ret = rkaiisp_queue_ispbuf(aidev, arg); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/* ISP params video device IOCTLs */ +static const struct v4l2_ioctl_ops rkaiisp_ioctl = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_fmt_meta_out = rkaiisp_enum_fmt_meta_out, + .vidioc_g_fmt_meta_out = rkaiisp_g_fmt_meta_out, + .vidioc_s_fmt_meta_out = rkaiisp_g_fmt_meta_out, + .vidioc_try_fmt_meta_out = rkaiisp_g_fmt_meta_out, + .vidioc_querycap = rkaiisp_querycap, + .vidioc_default = rkaiisp_ioctl_default, +}; + +static int rkaiisp_vb2_queue_setup(struct vb2_queue *vq, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_ctxs[]) +{ + struct rkaiisp_device *aidev = vq->drv_priv; + + *num_planes = 1; + *num_buffers = clamp_t(u32, *num_buffers, + RKAIISP_REQ_BUFS_MIN, + RKAIISP_REQ_BUFS_MAX); + + sizes[0] = sizeof(struct rkaiisp_params) + aidev->para_size * aidev->max_runcnt; + aidev->vdev_fmt.fmt.meta.buffersize = sizes[0]; + + INIT_LIST_HEAD(&aidev->params); + + return 0; +} + +static void rkaiisp_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkaiisp_buffer *buf = to_rkaiisp_buffer(vbuf); + struct vb2_queue *vq = vb->vb2_queue; + struct rkaiisp_device *aidev = vq->drv_priv; + struct sg_table *sgt; + unsigned long flags; + + sgt = vb2_dma_sg_plane_desc(vb, 0); + buf->vaddr[0] = vb2_plane_vaddr(vb, 0); + buf->buff_addr[0] = sg_dma_address(sgt->sgl); + + if (buf->vaddr[0]) + if (vb->vb2_queue->mem_ops->prepare) + vb->vb2_queue->mem_ops->prepare(vb->planes[0].mem_priv); + + spin_lock_irqsave(&aidev->config_lock, flags); + list_add_tail(&buf->queue, &aidev->params); + spin_unlock_irqrestore(&aidev->config_lock, flags); +} + +static void rkaiisp_vb2_stop_streaming(struct vb2_queue *vq) +{ + struct rkaiisp_device *aidev = vq->drv_priv; + struct rkaiisp_hw_dev *hw_dev = aidev->hw_dev; + struct rkaiisp_buffer *parabuf; + unsigned long flags; + int i, ret; + + /* stop params input firstly */ + spin_lock_irqsave(&hw_dev->hw_lock, flags); + if (aidev->streamon) { + aidev->streamon = false; + spin_unlock_irqrestore(&hw_dev->hw_lock, flags); + if (aidev->hwstate == HW_RUNNING) { + ret = wait_event_timeout(aidev->sync_onoff, + aidev->hwstate == HW_STOP, msecs_to_jiffies(200)); + if (!ret) + v4l2_warn(&aidev->v4l2_dev, "%s: wait dev %d stop timeout\n", + __func__, aidev->dev_id); + } + } else { + spin_unlock_irqrestore(&hw_dev->hw_lock, flags); + } + + spin_lock_irqsave(&aidev->config_lock, flags); + for (i = 0; i < RKAIISP_REQ_BUFS_MAX; i++) { + if (!list_empty(&aidev->params)) { + parabuf = list_first_entry(&aidev->params, + struct rkaiisp_buffer, queue); + list_del(¶buf->queue); + vb2_buffer_done(¶buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } else { + break; + } + } + spin_unlock_irqrestore(&aidev->config_lock, flags); + + if (aidev->cur_params) { + parabuf = aidev->cur_params; + vb2_buffer_done(¶buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + aidev->cur_params = NULL; + } + + pm_runtime_put_sync(aidev->dev); + atomic_dec(&hw_dev->refcnt); +} + +static int +rkaiisp_vb2_start_streaming(struct vb2_queue *queue, unsigned int count) +{ + struct rkaiisp_device *aidev = queue->drv_priv; + struct rkaiisp_hw_dev *hw_dev = aidev->hw_dev; + unsigned long flags; + + spin_lock_irqsave(&hw_dev->hw_lock, flags); + aidev->streamon = true; + kfifo_reset(&aidev->idxbuf_kfifo); + spin_unlock_irqrestore(&hw_dev->hw_lock, flags); + + pm_runtime_get_sync(aidev->dev); + atomic_inc(&hw_dev->refcnt); + + return 0; +} + +static const struct vb2_ops rkaiisp_vb2_ops = { + .queue_setup = rkaiisp_vb2_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_queue = rkaiisp_vb2_buf_queue, + .start_streaming = rkaiisp_vb2_start_streaming, + .stop_streaming = rkaiisp_vb2_stop_streaming, + +}; + +struct v4l2_file_operations rkaiisp_fops = { + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, + .poll = vb2_fop_poll, + .open = v4l2_fh_open, + .release = vb2_fop_release +}; + +static int +rkaiisp_init_vb2_queue(struct vb2_queue *q, + struct rkaiisp_device *aidev) +{ + q->type = V4L2_BUF_TYPE_META_OUTPUT; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = aidev; + q->ops = &rkaiisp_vb2_ops; + q->mem_ops = aidev->hw_dev->mem_ops; + q->buf_struct_size = sizeof(struct rkaiisp_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &aidev->apilock; + q->dev = aidev->hw_dev->dev; + if (aidev->hw_dev->is_dma_contig) + q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS; + + return vb2_queue_init(q); +} + +static void rkaiisp_init_vdev(struct rkaiisp_device *aidev) +{ + aidev->para_size = RKAIISP_DEFAULT_PARASIZE; + aidev->max_runcnt = RKAIISP_DEFAULT_MAXRUNCNT; + aidev->vdev_fmt.fmt.meta.dataformat = V4L2_META_FMT_RK_ISP1_PARAMS; + aidev->vdev_fmt.fmt.meta.buffersize = + sizeof(struct rkaiisp_params) + aidev->para_size * aidev->max_runcnt; +} + +int rkaiisp_register_vdev(struct rkaiisp_device *aidev, struct v4l2_device *v4l2_dev) +{ + int ret; + struct rkaiisp_vdev_node *node = &aidev->vnode; + struct video_device *vdev = &node->vdev; + + spin_lock_init(&aidev->config_lock); + aidev->mem_ops = aidev->hw_dev->mem_ops; + + ret = kfifo_alloc(&aidev->idxbuf_kfifo, + 16 * sizeof(struct rkisp_aiisp_st), GFP_KERNEL); + if (ret < 0) { + v4l2_err(v4l2_dev, "Failed to alloc kfifo %d", ret); + return ret; + } + + strscpy(vdev->name, "rkaiisp", sizeof(vdev->name)); + + vdev->ioctl_ops = &rkaiisp_ioctl; + vdev->fops = &rkaiisp_fops; + vdev->release = video_device_release_empty; + /* + * Provide a mutex to v4l2 core. It will be used + * to protect all fops and v4l2 ioctls. + */ + vdev->lock = &aidev->apilock; + vdev->v4l2_dev = v4l2_dev; + vdev->queue = &node->buf_queue; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT; + vdev->vfl_dir = VFL_DIR_TX; + rkaiisp_init_vb2_queue(vdev->queue, aidev); + rkaiisp_init_vdev(aidev); + video_set_drvdata(vdev, aidev); + init_waitqueue_head(&aidev->sync_onoff); + + node->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&vdev->entity, 0, &node->pad); + if (ret < 0) + goto err_release_queue; + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + dev_err(&vdev->dev, + "could not register Video for Linux device\n"); + goto err_cleanup_media_entity; + } + ret = rkaiisp_register_subdev(aidev, v4l2_dev); + if (ret < 0) { + dev_err(&vdev->dev, + "could not register Video for Linux device\n"); + goto err_unregister_device; + } + return 0; +err_unregister_device: + video_unregister_device(vdev); +err_cleanup_media_entity: + media_entity_cleanup(&vdev->entity); +err_release_queue: + vb2_queue_release(vdev->queue); + return ret; +} + +void rkaiisp_unregister_vdev(struct rkaiisp_device *aidev) +{ + struct rkaiisp_vdev_node *node = &aidev->vnode; + struct video_device *vdev = &node->vdev; + + kfifo_free(&aidev->idxbuf_kfifo); + rkaiisp_unregister_subdev(aidev); + video_unregister_device(vdev); + media_entity_cleanup(&vdev->entity); + vb2_queue_release(vdev->queue); +} + diff --git a/drivers/media/platform/rockchip/aiisp/aiisp.h b/drivers/media/platform/rockchip/aiisp/aiisp.h new file mode 100644 index 000000000000..f0ce2c0f3462 --- /dev/null +++ b/drivers/media/platform/rockchip/aiisp/aiisp.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKAIISP_DEV_H +#define _RKAIISP_DEV_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hw.h" + +#define DRIVER_NAME "rkaiisp" + +#define RKAIISP_SUBDEV_NAME DRIVER_NAME "-subdev" +#define RKAIISP_V4L2_EVENT_ELEMS 4 + +#define RKAIISP_MAX_CHANNEL 7 +#define RKAIISP_TMP_BUF_CNT 2 +#define RKAIISP_DEFAULT_MAXRUNCNT 8 +#define RKAIISP_DEFAULT_PARASIZE (16 * 1024) +#define RKAIISP_SW_REG_SIZE 0x3000 +#define RKAIISP_SW_MAX_SIZE (RKAIISP_SW_REG_SIZE * 2) + +enum rkaiisp_irqhdl_ret { + NOT_WREND = (0 << 0), + CONTINUE_RUN = (1 << 0), + RUN_COMPLETE = (2 << 0) +}; + +enum rkaiisp_hwstate { + HW_STOP, + HW_RUNNING +}; + +struct rkaiisp_vdev_node { + struct vb2_queue buf_queue; + struct video_device vdev; + struct media_pad pad; +}; + +struct rkaiisp_buffer { + struct vb2_v4l2_buffer vb; + struct list_head queue; + u32 buff_addr[VIDEO_MAX_PLANES]; + void *vaddr[VIDEO_MAX_PLANES]; +}; + +struct rkaiisp_dummy_buffer { + struct vb2_buffer vb; + struct vb2_queue vb2_queue; + s32 dma_fd; + struct dma_buf *dmabuf; + struct dma_buf_attachment *dba; + struct sg_table *sgt; + dma_addr_t dma_addr; + void *vaddr; + u32 size; + void *mem_priv; +}; + +struct rkaiisp_buffer_size { + u32 height; + u32 width; + u32 channel; + u32 stride; +}; + +struct rkaiiisp_subdev { + struct v4l2_subdev sd; + bool is_subs_evt; +}; + +struct rkaiisp_device { + char name[128]; + void *sw_base_addr; + struct v4l2_device v4l2_dev; + struct media_device media_dev; + struct device *dev; + + const struct vb2_mem_ops *mem_ops; + spinlock_t config_lock; + struct mutex apilock; + wait_queue_head_t sync_onoff; + + struct rkaiisp_hw_dev *hw_dev; + bool is_hw_link; + int dev_id; + + struct rkaiiisp_subdev subdev; + + struct rkaiisp_ispbuf_info ispbuf; + struct rkaiisp_dummy_buffer iirbuf[RKISP_BUFFER_MAX]; + struct rkaiisp_dummy_buffer aiprebuf[RKISP_BUFFER_MAX]; + struct rkaiisp_dummy_buffer vpslbuf[RKISP_BUFFER_MAX]; + struct rkaiisp_dummy_buffer aiispbuf[RKISP_BUFFER_MAX]; + struct rkaiisp_dummy_buffer temp_buf[RKAIISP_TMP_BUF_CNT]; + u32 outbuf_idx; + + struct kfifo idxbuf_kfifo; + struct rkisp_aiisp_st curr_idxbuf; + + struct rkaiisp_vdev_node vnode; + struct list_head params; + struct rkaiisp_buffer *cur_params; + struct v4l2_format vdev_fmt; + + struct rkaiisp_buffer_size outbuf_size[RKAIISP_MAX_RUNCNT]; + struct rkaiisp_buffer_size chn_size[RKAIISP_MAX_CHANNEL]; + enum rkaiisp_exemode exemode; + enum rkaiisp_model_mode model_mode; + enum rkaiisp_hwstate hwstate; + u32 para_size; + u32 max_runcnt; + u32 model_runcnt; + u32 run_idx; + u32 frame_id; + + bool streamon; + bool showreg; +}; + +extern int rkaiisp_debug; +extern int rkaiisp_showreg; + +static inline struct rkaiisp_buffer *to_rkaiisp_buffer(struct vb2_v4l2_buffer *vb) +{ + return container_of(vb, struct rkaiisp_buffer, vb); +} + +static inline void rkaiisp_write(struct rkaiisp_device *aidev, u32 reg, u32 val, bool is_direct) +{ + u32 *mem = aidev->sw_base_addr + reg; + u32 *flag = aidev->sw_base_addr + reg + RKAIISP_SW_REG_SIZE; + + *mem = val; + *flag = SW_REG_CACHE; + if (aidev->hw_dev->is_single || is_direct) { + *flag = SW_REG_CACHE_SYNC; + writel(val, aidev->hw_dev->base_addr + reg); + } +} + +static inline u32 rkaiisp_read(struct rkaiisp_device *aidev, u32 reg, bool is_direct) +{ + u32 val; + + if (aidev->hw_dev->is_single || is_direct) + val = readl(aidev->hw_dev->base_addr + reg); + else + val = *(u32 *)(aidev->sw_base_addr + reg); + + return val; +} + +extern struct platform_driver rkaiisp_plat_drv; +int rkaiisp_queue_ispbuf(struct rkaiisp_device *aidev, struct rkisp_aiisp_st *idxbuf); +void rkaiisp_update_list_reg(struct rkaiisp_device *aidev); +void rkaiisp_trigger(struct rkaiisp_device *aidev); +int rkaiisp_get_idxbuf_len(struct rkaiisp_device *aidev); +enum rkaiisp_irqhdl_ret rkaiisp_irq_hdl(struct rkaiisp_device *aidev, u32 mi_mis); +int rkaiisp_register_vdev(struct rkaiisp_device *aidev, struct v4l2_device *v4l2_dev); +void rkaiisp_unregister_vdev(struct rkaiisp_device *aidev); + +#endif diff --git a/drivers/media/platform/rockchip/aiisp/dev.c b/drivers/media/platform/rockchip/aiisp/dev.c new file mode 100644 index 000000000000..aed435bde556 --- /dev/null +++ b/drivers/media/platform/rockchip/aiisp/dev.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Rockchip Electronics Co., Ltd. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "regs.h" +#include "hw.h" +#include "aiisp.h" +#include "version.h" + +#define RKAIISP_VERNO_LEN 10 + +int rkaiisp_debug; +module_param_named(debug, rkaiisp_debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +int rkaiisp_showreg; +module_param_named(showreg, rkaiisp_showreg, int, 0644); +MODULE_PARM_DESC(showreg, "show register (0-1)"); + +static char rkaiisp_version[RKAIISP_VERNO_LEN]; +module_param_string(version, rkaiisp_version, RKAIISP_VERNO_LEN, 0444); +MODULE_PARM_DESC(version, "version number"); + +/***************************** platform deive *******************************/ +static int rkaiisp_attach_hw(struct rkaiisp_device *aidev) +{ + struct device_node *np; + struct platform_device *pdev; + struct rkaiisp_hw_dev *hw_dev; + const char *name; + int ret, dev_id = -1; + + of_property_read_string(aidev->dev->of_node, "name", &name); + if (name) { + ret = sscanf(name, "rkaiisp-vir%d", &dev_id); + if ((dev_id >= RKAIISP_DEV_MAX) || ret != 1) { + dev_err(aidev->dev, "dev_id %d, failed attach aidev hw, max dev:%d\n", + dev_id, RKAIISP_DEV_MAX); + return -EINVAL; + } + } + + np = of_parse_phandle(aidev->dev->of_node, "rockchip,hw", 0); + if (!np || !of_device_is_available(np)) { + dev_err(aidev->dev, "failed to get isp hw node\n"); + return -ENODEV; + } + + pdev = of_find_device_by_node(np); + of_node_put(np); + if (!pdev) { + dev_err(aidev->dev, "failed to get aidev hw from node\n"); + return -ENODEV; + } + + hw_dev = platform_get_drvdata(pdev); + if (!hw_dev) { + dev_err(aidev->dev, "failed attach aidev hw\n"); + return -EINVAL; + } + + mutex_lock(&hw_dev->dev_mutex); + if (hw_dev->dev_num >= RKAIISP_DEV_MAX) { + dev_err(aidev->dev, "failed attach aidev hw, max dev:%d\n", RKAIISP_DEV_MAX); + mutex_unlock(&hw_dev->dev_mutex); + return -EINVAL; + } + + if (dev_id == -1) + dev_id = hw_dev->dev_num; + + if (dev_id >= 0 && dev_id < RKAIISP_DEV_MAX) { + dev_id = array_index_nospec(dev_id, RKAIISP_DEV_MAX); + dev_info(aidev->dev, "dev_id %d\n", dev_id); + aidev->dev_id = dev_id; + hw_dev->aidev[dev_id] = aidev; + hw_dev->dev_num++; + aidev->hw_dev = hw_dev; + aidev->is_hw_link = true; + } + + hw_dev->is_single = (hw_dev->dev_num > 1) ? false : true; + mutex_unlock(&hw_dev->dev_mutex); + + return 0; +} + +static int rkaiisp_plat_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct v4l2_device *v4l2_dev; + struct rkaiisp_device *aidev; + int ret; + + snprintf(rkaiisp_version, sizeof(rkaiisp_version), + "v%02x.%02x.%02x", + RKAIISP_DRIVER_VERSION >> 16, + (RKAIISP_DRIVER_VERSION & 0xff00) >> 8, + RKAIISP_DRIVER_VERSION & 0x00ff); + + dev_info(dev, "rkaiisp driver version: %s\n", rkaiisp_version); + + aidev = devm_kzalloc(dev, sizeof(*aidev), GFP_KERNEL); + if (!aidev) + return -ENOMEM; + + dev_set_drvdata(dev, aidev); + aidev->dev = dev; + ret = rkaiisp_attach_hw(aidev); + if (ret) + return ret; + + aidev->sw_base_addr = devm_kzalloc(dev, RKAIISP_SW_MAX_SIZE, GFP_KERNEL); + if (!aidev->sw_base_addr) + return -ENOMEM; + + snprintf(aidev->media_dev.model, sizeof(aidev->media_dev.model), + "%s%d", DRIVER_NAME, aidev->dev_id); + strscpy(aidev->name, dev_name(dev), sizeof(aidev->name)); + strscpy(aidev->media_dev.driver_name, aidev->name, + sizeof(aidev->media_dev.driver_name)); + + mutex_init(&aidev->apilock); + aidev->media_dev.dev = dev; + + v4l2_dev = &aidev->v4l2_dev; + v4l2_dev->mdev = &aidev->media_dev; + strscpy(v4l2_dev->name, aidev->name, sizeof(v4l2_dev->name)); + + ret = v4l2_device_register(aidev->dev, &aidev->v4l2_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "Failed to register v4l2 device:%d\n", ret); + return ret; + } + + media_device_init(&aidev->media_dev); + ret = media_device_register(&aidev->media_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "Failed to register media device:%d\n", ret); + goto err_unreg_v4l2_dev; + } + + pm_runtime_enable(dev); + ret = rkaiisp_register_vdev(aidev, &aidev->v4l2_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "Failed to register platform subdevs:%d\n", ret); + goto err_unreg_media_dev; + } + + v4l2_info(v4l2_dev, "probe end.\n"); + return 0; + +err_unreg_media_dev: + media_device_unregister(&aidev->media_dev); +err_unreg_v4l2_dev: + v4l2_device_unregister(&aidev->v4l2_dev); + return ret; +} + +static int rkaiisp_plat_remove(struct platform_device *pdev) +{ + struct rkaiisp_device *aidev = platform_get_drvdata(pdev); + + aidev->is_hw_link = false; + aidev->hw_dev->aidev[aidev->dev_id] = NULL; + + pm_runtime_disable(&pdev->dev); + + media_device_unregister(&aidev->media_dev); + v4l2_device_unregister(&aidev->v4l2_dev); + rkaiisp_unregister_vdev(aidev); + media_device_cleanup(&aidev->media_dev); + return 0; +} + +static int __maybe_unused rkaiisp_runtime_suspend(struct device *dev) +{ + struct rkaiisp_device *aidev = dev_get_drvdata(dev); + int ret; + + mutex_lock(&aidev->hw_dev->dev_mutex); + ret = pm_runtime_put_sync(aidev->hw_dev->dev); + mutex_unlock(&aidev->hw_dev->dev_mutex); + return (ret > 0) ? 0 : ret; +} + +static int __maybe_unused rkaiisp_runtime_resume(struct device *dev) +{ + struct rkaiisp_device *aidev = dev_get_drvdata(dev); + int ret; + + mutex_lock(&aidev->hw_dev->dev_mutex); + ret = pm_runtime_get_sync(aidev->hw_dev->dev); + mutex_unlock(&aidev->hw_dev->dev_mutex); + return (ret > 0) ? 0 : ret; +} + +static const struct dev_pm_ops rkaiisp_plat_pm_ops = { + SET_RUNTIME_PM_OPS(rkaiisp_runtime_suspend, rkaiisp_runtime_resume, NULL) +}; + +static const struct of_device_id rkaiisp_plat_of_match[] = { + { + .compatible = "rockchip,rkaiisp-vir", + }, + {}, +}; + +struct platform_driver rkaiisp_plat_drv = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(rkaiisp_plat_of_match), + .pm = &rkaiisp_plat_pm_ops, + }, + .probe = rkaiisp_plat_probe, + .remove = rkaiisp_plat_remove, +}; + +MODULE_AUTHOR("Rockchip Camera/ISP team"); +MODULE_DESCRIPTION("Rockchip ISP platform driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(DMA_BUF); +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); diff --git a/drivers/media/platform/rockchip/aiisp/hw.c b/drivers/media/platform/rockchip/aiisp/hw.c new file mode 100644 index 000000000000..f355506880b7 --- /dev/null +++ b/drivers/media/platform/rockchip/aiisp/hw.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + +#define pr_fmt(fmt) "rkaiisp: %s:%d " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "regs.h" +#include "hw.h" +#include "aiisp.h" + +static struct rkaiisp_hw_dev *rkaiisp_hwdev; + +/* + * rkaiisp_hw share hardware resource with rkaiisp virtual device + * rkaiisp_device rkaiisp_device rkaiisp_device rkaiisp_device + * | | | | + * \ | | / + * -------------------------------------- + * | + * rkaiisp_hw + */ + +static irqreturn_t hw_irq_hdl(int irq, void *ctx) +{ + struct device *dev = ctx; + struct rkaiisp_hw_dev *hw_dev = dev_get_drvdata(dev); + struct rkaiisp_device *aidev = hw_dev->aidev[hw_dev->cur_dev_id]; + void __iomem *base = hw_dev->base_addr; + int i, max = 0, id = 0; + int len[RKAIISP_DEV_MAX] = { 0 }; + enum rkaiisp_irqhdl_ret irq_hdl_ret; + u32 mis_val; + + mis_val = readl(base + AIISP_MI_MIS); + if (mis_val) { + irq_hdl_ret = rkaiisp_irq_hdl(aidev, mis_val); + if (irq_hdl_ret == RUN_COMPLETE) { + spin_lock(&hw_dev->hw_lock); + for (i = 0; i < RKAIISP_DEV_MAX; i++) { + aidev = hw_dev->aidev[i]; + if (!aidev) + continue; + + if (!aidev->streamon) + continue; + + len[i] = rkaiisp_get_idxbuf_len(aidev); + if (max < len[i]) { + max = len[i]; + id = i; + } + } + spin_unlock(&hw_dev->hw_lock); + + if (max > 0) { + spin_lock(&hw_dev->hw_lock); + hw_dev->is_idle = false; + hw_dev->cur_dev_id = id; + aidev = hw_dev->aidev[hw_dev->cur_dev_id]; + spin_unlock(&hw_dev->hw_lock); + v4l2_dbg(1, rkaiisp_debug, &aidev->v4l2_dev, + "trigger aidev: %d, idxbuf len: %d\n", + hw_dev->cur_dev_id, max); + rkaiisp_trigger(aidev); + } else { + spin_lock(&hw_dev->hw_lock); + hw_dev->is_idle = true; + spin_unlock(&hw_dev->hw_lock); + } + } + } + + return IRQ_HANDLED; +} + +static int rkaiisp_register_irq(struct rkaiisp_hw_dev *hw_dev) +{ + const struct aiisp_match_data *match_data = hw_dev->match_data; + struct platform_device *pdev = hw_dev->pdev; + struct device *dev = &pdev->dev; + int ret, irq; + + irq = platform_get_irq_byname(pdev, "irq"); + if (irq < 0) { + dev_err(dev, "no irq %s in dts\n", + match_data->irqs[0].name); + return irq; + } + + ret = devm_request_irq(dev, irq, + match_data->irqs[0].irq_hdl, + 0, + dev_driver_string(dev), + dev); + if (ret < 0) { + dev_err(dev, "request %s failed: %d\n", + match_data->irqs[0].name, ret); + return ret; + } + + return 0; +} + +int rkaiisp_ispidx_queue(int dev_id, struct rkisp_aiisp_st *idxbuf) +{ + struct rkaiisp_hw_dev *hw_dev = rkaiisp_hwdev; + struct rkaiisp_device *aidev = NULL; + int i; + + if (!hw_dev) { + pr_err("Can not find hwdev!"); + return -EINVAL; + } + + for (i = 0; i < hw_dev->dev_num; i++) { + if (hw_dev->aidev[i]) { + if ((hw_dev->aidev[i]->is_hw_link) && hw_dev->aidev[i]->dev_id == dev_id) { + aidev = hw_dev->aidev[i]; + break; + } + } + } + + if (!aidev) { + pr_err("Can not find aidev for dev_id %d!", dev_id); + return -EINVAL; + } + + if (aidev->exemode != BOTHEVENT_TO_AIQ) { + pr_err("aidev %d exemode(%d) is not right!", dev_id, aidev->exemode); + return -EINVAL; + } + + return rkaiisp_queue_ispbuf(aidev, idxbuf); +} +EXPORT_SYMBOL(rkaiisp_ispidx_queue); + +static const char * const rv1126b_clks[] = { + "clk_aiisp_core", + "aclk_aiisp", + "hclk_aiisp", +}; + +static const struct aiisp_clk_info rv1126b_clk_rate[] = { + { + .clk_rate = 400, + .refer_data = 1920, //width + }, { + .clk_rate = 400, + .refer_data = 2688, + }, { + .clk_rate = 500, + .refer_data = 3072, + }, { + .clk_rate = 600, + .refer_data = 3840, + }, { + .clk_rate = 702, + .refer_data = 4672, + } +}; + +static struct aiisp_irqs_data rv1126b_irqs[] = { + {"irq", hw_irq_hdl}, +}; + +static const struct aiisp_match_data rv1126b_match_data = { + .clks = rv1126b_clks, + .num_clks = ARRAY_SIZE(rv1126b_clks), + .clk_rate_tbl = rv1126b_clk_rate, + .num_clk_rate_tbl = ARRAY_SIZE(rv1126b_clk_rate), + .irqs = rv1126b_irqs, + .num_irqs = ARRAY_SIZE(rv1126b_irqs), +}; + +static const struct of_device_id rkaiisp_hw_of_match[] = { + { + .compatible = "rockchip,rv1126b-rkaiisp", + .data = &rv1126b_match_data, + }, + {}, +}; + +static inline bool is_iommu_enable(struct device *dev) +{ + struct device_node *iommu; + + iommu = of_parse_phandle(dev->of_node, "iommus", 0); + if (!iommu) { + dev_info(dev, "no iommu attached, using non-iommu buffers\n"); + return false; + } else if (!of_device_is_available(iommu)) { + dev_info(dev, "iommu is disabled, using iommu buffers\n"); + of_node_put(iommu); + return false; + } + of_node_put(iommu); + + return true; +} + +static void rkaiisp_soft_reset(struct rkaiisp_hw_dev *dev, bool is_secure) +{ + void __iomem *base = dev->base_addr; + + if (is_secure) { + /* if aidev working, cru reset isn't secure. + * aidev soft reset first to protect aidev reset. + */ + writel(0x1, base + AIISP_CORE_SOFT_RST); + udelay(10); + } + + if (dev->reset) { + reset_control_assert(dev->reset); + udelay(10); + reset_control_deassert(dev->reset); + udelay(10); + } + + /* refresh iommu after reset */ + if (dev->is_mmu) { + rockchip_iommu_disable(dev->dev); + rockchip_iommu_enable(dev->dev); + } +} + +static void disable_sys_clk(struct rkaiisp_hw_dev *dev) +{ + int i; + + for (i = dev->num_clks - 1; i >= 0; i--) + if (!IS_ERR(dev->clks[i])) + clk_disable_unprepare(dev->clks[i]); +} + +static int enable_sys_clk(struct rkaiisp_hw_dev *dev) +{ + int i, ret = -EINVAL; + + for (i = 0; i < dev->num_clks; i++) { + if (!IS_ERR(dev->clks[i])) { + ret = clk_prepare_enable(dev->clks[i]); + if (ret < 0) + goto err; + } + } + + rkaiisp_soft_reset(dev, false); + return 0; +err: + for (--i; i >= 0; --i) + if (!IS_ERR(dev->clks[i])) + clk_disable_unprepare(dev->clks[i]); + return ret; +} + +static int rkaiisp_hw_probe(struct platform_device *pdev) +{ + const struct aiisp_match_data *match_data; + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct rkaiisp_hw_dev *hw_dev; + struct resource *res; + int i, ret; + bool is_mem_reserved = true; + + match_data = of_device_get_match_data(dev); + hw_dev = devm_kzalloc(dev, sizeof(*hw_dev), GFP_KERNEL); + if (!hw_dev) + return -ENOMEM; + + dev_set_drvdata(dev, hw_dev); + hw_dev->dev = dev; + + hw_dev->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf"); + if (IS_ERR(hw_dev->grf)) + dev_warn(dev, "Missing rockchip,grf property\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "get resource failed\n"); + ret = -EINVAL; + goto err; + } + hw_dev->base_addr = devm_ioremap_resource(dev, res); + if (PTR_ERR(hw_dev->base_addr) == -EBUSY) { + resource_size_t offset = res->start; + resource_size_t size = resource_size(res); + + hw_dev->base_addr = devm_ioremap(dev, offset, size); + } + if (IS_ERR(hw_dev->base_addr)) { + dev_err(dev, "ioremap failed\n"); + ret = PTR_ERR(hw_dev->base_addr); + goto err; + } + + hw_dev->pdev = pdev; + hw_dev->match_data = match_data; + + for (i = 0; i < match_data->num_clks; i++) { + struct clk *clk = devm_clk_get(dev, match_data->clks[i]); + + if (IS_ERR(clk)) { + dev_err(dev, "failed to get %s\n", match_data->clks[i]); + ret = PTR_ERR(clk); + goto err; + } + hw_dev->clks[i] = clk; + } + hw_dev->num_clks = match_data->num_clks; + hw_dev->clk_rate_tbl = match_data->clk_rate_tbl; + hw_dev->num_clk_rate_tbl = match_data->num_clk_rate_tbl; + + hw_dev->reset = devm_reset_control_array_get(dev, false, false); + if (IS_ERR(hw_dev->reset)) { + dev_dbg(dev, "failed to get reset\n"); + hw_dev->reset = NULL; + } + + hw_dev->dev_num = 0; + hw_dev->cur_dev_id = 0; + mutex_init(&hw_dev->dev_mutex); + spin_lock_init(&hw_dev->hw_lock); + atomic_set(&hw_dev->refcnt, 0); + + hw_dev->is_idle = true; + hw_dev->is_single = true; + hw_dev->is_dma_contig = true; + hw_dev->is_dma_sg_ops = true; + hw_dev->is_shutdown = false; + hw_dev->is_mmu = is_iommu_enable(dev); + ret = of_reserved_mem_device_init(dev); + if (ret) { + is_mem_reserved = false; + if (!hw_dev->is_mmu) + dev_info(dev, "No reserved memory region. default cma area!\n"); + } + if (hw_dev->is_mmu && !is_mem_reserved) + hw_dev->is_dma_contig = false; + hw_dev->mem_ops = &vb2_cma_sg_memops; + + rkaiisp_hwdev = hw_dev; + rkaiisp_register_irq(hw_dev); + pm_runtime_enable(dev); + + dev_info(dev, "probe end.\n"); + return 0; +err: + return ret; +} + +static int rkaiisp_hw_remove(struct platform_device *pdev) +{ + struct rkaiisp_hw_dev *hw_dev = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + mutex_destroy(&hw_dev->dev_mutex); + rkaiisp_hwdev = NULL; + return 0; +} + +static void rkaiisp_hw_shutdown(struct platform_device *pdev) +{ + struct rkaiisp_hw_dev *hw_dev = platform_get_drvdata(pdev); + + hw_dev->is_shutdown = true; +} + +static int __maybe_unused rkaiisp_runtime_suspend(struct device *dev) +{ + struct rkaiisp_hw_dev *hw_dev = dev_get_drvdata(dev); + + hw_dev->is_idle = true; + disable_sys_clk(hw_dev); + return pinctrl_pm_select_sleep_state(dev); +} + +static int __maybe_unused rkaiisp_runtime_resume(struct device *dev) +{ + struct rkaiisp_hw_dev *hw_dev = dev_get_drvdata(dev); + int ret; + + ret = pinctrl_pm_select_default_state(dev); + if (ret < 0) + return ret; + + enable_sys_clk(hw_dev); + + return 0; +} + +static const struct dev_pm_ops rkaiisp_hw_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(rkaiisp_runtime_suspend, rkaiisp_runtime_resume, NULL) +}; + +static struct platform_driver rkaiisp_hw_drv = { + .driver = { + .name = "rkaiisp_hw", + .of_match_table = of_match_ptr(rkaiisp_hw_of_match), + .pm = &rkaiisp_hw_pm_ops, + }, + .probe = rkaiisp_hw_probe, + .remove = rkaiisp_hw_remove, + .shutdown = rkaiisp_hw_shutdown, +}; + +static int __init rkaiisp_hw_drv_init(void) +{ + int ret = 0; + + ret = platform_driver_register(&rkaiisp_hw_drv); + if (!ret) + ret = platform_driver_register(&rkaiisp_plat_drv); + + return ret; +} + +static void __exit rkaiisp_hw_drv_exit(void) +{ + platform_driver_unregister(&rkaiisp_plat_drv); + platform_driver_unregister(&rkaiisp_hw_drv); +} + +module_init(rkaiisp_hw_drv_init); +module_exit(rkaiisp_hw_drv_exit); diff --git a/drivers/media/platform/rockchip/aiisp/hw.h b/drivers/media/platform/rockchip/aiisp/hw.h new file mode 100644 index 000000000000..c91b595fcb48 --- /dev/null +++ b/drivers/media/platform/rockchip/aiisp/hw.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKAIISP_HW_H +#define _RKAIISP_HW_H + +#include +#include + +#define RKAIISP_MAX_BUS_CLK 10 + +enum rkaiisp_dev { + RKAIISP_DEV_ID0 = 0, + RKAIISP_DEV_ID1, + RKAIISP_DEV_ID2, + RKAIISP_DEV_ID3, + RKAIISP_DEV_ID4, + RKAIISP_DEV_ID5, + RKAIISP_DEV_ID6, + RKAIISP_DEV_ID7, + RKAIISP_DEV_MAX, +}; + +enum rkaiisp_sw_reg { + SW_REG_CACHE = 0xffffffff, + SW_REG_CACHE_SYNC = 0xeeeeeeee, +}; + +struct aiisp_irqs_data { + const char *name; + irqreturn_t (*irq_hdl)(int irq, void *ctx); +}; + +struct aiisp_clk_info { + u32 clk_rate; + u32 refer_data; +}; + +struct aiisp_match_data { + const char * const *clks; + int num_clks; + const struct aiisp_clk_info *clk_rate_tbl; + int num_clk_rate_tbl; + struct aiisp_irqs_data *irqs; + int num_irqs; +}; + +struct rkaiisp_hw_dev { + const struct aiisp_match_data *match_data; + struct platform_device *pdev; + struct device *dev; + struct regmap *grf; + void __iomem *base_addr; + struct clk *clks[RKAIISP_MAX_BUS_CLK]; + int num_clks; + const struct aiisp_clk_info *clk_rate_tbl; + int num_clk_rate_tbl; + struct reset_control *reset; + + struct rkaiisp_device *aidev[RKAIISP_DEV_MAX]; + int dev_num; + int cur_dev_id; + + /* lock for multi dev */ + struct mutex dev_mutex; + spinlock_t hw_lock; + atomic_t refcnt; + const struct vb2_mem_ops *mem_ops; + + bool is_dma_contig; + bool is_dma_sg_ops; + bool is_mmu; + bool is_idle; + bool is_single; + bool is_shutdown; +}; + +int rkaiisp_ispidx_queue(int dev_id, struct rkisp_aiisp_st *idxbuf); + +#endif diff --git a/drivers/media/platform/rockchip/aiisp/regs.h b/drivers/media/platform/rockchip/aiisp/regs.h new file mode 100644 index 000000000000..135ad4772465 --- /dev/null +++ b/drivers/media/platform/rockchip/aiisp/regs.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKAIISP_REGS_H +#define _RKAIISP_REGS_H + +#define AIISP_CORE_BASE 0x00000000 +#define AIISP_CORE_CTRL (AIISP_CORE_BASE + 0x00000) +#define AIISP_CORE_SOFT_RST (AIISP_CORE_BASE + 0x00008) +#define AIISP_CORE_LEVEL_CTRL0 (AIISP_CORE_BASE + 0x00020) +#define AIISP_CORE_LEVEL_CTRL1 (AIISP_CORE_BASE + 0x00024) +#define AIISP_CORE_LEVEL_CTRL2 (AIISP_CORE_BASE + 0x00028) +#define AIISP_CORE_LEVEL_CTRL3 (AIISP_CORE_BASE + 0x0002c) +#define AIISP_CORE_OUT_CTRL (AIISP_CORE_BASE + 0x00040) +#define AIISP_CORE_NOISE_LMT (AIISP_CORE_BASE + 0x00044) +#define AIISP_CORE_COMP0 (AIISP_CORE_BASE + 0x00200) +#define AIISP_CORE_COMP1 (AIISP_CORE_BASE + 0x00204) +#define AIISP_CORE_COMP2 (AIISP_CORE_BASE + 0x00208) +#define AIISP_CORE_COMP3 (AIISP_CORE_BASE + 0x0020c) +#define AIISP_CORE_COMP4 (AIISP_CORE_BASE + 0x00210) +#define AIISP_CORE_COMP5 (AIISP_CORE_BASE + 0x00214) +#define AIISP_CORE_COMP6 (AIISP_CORE_BASE + 0x00218) +#define AIISP_CORE_COMP7 (AIISP_CORE_BASE + 0x0021c) +#define AIISP_CORE_COMP8 (AIISP_CORE_BASE + 0x00220) +#define AIISP_CORE_COMP9 (AIISP_CORE_BASE + 0x00224) +#define AIISP_CORE_COMP10 (AIISP_CORE_BASE + 0x00228) +#define AIISP_CORE_COMP11 (AIISP_CORE_BASE + 0x0022c) +#define AIISP_CORE_COMP12 (AIISP_CORE_BASE + 0x00230) +#define AIISP_CORE_COMP13 (AIISP_CORE_BASE + 0x00234) +#define AIISP_CORE_COMP14 (AIISP_CORE_BASE + 0x00238) +#define AIISP_CORE_COMP15 (AIISP_CORE_BASE + 0x0023c) +#define AIISP_CORE_COMP16 (AIISP_CORE_BASE + 0x00240) +#define AIISP_CORE_DECOMP0 (AIISP_CORE_BASE + 0x00280) +#define AIISP_CORE_DECOMP1 (AIISP_CORE_BASE + 0x00284) +#define AIISP_CORE_DECOMP2 (AIISP_CORE_BASE + 0x00288) +#define AIISP_CORE_DECOMP3 (AIISP_CORE_BASE + 0x0028c) +#define AIISP_CORE_DECOMP4 (AIISP_CORE_BASE + 0x00290) +#define AIISP_CORE_DECOMP5 (AIISP_CORE_BASE + 0x00294) +#define AIISP_CORE_DECOMP6 (AIISP_CORE_BASE + 0x00298) +#define AIISP_CORE_DECOMP7 (AIISP_CORE_BASE + 0x0029c) +#define AIISP_CORE_DECOMP8 (AIISP_CORE_BASE + 0x002a0) +#define AIISP_CORE_DECOMP9 (AIISP_CORE_BASE + 0x002a4) +#define AIISP_CORE_DECOMP10 (AIISP_CORE_BASE + 0x002a8) +#define AIISP_CORE_DECOMP11 (AIISP_CORE_BASE + 0x002ac) +#define AIISP_CORE_DECOMP12 (AIISP_CORE_BASE + 0x002b0) +#define AIISP_CORE_DECOMP13 (AIISP_CORE_BASE + 0x002b4) +#define AIISP_CORE_DECOMP14 (AIISP_CORE_BASE + 0x002b8) +#define AIISP_CORE_DECOMP15 (AIISP_CORE_BASE + 0x002bc) +#define AIISP_CORE_DECOMP16 (AIISP_CORE_BASE + 0x002c0) + +#define AIISP_MI_BASE 0x00002000 +#define AIISP_MI_HURRY_CTRL (AIISP_MI_BASE + 0x00040) +#define AIISP_MI_ARQOS_CTRL (AIISP_MI_BASE + 0x00044) +#define AIISP_MI_AWQOS_CTRL (AIISP_MI_BASE + 0x00048) +#define AIISP_MI_IMSC (AIISP_MI_BASE + 0x00050) +#define AIISP_MI_RIS (AIISP_MI_BASE + 0x00054) +#define AIISP_MI_MIS (AIISP_MI_BASE + 0x00058) +#define AIISP_MI_ICR (AIISP_MI_BASE + 0x0005c) +#define AIISP_MI_ISR (AIISP_MI_BASE + 0x00060) +#define AIISP_MI_RD_START (AIISP_MI_BASE + 0x00100) +#define AIISP_MI_CTRL (AIISP_MI_BASE + 0x00104) +#define AIISP_MI_SLICE_CTRL (AIISP_MI_BASE + 0x00180) +#define AIISP_MI_MANUAL_CTRL (AIISP_MI_BASE + 0x00184) +#define AIISP_MI_CORE_WIDTH (AIISP_MI_BASE + 0x001a0) +#define AIISP_MI_CORE_HEIGHT (AIISP_MI_BASE + 0x001a4) +#define AIISP_MI_RD_CH0_CTRL (AIISP_MI_BASE + 0x00200) +#define AIISP_MI_RD_CH0_BASE (AIISP_MI_BASE + 0x00204) +#define AIISP_MI_RD_CH0_HEIGHT (AIISP_MI_BASE + 0x0020c) +#define AIISP_MI_RD_CH0_STRIDE (AIISP_MI_BASE + 0x00210) +#define AIISP_MI_RD_CH1_CTRL (AIISP_MI_BASE + 0x00300) +#define AIISP_MI_RD_CH1_BASE (AIISP_MI_BASE + 0x00304) +#define AIISP_MI_RD_CH1_HEIGHT (AIISP_MI_BASE + 0x0030c) +#define AIISP_MI_RD_CH1_STRIDE (AIISP_MI_BASE + 0x00310) +#define AIISP_MI_RD_CH2_CTRL (AIISP_MI_BASE + 0x00400) +#define AIISP_MI_RD_CH2_BASE (AIISP_MI_BASE + 0x00404) +#define AIISP_MI_RD_CH2_HEIGHT (AIISP_MI_BASE + 0x0040c) +#define AIISP_MI_RD_CH2_STRIDE (AIISP_MI_BASE + 0x00410) +#define AIISP_MI_RD_CH3_CTRL (AIISP_MI_BASE + 0x00500) +#define AIISP_MI_RD_CH3_BASE (AIISP_MI_BASE + 0x00504) +#define AIISP_MI_RD_CH3_HEIGHT (AIISP_MI_BASE + 0x0050c) +#define AIISP_MI_RD_CH3_STRIDE (AIISP_MI_BASE + 0x00510) +#define AIISP_MI_RD_CH4_CTRL (AIISP_MI_BASE + 0x00600) +#define AIISP_MI_RD_CH4_BASE (AIISP_MI_BASE + 0x00604) +#define AIISP_MI_RD_CH4_HEIGHT (AIISP_MI_BASE + 0x0060c) +#define AIISP_MI_RD_CH4_STRIDE (AIISP_MI_BASE + 0x00610) +#define AIISP_MI_RD_CH5_CTRL (AIISP_MI_BASE + 0x00700) +#define AIISP_MI_RD_CH5_BASE (AIISP_MI_BASE + 0x00704) +#define AIISP_MI_RD_CH5_HEIGHT (AIISP_MI_BASE + 0x0070c) +#define AIISP_MI_RD_CH5_STRIDE (AIISP_MI_BASE + 0x00710) +#define AIISP_MI_RD_CH6_CTRL (AIISP_MI_BASE + 0x00800) +#define AIISP_MI_RD_CH6_BASE (AIISP_MI_BASE + 0x00804) +#define AIISP_MI_RD_CH6_HEIGHT (AIISP_MI_BASE + 0x0080c) +#define AIISP_MI_RD_CH6_STRIDE (AIISP_MI_BASE + 0x00810) +#define AIISP_MI_RD_KWT_CTRL (AIISP_MI_BASE + 0x00900) +#define AIISP_MI_RD_KWT_BASE (AIISP_MI_BASE + 0x00904) +#define AIISP_MI_RD_KWT_WIDTH (AIISP_MI_BASE + 0x00908) +#define AIISP_MI_RD_KWT_HEIGHT (AIISP_MI_BASE + 0x0090c) +#define AIISP_MI_RD_KWT_STRIDE (AIISP_MI_BASE + 0x00910) +#define AIISP_MI_WR_CTRL (AIISP_MI_BASE + 0x00a00) +#define AIISP_MI_WR_INIT (AIISP_MI_BASE + 0x00a04) +#define AIISP_MI_CHN0_WR_CRTL (AIISP_MI_BASE + 0x00a08) +#define AIISP_MI_CHN0_WR_BASE (AIISP_MI_BASE + 0x00a10) +#define AIISP_MI_CHN0_WR_SIZE (AIISP_MI_BASE + 0x00a24) +#define AIISP_MI_CHN0_WR_STRIDE (AIISP_MI_BASE + 0x00a28) + + +#define AIISP_MI_ISR_BUSERR BIT(0) +#define AIISP_MI_ISR_WREND BIT(3) +#define AIISP_MI_ISR_ALL (AIISP_MI_ISR_BUSERR | AIISP_MI_ISR_WREND) + +#define AIISP_MI_RD_START_EN BIT(0) +#define AIISP_MI_RD_KWT_EN BIT(0) +#define AIISP_MI_RD_CH_EN BIT(0) + +#define AIISP_MI_RD_CH0_GROUP_MODE 2 +#define AIISP_MI_RD_CH1_GROUP_MODE 2 +#define AIISP_MI_RD_CH2_GROUP_MODE 2 +#define AIISP_MI_RD_CH3_GROUP_MODE 0 +#define AIISP_MI_RD_CH4_GROUP_MODE 0 +#define AIISP_MI_RD_CH5_GROUP_MODE 0 +#define AIISP_MI_RD_CH6_GROUP_MODE 0 +#define AIISP_MI_RD_KWT_GROUP_MODE 0 +#define AIISP_MI_WR_GROUP_MODE 2 + +#define AIISP_MI_WR_INIT_BASE_EN BIT(4) +#define AIISP_MI_WR_INIT_OFFSET_EN BIT(5) + +#define AIISP_MI_CHN0_WR_EN BIT(0) +#define AIISP_MI_CHN0_WR_AUTOUPD BIT(1) + +#define AIISP_MI_CHN0SELF_FORCE_UPD BIT(4) + +#define AIISP_MI_RD_ALWAYS_ON BIT(0) +#define AIISP_MI_WR_ALWAYS_ON BIT(4) + +#endif /* _RKAIISP_REGS_H */ diff --git a/drivers/media/platform/rockchip/aiisp/version.h b/drivers/media/platform/rockchip/aiisp/version.h new file mode 100644 index 000000000000..c6b0b8b404bb --- /dev/null +++ b/drivers/media/platform/rockchip/aiisp/version.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2025 Rockchip Electronics Co., Ltd. */ + +#ifndef _RKAIISP_VERSION_H +#define _RKAIISP_VERSION_H +#include + +/* + *RKAIISP DRIVER VERSION NOTE + * + *v0.1.1: + *1. First version; + * + */ + +#define RKAIISP_DRIVER_VERSION KERNEL_VERSION(0, 1, 0x1) + +#endif diff --git a/include/uapi/linux/rk-aiisp-config.h b/include/uapi/linux/rk-aiisp-config.h new file mode 100644 index 000000000000..35d22efa040d --- /dev/null +++ b/include/uapi/linux/rk-aiisp-config.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: (GPL-2.0+ WITH Linux-syscall-note) OR MIT + * + * Rockchip AIISP + * Copyright (C) 2025 Rockchip Electronics Co., Ltd. + */ + +#ifndef _UAPI_RK_AIISP_CONFIG_H +#define _UAPI_RK_AIISP_CONFIG_H + +#include + +#define RKAIISP_PYRAMID_LAYER_NUM 4 +#define RKAIISP_MAX_RUNCNT 8 +#define RKAIISP_MAX_ISPBUF 8 +#define RKAIISP_MODEL_UPDATE 0x01 +#define RKAIISP_OTHER_UPDATE 0x02 + +#define RKAIISP_CMD_SET_PARAM_INFO \ + _IOW('V', BASE_VIDIOC_PRIVATE + 0, struct rkaiisp_param_info) + +#define RKAIISP_CMD_INIT_BUFPOOL \ + _IOW('V', BASE_VIDIOC_PRIVATE + 1, struct rkaiisp_ispbuf_info) + +#define RKAIISP_CMD_FREE_BUFPOOL \ + _IO('V', BASE_VIDIOC_PRIVATE + 2) + +#define RKAIISP_CMD_QUEUE_BUF \ + _IOW('V', BASE_VIDIOC_PRIVATE + 3, struct rkisp_aiisp_st) + +/**********************EVENT_PRIVATE***************************/ +#define RKAIISP_V4L2_EVENT_AIISP_DONE (V4L2_EVENT_PRIVATE_START + 1) + +enum rkaiisp_chn_src { + ISP_IIR, + VPSL_YRAW_CHN0, + VPSL_YRAW_CHN1, + VPSL_YRAW_CHN2, + VPSL_YRAW_CHN3, + VPSL_YRAW_CHN4, + VPSL_YRAW_CHN5, + VPSL_SIG_CHN0, + VPSL_SIG_CHN1, + VPSL_SIG_CHN2, + VPSL_SIG_CHN3, + VPSL_SIG_CHN4, + ISP_AIPRE_NARMAP, + AIISP_LAST_OUT +}; + +enum rkaiisp_model_mode { + SINGLE_MODE, + COMBO_MODE, + SINGLEX2_MODE +}; + +enum rkaiisp_exemode { + BOTHEVENT_TO_AIQ, + ISPEVENT_IN_KERNEL, + BOTHEVENT_IN_KERNEL +}; + +struct rkaiisp_param_info { + enum rkaiisp_exemode exemode; + __u32 para_size; + __u32 max_runcnt; +} __attribute__ ((packed)); + +struct rkaiisp_ispbuf_info { + struct rkisp_bnr_buf_info bnr_buf; + __u32 iir_width; + __u32 iir_height; + __u32 raw_width[6]; + __u32 raw_height[6]; + __u32 sig_width[5]; + __u32 sig_height[5]; + __u32 narmap_width; + __u32 narmap_height; +} __attribute__ ((packed)); + +struct rkaiisp_other_cfg { + __u16 sw_neg_noiselimit; + __u16 sw_pos_noiselimit; + + __u16 sw_prev_blacklvl; + __u16 sw_post_blacklvl; + + __u16 sw_in_comp_y[33]; + __u16 sw_out_decomp_y[33]; +} __attribute__ ((packed)); + +struct rkaiisp_model_cfg { + enum rkaiisp_chn_src mi_chn_src[7]; + __u32 sw_aiisp_mode; + __u32 sw_aiisp_level_num; + __u32 sw_aiisp_l1_level_num; + __u32 sw_aiisp_op_mode; + __u32 sw_aiisp_drop_en; + __u32 sw_aiisp_lv_active[16]; + __u32 sw_aiisp_lv_mode[16]; + __u32 sw_mi_chn_en[7]; + __u32 sw_mi_chn_mode[7]; + __u32 sw_mi_chn_num[7]; + __u32 sw_mi_chn_data_mode[7]; + __u32 sw_mi_chn1_sel; + __u32 sw_mi_chn3_sel; + __u32 sw_out_d2s_en; + __u32 sw_out_mode; + __u32 sw_lastlvlm1_clip8bit; +} __attribute__ ((packed)); + +struct rkaiisp_kwt_cfg { + __u32 kwt_offet[RKAIISP_MAX_RUNCNT]; + __u32 kwt_size[RKAIISP_MAX_RUNCNT]; + __u32 kwt_pad_size[RKAIISP_MAX_RUNCNT]; +} __attribute__ ((packed)); + +struct rkaiisp_params { + __u32 frame_id; + __u32 module_update; + __u32 model_runcnt; + enum rkaiisp_model_mode model_mode; + + struct rkaiisp_other_cfg other_cfg; + struct rkaiisp_model_cfg model_cfg[RKAIISP_MAX_RUNCNT]; + struct rkaiisp_kwt_cfg kwt_cfg; + __u8 reserved[36]; +} __attribute__ ((packed)); + +struct rkaiisp_model_info { + __u32 checksum; + __u32 model_runcnt; + float model_qr; + enum rkaiisp_model_mode model_mode; + + struct rkaiisp_model_cfg model_cfg[RKAIISP_MAX_RUNCNT]; + struct rkaiisp_kwt_cfg kwt_cfg; + __u8 reserved[48]; +} __attribute__ ((packed)); + +#endif /* _UAPI_RK_AIISP_CONFIG_H */