/* * Rockchip isp1 driver * * Copyright (C) 2017 Rockchip Electronics Co., Ltd. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include #include #include #include "dev.h" #include "regs.h" /* * NOTE: * 1. There are two capture video devices in rkisp1, selfpath and mainpath * 2. Two capture device have separated memory-interface/crop/scale units. * 3. Besides describing stream hardware, this file also contain entries * for pipeline operations. * 4. The register read/write operations in this file are put into regs.c. */ /* * differences between selfpatch and mainpath * available mp sink input: isp * available sp sink input : isp, dma(TODO) * available mp sink pad fmts: yuv422, raw * available sp sink pad fmts: yuv422, yuv420...... * available mp source fmts: yuv, raw, jpeg(TODO) * available sp source fmts: yuv, rgb */ #define CIF_ISP_REQ_BUFS_MIN 1 #define CIF_ISP_REQ_BUFS_MAX 8 #define STREAM_PAD_SINK 0 #define STREAM_PAD_SOURCE 1 #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 1920 #define STREAM_MIN_RSZ_OUTPUT_WIDTH 32 #define STREAM_MIN_RSZ_OUTPUT_HEIGHT 16 #define STREAM_MAX_MP_SP_INPUT_WIDTH STREAM_MAX_MP_RSZ_OUTPUT_WIDTH #define STREAM_MAX_MP_SP_INPUT_HEIGHT STREAM_MAX_MP_RSZ_OUTPUT_HEIGHT #define STREAM_MIN_MP_SP_INPUT_WIDTH 32 #define STREAM_MIN_MP_SP_INPUT_HEIGHT 32 /* 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 */ static int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs) { switch (fcc) { case V4L2_PIX_FMT_GREY: case V4L2_PIX_FMT_YUV444M: *xsubs = 1; *ysubs = 1; break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_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: *xsubs = 2; *ysubs = 2; break; default: return -EINVAL; } return 0; } static int 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 int mbus_code_sp_in_fmt(u32 code, u32 *format) { switch (code) { case MEDIA_BUS_FMT_YUYV8_2X8: *format = MI_CTRL_SP_INPUT_YUV422; break; default: return -EINVAL; } return 0; } static const struct capture_fmt mp_fmts[] = { /* yuv422 */ { .fourcc = V4L2_PIX_FMT_YUYV, .fmt_type = FMT_YUV, .bpp = { 16 }, .cplanes = 1, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_MP_WRITE_YUVINT, }, { .fourcc = V4L2_PIX_FMT_YVYU, .fmt_type = FMT_YUV, .bpp = { 16 }, .cplanes = 1, .mplanes = 1, .uv_swap = 1, .write_format = MI_CTRL_MP_WRITE_YUVINT, }, { .fourcc = V4L2_PIX_FMT_VYUY, .fmt_type = FMT_YUV, .bpp = { 16 }, .cplanes = 1, .mplanes = 1, .uv_swap = 1, .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_YVU422M, .fmt_type = FMT_YUV, .bpp = { 8, 8, 8 }, .cplanes = 3, .mplanes = 3, .uv_swap = 1, .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, }, { .fourcc = V4L2_PIX_FMT_YVU420, .fmt_type = FMT_YUV, .bpp = { 8, 8, 8 }, .cplanes = 3, .mplanes = 1, .uv_swap = 1, .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, }, /* yuv400 */ { .fourcc = V4L2_PIX_FMT_GREY, .fmt_type = FMT_YUV, .bpp = { 8 }, .cplanes = 1, .mplanes = 1, .uv_swap = 0, .write_format = MI_CTRL_MP_WRITE_YUVINT, }, /* 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_SRGGB8, .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_YUYV, .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_YVYU, .fmt_type = FMT_YUV, .bpp = { 16 }, .cplanes = 1, .mplanes = 1, .uv_swap = 1, .write_format = MI_CTRL_SP_WRITE_INT, .output_format = MI_CTRL_SP_OUTPUT_YUV422, }, { .fourcc = V4L2_PIX_FMT_VYUY, .fmt_type = FMT_YUV, .bpp = { 16 }, .cplanes = 1, .mplanes = 1, .uv_swap = 1, .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_YVU422M, .fmt_type = FMT_YUV, .bpp = { 8, 8, 8 }, .cplanes = 3, .mplanes = 3, .uv_swap = 1, .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, }, { .fourcc = V4L2_PIX_FMT_YVU420, .fmt_type = FMT_YUV, .bpp = { 8, 8, 8 }, .cplanes = 3, .mplanes = 1, .uv_swap = 1, .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_INT, .output_format = MI_CTRL_SP_OUTPUT_YUV400, }, /* rgb */ { .fourcc = V4L2_PIX_FMT_RGB24, .fmt_type = FMT_RGB, .bpp = { 24 }, .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, }, { .fourcc = V4L2_PIX_FMT_BGR666, .fmt_type = FMT_RGB, .bpp = { 18 }, .mplanes = 1, .write_format = MI_CTRL_SP_WRITE_PLA, .output_format = MI_CTRL_SP_OUTPUT_RGB666, }, }; static struct stream_config rkisp1_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, /* 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, }, }; static struct stream_config rkisp1_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, /* 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, }, }; static const struct capture_fmt *find_fmt(struct rkisp1_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; } /* configure dual-crop unit */ static int rkisp1_config_dcrop(struct rkisp1_stream *stream, bool async) { struct rkisp1_device *dev = stream->ispdev; struct v4l2_rect *dcrop = &stream->dcrop; struct v4l2_rect *input_win; printk("%s:%d: called\n", __func__, __LINE__); /* dual-crop unit get data from isp */ input_win = rkisp1_get_isp_sd_win(&dev->isp_sdev); if (dcrop->width == input_win->width && dcrop->height == input_win->height && dcrop->left == 0 && dcrop->top == 0) { disable_dcrop(stream, async); v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "stream %d crop disabled\n", stream->id); return 0; } config_dcrop(stream, dcrop, async); v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "stream %d crop: %dx%d -> %dx%d\n", stream->id, input_win->width, input_win->height, dcrop->width, dcrop->height); return 0; } /* configure scale unit */ static int rkisp1_config_rsz(struct rkisp1_stream *stream, bool async) { struct rkisp1_device *dev = stream->ispdev; struct v4l2_pix_format_mplane output_fmt = stream->out_fmt; struct capture_fmt *output_isp_fmt = &stream->out_isp_fmt; struct ispsd_out_fmt *input_isp_fmt = rkisp1_get_ispsd_out_fmt(&dev->isp_sdev); struct v4l2_rect in_y, in_c, out_y, out_c; u32 xsubs_in, ysubs_in, xsubs_out, ysubs_out; printk("%s:%d: called\n", __func__, __LINE__); if (input_isp_fmt->fmt_type == FMT_BAYER) goto disable; /* set input and output sizes for scale calculation */ in_y.width = stream->dcrop.width; in_y.height = stream->dcrop.height; out_y.width = output_fmt.width; out_y.height = output_fmt.height; /* The size of Cb,Cr are related to the format */ if (mbus_code_xysubs(input_isp_fmt->mbus_code, &xsubs_in, &ysubs_in)) { v4l2_err(&dev->v4l2_dev, "Not xsubs/ysubs found\n"); return -EINVAL; } in_c.width = in_y.width / xsubs_in; in_c.height = in_y.height / ysubs_in; if (output_isp_fmt->fmt_type == FMT_YUV) { fcc_xysubs(output_isp_fmt->fourcc, &xsubs_out, &ysubs_out); out_c.width = out_y.width / xsubs_out; out_c.height = out_y.height / ysubs_out; } else { out_c.width = out_y.width / xsubs_in; out_c.height = out_y.height / ysubs_in; } if (in_c.width == out_c.width && in_c.height == out_c.height) goto disable; /* set RSZ input and output */ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "stream %d rsz/scale: %dx%d -> %dx%d\n", stream->id, stream->dcrop.width, stream->dcrop.height, output_fmt.width, output_fmt.height); v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "chroma scaling %dx%d -> %dx%d\n", in_c.width, in_c.height, out_c.width, out_c.height); /* calculate and set scale */ config_rsz(stream, &in_y, &in_c, &out_y, &out_c, async); if (rkisp1_debug) dump_rsz_regs(stream); return 0; disable: disable_rsz(stream, async); return 0; } /***************************** stream operations*******************************/ /* * configure memory interface for mainpath * This should only be called when stream-on */ static int mp_config_mi(struct rkisp1_stream *stream) { void __iomem *base = stream->ispdev->base_addr; printk("%s:%d: called\n", __func__, __LINE__); /* * NOTE: plane_fmt[0].sizeimage is total size of all planes for single * memory plane formats, so calculate the size explicitly. */ mi_set_y_size(stream, stream->out_fmt.plane_fmt[0].bytesperline * stream->out_fmt.height); mi_set_cb_size(stream, stream->out_fmt.plane_fmt[1].sizeimage); mi_set_cr_size(stream, stream->out_fmt.plane_fmt[2].sizeimage); mp_frame_end_int_enable(base); if (stream->out_isp_fmt.uv_swap) mp_set_uv_swap(base); config_mi_ctrl(stream); mp_mi_ctrl_set_format(base, stream->out_isp_fmt.write_format); mp_mi_ctrl_autoupdate_en(base); return 0; } /* * configure memory interface for selfpath * This should only be called when stream-on */ static int sp_config_mi(struct rkisp1_stream *stream) { void __iomem *base = stream->ispdev->base_addr; struct rkisp1_device *dev = stream->ispdev; struct capture_fmt *output_isp_fmt = &stream->out_isp_fmt; struct ispsd_out_fmt *input_isp_fmt = rkisp1_get_ispsd_out_fmt(&dev->isp_sdev); u32 sp_in_fmt; printk("%s:%d: called\n", __func__, __LINE__); if (mbus_code_sp_in_fmt(input_isp_fmt->mbus_code, &sp_in_fmt)) { v4l2_err(&dev->v4l2_dev, "Can't find the input format\n"); return -EINVAL; } /* * NOTE: plane_fmt[0].sizeimage is total size of all planes for single * memory plane formats, so calculate the size explicitly. */ mi_set_y_size(stream, stream->out_fmt.plane_fmt[0].bytesperline * stream->out_fmt.height); mi_set_cb_size(stream, stream->out_fmt.plane_fmt[1].sizeimage); mi_set_cr_size(stream, stream->out_fmt.plane_fmt[2].sizeimage); sp_set_y_width(base, stream->out_fmt.width); sp_set_y_height(base, stream->out_fmt.height); sp_set_y_line_length(base, stream->u.sp.y_stride); sp_frame_end_int_enable(base); if (output_isp_fmt->uv_swap) sp_set_uv_swap(base); config_mi_ctrl(stream); sp_mi_ctrl_set_format(base, stream->out_isp_fmt.write_format | sp_in_fmt | output_isp_fmt->output_format); sp_mi_ctrl_autoupdate_en(base); return 0; } static void mp_enable_mi(struct rkisp1_stream *stream) { void __iomem *base = stream->ispdev->base_addr; struct capture_fmt *isp_fmt = &stream->out_isp_fmt; printk("%s:%d: called\n", __func__, __LINE__); mi_ctrl_mp_disable(base); if (isp_fmt->fmt_type == FMT_BAYER) mi_ctrl_mpraw_enable(base); else if (isp_fmt->fmt_type == FMT_YUV) mi_ctrl_mpyuv_enable(base); } static void sp_enable_mi(struct rkisp1_stream *stream) { void __iomem *base = stream->ispdev->base_addr; printk("%s:%d: called\n", __func__, __LINE__); mi_ctrl_spyuv_enable(base); } static void mp_disable_mi(struct rkisp1_stream *stream) { void __iomem *base = stream->ispdev->base_addr; printk("%s:%d: called\n", __func__, __LINE__); mi_ctrl_mp_disable(base); } static void sp_disable_mi(struct rkisp1_stream *stream) { void __iomem *base = stream->ispdev->base_addr; printk("%s:%d: called\n", __func__, __LINE__); mi_ctrl_spyuv_disable(base); } /* Update buffer info to memory interface, it's called in interrupt */ static void update_mi(struct rkisp1_stream *stream) { struct rkisp1_dummy_buffer *dummy_buf = &stream->dummy_buf; printk_ratelimited(KERN_DEBUG "%s:%d: called\n", __func__, __LINE__); /* The dummy space allocated by dma_alloc_coherent is used, we can * throw data to it if there is no available buffer. */ if (stream->next_buf) { mi_set_y_addr(stream, stream->next_buf->buff_addr[RKISP1_PLANE_Y]); mi_set_cb_addr(stream, stream->next_buf->buff_addr[RKISP1_PLANE_CB]); mi_set_cr_addr(stream, stream->next_buf->buff_addr[RKISP1_PLANE_CR]); } else { v4l2_dbg(1, rkisp1_debug, &stream->ispdev->v4l2_dev, "stream %d: to dummy buf\n", stream->id); mi_set_y_addr(stream, dummy_buf->dma_addr); mi_set_cb_addr(stream, dummy_buf->dma_addr); mi_set_cr_addr(stream, dummy_buf->dma_addr); } mi_set_y_offset(stream, 0); mi_set_cb_offset(stream, 0); mi_set_cr_offset(stream, 0); } static void mp_stop_mi(struct rkisp1_stream *stream) { void __iomem *base = stream->ispdev->base_addr; printk("%s:%d: called\n", __func__, __LINE__); if (stream->state != RKISP1_STATE_STREAMING) return; stream->ops->clr_frame_end_int(base); stream->ops->disable_mi(stream); } static void sp_stop_mi(struct rkisp1_stream *stream) { void __iomem *base = stream->ispdev->base_addr; printk("%s:%d: called\n", __func__, __LINE__); if (stream->state != RKISP1_STATE_STREAMING) return; stream->ops->clr_frame_end_int(base); stream->ops->disable_mi(stream); } static struct streams_ops rkisp1_mp_streams_ops = { .config_mi = mp_config_mi, .enable_mi = mp_enable_mi, .disable_mi = mp_disable_mi, .stop_mi = mp_stop_mi, .set_data_path = mp_set_data_path, .clr_frame_end_int = mp_clr_frame_end_int, .is_frame_end_int_masked = mp_is_frame_end_int_masked, .is_stream_stopped = mp_is_stream_stopped, }; static struct streams_ops rkisp1_sp_streams_ops = { .config_mi = sp_config_mi, .enable_mi = sp_enable_mi, .disable_mi = sp_disable_mi, .stop_mi = sp_stop_mi, .set_data_path = sp_set_data_path, .clr_frame_end_int = sp_clr_frame_end_int, .is_frame_end_int_masked = sp_is_frame_end_int_masked, .is_stream_stopped = sp_is_stream_stopped, }; /* * This function is called when a frame end come. The next frame * is processing and we should set up buffer for next-next frame, * otherwise it will overflow. */ static int mi_frame_end(struct rkisp1_stream *stream) { struct rkisp1_device *isp_dev = stream->ispdev; struct rkisp1_isp_subdev *isp_sd = &isp_dev->isp_sdev; struct capture_fmt *isp_fmt = &stream->out_isp_fmt; unsigned long lock_flags = 0; int i = 0; printk_ratelimited(KERN_DEBUG "%s:%d: called\n", __func__, __LINE__); if (stream->curr_buf) { u64 ns = ktime_get_ns(); /* Dequeue a filled buffer */ for (i = 0; i < isp_fmt->mplanes; i++) { u32 payload_size = stream->out_fmt.plane_fmt[i].sizeimage; vb2_set_plane_payload( &stream->curr_buf->vb.vb2_buf, i, payload_size); } stream->curr_buf->vb.sequence = atomic_read(&isp_sd->frm_sync_seq) - 1; stream->curr_buf->vb.timestamp = ns_to_timeval(ns); vb2_buffer_done(&stream->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } /* Next frame is writing to it */ stream->curr_buf = stream->next_buf; stream->next_buf = NULL; /* Set up an empty buffer for the next-next frame */ spin_lock_irqsave(&stream->vbq_lock, lock_flags); if (!list_empty(&stream->buf_queue)) { stream->next_buf = list_first_entry(&stream->buf_queue, struct rkisp1_buffer, queue); list_del(&stream->next_buf->queue); } spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); update_mi(stream); return 0; } /***************************** vb2 operations*******************************/ /* * Set flags and wait, it should stop in interrupt. * If it didn't, stop it by force. */ static void rkisp1_stream_stop(struct rkisp1_stream *stream) { struct rkisp1_device *dev = stream->ispdev; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; int ret = 0; printk("%s:%d: called\n", __func__, __LINE__); stream->stopping = true; ret = wait_event_timeout(stream->done, stream->state != RKISP1_STATE_STREAMING, msecs_to_jiffies(1000)); if (!ret) { v4l2_warn(v4l2_dev, "waiting on event return error %d\n", ret); stream->ops->stop_mi(stream); stream->stopping = false; stream->state = RKISP1_STATE_READY; } disable_dcrop(stream, true); disable_rsz(stream, true); } /* * Most of registers inside rockchip isp1 have shadow register since * they must be not changed during processing a frame. * Usually, each sub-module updates its shadow register after * processing the last pixel of a frame. */ static int rkisp1_start(struct rkisp1_stream *stream) { void __iomem *base = stream->ispdev->base_addr; struct rkisp1_device *dev = stream->ispdev; struct rkisp1_stream *other = &dev->stream[stream->id ^ 1]; int ret; printk("%s:%d: called\n", __func__, __LINE__); stream->ops->set_data_path(base); ret = stream->ops->config_mi(stream); if (ret) return ret; /* Set up an buffer for the next frame */ mi_frame_end(stream); stream->ops->enable_mi(stream); /* It's safe to config ACTIVE and SHADOW regs for the * first stream. While when the second is starting, do NOT * force_cfg_update() because it also update the first one. * * The latter case would drop one more buf(that is 2) since * there's not buf in shadow when the second FE received. This's * also required because the sencond FE maybe corrupt especially * when run at 120fps. */ if (other->state != RKISP1_STATE_STREAMING) { force_cfg_update(base); mi_frame_end(stream); } stream->state = RKISP1_STATE_STREAMING; return 0; } static int rkisp1_queue_setup(struct vb2_queue *queue, const void *parg, unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { struct rkisp1_stream *stream = queue->drv_priv; struct rkisp1_device *dev = stream->ispdev; const struct v4l2_format *pfmt = parg; const struct v4l2_pix_format_mplane *pixm = NULL; const struct capture_fmt *isp_fmt = NULL; u32 i; printk("%s:%d: called\n", __func__, __LINE__); if (pfmt) { pixm = &pfmt->fmt.pix_mp; isp_fmt = find_fmt(stream, pixm->pixelformat); } else { pixm = &stream->out_fmt; isp_fmt = &stream->out_isp_fmt; } *num_planes = isp_fmt->mplanes; for (i = 0; i < isp_fmt->mplanes; i++) { const struct v4l2_plane_pix_format *plane_fmt; plane_fmt = &pixm->plane_fmt[i]; sizes[i] = plane_fmt->sizeimage; alloc_ctxs[i] = dev->alloc_ctx; } v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "%s count %d, size %d\n", v4l2_type_names[queue->type], *num_buffers, sizes[0]); return 0; } /* * The vb2_buffer are stored in rkisp1_buffer, in order to unify * mplane buffer and none-mplane buffer. */ static void rkisp1_buf_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct rkisp1_buffer *ispbuf = to_rkisp1_buffer(vbuf); struct vb2_queue *queue = vb->vb2_queue; struct rkisp1_stream *stream = queue->drv_priv; unsigned long lock_flags = 0; struct v4l2_pix_format_mplane *pixm = &stream->out_fmt; struct capture_fmt *isp_fmt = &stream->out_isp_fmt; int i; printk("%s:%d: called\n", __func__, __LINE__); memset(ispbuf->buff_addr, 0, sizeof(ispbuf->buff_addr)); for (i = 0; i < isp_fmt->mplanes; i++) ispbuf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); if (isp_fmt->mplanes == 1) { for (i = 0; i < isp_fmt->cplanes - 1; i++) { ispbuf->buff_addr[i + 1] = ispbuf->buff_addr[i] + pixm->plane_fmt[i].bytesperline * pixm->height; } } spin_lock_irqsave(&stream->vbq_lock, lock_flags); /* XXX: replace dummy to speed up */ if (stream->state == RKISP1_STATE_STREAMING && stream->next_buf == NULL && atomic_read(&stream->ispdev->isp_sdev.frm_sync_seq) == 0) { stream->next_buf = ispbuf; update_mi(stream); } else { list_add_tail(&ispbuf->queue, &stream->buf_queue); } spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); } static int rkisp1_create_dummy_buf(struct rkisp1_stream *stream) { struct rkisp1_dummy_buffer *dummy_buf = &stream->dummy_buf; struct rkisp1_device *dev = stream->ispdev; /* get a maximum size */ dummy_buf->size = max3(stream->out_fmt.plane_fmt[0].bytesperline * stream->out_fmt.height, stream->out_fmt.plane_fmt[1].sizeimage, stream->out_fmt.plane_fmt[2].sizeimage); dummy_buf->vaddr = dma_alloc_coherent(dev->dev, dummy_buf->size, &dummy_buf->dma_addr, GFP_KERNEL); if (!dummy_buf->vaddr) { v4l2_err(&dev->v4l2_dev, "Failed to allocate the memory for dummy buffer\n"); return -ENOMEM; } return 0; } static void rkisp1_destroy_dummy_buf(struct rkisp1_stream *stream) { struct rkisp1_dummy_buffer *dummy_buf = &stream->dummy_buf; struct rkisp1_device *dev = stream->ispdev; dma_free_coherent(dev->dev, dummy_buf->size, dummy_buf->vaddr, dummy_buf->dma_addr); } static void rkisp1_stop_streaming(struct vb2_queue *queue) { struct rkisp1_stream *stream = queue->drv_priv; struct rkisp1_vdev_node *node = &stream->vnode; struct rkisp1_device *dev = stream->ispdev; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; struct rkisp1_buffer *buf; unsigned long lock_flags = 0; int ret; printk("%s:%d: called\n", __func__, __LINE__); rkisp1_stream_stop(stream); /* call to the other devices */ media_entity_pipeline_stop(&node->vdev.entity); ret = dev->pipe.set_stream(&dev->pipe, false); if (ret < 0) v4l2_err(v4l2_dev, "pipeline stream-off failed error:%d\n", ret); /* release buffers */ spin_lock_irqsave(&stream->vbq_lock, lock_flags); if (stream->curr_buf) { list_add_tail(&stream->curr_buf->queue, &stream->buf_queue); stream->curr_buf = NULL; } if (stream->next_buf) { list_add_tail(&stream->next_buf->queue, &stream->buf_queue); stream->next_buf = NULL; } while (!list_empty(&stream->buf_queue)) { buf = list_first_entry(&stream->buf_queue, struct rkisp1_buffer, queue); list_del(&buf->queue); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); ret = dev->pipe.close(&dev->pipe); if (ret < 0) v4l2_err(v4l2_dev, "pipeline close failed error:%d\n", ret); rkisp1_destroy_dummy_buf(stream); } static int rkisp1_stream_start(struct rkisp1_stream *stream) { struct v4l2_device *v4l2_dev = &stream->ispdev->v4l2_dev; struct rkisp1_device *dev = stream->ispdev; struct rkisp1_stream *other = &dev->stream[stream->id ^ 1]; bool async = false; int ret; printk("%s:%d: called\n", __func__, __LINE__); if (other->state == RKISP1_STATE_STREAMING) async = true; ret = rkisp1_config_rsz(stream, async); if (ret < 0) { v4l2_err(v4l2_dev, "config rsz failed with error %d\n", ret); return ret; } /* * can't be async now, otherwise the latter started stream fails to * produce mi interrupt. */ ret = rkisp1_config_dcrop(stream, false); if (ret < 0) { v4l2_err(v4l2_dev, "config dcrop failed with error %d\n", ret); return ret; } return rkisp1_start(stream); } static int rkisp1_start_streaming(struct vb2_queue *queue, unsigned int count) { struct rkisp1_stream *stream = queue->drv_priv; struct rkisp1_vdev_node *node = &stream->vnode; struct rkisp1_device *dev = stream->ispdev; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; int ret; printk("%s:%d: called\n", __func__, __LINE__); if (WARN_ON(stream->state != RKISP1_STATE_READY)) return -EBUSY; ret = rkisp1_create_dummy_buf(stream); if (ret < 0) return ret; /* enable clocks/power-domains */ ret = dev->pipe.open(&dev->pipe, &node->vdev.entity, true); if (ret < 0) { v4l2_err(v4l2_dev, "open cif pipeline failed %d\n", ret); goto destroy_dummy_buf; } /* configure stream hardware to start */ ret = rkisp1_stream_start(stream); if (ret < 0) { v4l2_err(v4l2_dev, "start streaming failed\n"); goto close_pipe; } /* start sub-devices */ ret = dev->pipe.set_stream(&dev->pipe, true); if (ret < 0) goto stop_stream; ret = media_entity_pipeline_start(&node->vdev.entity, &dev->pipe.pipe); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "start pipeline failed %d\n", ret); goto pipe_stream_off; } return 0; pipe_stream_off: dev->pipe.set_stream(&dev->pipe, false); stop_stream: rkisp1_stream_stop(stream); close_pipe: dev->pipe.close(&dev->pipe); destroy_dummy_buf: rkisp1_destroy_dummy_buf(stream); return ret; } static struct vb2_ops rkisp1_vb2_ops = { .queue_setup = rkisp1_queue_setup, .buf_queue = rkisp1_buf_queue, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, .stop_streaming = rkisp1_stop_streaming, .start_streaming = rkisp1_start_streaming, }; static int rkisp_init_vb2_queue(struct vb2_queue *q, struct rkisp1_stream *stream, enum v4l2_buf_type buf_type) { struct rkisp1_vdev_node *node; printk("%s:%d: called\n", __func__, __LINE__); node = queue_to_node(q); q->type = buf_type; q->io_modes = VB2_MMAP | VB2_DMABUF; q->drv_priv = stream; q->ops = &rkisp1_vb2_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct rkisp1_buffer); q->min_buffers_needed = CIF_ISP_REQ_BUFS_MIN; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &node->vlock; return vb2_queue_init(q); } static void rkisp1_set_fmt(struct rkisp1_stream *stream, struct v4l2_pix_format_mplane *pixm, bool try) { const struct capture_fmt *fmt; const struct stream_config *config = stream->config; struct rkisp1_stream *other_stream = &stream->ispdev->stream[!stream->id]; unsigned int imagsize = 0; unsigned int planes; u32 xsubs = 1, ysubs = 1; int i; printk("%s:%d: called\n", __func__, __LINE__); fmt = find_fmt(stream, pixm->pixelformat); if (!fmt) fmt = config->fmts; /* do checks on resolution */ pixm->width = clamp_t(u32, pixm->width, config->min_rsz_width, config->max_rsz_width); pixm->height = clamp_t(u32, pixm->height, config->min_rsz_height, config->max_rsz_height); 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->state == RKISP1_STATE_STREAMING) pixm->quantization = other_stream->out_fmt.quantization; /* calculate size */ 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; 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; } bytesperline = width * DIV_ROUND_UP(fmt->bpp[i], 8); /* stride is only available for sp stream and y plane */ if (stream->id != RKISP1_STREAM_SP || i != 0 || plane_fmt->bytesperline < bytesperline) plane_fmt->bytesperline = bytesperline; plane_fmt->sizeimage = plane_fmt->bytesperline * height; 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) pixm->plane_fmt[0].sizeimage = imagsize; if (!try) { stream->out_isp_fmt = *fmt; stream->out_fmt = *pixm; if (stream->id == RKISP1_STREAM_SP) { stream->u.sp.y_stride = pixm->plane_fmt[0].bytesperline / DIV_ROUND_UP(fmt->bpp[0], 8); } else { stream->u.mp.raw_enable = (fmt->fmt_type == FMT_BAYER); } v4l2_dbg(1, rkisp1_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); } } /************************* v4l2_file_operations***************************/ void rkisp1_stream_init(struct rkisp1_device *dev, u32 id) { struct rkisp1_stream *stream = &dev->stream[id]; struct v4l2_pix_format_mplane pixm; printk("%s:%d: called\n", __func__, __LINE__); memset(stream, 0, sizeof(*stream)); stream->id = id; stream->ispdev = dev; INIT_LIST_HEAD(&stream->buf_queue); init_waitqueue_head(&stream->done); spin_lock_init(&stream->vbq_lock); if (stream->id == RKISP1_STREAM_SP) { stream->ops = &rkisp1_sp_streams_ops; stream->config = &rkisp1_sp_stream_config; } else { stream->ops = &rkisp1_mp_streams_ops; stream->config = &rkisp1_mp_stream_config; } stream->state = RKISP1_STATE_READY; memset(&pixm, 0, sizeof(pixm)); pixm.pixelformat = V4L2_PIX_FMT_YUYV; pixm.width = RKISP1_DEFAULT_WIDTH; pixm.height = RKISP1_DEFAULT_HEIGHT; rkisp1_set_fmt(stream, &pixm, false); stream->dcrop.left = 0; stream->dcrop.top = 0; stream->dcrop.width = RKISP1_DEFAULT_WIDTH; stream->dcrop.height = RKISP1_DEFAULT_HEIGHT; } static const struct v4l2_file_operations rkisp1_fops = { .open = v4l2_fh_open, .release = vb2_fop_release, .unlocked_ioctl = video_ioctl2, .poll = vb2_fop_poll, .mmap = vb2_fop_mmap, }; /* * mp and sp v4l2_ioctl_ops */ static int rkisp1_enum_input(struct file *file, void *priv, struct v4l2_input *input) { printk("%s:%d: called\n", __func__, __LINE__); if (input->index > 0) return -EINVAL; input->type = V4L2_INPUT_TYPE_CAMERA; strlcpy(input->name, "Camera", sizeof(input->name)); return 0; } static int rkisp1_try_fmt_vid_cap_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct rkisp1_stream *stream = video_drvdata(file); printk("%s:%d: called\n", __func__, __LINE__); rkisp1_set_fmt(stream, &f->fmt.pix_mp, true); return 0; } static int rkisp1_enum_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct rkisp1_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 rkisp1_s_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct rkisp1_stream *stream = video_drvdata(file); struct video_device *vdev = &stream->vnode.vdev; struct rkisp1_vdev_node *node = vdev_to_node(vdev); struct rkisp1_device *dev = stream->ispdev; if (vb2_is_busy(&node->buf_queue)) { v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); return -EBUSY; } rkisp1_set_fmt(stream, &f->fmt.pix_mp, false); return 0; } static int rkisp1_g_fmt_vid_cap_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct rkisp1_stream *stream = video_drvdata(file); f->fmt.pix_mp = stream->out_fmt; return 0; } static int rkisp1_g_selection(struct file *file, void *prv, struct v4l2_selection *sel) { struct rkisp1_stream *stream = video_drvdata(file); struct rkisp1_device *dev = stream->ispdev; struct v4l2_rect *dcrop = &stream->dcrop; struct v4l2_rect *input_win; input_win = rkisp1_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 *rkisp1_update_crop(struct rkisp1_stream *stream, struct v4l2_rect *sel, const struct v4l2_rect *in) { printk("%s:%d: called\n", __func__, __LINE__); /* Not crop for MP bayer raw data */ if (stream->id == RKISP1_STREAM_MP && stream->out_isp_fmt.fmt_type == FMT_BAYER) { 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 rkisp1_s_selection(struct file *file, void *prv, struct v4l2_selection *sel) { struct rkisp1_stream *stream = video_drvdata(file); struct video_device *vdev = &stream->vnode.vdev; struct rkisp1_vdev_node *node = vdev_to_node(vdev); struct rkisp1_device *dev = stream->ispdev; struct v4l2_rect *dcrop = &stream->dcrop; const struct v4l2_rect *input_win; printk("%s:%d: called\n", __func__, __LINE__); if (vb2_is_busy(&node->buf_queue)) { v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); return -EBUSY; } input_win = rkisp1_get_isp_sd_win(&dev->isp_sdev); if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; if (sel->flags != 0) return -EINVAL; *dcrop = *rkisp1_update_crop(stream, &sel->r, input_win); v4l2_dbg(1, rkisp1_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 rkisp1_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct rkisp1_stream *stream = video_drvdata(file); struct device *dev = stream->ispdev->dev; printk("%s:%d: called\n", __func__, __LINE__); strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver)); strlcpy(cap->card, dev->driver->name, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(dev)); return 0; } static const struct v4l2_ioctl_ops rkisp1_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 = rkisp1_enum_input, .vidioc_try_fmt_vid_cap_mplane = rkisp1_try_fmt_vid_cap_mplane, .vidioc_enum_fmt_vid_cap_mplane = rkisp1_enum_fmt_vid_cap_mplane, .vidioc_s_fmt_vid_cap_mplane = rkisp1_s_fmt_vid_cap_mplane, .vidioc_g_fmt_vid_cap_mplane = rkisp1_g_fmt_vid_cap_mplane, .vidioc_s_selection = rkisp1_s_selection, .vidioc_g_selection = rkisp1_g_selection, .vidioc_querycap = rkisp1_querycap, }; static void rkisp1_unregister_stream_vdev(struct rkisp1_stream *stream) { printk("%s:%d: called\n", __func__, __LINE__); media_entity_cleanup(&stream->vnode.vdev.entity); video_unregister_device(&stream->vnode.vdev); } void rkisp1_unregister_stream_vdevs(struct rkisp1_device *dev) { struct rkisp1_stream *mp_stream = &dev->stream[RKISP1_STREAM_MP]; struct rkisp1_stream *sp_stream = &dev->stream[RKISP1_STREAM_SP]; printk("%s:%d: called\n", __func__, __LINE__); rkisp1_unregister_stream_vdev(mp_stream); rkisp1_unregister_stream_vdev(sp_stream); } static int rkisp1_register_stream_vdev(struct rkisp1_stream *stream) { struct rkisp1_device *dev = stream->ispdev; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; struct video_device *vdev = &stream->vnode.vdev; struct rkisp1_vdev_node *node; int ret; printk("%s:%d: called\n", __func__, __LINE__); strlcpy(vdev->name, stream->id == RKISP1_STREAM_SP ? SP_VDEV_NAME : MP_VDEV_NAME, sizeof(vdev->name)); node = vdev_to_node(vdev); mutex_init(&node->vlock); vdev->ioctl_ops = &rkisp1_v4l2_ioctl_ops; vdev->release = video_device_release_empty; vdev->fops = &rkisp1_fops; vdev->minor = -1; vdev->v4l2_dev = v4l2_dev; vdev->lock = &node->vlock; 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; rkisp_init_vb2_queue(&node->buf_queue, stream, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); 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_init(&vdev->entity, 1, &node->pad, 0); if (ret < 0) goto unreg; return 0; unreg: video_unregister_device(vdev); return ret; } int rkisp1_register_stream_vdevs(struct rkisp1_device *dev) { struct rkisp1_stream *stream; int i, j, ret; printk("%s:%d: called\n", __func__, __LINE__); for (i = 0; i < RKISP1_MAX_STREAM; i++) { stream = &dev->stream[i]; stream->ispdev = dev; ret = rkisp1_register_stream_vdev(stream); if (ret < 0) goto err; } return 0; err: for (j = 0; j < i; j++) { stream = &dev->stream[j]; rkisp1_unregister_stream_vdev(stream); } return ret; } /**************** Interrupter Handler ****************/ void rkisp1_mi_isr(struct rkisp1_stream *stream) { struct rkisp1_device *dev = stream->ispdev; void __iomem *base = stream->ispdev->base_addr; u32 val; printk_ratelimited(KERN_DEBUG "%s:%d: called\n", __func__, __LINE__); stream->ops->clr_frame_end_int(base); if (stream->ops->is_frame_end_int_masked(base)) { val = mi_get_masked_int_status(base); v4l2_err(&dev->v4l2_dev, "icr err: 0x%x\n", val); } if (stream->stopping) { printk("%s:%d: stream stopping\n", __func__, __LINE__); /* Make sure stream is actually stopped, whose state * can be read from the shadow register, before wake_up() * thread which would immediately free all frame buffers. * stop_mi() takes effect at the next frame end * that sync the configurations to shadow regs. */ if (stream->ops->is_stream_stopped(dev->base_addr)) { stream->stopping = false; stream->state = RKISP1_STATE_READY; wake_up(&stream->done); } else { stream->ops->stop_mi(stream); } } else { mi_frame_end(stream); } }