// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2020 Rockchip Electronics Co., Ltd. */ #include #include #include #include #include #include #include #include #include "dev.h" #include "regs.h" #define STREAM_MAX_MP_RSZ_OUTPUT_WIDTH 4416 #define STREAM_MAX_MP_RSZ_OUTPUT_HEIGHT 3312 #define STREAM_MAX_SP_RSZ_OUTPUT_WIDTH 1920 #define STREAM_MAX_SP_RSZ_OUTPUT_HEIGHT 1080 #define STREAM_MIN_RSZ_OUTPUT_WIDTH 32 #define STREAM_MIN_RSZ_OUTPUT_HEIGHT 16 #define STREAM_OUTPUT_STEP_WISE 8 #define STREAM_MIN_MP_SP_INPUT_WIDTH 32 #define STREAM_MIN_MP_SP_INPUT_HEIGHT 32 static int hdr_dma_frame(struct rkisp_device *dev) { int max_dma; switch (dev->hdr.op_mode) { case HDR_FRAMEX2_DDR: case HDR_LINEX2_DDR: case HDR_RDBK_FRAME1: max_dma = 1; break; case HDR_FRAMEX3_DDR: case HDR_LINEX3_DDR: case HDR_RDBK_FRAME2: max_dma = 2; break; case HDR_RDBK_FRAME3: max_dma = HDR_DMA_MAX; break; case HDR_LINEX2_NO_DDR: case HDR_NORMAL: default: max_dma = 0; } return max_dma; } static int rkisp_create_hdr_buf(struct rkisp_device *dev) { int i, j, max_dma, max_buf = 1; struct rkisp_dummy_buffer *buf; struct rkisp_stream *stream; u32 size; stream = &dev->cap_dev.stream[RKISP_STREAM_DMATX0]; size = stream->out_fmt.plane_fmt[0].sizeimage; max_dma = hdr_dma_frame(dev); /* hdr read back mode using base and shd address * this support multi-buffer */ if (IS_HDR_RDBK(dev->hdr.op_mode)) { if (!dev->dmarx_dev.trigger) max_buf = HDR_MAX_DUMMY_BUF; else max_buf = 0; } for (i = 0; i < max_dma; i++) { for (j = 0; j < max_buf; j++) { buf = &dev->hdr.dummy_buf[i][j]; buf->size = size; if (rkisp_alloc_buffer(dev, buf) < 0) { v4l2_err(&dev->v4l2_dev, "Failed to allocate the memory for hdr buffer\n"); return -ENOMEM; } hdr_qbuf(&dev->hdr.q_tx[i], buf); v4l2_dbg(1, rkisp_debug, &dev->v4l2_dev, "hdr buf[%d][%d]:0x%x\n", i, j, (u32)buf->dma_addr); } dev->hdr.index[i] = i; } /* * normal: q_tx[0] to dma0 * q_tx[1] to dma1 * rdbk1: using dma2 q_tx[0] to dma2 * rdbk2: using dma0 (as M), dma2 (as S) * q_tx[0] to dma0 * q_tx[1] to dma2 * rdbk3: using dma0 (as M), dam1 (as L), dma2 (as S) * q_tx[0] to dma0 * q_tx[1] to dma1 * q_tx[2] to dma2 */ if (dev->hdr.op_mode == HDR_RDBK_FRAME1) { dev->hdr.index[HDR_DMA2] = 0; dev->hdr.index[HDR_DMA0] = 1; dev->hdr.index[HDR_DMA1] = 2; } else if (dev->hdr.op_mode == HDR_RDBK_FRAME2) { dev->hdr.index[HDR_DMA0] = 0; dev->hdr.index[HDR_DMA2] = 1; dev->hdr.index[HDR_DMA1] = 2; } v4l2_dbg(1, rkisp_debug, &dev->v4l2_dev, "hdr:%d buf index dma0:%d dma1:%d dma2:%d\n", max_dma, dev->hdr.index[HDR_DMA0], dev->hdr.index[HDR_DMA1], dev->hdr.index[HDR_DMA2]); return 0; } void hdr_destroy_buf(struct rkisp_device *dev) { int i, j; struct rkisp_dummy_buffer *buf; if (atomic_read(&dev->cap_dev.refcnt) > 1 || !dev->active_sensor || (dev->active_sensor && dev->active_sensor->mbus.type != V4L2_MBUS_CSI2) || (dev->isp_inp & INP_CIF) || (dev->isp_ver != ISP_V20 && dev->isp_ver != ISP_V21)) return; atomic_set(&dev->hdr.refcnt, 0); for (i = 0; i < HDR_DMA_MAX; i++) { buf = dev->hdr.rx_cur_buf[i]; if (buf) { rkisp_free_buffer(dev, buf); dev->hdr.rx_cur_buf[i] = NULL; } for (j = 0; j < HDR_MAX_DUMMY_BUF; j++) { buf = hdr_dqbuf(&dev->hdr.q_tx[i]); if (buf) rkisp_free_buffer(dev, buf); buf = hdr_dqbuf(&dev->hdr.q_rx[i]); if (buf) rkisp_free_buffer(dev, buf); } } } int hdr_update_dmatx_buf(struct rkisp_device *dev) { void __iomem *base = dev->base_addr; struct rkisp_stream *dmatx; struct rkisp_dummy_buffer *buf; u8 i, index; if (!dev->active_sensor || (dev->active_sensor && dev->active_sensor->mbus.type != V4L2_MBUS_CSI2) || (dev->isp_inp & INP_CIF) || (dev->isp_ver != ISP_V20 && dev->isp_ver != ISP_V21)) return 0; for (i = RKISP_STREAM_DMATX0; i <= RKISP_STREAM_DMATX2; i++) { dmatx = &dev->cap_dev.stream[i]; if (dmatx->ops && dmatx->ops->frame_end) dmatx->ops->frame_end(dmatx); } if (dev->dmarx_dev.trigger) goto end; /* for rawrd auto trigger mode, config first buf */ index = dev->hdr.index[HDR_DMA0]; buf = hdr_dqbuf(&dev->hdr.q_rx[index]); if (buf) { mi_raw0_rd_set_addr(base, buf->dma_addr); dev->hdr.rx_cur_buf[index] = buf; } else { mi_raw0_rd_set_addr(base, readl(base + MI_RAW0_WR_BASE_SHD)); } index = dev->hdr.index[HDR_DMA1]; buf = hdr_dqbuf(&dev->hdr.q_rx[index]); if (buf) { mi_raw1_rd_set_addr(base, buf->dma_addr); dev->hdr.rx_cur_buf[index] = buf; } else { mi_raw1_rd_set_addr(base, readl(base + MI_RAW1_WR_BASE_SHD)); } index = dev->hdr.index[HDR_DMA2]; buf = hdr_dqbuf(&dev->hdr.q_rx[index]); if (buf) { mi_raw2_rd_set_addr(base, buf->dma_addr); dev->hdr.rx_cur_buf[index] = buf; } else { mi_raw2_rd_set_addr(base, readl(base + MI_RAW2_WR_BASE_SHD)); } end: v4l2_dbg(1, rkisp_debug, &dev->v4l2_dev, "CSI2RX CTRL0:0x%x CTRL1:0x%x\n" "WR CTRL RAW0:0x%x RAW1:0x%x RAW2:0x%x\n" "RD CTRL:0x%x\n", readl(base + CSI2RX_CTRL0), readl(base + CSI2RX_CTRL1), readl(base + CSI2RX_RAW0_WR_CTRL), readl(base + CSI2RX_RAW1_WR_CTRL), readl(base + CSI2RX_RAW2_WR_CTRL), readl(base + CSI2RX_RAW_RD_CTRL)); return 0; } int hdr_config_dmatx(struct rkisp_device *dev) { struct rkisp_stream *stream; struct v4l2_pix_format_mplane pixm; u32 memory = 0; if (atomic_inc_return(&dev->hdr.refcnt) > 1 || !dev->active_sensor || (dev->active_sensor && dev->active_sensor->mbus.type != V4L2_MBUS_CSI2) || (dev->isp_inp & INP_CIF) || (dev->isp_ver != ISP_V20 && dev->isp_ver != ISP_V21)) return 0; rkisp_create_hdr_buf(dev); memset(&pixm, 0, sizeof(pixm)); if (dev->hdr.op_mode == HDR_FRAMEX2_DDR || dev->hdr.op_mode == HDR_LINEX2_DDR || dev->hdr.op_mode == HDR_FRAMEX3_DDR || dev->hdr.op_mode == HDR_LINEX3_DDR || dev->hdr.op_mode == HDR_RDBK_FRAME2 || dev->hdr.op_mode == HDR_RDBK_FRAME3) { stream = &dev->cap_dev.stream[RKISP_STREAM_DMATX0]; if (stream->ops && stream->ops->config_mi) stream->ops->config_mi(stream); if (!dev->dmarx_dev.trigger) { pixm = stream->out_fmt; stream = &dev->dmarx_dev.stream[RKISP_STREAM_RAWRD0]; rkisp_dmarx_set_fmt(stream, pixm); mi_raw_length(stream); } } if (dev->hdr.op_mode == HDR_FRAMEX3_DDR || dev->hdr.op_mode == HDR_LINEX3_DDR || dev->hdr.op_mode == HDR_RDBK_FRAME3) { stream = &dev->cap_dev.stream[RKISP_STREAM_DMATX1]; if (stream->ops && stream->ops->config_mi) stream->ops->config_mi(stream); if (!dev->dmarx_dev.trigger) { pixm = stream->out_fmt; stream = &dev->dmarx_dev.stream[RKISP_STREAM_RAWRD1]; rkisp_dmarx_set_fmt(stream, pixm); mi_raw_length(stream); } } if (dev->hdr.op_mode == HDR_RDBK_FRAME1 || dev->hdr.op_mode == HDR_RDBK_FRAME2 || dev->hdr.op_mode == HDR_RDBK_FRAME3) { stream = &dev->cap_dev.stream[RKISP_STREAM_DMATX2]; if (stream->ops && stream->ops->config_mi) stream->ops->config_mi(stream); if (!dev->dmarx_dev.trigger) { memory = stream->memory; pixm = stream->out_fmt; stream = &dev->dmarx_dev.stream[RKISP_STREAM_RAWRD2]; rkisp_dmarx_set_fmt(stream, pixm); stream->ops->config_mi(stream); } } if (dev->hdr.op_mode != HDR_NORMAL && !dev->dmarx_dev.trigger) { raw_rd_ctrl(dev->base_addr, memory << 2); if (pixm.width && pixm.height) rkisp_rawrd_set_pic_size(dev, pixm.width, pixm.height); } return 0; } void hdr_stop_dmatx(struct rkisp_device *dev) { struct rkisp_stream *stream; if (atomic_dec_return(&dev->hdr.refcnt) || !dev->active_sensor || (dev->active_sensor && dev->active_sensor->mbus.type != V4L2_MBUS_CSI2) || (dev->isp_inp & INP_CIF) || (dev->isp_ver != ISP_V20 && dev->isp_ver != ISP_V21)) return; if (dev->hdr.op_mode == HDR_FRAMEX2_DDR || dev->hdr.op_mode == HDR_LINEX2_DDR || dev->hdr.op_mode == HDR_FRAMEX3_DDR || dev->hdr.op_mode == HDR_LINEX3_DDR || dev->hdr.op_mode == HDR_RDBK_FRAME2 || dev->hdr.op_mode == HDR_RDBK_FRAME3) { stream = &dev->cap_dev.stream[RKISP_STREAM_DMATX0]; stream->ops->stop_mi(stream); } if (dev->hdr.op_mode == HDR_FRAMEX3_DDR || dev->hdr.op_mode == HDR_LINEX3_DDR || dev->hdr.op_mode == HDR_RDBK_FRAME3) { stream = &dev->cap_dev.stream[RKISP_STREAM_DMATX1]; stream->ops->stop_mi(stream); } if (dev->hdr.op_mode == HDR_RDBK_FRAME1 || dev->hdr.op_mode == HDR_RDBK_FRAME2 || dev->hdr.op_mode == HDR_RDBK_FRAME3) { stream = &dev->cap_dev.stream[RKISP_STREAM_DMATX2]; stream->ops->stop_mi(stream); } } struct rkisp_dummy_buffer *hdr_dqbuf(struct list_head *q) { struct rkisp_dummy_buffer *buf = NULL; if (!list_empty(q)) { buf = list_first_entry(q, struct rkisp_dummy_buffer, queue); list_del(&buf->queue); } return buf; } void hdr_qbuf(struct list_head *q, struct rkisp_dummy_buffer *buf) { if (buf) list_add_tail(&buf->queue, q); } void rkisp_config_dmatx_valid_buf(struct rkisp_device *dev) { struct rkisp_hw_dev *hw = dev->hw_dev; struct rkisp_stream *stream; struct rkisp_device *isp; u32 i, j; if (!hw->dummy_buf.mem_priv || !dev->active_sensor || (dev->active_sensor && dev->active_sensor->mbus.type != V4L2_MBUS_CSI2) || (dev->isp_inp & INP_CIF) || (dev->isp_ver != ISP_V20 && dev->isp_ver != ISP_V21)) return; /* dmatx buf update by mi force or oneself frame end, * for async dmatx enable need to update to valid buf first. */ for (i = 0; i < hw->dev_num; i++) { isp = hw->isp[i]; if (!(isp->isp_inp & INP_CSI)) continue; for (j = RKISP_STREAM_DMATX0; j < RKISP_MAX_STREAM; j++) { stream = &isp->cap_dev.stream[j]; if (!stream->linked || stream->u.dmatx.is_config) continue; mi_set_y_addr(stream, hw->dummy_buf.dma_addr); } } } /* Get xsubs and ysubs for fourcc formats * * @xsubs: horizontal color samples in a 4*4 matrix, for yuv * @ysubs: vertical color samples in a 4*4 matrix, for yuv */ int rkisp_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: *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_FBCG: *xsubs = 2; *ysubs = 2; break; default: return -EINVAL; } return 0; } int rkisp_mbus_code_xysubs(u32 code, u32 *xsubs, u32 *ysubs) { switch (code) { case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_YUYV8_1X16: case MEDIA_BUS_FMT_YVYU8_1X16: case MEDIA_BUS_FMT_UYVY8_1X16: case MEDIA_BUS_FMT_VYUY8_1X16: *xsubs = 2; *ysubs = 1; break; default: return -EINVAL; } return 0; } static const struct capture_fmt mp_fmts[] = { /* yuv422 */ { .fourcc = V4L2_PIX_FMT_UYVY, .fmt_type = FMT_YUV, .bpp = { 16 }, .cplanes = 1, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_MP_WRITE_YUVINT, }, { .fourcc = V4L2_PIX_FMT_YUV422P, .fmt_type = FMT_YUV, .bpp = { 8, 4, 4 }, .cplanes = 3, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8, }, { .fourcc = V4L2_PIX_FMT_NV16, .fmt_type = FMT_YUV, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_MP_WRITE_YUV_SPLA, }, { .fourcc = V4L2_PIX_FMT_NV61, .fmt_type = FMT_YUV, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 1, .uv_swap = 1, .write_format = MI_CTRL_MP_WRITE_YUV_SPLA, }, { .fourcc = V4L2_PIX_FMT_YUV422M, .fmt_type = FMT_YUV, .bpp = { 8, 8, 8 }, .cplanes = 3, .mplanes = 3, .uv_swap = 0, .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8, }, /* yuv420 */ { .fourcc = V4L2_PIX_FMT_NV21, .fmt_type = FMT_YUV, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 1, .uv_swap = 1, .write_format = MI_CTRL_MP_WRITE_YUV_SPLA, }, { .fourcc = V4L2_PIX_FMT_NV12, .fmt_type = FMT_YUV, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_MP_WRITE_YUV_SPLA, }, { .fourcc = V4L2_PIX_FMT_NV21M, .fmt_type = FMT_YUV, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 2, .uv_swap = 1, .write_format = MI_CTRL_MP_WRITE_YUV_SPLA, }, { .fourcc = V4L2_PIX_FMT_NV12M, .fmt_type = FMT_YUV, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 2, .uv_swap = 0, .write_format = MI_CTRL_MP_WRITE_YUV_SPLA, }, { .fourcc = V4L2_PIX_FMT_YUV420, .fmt_type = FMT_YUV, .bpp = { 8, 8, 8 }, .cplanes = 3, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8, }, /* yuv444 */ { .fourcc = V4L2_PIX_FMT_YUV444M, .fmt_type = FMT_YUV, .bpp = { 8, 8, 8 }, .cplanes = 3, .mplanes = 3, .uv_swap = 0, .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8, }, /* raw */ { .fourcc = V4L2_PIX_FMT_SRGGB8, .fmt_type = FMT_BAYER, .bpp = { 8 }, .mplanes = 1, .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8, }, { .fourcc = V4L2_PIX_FMT_SGRBG8, .fmt_type = FMT_BAYER, .bpp = { 8 }, .mplanes = 1, .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8, }, { .fourcc = V4L2_PIX_FMT_SGBRG8, .fmt_type = FMT_BAYER, .bpp = { 8 }, .mplanes = 1, .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8, }, { .fourcc = V4L2_PIX_FMT_SBGGR8, .fmt_type = FMT_BAYER, .bpp = { 8 }, .mplanes = 1, .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8, }, { .fourcc = V4L2_PIX_FMT_SRGGB10, .fmt_type = FMT_BAYER, .bpp = { 10 }, .mplanes = 1, .write_format = MI_CTRL_MP_WRITE_RAW12, }, { .fourcc = V4L2_PIX_FMT_SGRBG10, .fmt_type = FMT_BAYER, .bpp = { 10 }, .mplanes = 1, .write_format = MI_CTRL_MP_WRITE_RAW12, }, { .fourcc = V4L2_PIX_FMT_SGBRG10, .fmt_type = FMT_BAYER, .bpp = { 10 }, .mplanes = 1, .write_format = MI_CTRL_MP_WRITE_RAW12, }, { .fourcc = V4L2_PIX_FMT_SBGGR10, .fmt_type = FMT_BAYER, .bpp = { 10 }, .mplanes = 1, .write_format = MI_CTRL_MP_WRITE_RAW12, }, { .fourcc = V4L2_PIX_FMT_SRGGB12, .fmt_type = FMT_BAYER, .bpp = { 12 }, .mplanes = 1, .write_format = MI_CTRL_MP_WRITE_RAW12, }, { .fourcc = V4L2_PIX_FMT_SGRBG12, .fmt_type = FMT_BAYER, .bpp = { 12 }, .mplanes = 1, .write_format = MI_CTRL_MP_WRITE_RAW12, }, { .fourcc = V4L2_PIX_FMT_SGBRG12, .fmt_type = FMT_BAYER, .bpp = { 12 }, .mplanes = 1, .write_format = MI_CTRL_MP_WRITE_RAW12, }, { .fourcc = V4L2_PIX_FMT_SBGGR12, .fmt_type = FMT_BAYER, .bpp = { 12 }, .mplanes = 1, .write_format = MI_CTRL_MP_WRITE_RAW12, }, }; static const struct capture_fmt sp_fmts[] = { /* yuv422 */ { .fourcc = V4L2_PIX_FMT_UYVY, .fmt_type = FMT_YUV, .bpp = { 16 }, .cplanes = 1, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_SP_WRITE_INT, .output_format = MI_CTRL_SP_OUTPUT_YUV422, }, { .fourcc = V4L2_PIX_FMT_YUV422P, .fmt_type = FMT_YUV, .bpp = { 8, 8, 8 }, .cplanes = 3, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_SP_WRITE_PLA, .output_format = MI_CTRL_SP_OUTPUT_YUV422, }, { .fourcc = V4L2_PIX_FMT_NV16, .fmt_type = FMT_YUV, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_SP_WRITE_SPLA, .output_format = MI_CTRL_SP_OUTPUT_YUV422, }, { .fourcc = V4L2_PIX_FMT_NV61, .fmt_type = FMT_YUV, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 1, .uv_swap = 1, .write_format = MI_CTRL_SP_WRITE_SPLA, .output_format = MI_CTRL_SP_OUTPUT_YUV422, }, { .fourcc = V4L2_PIX_FMT_YUV422M, .fmt_type = FMT_YUV, .bpp = { 8, 8, 8 }, .cplanes = 3, .mplanes = 3, .uv_swap = 0, .write_format = MI_CTRL_SP_WRITE_PLA, .output_format = MI_CTRL_SP_OUTPUT_YUV422, }, /* yuv420 */ { .fourcc = V4L2_PIX_FMT_NV21, .fmt_type = FMT_YUV, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 1, .uv_swap = 1, .write_format = MI_CTRL_SP_WRITE_SPLA, .output_format = MI_CTRL_SP_OUTPUT_YUV420, }, { .fourcc = V4L2_PIX_FMT_NV12, .fmt_type = FMT_YUV, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_SP_WRITE_SPLA, .output_format = MI_CTRL_SP_OUTPUT_YUV420, }, { .fourcc = V4L2_PIX_FMT_NV21M, .fmt_type = FMT_YUV, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 2, .uv_swap = 1, .write_format = MI_CTRL_SP_WRITE_SPLA, .output_format = MI_CTRL_SP_OUTPUT_YUV420, }, { .fourcc = V4L2_PIX_FMT_NV12M, .fmt_type = FMT_YUV, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 2, .uv_swap = 0, .write_format = MI_CTRL_SP_WRITE_SPLA, .output_format = MI_CTRL_SP_OUTPUT_YUV420, }, { .fourcc = V4L2_PIX_FMT_YUV420, .fmt_type = FMT_YUV, .bpp = { 8, 8, 8 }, .cplanes = 3, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_SP_WRITE_PLA, .output_format = MI_CTRL_SP_OUTPUT_YUV420, }, /* yuv444 */ { .fourcc = V4L2_PIX_FMT_YUV444M, .fmt_type = FMT_YUV, .bpp = { 8, 8, 8 }, .cplanes = 3, .mplanes = 3, .uv_swap = 0, .write_format = MI_CTRL_SP_WRITE_PLA, .output_format = MI_CTRL_SP_OUTPUT_YUV444, }, /* yuv400 */ { .fourcc = V4L2_PIX_FMT_GREY, .fmt_type = FMT_YUV, .bpp = { 8 }, .cplanes = 1, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_SP_WRITE_PLA, .output_format = MI_CTRL_SP_OUTPUT_YUV400, }, /* rgb */ { .fourcc = V4L2_PIX_FMT_XBGR32, .fmt_type = FMT_RGB, .bpp = { 32 }, .mplanes = 1, .write_format = MI_CTRL_SP_WRITE_PLA, .output_format = MI_CTRL_SP_OUTPUT_RGB888, }, { .fourcc = V4L2_PIX_FMT_RGB565, .fmt_type = FMT_RGB, .bpp = { 16 }, .mplanes = 1, .write_format = MI_CTRL_SP_WRITE_PLA, .output_format = MI_CTRL_SP_OUTPUT_RGB565, }, /* fbcg */ { .fourcc = V4L2_PIX_FMT_FBCG, .fmt_type = FMT_FBCGAIN, .bpp = { 8, 16 }, .cplanes = 2, .mplanes = 2, .uv_swap = 0, .write_format = MI_CTRL_SP_WRITE_SPLA, .output_format = MI_CTRL_SP_OUTPUT_YUV420, } }; struct stream_config rkisp_mp_stream_config = { .fmts = mp_fmts, .fmt_size = ARRAY_SIZE(mp_fmts), /* constraints */ .max_rsz_width = STREAM_MAX_MP_RSZ_OUTPUT_WIDTH, .max_rsz_height = STREAM_MAX_MP_RSZ_OUTPUT_HEIGHT, .min_rsz_width = STREAM_MIN_RSZ_OUTPUT_WIDTH, .min_rsz_height = STREAM_MIN_RSZ_OUTPUT_HEIGHT, .frame_end_id = CIF_MI_MP_FRAME, /* registers */ .rsz = { .ctrl = CIF_MRSZ_CTRL, .scale_hy = CIF_MRSZ_SCALE_HY, .scale_hcr = CIF_MRSZ_SCALE_HCR, .scale_hcb = CIF_MRSZ_SCALE_HCB, .scale_vy = CIF_MRSZ_SCALE_VY, .scale_vc = CIF_MRSZ_SCALE_VC, .scale_lut = CIF_MRSZ_SCALE_LUT, .scale_lut_addr = CIF_MRSZ_SCALE_LUT_ADDR, .scale_hy_shd = CIF_MRSZ_SCALE_HY_SHD, .scale_hcr_shd = CIF_MRSZ_SCALE_HCR_SHD, .scale_hcb_shd = CIF_MRSZ_SCALE_HCB_SHD, .scale_vy_shd = CIF_MRSZ_SCALE_VY_SHD, .scale_vc_shd = CIF_MRSZ_SCALE_VC_SHD, .phase_hy = CIF_MRSZ_PHASE_HY, .phase_hc = CIF_MRSZ_PHASE_HC, .phase_vy = CIF_MRSZ_PHASE_VY, .phase_vc = CIF_MRSZ_PHASE_VC, .ctrl_shd = CIF_MRSZ_CTRL_SHD, .phase_hy_shd = CIF_MRSZ_PHASE_HY_SHD, .phase_hc_shd = CIF_MRSZ_PHASE_HC_SHD, .phase_vy_shd = CIF_MRSZ_PHASE_VY_SHD, .phase_vc_shd = CIF_MRSZ_PHASE_VC_SHD, }, .dual_crop = { .ctrl = CIF_DUAL_CROP_CTRL, .yuvmode_mask = CIF_DUAL_CROP_MP_MODE_YUV, .rawmode_mask = CIF_DUAL_CROP_MP_MODE_RAW, .h_offset = CIF_DUAL_CROP_M_H_OFFS, .v_offset = CIF_DUAL_CROP_M_V_OFFS, .h_size = CIF_DUAL_CROP_M_H_SIZE, .v_size = CIF_DUAL_CROP_M_V_SIZE, }, .mi = { .y_size_init = CIF_MI_MP_Y_SIZE_INIT, .cb_size_init = CIF_MI_MP_CB_SIZE_INIT, .cr_size_init = CIF_MI_MP_CR_SIZE_INIT, .y_base_ad_init = CIF_MI_MP_Y_BASE_AD_INIT, .cb_base_ad_init = CIF_MI_MP_CB_BASE_AD_INIT, .cr_base_ad_init = CIF_MI_MP_CR_BASE_AD_INIT, .y_offs_cnt_init = CIF_MI_MP_Y_OFFS_CNT_INIT, .cb_offs_cnt_init = CIF_MI_MP_CB_OFFS_CNT_INIT, .cr_offs_cnt_init = CIF_MI_MP_CR_OFFS_CNT_INIT, .y_base_ad_shd = CIF_MI_MP_Y_BASE_AD_SHD, }, }; struct stream_config rkisp_sp_stream_config = { .fmts = sp_fmts, .fmt_size = ARRAY_SIZE(sp_fmts), /* constraints */ .max_rsz_width = STREAM_MAX_SP_RSZ_OUTPUT_WIDTH, .max_rsz_height = STREAM_MAX_SP_RSZ_OUTPUT_HEIGHT, .min_rsz_width = STREAM_MIN_RSZ_OUTPUT_WIDTH, .min_rsz_height = STREAM_MIN_RSZ_OUTPUT_HEIGHT, .frame_end_id = CIF_MI_SP_FRAME, /* registers */ .rsz = { .ctrl = CIF_SRSZ_CTRL, .scale_hy = CIF_SRSZ_SCALE_HY, .scale_hcr = CIF_SRSZ_SCALE_HCR, .scale_hcb = CIF_SRSZ_SCALE_HCB, .scale_vy = CIF_SRSZ_SCALE_VY, .scale_vc = CIF_SRSZ_SCALE_VC, .scale_lut = CIF_SRSZ_SCALE_LUT, .scale_lut_addr = CIF_SRSZ_SCALE_LUT_ADDR, .scale_hy_shd = CIF_SRSZ_SCALE_HY_SHD, .scale_hcr_shd = CIF_SRSZ_SCALE_HCR_SHD, .scale_hcb_shd = CIF_SRSZ_SCALE_HCB_SHD, .scale_vy_shd = CIF_SRSZ_SCALE_VY_SHD, .scale_vc_shd = CIF_SRSZ_SCALE_VC_SHD, .phase_hy = CIF_SRSZ_PHASE_HY, .phase_hc = CIF_SRSZ_PHASE_HC, .phase_vy = CIF_SRSZ_PHASE_VY, .phase_vc = CIF_SRSZ_PHASE_VC, .ctrl_shd = CIF_SRSZ_CTRL_SHD, .phase_hy_shd = CIF_SRSZ_PHASE_HY_SHD, .phase_hc_shd = CIF_SRSZ_PHASE_HC_SHD, .phase_vy_shd = CIF_SRSZ_PHASE_VY_SHD, .phase_vc_shd = CIF_SRSZ_PHASE_VC_SHD, }, .dual_crop = { .ctrl = CIF_DUAL_CROP_CTRL, .yuvmode_mask = CIF_DUAL_CROP_SP_MODE_YUV, .rawmode_mask = CIF_DUAL_CROP_SP_MODE_RAW, .h_offset = CIF_DUAL_CROP_S_H_OFFS, .v_offset = CIF_DUAL_CROP_S_V_OFFS, .h_size = CIF_DUAL_CROP_S_H_SIZE, .v_size = CIF_DUAL_CROP_S_V_SIZE, }, .mi = { .y_size_init = CIF_MI_SP_Y_SIZE_INIT, .cb_size_init = CIF_MI_SP_CB_SIZE_INIT, .cr_size_init = CIF_MI_SP_CR_SIZE_INIT, .y_base_ad_init = CIF_MI_SP_Y_BASE_AD_INIT, .cb_base_ad_init = CIF_MI_SP_CB_BASE_AD_INIT, .cr_base_ad_init = CIF_MI_SP_CR_BASE_AD_INIT, .y_offs_cnt_init = CIF_MI_SP_Y_OFFS_CNT_INIT, .cb_offs_cnt_init = CIF_MI_SP_CB_OFFS_CNT_INIT, .cr_offs_cnt_init = CIF_MI_SP_CR_OFFS_CNT_INIT, .y_base_ad_shd = CIF_MI_SP_Y_BASE_AD_SHD, }, }; static const struct capture_fmt *find_fmt(struct rkisp_stream *stream, const u32 pixelfmt) { const struct capture_fmt *fmt; int i; for (i = 0; i < stream->config->fmt_size; i++) { fmt = &stream->config->fmts[i]; if (fmt->fourcc == pixelfmt) return fmt; } return NULL; } /* * Make sure max resize/output resolution is smaller than * isp sub device output size. This assumes it's not * recommended to use ISP scale-up function to get output size * that exceeds sensor max resolution. */ static void restrict_rsz_resolution(struct rkisp_device *dev, const struct stream_config *config, struct v4l2_rect *max_rsz) { struct v4l2_rect *input_win; input_win = rkisp_get_isp_sd_win(&dev->isp_sdev); max_rsz->width = min_t(int, input_win->width, config->max_rsz_width); max_rsz->height = min_t(int, input_win->height, config->max_rsz_height); } static int rkisp_set_fmt(struct rkisp_stream *stream, struct v4l2_pix_format_mplane *pixm, bool try) { const struct capture_fmt *fmt; const struct stream_config *config = stream->config; struct rkisp_device *dev = stream->ispdev; struct rkisp_stream *other_stream; unsigned int imagsize = 0; unsigned int planes; u32 xsubs = 1, ysubs = 1; unsigned int i; fmt = find_fmt(stream, pixm->pixelformat); if (!fmt) { v4l2_err(&stream->ispdev->v4l2_dev, "nonsupport pixelformat:%c%c%c%c\n", pixm->pixelformat, pixm->pixelformat >> 8, pixm->pixelformat >> 16, pixm->pixelformat >> 24); return -EINVAL; } if (stream->id == RKISP_STREAM_MP || stream->id == RKISP_STREAM_SP) { struct v4l2_rect max_rsz; other_stream = (stream->id == RKISP_STREAM_MP) ? &dev->cap_dev.stream[RKISP_STREAM_SP] : &dev->cap_dev.stream[RKISP_STREAM_MP]; /* do checks on resolution */ restrict_rsz_resolution(stream->ispdev, config, &max_rsz); pixm->width = clamp_t(u32, pixm->width, config->min_rsz_width, max_rsz.width); pixm->height = clamp_t(u32, pixm->height, config->min_rsz_height, max_rsz.height); } else { other_stream = &stream->ispdev->cap_dev.stream[RKISP_STREAM_MP]; } pixm->num_planes = fmt->mplanes; pixm->field = V4L2_FIELD_NONE; /* get quantization from ispsd */ pixm->quantization = stream->ispdev->isp_sdev.quantization; /* output full range by default, take effect in isp_params */ if (!pixm->quantization) pixm->quantization = V4L2_QUANTIZATION_FULL_RANGE; /* can not change quantization when stream-on */ if (other_stream->streaming) pixm->quantization = other_stream->out_fmt.quantization; /* calculate size */ rkisp_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; unsigned int width, height, bytesperline; plane_fmt = pixm->plane_fmt + i; if (i == 0) { width = pixm->width; height = pixm->height; } else { width = pixm->width / xsubs; height = pixm->height / ysubs; } if (dev->isp_ver == ISP_V20 && fmt->fmt_type == FMT_BAYER && stream->id == RKISP_STREAM_DMATX2) height += RKMODULE_EXTEND_LINE; if ((dev->isp_ver == ISP_V20 || dev->isp_ver == ISP_V21) && !stream->memory && fmt->fmt_type == FMT_BAYER && stream->id != RKISP_STREAM_MP && stream->id != RKISP_STREAM_SP) /* compact mode need bytesperline 4byte align */ bytesperline = ALIGN(width * fmt->bpp[i] / 8, 256); else bytesperline = width * DIV_ROUND_UP(fmt->bpp[i], 8); /* 128bit AXI, 16byte align for bytesperline */ if (dev->isp_ver >= ISP_V20 && stream->id == RKISP_STREAM_SP) bytesperline = ALIGN(bytesperline, 16); /* stride is only available for sp stream and y plane */ if (stream->id != RKISP_STREAM_SP || i != 0 || plane_fmt->bytesperline < bytesperline) plane_fmt->bytesperline = bytesperline; plane_fmt->sizeimage = plane_fmt->bytesperline * height; /* uv address is y size offset need 64 align */ if (fmt->fmt_type == FMT_FBCGAIN && i == 0) plane_fmt->sizeimage = ALIGN(plane_fmt->sizeimage, 64); imagsize += plane_fmt->sizeimage; } /* convert to non-MPLANE format. * it's important since we want to unify none-MPLANE * and MPLANE. */ if (fmt->mplanes == 1 || fmt->fmt_type == FMT_FBCGAIN) pixm->plane_fmt[0].sizeimage = imagsize; if (!try && !stream->start_stream && !stream->streaming) { stream->out_isp_fmt = *fmt; stream->out_fmt = *pixm; if (stream->id == RKISP_STREAM_SP) { stream->u.sp.y_stride = pixm->plane_fmt[0].bytesperline / DIV_ROUND_UP(fmt->bpp[0], 8); } else if (stream->id == RKISP_STREAM_MP) { stream->u.mp.raw_enable = (fmt->fmt_type == FMT_BAYER); } v4l2_dbg(1, rkisp_debug, &stream->ispdev->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; } int rkisp_fh_open(struct file *filp) { struct rkisp_stream *stream = video_drvdata(filp); int ret; ret = v4l2_fh_open(filp); if (!ret) { ret = v4l2_pipeline_pm_use(&stream->vnode.vdev.entity, 1); if (ret < 0) vb2_fop_release(filp); } return ret; } int rkisp_fop_release(struct file *file) { struct rkisp_stream *stream = video_drvdata(file); int ret; ret = vb2_fop_release(file); if (!ret) { ret = v4l2_pipeline_pm_use(&stream->vnode.vdev.entity, 0); if (ret < 0) v4l2_err(&stream->ispdev->v4l2_dev, "set pipeline power failed %d\n", ret); } return ret; } void rkisp_set_stream_def_fmt(struct rkisp_device *dev, u32 id, u32 width, u32 height, u32 pixelformat) { struct rkisp_stream *stream = &dev->cap_dev.stream[id]; struct v4l2_pix_format_mplane pixm; memset(&pixm, 0, sizeof(pixm)); if (pixelformat) pixm.pixelformat = pixelformat; else pixm.pixelformat = stream->out_isp_fmt.fourcc; if (!pixm.pixelformat) return; pixm.width = width; pixm.height = height; rkisp_set_fmt(stream, &pixm, false); stream->dcrop.left = 0; stream->dcrop.top = 0; stream->dcrop.width = width; stream->dcrop.height = height; } /************************* v4l2_file_operations***************************/ static const struct v4l2_file_operations rkisp_fops = { .open = rkisp_fh_open, .release = rkisp_fop_release, .unlocked_ioctl = video_ioctl2, .poll = vb2_fop_poll, .mmap = vb2_fop_mmap, }; /* * mp and sp v4l2_ioctl_ops */ static int rkisp_enum_input(struct file *file, void *priv, struct v4l2_input *input) { if (input->index > 0) return -EINVAL; input->type = V4L2_INPUT_TYPE_CAMERA; strlcpy(input->name, "Camera", sizeof(input->name)); return 0; } static int rkisp_try_fmt_vid_cap_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct rkisp_stream *stream = video_drvdata(file); return rkisp_set_fmt(stream, &f->fmt.pix_mp, true); } static int rkisp_enum_framesizes(struct file *file, void *prov, struct v4l2_frmsizeenum *fsize) { struct rkisp_stream *stream = video_drvdata(file); const struct stream_config *config = stream->config; struct v4l2_frmsize_stepwise *s = &fsize->stepwise; struct v4l2_frmsize_discrete *d = &fsize->discrete; const struct ispsd_out_fmt *input_isp_fmt; struct v4l2_rect max_rsz; if (fsize->index != 0) return -EINVAL; if (!find_fmt(stream, fsize->pixel_format)) return -EINVAL; restrict_rsz_resolution(stream->ispdev, config, &max_rsz); input_isp_fmt = rkisp_get_ispsd_out_fmt(&stream->ispdev->isp_sdev); if (input_isp_fmt->fmt_type == FMT_BAYER) { fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; d->width = max_rsz.width; d->height = max_rsz.height; } else { fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; s->min_width = STREAM_MIN_RSZ_OUTPUT_WIDTH; s->min_height = STREAM_MIN_RSZ_OUTPUT_HEIGHT; s->max_width = max_rsz.width; s->max_height = max_rsz.height; s->step_width = STREAM_OUTPUT_STEP_WISE; s->step_height = STREAM_OUTPUT_STEP_WISE; } return 0; } static long rkisp_ioctl_default(struct file *file, void *fh, bool valid_prio, unsigned int cmd, void *arg) { struct rkisp_stream *stream = video_drvdata(file); long ret = 0; if (!arg) return -EINVAL; switch (cmd) { case RKISP_CMD_GET_CSI_MEMORY_MODE: if (stream->id != RKISP_STREAM_DMATX0 && stream->id != RKISP_STREAM_DMATX1 && stream->id != RKISP_STREAM_DMATX2 && stream->id != RKISP_STREAM_DMATX3) ret = -EINVAL; else if (stream->memory == 0) *(int *)arg = CSI_MEM_COMPACT; else if (stream->memory == SW_CSI_RAW_WR_SIMG_MODE) *(int *)arg = CSI_MEM_WORD_BIG_ALIGN; else *(int *)arg = CSI_MEM_WORD_LITTLE_ALIGN; break; case RKISP_CMD_SET_CSI_MEMORY_MODE: if (stream->id != RKISP_STREAM_DMATX0 && stream->id != RKISP_STREAM_DMATX1 && stream->id != RKISP_STREAM_DMATX2 && stream->id != RKISP_STREAM_DMATX3) ret = -EINVAL; else if (*(int *)arg == CSI_MEM_COMPACT) stream->memory = 0; else if (*(int *)arg == CSI_MEM_WORD_BIG_ALIGN) stream->memory = SW_CSI_RAW_WR_SIMG_MODE; else stream->memory = SW_CSI_RWA_WR_SIMG_SWP | SW_CSI_RAW_WR_SIMG_MODE; break; default: ret = -EINVAL; } return ret; } static int rkisp_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival) { const struct rkisp_stream *stream = video_drvdata(file); struct rkisp_device *dev = stream->ispdev; struct rkisp_sensor_info *sensor = dev->active_sensor; struct v4l2_subdev_frame_interval fi; int ret; if (fival->index != 0) return -EINVAL; if (!sensor) { /* TODO: active_sensor is NULL if using DMARX path */ v4l2_err(&dev->v4l2_dev, "%s Not active sensor\n", __func__); return -ENODEV; } ret = v4l2_subdev_call(sensor->sd, video, g_frame_interval, &fi); if (ret && ret != -ENOIOCTLCMD) { return ret; } else if (ret == -ENOIOCTLCMD) { /* Set a default value for sensors not implements ioctl */ fi.interval.numerator = 1; fi.interval.denominator = 30; } fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; fival->stepwise.step.numerator = 1; fival->stepwise.step.denominator = 1; fival->stepwise.max.numerator = 1; fival->stepwise.max.denominator = 1; fival->stepwise.min.numerator = fi.interval.numerator; fival->stepwise.min.denominator = fi.interval.denominator; return 0; } static int rkisp_enum_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct rkisp_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; return 0; } static int rkisp_s_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct rkisp_stream *stream = video_drvdata(file); struct video_device *vdev = &stream->vnode.vdev; struct rkisp_vdev_node *node = vdev_to_node(vdev); struct rkisp_device *dev = stream->ispdev; if (vb2_is_streaming(&node->buf_queue)) { v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); return -EBUSY; } return rkisp_set_fmt(stream, &f->fmt.pix_mp, false); } static int rkisp_g_fmt_vid_cap_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct rkisp_stream *stream = video_drvdata(file); f->fmt.pix_mp = stream->out_fmt; return 0; } static int rkisp_g_selection(struct file *file, void *prv, struct v4l2_selection *sel) { struct rkisp_stream *stream = video_drvdata(file); struct rkisp_device *dev = stream->ispdev; struct v4l2_rect *dcrop = &stream->dcrop; struct v4l2_rect *input_win; input_win = rkisp_get_isp_sd_win(&dev->isp_sdev); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.width = input_win->width; sel->r.height = input_win->height; sel->r.left = 0; sel->r.top = 0; break; case V4L2_SEL_TGT_CROP: sel->r = *dcrop; break; default: return -EINVAL; } return 0; } static struct v4l2_rect *rkisp_update_crop(struct rkisp_stream *stream, struct v4l2_rect *sel, const struct v4l2_rect *in) { /* Not crop for MP bayer raw data and dmatx path */ if ((stream->id == RKISP_STREAM_MP && stream->out_isp_fmt.fmt_type == FMT_BAYER) || stream->id == RKISP_STREAM_DMATX0 || stream->id == RKISP_STREAM_DMATX1 || stream->id == RKISP_STREAM_DMATX2 || stream->id == RKISP_STREAM_DMATX3) { sel->left = 0; sel->top = 0; sel->width = in->width; sel->height = in->height; return sel; } sel->left = ALIGN(sel->left, 2); sel->width = ALIGN(sel->width, 2); sel->left = clamp_t(u32, sel->left, 0, in->width - STREAM_MIN_MP_SP_INPUT_WIDTH); sel->top = clamp_t(u32, sel->top, 0, in->height - STREAM_MIN_MP_SP_INPUT_HEIGHT); sel->width = clamp_t(u32, sel->width, STREAM_MIN_MP_SP_INPUT_WIDTH, in->width - sel->left); sel->height = clamp_t(u32, sel->height, STREAM_MIN_MP_SP_INPUT_HEIGHT, in->height - sel->top); return sel; } static int rkisp_s_selection(struct file *file, void *prv, struct v4l2_selection *sel) { struct rkisp_stream *stream = video_drvdata(file); struct video_device *vdev = &stream->vnode.vdev; struct rkisp_vdev_node *node = vdev_to_node(vdev); struct rkisp_device *dev = stream->ispdev; struct v4l2_rect *dcrop = &stream->dcrop; const struct v4l2_rect *input_win; if (vb2_is_busy(&node->buf_queue)) { v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); return -EBUSY; } input_win = rkisp_get_isp_sd_win(&dev->isp_sdev); if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; if (sel->flags != 0) return -EINVAL; *dcrop = *rkisp_update_crop(stream, &sel->r, input_win); v4l2_dbg(1, rkisp_debug, &dev->v4l2_dev, "stream %d crop(%d,%d)/%dx%d\n", stream->id, dcrop->left, dcrop->top, dcrop->width, dcrop->height); return 0; } static int rkisp_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct rkisp_stream *stream = video_drvdata(file); struct device *dev = stream->ispdev->dev; struct video_device *vdev = video_devdata(file); strlcpy(cap->card, vdev->name, sizeof(cap->card)); snprintf(cap->driver, sizeof(cap->driver), "%s_v%d", dev->driver->name, stream->ispdev->isp_ver >> 4); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(dev)); cap->version = RKISP_DRIVER_VERSION; return 0; } static const struct v4l2_ioctl_ops rkisp_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 = rkisp_enum_input, .vidioc_try_fmt_vid_cap_mplane = rkisp_try_fmt_vid_cap_mplane, .vidioc_enum_fmt_vid_cap_mplane = rkisp_enum_fmt_vid_cap_mplane, .vidioc_s_fmt_vid_cap_mplane = rkisp_s_fmt_vid_cap_mplane, .vidioc_g_fmt_vid_cap_mplane = rkisp_g_fmt_vid_cap_mplane, .vidioc_s_selection = rkisp_s_selection, .vidioc_g_selection = rkisp_g_selection, .vidioc_querycap = rkisp_querycap, .vidioc_enum_frameintervals = rkisp_enum_frameintervals, .vidioc_enum_framesizes = rkisp_enum_framesizes, .vidioc_default = rkisp_ioctl_default, }; void rkisp_unregister_stream_vdev(struct rkisp_stream *stream) { media_entity_cleanup(&stream->vnode.vdev.entity); video_unregister_device(&stream->vnode.vdev); } int rkisp_register_stream_vdev(struct rkisp_stream *stream) { struct rkisp_device *dev = stream->ispdev; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; struct video_device *vdev = &stream->vnode.vdev; struct rkisp_vdev_node *node; struct media_entity *source, *sink; int ret = 0, pad; mutex_init(&stream->apilock); node = vdev_to_node(vdev); vdev->ioctl_ops = &rkisp_v4l2_ioctl_ops; vdev->release = video_device_release_empty; vdev->fops = &rkisp_fops; vdev->minor = -1; vdev->v4l2_dev = v4l2_dev; vdev->lock = &stream->apilock; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; video_set_drvdata(vdev, stream); vdev->vfl_dir = VFL_DIR_RX; node->pad.flags = MEDIA_PAD_FL_SINK; vdev->queue = &node->buf_queue; ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { v4l2_err(v4l2_dev, "video_register_device failed with error %d\n", ret); return ret; } ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); if (ret < 0) goto unreg; source = &dev->csi_dev.sd.entity; switch (stream->id) { case RKISP_STREAM_DMATX0://CSI_SRC_CH1 case RKISP_STREAM_DMATX1://CSI_SRC_CH2 case RKISP_STREAM_DMATX2://CSI_SRC_CH3 case RKISP_STREAM_DMATX3://CSI_SRC_CH4 pad = stream->id; dev->csi_dev.sink[pad - 1].linked = true; dev->csi_dev.sink[pad - 1].index = BIT(pad - 1); break; default: source = &dev->isp_sdev.sd.entity; pad = RKISP_ISP_PAD_SOURCE_PATH; } sink = &vdev->entity; ret = media_create_pad_link(source, pad, sink, 0, stream->linked); if (ret < 0) goto unreg; return 0; unreg: video_unregister_device(vdev); return ret; } int rkisp_register_stream_vdevs(struct rkisp_device *dev) { struct rkisp_capture_device *cap_dev = &dev->cap_dev; int ret = 0; memset(cap_dev, 0, sizeof(*cap_dev)); cap_dev->ispdev = dev; atomic_set(&cap_dev->refcnt, 0); if (dev->isp_ver <= ISP_V13) ret = rkisp_register_stream_v1x(dev); else if (dev->isp_ver == ISP_V20) ret = rkisp_register_stream_v20(dev); else if (dev->isp_ver == ISP_V21) ret = rkisp_register_stream_v21(dev); return ret; } void rkisp_unregister_stream_vdevs(struct rkisp_device *dev) { if (dev->isp_ver <= ISP_V13) rkisp_unregister_stream_v1x(dev); else if (dev->isp_ver == ISP_V20) rkisp_unregister_stream_v20(dev); else if (dev->isp_ver == ISP_V21) rkisp_unregister_stream_v21(dev); } void rkisp_mi_isr(u32 mis_val, struct rkisp_device *dev) { if (dev->isp_ver <= ISP_V13) rkisp_mi_v1x_isr(mis_val, dev); else if (dev->isp_ver == ISP_V20) rkisp_mi_v20_isr(mis_val, dev); else if (dev->isp_ver == ISP_V21) rkisp_mi_v21_isr(mis_val, dev); }