2314 lines
61 KiB
C

/*
* 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 <linux/delay.h>
#include <linux/pm_runtime.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-subdev.h>
#include <media/videobuf2-dma-contig.h>
#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 0
#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_OUTPUT_STEP_WISE 8
#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
*/
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 in_mbus_code, u32 out_fourcc,
u32 *format)
{
switch (in_mbus_code) {
case MEDIA_BUS_FMT_YUYV8_2X8:
*format = MI_CTRL_SP_INPUT_YUV422;
break;
default:
return -EINVAL;
}
/*
* Only SP can support output format of YCbCr4:0:0,
* and the input format of SP must be YCbCr4:0:0
* when outputting YCbCr4:0:0.
* The output format of isp is YCbCr4:2:2,
* so the CbCr data is discarded here.
*/
if (out_fourcc == V4L2_PIX_FMT_GREY)
*format = MI_CTRL_SP_INPUT_YUV400;
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_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_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_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,
}
};
static const struct capture_fmt raw_fmts[] = {
/* raw */
{
.fourcc = V4L2_PIX_FMT_SRGGB8,
.fmt_type = FMT_BAYER,
.bpp = { 8 },
.mplanes = 1,
}, {
.fourcc = V4L2_PIX_FMT_SGRBG8,
.fmt_type = FMT_BAYER,
.bpp = { 8 },
.mplanes = 1,
}, {
.fourcc = V4L2_PIX_FMT_SGBRG8,
.fmt_type = FMT_BAYER,
.bpp = { 8 },
.mplanes = 1,
}, {
.fourcc = V4L2_PIX_FMT_SBGGR8,
.fmt_type = FMT_BAYER,
.bpp = { 8 },
.mplanes = 1,
}, {
.fourcc = V4L2_PIX_FMT_SRGGB10,
.fmt_type = FMT_BAYER,
.bpp = { 10 },
.mplanes = 1,
}, {
.fourcc = V4L2_PIX_FMT_SGRBG10,
.fmt_type = FMT_BAYER,
.bpp = { 10 },
.mplanes = 1,
}, {
.fourcc = V4L2_PIX_FMT_SGBRG10,
.fmt_type = FMT_BAYER,
.bpp = { 10 },
.mplanes = 1,
}, {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.fmt_type = FMT_BAYER,
.bpp = { 10 },
.mplanes = 1,
}, {
.fourcc = V4L2_PIX_FMT_SRGGB12,
.fmt_type = FMT_BAYER,
.bpp = { 12 },
.mplanes = 1,
}, {
.fourcc = V4L2_PIX_FMT_SGRBG12,
.fmt_type = FMT_BAYER,
.bpp = { 12 },
.mplanes = 1,
}, {
.fourcc = V4L2_PIX_FMT_SGBRG12,
.fmt_type = FMT_BAYER,
.bpp = { 12 },
.mplanes = 1,
}, {
.fourcc = V4L2_PIX_FMT_SBGGR12,
.fmt_type = FMT_BAYER,
.bpp = { 12 },
.mplanes = 1,
},
};
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 struct stream_config rkisp1_raw_stream_config = {
.fmts = raw_fmts,
.fmt_size = ARRAY_SIZE(raw_fmts),
};
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;
/* 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 = 1, ysubs_in = 1;
u32 xsubs_out = 1, ysubs_out = 1;
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*******************************/
/*
* memory base addresses should be with respect
* to the burst alignment restriction for AXI.
*/
static u32 calc_burst_len(struct rkisp1_stream *stream)
{
struct rkisp1_device *dev = stream->ispdev;
u32 y_size = stream->out_fmt.plane_fmt[0].bytesperline *
stream->out_fmt.height;
u32 cb_size = stream->out_fmt.plane_fmt[1].sizeimage;
u32 cr_size = stream->out_fmt.plane_fmt[2].sizeimage;
u32 cb_offs, cr_offs;
u32 bus, burst;
int i;
/* MI128bit and MI64bit */
bus = 8;
if (dev->isp_ver == ISP_V12 || dev->isp_ver == ISP_V13)
bus = 16;
/* y/c base addr: burstN * bus alignment */
cb_offs = y_size;
cr_offs = cr_size ? (cb_size + cb_offs) : 0;
if (!(cb_offs % (bus * 16)) &&
!(cr_offs % (bus * 16)))
burst = CIF_MI_CTRL_BURST_LEN_LUM_16 |
CIF_MI_CTRL_BURST_LEN_CHROM_16;
else if (!(cb_offs % (bus * 8)) &&
!(cr_offs % (bus * 8)))
burst = CIF_MI_CTRL_BURST_LEN_LUM_8 |
CIF_MI_CTRL_BURST_LEN_CHROM_8;
else
burst = CIF_MI_CTRL_BURST_LEN_LUM_4 |
CIF_MI_CTRL_BURST_LEN_CHROM_4;
if (cb_offs % (bus * 4) ||
cr_offs % (bus * 4))
v4l2_warn(&dev->v4l2_dev,
"%dx%d fmt:0x%x not support, should be %d aligned\n",
stream->out_fmt.width,
stream->out_fmt.height,
stream->out_fmt.pixelformat,
(cr_offs == 0) ? bus * 4 : bus * 16);
stream->burst = burst;
for (i = 0; i < RKISP1_MAX_STREAM; i++)
if (burst > dev->stream[i].burst)
burst = dev->stream[i].burst;
if (stream->interlaced) {
if (!stream->out_fmt.width % (bus * 16))
stream->burst = CIF_MI_CTRL_BURST_LEN_LUM_16 |
CIF_MI_CTRL_BURST_LEN_CHROM_16;
else if (!stream->out_fmt.width % (bus * 8))
stream->burst = CIF_MI_CTRL_BURST_LEN_LUM_8 |
CIF_MI_CTRL_BURST_LEN_CHROM_8;
else
stream->burst = CIF_MI_CTRL_BURST_LEN_LUM_4 |
CIF_MI_CTRL_BURST_LEN_CHROM_4;
if (stream->out_fmt.width % (bus * 4))
v4l2_warn(&dev->v4l2_dev,
"interlaced: width should be %d aligned\n",
bus * 4);
burst = min(stream->burst, burst);
stream->burst = burst;
}
return burst;
}
/*
* 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;
/*
* 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);
mi_frame_end_int_enable(stream);
if (stream->out_isp_fmt.uv_swap)
mp_set_uv_swap(base);
config_mi_ctrl(stream, calc_burst_len(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;
if (mbus_code_sp_in_fmt(input_isp_fmt->mbus_code,
output_isp_fmt->fourcc, &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);
if (stream->interlaced) {
stream->u.sp.vir_offs =
stream->out_fmt.plane_fmt[0].bytesperline;
sp_set_y_height(base, stream->out_fmt.height / 2);
sp_set_y_line_length(base, stream->u.sp.y_stride * 2);
} else {
sp_set_y_height(base, stream->out_fmt.height);
sp_set_y_line_length(base, stream->u.sp.y_stride);
}
mi_frame_end_int_enable(stream);
if (output_isp_fmt->uv_swap)
sp_set_uv_swap(base);
config_mi_ctrl(stream, calc_burst_len(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;
}
/*
* configure memory interface for rawpath
* This should only be called when stream-on
*/
static int raw_config_mi(struct rkisp1_stream *stream)
{
void __iomem *base = stream->ispdev->base_addr;
struct rkisp1_device *dev = stream->ispdev;
struct v4l2_mbus_framefmt *in_frm;
u32 in_size;
if (!dev->active_sensor ||
(dev->active_sensor &&
dev->active_sensor->mbus.type != V4L2_MBUS_CSI2_DPHY)) {
if (stream->id == RKISP1_STREAM_RAW)
v4l2_err(&dev->v4l2_dev,
"only mipi sensor support raw path\n");
return -EINVAL;
}
if (dev->stream[RKISP1_STREAM_RAW].streaming)
return 0;
in_frm = &dev->active_sensor->fmt.format;
v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
"stream:%d input %dx%d\n",
stream->id, in_frm->width, in_frm->height);
/* raw output size equal to sensor input size */
if (stream->id == RKISP1_STREAM_RAW) {
in_size = stream->out_fmt.plane_fmt[0].sizeimage;
} else {
struct rkisp1_stream *raw = &dev->stream[RKISP1_STREAM_RAW];
in_size = raw->out_fmt.plane_fmt[0].sizeimage;
}
dmatx0_set_pic_size(base, in_frm->width, in_frm->height);
dmatx0_set_pic_off(base, 0);
dmatx0_ctrl(base,
CIF_ISP_CSI0_DMATX0_VC(1) |
CIF_ISP_CSI0_DMATX0_SIMG_SWP |
CIF_ISP_CSI0_DMATX0_SIMG_MODE);
mi_raw0_set_size(base, in_size);
mi_raw0_set_offs(base, 0);
mi_raw0_set_length(base, 0);
mi_raw0_set_irq_offs(base, 0);
/* dummy buf for raw first address shadow */
mi_raw0_set_addr(base, stream->dummy_buf.dma_addr);
mi_ctrl2(base, CIF_MI_CTRL2_MIPI_RAW0_AUTO_UPDATE);
if (stream->id == RKISP1_STREAM_RAW)
stream->u.raw.pre_stop = false;
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;
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;
mi_ctrl_spyuv_enable(base);
}
static void raw_enable_mi(struct rkisp1_stream *stream)
{
void __iomem *base = stream->ispdev->base_addr;
mi_mipi_raw0_enable(base);
}
static void mp_disable_mi(struct rkisp1_stream *stream)
{
void __iomem *base = stream->ispdev->base_addr;
mi_ctrl_mp_disable(base);
}
static void sp_disable_mi(struct rkisp1_stream *stream)
{
void __iomem *base = stream->ispdev->base_addr;
mi_ctrl_spyuv_disable(base);
}
static void update_dmatx0(struct rkisp1_stream *stream)
{
void __iomem *base = stream->ispdev->base_addr;
struct rkisp1_dummy_buffer *dummy_buf = &stream->dummy_buf;
if (stream->next_buf)
mi_raw0_set_addr(base,
stream->next_buf->buff_addr[RKISP1_PLANE_Y]);
else
mi_raw0_set_addr(base, dummy_buf->dma_addr);
}
/* 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;
/* 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)
{
if (!stream->streaming)
return;
mi_frame_end_int_clear(stream);
stream->ops->disable_mi(stream);
}
static void sp_stop_mi(struct rkisp1_stream *stream)
{
if (!stream->streaming)
return;
mi_frame_end_int_clear(stream);
stream->ops->disable_mi(stream);
}
static void raw_stop_mi(struct rkisp1_stream *stream)
{
void __iomem *base = stream->ispdev->base_addr;
if (!stream->streaming)
return;
mi_mipi_raw0_disable(base);
}
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,
.is_stream_stopped = mp_is_stream_stopped,
.update_mi = update_mi,
};
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,
.is_stream_stopped = sp_is_stream_stopped,
.update_mi = update_mi,
};
static struct streams_ops rkisp1_raw_streams_ops = {
.config_mi = raw_config_mi,
.enable_mi = raw_enable_mi,
.stop_mi = raw_stop_mi,
.update_mi = update_dmatx0,
};
/*
* 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;
bool interlaced = stream->interlaced;
unsigned long lock_flags = 0;
int i = 0;
if (stream->curr_buf &&
(!interlaced ||
(stream->u.sp.field_rec == RKISP_FIELD_ODD &&
stream->u.sp.field == RKISP_FIELD_EVEN))) {
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.vb2_buf.timestamp = ns;
vb2_buffer_done(&stream->curr_buf->vb.vb2_buf,
VB2_BUF_STATE_DONE);
stream->curr_buf = NULL;
}
if (!interlaced ||
(stream->curr_buf == stream->next_buf &&
stream->u.sp.field == RKISP_FIELD_ODD)) {
/* Next frame is writing to it
* Interlaced: odd field next buffer address
*/
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);
} else if (stream->u.sp.field_rec == RKISP_FIELD_ODD &&
stream->u.sp.field == RKISP_FIELD_EVEN) {
/* Interlaced: event field next buffer address */
if (stream->next_buf) {
stream->next_buf->buff_addr[RKISP1_PLANE_Y] +=
stream->u.sp.vir_offs;
stream->next_buf->buff_addr[RKISP1_PLANE_CB] +=
stream->u.sp.vir_offs;
stream->next_buf->buff_addr[RKISP1_PLANE_CR] +=
stream->u.sp.vir_offs;
}
stream->curr_buf = stream->next_buf;
}
stream->ops->update_mi(stream);
if (interlaced)
stream->u.sp.field_rec = stream->u.sp.field;
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;
stream->stopping = true;
stream->ops->stop_mi(stream);
if (dev->isp_state == ISP_START &&
dev->isp_inp != INP_DMARX_ISP) {
ret = wait_event_timeout(stream->done,
!stream->streaming,
msecs_to_jiffies(1000));
if (!ret) {
v4l2_warn(v4l2_dev, "waiting on event return error %d\n", ret);
stream->stopping = false;
stream->streaming = false;
}
} else {
stream->stopping = false;
stream->streaming = false;
}
if (stream->id != RKISP1_STREAM_RAW) {
disable_dcrop(stream, true);
disable_rsz(stream, true);
}
stream->burst =
CIF_MI_CTRL_BURST_LEN_LUM_16 |
CIF_MI_CTRL_BURST_LEN_CHROM_16;
stream->interlaced = false;
}
/*
* 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;
bool other_streaming = false;
int i, ret;
for (i = 0; i < RKISP1_MAX_STREAM; i++) {
if (i != stream->id &&
dev->stream[i].streaming) {
other_streaming = true;
break;
}
}
/* stream raw need mi_cfg_upd to update first base address shadow
* config raw in first stream (sp/mp), and enable when raw stream open.
*/
if (!other_streaming &&
stream->id == RKISP1_STREAM_RAW) {
v4l2_err(&dev->v4l2_dev,
"stream raw only support to open after stream mp/sp");
return -EINVAL;
}
#if RKISP1_RK3326_USE_OLDMIPI
if (dev->isp_ver == ISP_V13)
#else
if (dev->isp_ver == ISP_V12 ||
dev->isp_ver == ISP_V13)
#endif
raw_config_mi(stream);
if (stream->ops->set_data_path)
stream->ops->set_data_path(base);
ret = stream->ops->config_mi(stream);
if (ret)
return ret;
/* for mp/sp Set up an buffer for the next frame */
if (stream->id != RKISP1_STREAM_RAW)
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_streaming) {
force_cfg_update(base);
mi_frame_end(stream);
}
stream->streaming = true;
return 0;
}
static int rkisp1_queue_setup(struct vb2_queue *queue,
unsigned int *num_buffers,
unsigned int *num_planes,
unsigned int sizes[],
struct device *alloc_ctxs[])
{
struct rkisp1_stream *stream = queue->drv_priv;
struct rkisp1_device *dev = stream->ispdev;
const struct v4l2_pix_format_mplane *pixm = NULL;
const struct capture_fmt *isp_fmt = NULL;
u32 i;
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;
}
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;
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 (stream->id == RKISP1_STREAM_RAW) {
/* for check dmatx to ddr complete */
u32 sizeimage = pixm->plane_fmt[0].sizeimage;
u32 *buf = vb2_plane_vaddr(vb, 0);
if (buf) {
*buf = RKISP1_DMATX_CHECK;
*(buf + sizeimage / 4 - 1) = RKISP1_DMATX_CHECK;
}
}
}
/*
* NOTE: plane_fmt[0].sizeimage is total size of all planes for single
* memory plane formats, so calculate the size explicitly.
*/
if (isp_fmt->mplanes == 1) {
for (i = 0; i < isp_fmt->cplanes - 1; i++) {
ispbuf->buff_addr[i + 1] = (i == 0) ?
ispbuf->buff_addr[i] +
pixm->plane_fmt[i].bytesperline *
pixm->height :
ispbuf->buff_addr[i] +
pixm->plane_fmt[i].sizeimage;
}
}
spin_lock_irqsave(&stream->vbq_lock, lock_flags);
/* XXX: replace dummy to speed up */
if (stream->streaming &&
!stream->next_buf &&
!stream->interlaced &&
stream->id != RKISP1_STREAM_RAW &&
atomic_read(&stream->ispdev->isp_sdev.frm_sync_seq) == 0) {
stream->next_buf = ispbuf;
stream->ops->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);
if (dev->active_sensor &&
dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY &&
(dev->isp_ver == ISP_V12 ||
dev->isp_ver == ISP_V13)) {
u32 in_size;
struct rkisp1_stream *raw = &dev->stream[RKISP1_STREAM_RAW];
in_size = raw->out_fmt.plane_fmt[0].sizeimage;
dummy_buf->size = max(dummy_buf->size, in_size);
}
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;
rkisp1_stream_stop(stream);
/* call to the other devices */
video_device_pipeline_stop(&node->vdev);
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);
if (stream->curr_buf == stream->next_buf)
stream->next_buf = NULL;
stream->curr_buf = NULL;
}
if (stream->next_buf) {
list_add_tail(&stream->next_buf->queue, &stream->buf_queue);
stream->next_buf = NULL;
}
while (!list_empty(&stream->buf_queue)) {
buf = list_first_entry(&stream->buf_queue,
struct 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;
/* STREAM RAW don't have rsz and dcrop */
if (stream->id == RKISP1_STREAM_RAW)
goto end;
if (other->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;
}
end:
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;
unsigned int i;
if (WARN_ON(stream->streaming))
return -EBUSY;
if (dev->isp_inp != INP_DMARX_ISP) {
/* Always update sensor info in case media topology changed */
ret = rkisp1_update_sensor_info(dev);
if (ret < 0) {
v4l2_err(v4l2_dev,
"update sensor info failed %d\n",
ret);
goto buffer_done;
}
}
if (dev->active_sensor &&
dev->active_sensor->fmt.format.field ==
V4L2_FIELD_INTERLACED) {
if (stream->id != RKISP1_STREAM_SP) {
v4l2_err(v4l2_dev,
"only selfpath support interlaced\n");
ret = -EINVAL;
goto buffer_done;
}
stream->interlaced = true;
stream->u.sp.field = RKISP_FIELD_INVAL;
stream->u.sp.field_rec = RKISP_FIELD_INVAL;
}
ret = rkisp1_create_dummy_buf(stream);
if (ret < 0)
goto buffer_done;
/* 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 = video_device_pipeline_start(&node->vdev, &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);
buffer_done:
for (i = 0; i < queue->num_buffers; ++i) {
struct vb2_buffer *vb;
vb = queue->bufs[i];
if (vb->state == VB2_BUF_STATE_ACTIVE)
vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);
}
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)
{
q->type = buf_type;
q->io_modes = VB2_MMAP | VB2_USERPTR | 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 = &stream->ispdev->apilock;
q->dev = stream->ispdev->dev;
return vb2_queue_init(q);
}
/*
* 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 rkisp1_device *dev,
const struct stream_config *config,
struct v4l2_rect *max_rsz)
{
struct v4l2_rect *input_win;
input_win = rkisp1_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 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;
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 != RKISP1_STREAM_RAW) {
struct v4l2_rect max_rsz;
other_stream =
&stream->ispdev->stream[!stream->id ^ 1];
/* 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->stream[RKISP1_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 */
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;
}
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 if (stream->id == RKISP1_STREAM_MP) {
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);
}
return 0;
}
int rkisp1_fh_open(struct file *filp)
{
struct rkisp1_stream *stream = video_drvdata(filp);
struct rkisp1_device *dev = stream->ispdev;
int ret;
ret = v4l2_fh_open(filp);
if (!ret) {
atomic_inc(&dev->open_cnt);
ret = v4l2_pipeline_pm_get(&stream->vnode.vdev.entity);
if (ret < 0)
vb2_fop_release(filp);
}
return ret;
}
int rkisp1_fop_release(struct file *file)
{
struct rkisp1_stream *stream = video_drvdata(file);
struct rkisp1_device *dev = stream->ispdev;
int ret;
ret = vb2_fop_release(file);
if (!ret) {
ret = v4l2_pipeline_pm_get(&stream->vnode.vdev.entity);
if (ret < 0)
v4l2_err(&dev->v4l2_dev,
"set pipeline power failed %d\n", ret);
atomic_dec(&dev->open_cnt);
}
return ret;
}
void rkisp1_set_stream_def_fmt(struct rkisp1_device *dev, u32 id,
u32 width, u32 height, u32 pixelformat)
{
struct rkisp1_stream *stream = &dev->stream[id];
struct v4l2_pix_format_mplane pixm;
memset(&pixm, 0, sizeof(pixm));
pixm.pixelformat = pixelformat;
pixm.width = width;
pixm.height = height;
rkisp1_set_fmt(stream, &pixm, false);
stream->dcrop.left = 0;
stream->dcrop.top = 0;
stream->dcrop.width = width;
stream->dcrop.height = height;
}
/************************* v4l2_file_operations***************************/
void rkisp1_stream_init(struct rkisp1_device *dev, u32 id)
{
struct rkisp1_stream *stream = &dev->stream[id];
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 if (stream->id == RKISP1_STREAM_RAW) {
stream->ops = &rkisp1_raw_streams_ops;
stream->config = &rkisp1_raw_stream_config;
} else {
stream->ops = &rkisp1_mp_streams_ops;
stream->config = &rkisp1_mp_stream_config;
}
stream->streaming = false;
stream->interlaced = false;
stream->burst =
CIF_MI_CTRL_BURST_LEN_LUM_16 |
CIF_MI_CTRL_BURST_LEN_CHROM_16;
}
static const struct v4l2_file_operations rkisp1_fops = {
.open = rkisp1_fh_open,
.release = rkisp1_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)
{
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);
return rkisp1_set_fmt(stream, &f->fmt.pix_mp, true);
}
static int rkisp_enum_framesizes(struct file *file, void *prov,
struct v4l2_frmsizeenum *fsize)
{
struct rkisp1_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 = rkisp1_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 int rkisp_enum_frameintervals(struct file *file, void *fh,
struct v4l2_frmivalenum *fival)
{
const struct rkisp1_stream *stream = video_drvdata(file);
struct rkisp1_device *dev = stream->ispdev;
struct rkisp1_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 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;
}
return rkisp1_set_fmt(stream, &f->fmt.pix_mp, false);
}
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)
{
/* Not crop for MP bayer raw data and RAW path */
if ((stream->id == RKISP1_STREAM_MP &&
stream->out_isp_fmt.fmt_type == FMT_BAYER) ||
stream->id == RKISP1_STREAM_RAW) {
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;
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;
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));
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 = 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,
.vidioc_enum_frameintervals = rkisp_enum_frameintervals,
.vidioc_enum_framesizes = rkisp_enum_framesizes,
};
static void rkisp1_unregister_stream_vdev(struct rkisp1_stream *stream)
{
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];
struct rkisp1_stream *raw_stream = &dev->stream[RKISP1_STREAM_RAW];
rkisp1_unregister_stream_vdev(mp_stream);
if (dev->isp_ver != ISP_V10_1)
rkisp1_unregister_stream_vdev(sp_stream);
#if RKISP1_RK3326_USE_OLDMIPI
if (dev->isp_ver == ISP_V13)
#else
if (dev->isp_ver == ISP_V12 ||
dev->isp_ver == ISP_V13)
#endif
rkisp1_unregister_stream_vdev(raw_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 = 0;
char *vdev_name;
switch (stream->id) {
case RKISP1_STREAM_SP:
vdev_name = SP_VDEV_NAME;
if (dev->isp_ver == ISP_V10_1)
return 0;
break;
case RKISP1_STREAM_MP:
vdev_name = MP_VDEV_NAME;
break;
case RKISP1_STREAM_RAW:
vdev_name = RAW_VDEV_NAME;
#if RKISP1_RK3326_USE_OLDMIPI
if (dev->isp_ver != ISP_V13)
#else
if (dev->isp_ver != ISP_V12 &&
dev->isp_ver != ISP_V13)
#endif
return 0;
break;
default:
v4l2_err(v4l2_dev, "Invalid stream\n");
return -EINVAL;
}
strlcpy(vdev->name, vdev_name, sizeof(vdev->name));
node = vdev_to_node(vdev);
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 = &dev->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;
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_VIDEO, -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;
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;
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(u32 mis_val, struct rkisp1_device *dev)
{
unsigned int i;
if (mis_val & CIF_MI_DMA_READY)
rkisp1_dmarx_isr(mis_val, dev);
for (i = 0; i < ARRAY_SIZE(dev->stream); ++i) {
struct rkisp1_stream *stream = &dev->stream[i];
if (!(mis_val & CIF_MI_FRAME(stream)))
continue;
mi_frame_end_int_clear(stream);
if (stream->stopping) {
/*
* 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->streaming = false;
wake_up(&stream->done);
}
} else {
mi_frame_end(stream);
}
}
}
void rkisp1_mipi_dmatx0_end(u32 status, struct rkisp1_device *dev)
{
struct rkisp1_stream *stream = &dev->stream[RKISP1_STREAM_RAW];
u32 *buf, end, timeout = 100;
if (!(status & 0x1) || !stream->streaming)
return;
dmatx0_enable(dev->base_addr);
if (stream->stopping) {
/* update dmatx buf to other stream dummy buf if other
* stream don't close, but dmatx is reopen.
* dmatx first buf will write to this.
*/
if (!stream->u.raw.pre_stop) {
int i;
struct rkisp1_stream *other = NULL;
for (i = 0; i < RKISP1_MAX_STREAM; i++) {
if (i != stream->id &&
dev->stream[i].streaming) {
other = &dev->stream[i];
break;
}
}
stream->u.raw.pre_stop = true;
if (other) {
mi_raw0_set_addr(dev->base_addr,
other->dummy_buf.dma_addr);
return;
}
}
if (stream->u.raw.pre_stop) {
dmatx0_disable(dev->base_addr);
stream->u.raw.pre_stop = false;
stream->stopping = false;
stream->streaming = false;
wake_up(&stream->done);
}
} else {
if (stream->curr_buf) {
/* for check dmatx to ddr complete */
u32 sizeimage = stream->out_fmt.plane_fmt[0].sizeimage;
buf = (u32 *)vb2_plane_vaddr(&stream->curr_buf->vb.vb2_buf, 0);
if (!buf)
goto out;
end = *(buf + sizeimage / 4 - 1);
while (end == RKISP1_DMATX_CHECK) {
udelay(1);
end = *(buf + sizeimage / 4 - 1);
if (timeout-- == 0) {
/* if shd don't update
* check aclk_isp >= clk_isp
* input equal to sensor output, no crop
*/
v4l2_err(&dev->v4l2_dev,
"dmatx to ddr timeout!\n"
"base:0x%x shd:0x%x data:0x%x~0x%x\n",
readl(dev->base_addr + CIF_MI_RAW0_BASE_AD_INIT),
readl(dev->base_addr + CIF_MI_RAW0_BASE_AS_SHD),
*buf, end);
break;
}
}
}
out:
mi_frame_end(stream);
}
}