2704 lines
78 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023 Rockchip Electronics Co., Ltd. */
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mc.h>
#include <media/v4l2-subdev.h>
#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-dma-sg.h>
#include <uapi/linux/rk-video-format.h>
#include "dev.h"
#include "regs.h"
#define STREAM_OUT_REQ_BUFS_MIN 0
static void rkvpss_frame_end(struct rkvpss_stream *stream);
static int rkvpss_stream_crop(struct rkvpss_stream *stream, bool on, bool sync);
static int rkvpss_stream_scale(struct rkvpss_stream *stream, bool on, bool sync);
static void rkvpss_stream_mf(struct rkvpss_stream *stream);
static const struct capture_fmt scl0_fmts[] = {
{
.fourcc = V4L2_PIX_FMT_NV16,
.fmt_type = FMT_YUV,
.bpp = { 8, 16 },
.cplanes = 2,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}, {
.fourcc = V4L2_PIX_FMT_NV12,
.fmt_type = FMT_YUV,
.bpp = { 8, 16 },
.cplanes = 2,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420,
}, {
.fourcc = V4L2_PIX_FMT_GREY,
.fmt_type = FMT_YUV,
.bpp = { 8 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400,
}, {
.fourcc = V4L2_PIX_FMT_UYVY,
.fmt_type = FMT_YUV,
.bpp = { 16 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_422P,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}, {
.fourcc = V4L2_PIX_FMT_NV61,
.fmt_type = FMT_YUV,
.bpp = { 8, 16 },
.cplanes = 2,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}, {
.fourcc = V4L2_PIX_FMT_NV21,
.fmt_type = FMT_YUV,
.bpp = { 8, 16 },
.cplanes = 2,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420,
}, {
.fourcc = V4L2_PIX_FMT_VYUY,
.fmt_type = FMT_YUV,
.bpp = { 16 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_422P,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}, {
.fourcc = V4L2_PIX_FMT_TILE420,
.fmt_type = FMT_YUV,
.bpp = { 24 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = 0,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420,
}, {
.fourcc = V4L2_PIX_FMT_TILE422,
.fmt_type = FMT_YUV,
.bpp = { 32 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = 0,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}
};
static const struct capture_fmt scl1_fmts[] = {
{
.fourcc = V4L2_PIX_FMT_NV16,
.fmt_type = FMT_YUV,
.bpp = { 8, 16 },
.cplanes = 2,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}, {
.fourcc = V4L2_PIX_FMT_NV12,
.fmt_type = FMT_YUV,
.bpp = { 8, 16 },
.cplanes = 2,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420,
}, {
.fourcc = V4L2_PIX_FMT_GREY,
.fmt_type = FMT_YUV,
.bpp = { 8 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400,
}, {
.fourcc = V4L2_PIX_FMT_UYVY,
.fmt_type = FMT_YUV,
.bpp = { 16 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_422P,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}, {
.fourcc = V4L2_PIX_FMT_RGB565,
.fmt_type = FMT_RGB,
.bpp = { 16 },
.mplanes = 1,
.cplanes = 1,
.wr_fmt = 0,
.swap = RKVPSS_MI_CHN_WR_RB_SWAP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB565,
}, {
.fourcc = V4L2_PIX_FMT_RGB24,
.fmt_type = FMT_RGB,
.bpp = { 24 },
.mplanes = 1,
.cplanes = 1,
.wr_fmt = 0,
.swap = RKVPSS_MI_CHN_WR_RB_SWAP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB888,
}, {
.fourcc = V4L2_PIX_FMT_XBGR32,
.fmt_type = FMT_RGB,
.bpp = { 32 },
.mplanes = 1,
.cplanes = 1,
.wr_fmt = 0,
.swap = 0,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_ARGB888,
}, {
.fourcc = V4L2_PIX_FMT_RGB565X,
.fmt_type = FMT_RGB,
.bpp = { 16 },
.mplanes = 1,
.cplanes = 1,
.wr_fmt = 0,
.swap = 0,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB565,
}, {
.fourcc = V4L2_PIX_FMT_BGR24,
.fmt_type = FMT_RGB,
.bpp = { 24 },
.mplanes = 1,
.cplanes = 1,
.wr_fmt = 0,
.swap = 0,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_RGB888,
}, {
.fourcc = V4L2_PIX_FMT_XRGB32,
.fmt_type = FMT_RGB,
.bpp = { 32 },
.mplanes = 1,
.cplanes = 1,
.wr_fmt = 0,
.swap = RKVPSS_MI_CHN_WR_RB_SWAP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_ARGB888,
}, {
.fourcc = V4L2_PIX_FMT_NV61,
.fmt_type = FMT_YUV,
.bpp = { 8, 16 },
.cplanes = 2,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}, {
.fourcc = V4L2_PIX_FMT_NV21,
.fmt_type = FMT_YUV,
.bpp = { 8, 16 },
.cplanes = 2,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420,
}, {
.fourcc = V4L2_PIX_FMT_VYUY,
.fmt_type = FMT_YUV,
.bpp = { 16 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_422P,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}, {
.fourcc = V4L2_PIX_FMT_TILE420,
.fmt_type = FMT_YUV,
.bpp = { 24 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = 0,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420,
}, {
.fourcc = V4L2_PIX_FMT_TILE422,
.fmt_type = FMT_YUV,
.bpp = { 32 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = 0,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}
};
static const struct capture_fmt scl2_3_fmts[] = {
{
.fourcc = V4L2_PIX_FMT_NV16,
.fmt_type = FMT_YUV,
.bpp = { 8, 16 },
.cplanes = 2,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}, {
.fourcc = V4L2_PIX_FMT_NV12,
.fmt_type = FMT_YUV,
.bpp = { 8, 16 },
.cplanes = 2,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420,
}, {
.fourcc = V4L2_PIX_FMT_GREY,
.fmt_type = FMT_YUV,
.bpp = { 8 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV400,
}, {
.fourcc = V4L2_PIX_FMT_UYVY,
.fmt_type = FMT_YUV,
.bpp = { 16 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_422P,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}, {
.fourcc = V4L2_PIX_FMT_NV61,
.fmt_type = FMT_YUV,
.bpp = { 8, 16 },
.cplanes = 2,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}, {
.fourcc = V4L2_PIX_FMT_NV21,
.fmt_type = FMT_YUV,
.bpp = { 8, 16 },
.cplanes = 2,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_42XSP,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV420,
}, {
.fourcc = V4L2_PIX_FMT_VYUY,
.fmt_type = FMT_YUV,
.bpp = { 16 },
.cplanes = 1,
.mplanes = 1,
.swap = 0,
.wr_fmt = RKVPSS_MI_CHN_WR_422P,
.output_fmt = RKVPSS_MI_CHN_WR_OUTPUT_YUV422,
}
};
static struct stream_config scl0_config = {
.fmts = scl0_fmts,
.fmt_size = ARRAY_SIZE(scl0_fmts),
.frame_end_id = RKVPSS_MI_CHN0_FRM_END,
.crop = {
.ctrl = RKVPSS_CROP1_CTRL,
.update = RKVPSS_CROP1_UPDATE,
.h_offs = RKVPSS_CROP1_0_H_OFFS,
.v_offs = RKVPSS_CROP1_0_V_OFFS,
.h_size = RKVPSS_CROP1_0_H_SIZE,
.v_size = RKVPSS_CROP1_0_V_SIZE,
.ctrl_shd = RKVPSS_CROP1_CTRL_SHD,
.h_offs_shd = RKVPSS_CROP1_0_H_OFFS_SHD,
.v_offs_shd = RKVPSS_CROP1_0_V_OFFS_SHD,
.h_size_shd = RKVPSS_CROP1_0_H_SIZE_SHD,
.v_size_shd = RKVPSS_CROP1_0_V_SIZE_SHD,
},
.mi = {
.ctrl = RKVPSS_MI_CHN0_WR_CTRL,
.stride = RKVPSS_MI_CHN0_WR_Y_STRIDE,
.y_base = RKVPSS_MI_CHN0_WR_Y_BASE,
.uv_base = RKVPSS_MI_CHN0_WR_CB_BASE,
.y_size = RKVPSS_MI_CHN0_WR_Y_SIZE,
.uv_size = RKVPSS_MI_CHN0_WR_CB_SIZE,
.y_offs_cnt = RKVPSS_MI_CHN0_WR_Y_OFFS_CNT,
.uv_offs_cnt = RKVPSS_MI_CHN0_WR_CB_OFFS_CNT,
.y_pic_width = RKVPSS_MI_CHN0_WR_Y_PIC_WIDTH,
.y_pic_size = RKVPSS_MI_CHN0_WR_Y_PIC_SIZE,
.ctrl_shd = RKVPSS_MI_CHN0_WR_CTRL_SHD,
.y_shd = RKVPSS_MI_CHN0_WR_Y_BASE_SHD,
.uv_shd = RKVPSS_MI_CHN0_WR_CB_BASE_SHD,
},
};
static struct stream_config scl1_config = {
.fmts = scl1_fmts,
.fmt_size = ARRAY_SIZE(scl1_fmts),
.frame_end_id = RKVPSS_MI_CHN1_FRM_END,
.crop = {
.ctrl = RKVPSS_CROP1_CTRL,
.update = RKVPSS_CROP1_UPDATE,
.h_offs = RKVPSS_CROP1_1_H_OFFS,
.v_offs = RKVPSS_CROP1_1_V_OFFS,
.h_size = RKVPSS_CROP1_1_H_SIZE,
.v_size = RKVPSS_CROP1_1_V_SIZE,
.ctrl_shd = RKVPSS_CROP1_CTRL_SHD,
.h_offs_shd = RKVPSS_CROP1_1_H_OFFS_SHD,
.v_offs_shd = RKVPSS_CROP1_1_V_OFFS_SHD,
.h_size_shd = RKVPSS_CROP1_1_H_SIZE_SHD,
.v_size_shd = RKVPSS_CROP1_1_V_SIZE_SHD,
},
.scale = {
.ctrl = RKVPSS_SCALE1_CTRL,
.update = RKVPSS_SCALE1_UPDATE,
.src_size = RKVPSS_SCALE1_SRC_SIZE,
.dst_size = RKVPSS_SCALE1_DST_SIZE,
.hy_fac = RKVPSS_SCALE1_HY_FAC,
.hc_fac = RKVPSS_SCALE1_HC_FAC,
.vy_fac = RKVPSS_SCALE1_VY_FAC,
.vc_fac = RKVPSS_SCALE1_VC_FAC,
.hy_offs = RKVPSS_SCALE1_HY_OFFS,
.hc_offs = RKVPSS_SCALE1_HC_OFFS,
.vy_offs = RKVPSS_SCALE1_VY_OFFS,
.vc_offs = RKVPSS_SCALE1_VC_OFFS,
.hy_size = RKVPSS_SCALE1_HY_SIZE,
.hc_size = RKVPSS_SCALE1_HC_SIZE,
.hy_offs_mi = RKVPSS_SCALE1_HY_OFFS_MI,
.hc_offs_mi = RKVPSS_SCALE1_HC_OFFS_MI,
.in_crop_offs = RKVPSS_SCALE1_IN_CROP_OFFSET,
.ctrl_shd = RKVPSS_SCALE1_CTRL_SHD,
.src_size_shd = RKVPSS_SCALE1_SRC_SIZE_SHD,
.dst_size_shd = RKVPSS_SCALE1_DST_SIZE_SHD,
.hy_fac_shd = RKVPSS_SCALE1_HY_FAC_SHD,
.hc_fac_shd = RKVPSS_SCALE1_HC_FAC_SHD,
.vy_fac_shd = RKVPSS_SCALE1_VY_FAC_SHD,
.vc_fac_shd = RKVPSS_SCALE1_VC_FAC_SHD,
.hy_offs_shd = RKVPSS_SCALE1_HY_OFFS_SHD,
.hc_offs_shd = RKVPSS_SCALE1_HC_OFFS_SHD,
.vy_offs_shd = RKVPSS_SCALE1_VY_OFFS_SHD,
.vc_offs_shd = RKVPSS_SCALE1_VC_OFFS_SHD,
.hy_size_shd = RKVPSS_SCALE1_HY_SIZE_SHD,
.hc_size_shd = RKVPSS_SCALE1_HC_SIZE_SHD,
.hy_offs_mi_shd = RKVPSS_SCALE1_HY_OFFS_MI_SHD,
.hc_offs_mi_shd = RKVPSS_SCALE1_HC_OFFS_MI_SHD,
.in_crop_offs_shd = RKVPSS_SCALE1_IN_CROP_OFFSET_SHD,
},
.mi = {
.ctrl = RKVPSS_MI_CHN1_WR_CTRL,
.stride = RKVPSS_MI_CHN1_WR_Y_STRIDE,
.y_base = RKVPSS_MI_CHN1_WR_Y_BASE,
.uv_base = RKVPSS_MI_CHN1_WR_CB_BASE,
.y_size = RKVPSS_MI_CHN1_WR_Y_SIZE,
.uv_size = RKVPSS_MI_CHN1_WR_CB_SIZE,
.y_offs_cnt = RKVPSS_MI_CHN1_WR_Y_OFFS_CNT,
.uv_offs_cnt = RKVPSS_MI_CHN1_WR_CB_OFFS_CNT,
.y_pic_width = RKVPSS_MI_CHN1_WR_Y_PIC_WIDTH,
.y_pic_size = RKVPSS_MI_CHN1_WR_Y_PIC_SIZE,
.ctrl_shd = RKVPSS_MI_CHN1_WR_CTRL_SHD,
.y_shd = RKVPSS_MI_CHN1_WR_Y_BASE_SHD,
.uv_shd = RKVPSS_MI_CHN1_WR_CB_BASE_SHD,
},
};
static struct stream_config scl2_config = {
.fmts = scl2_3_fmts,
.fmt_size = ARRAY_SIZE(scl2_3_fmts),
.frame_end_id = RKVPSS_MI_CHN2_FRM_END,
.crop = {
.ctrl = RKVPSS_CROP1_CTRL,
.update = RKVPSS_CROP1_UPDATE,
.h_offs = RKVPSS_CROP1_2_H_OFFS,
.v_offs = RKVPSS_CROP1_2_V_OFFS,
.h_size = RKVPSS_CROP1_2_H_SIZE,
.v_size = RKVPSS_CROP1_2_V_SIZE,
.ctrl_shd = RKVPSS_CROP1_CTRL_SHD,
.h_offs_shd = RKVPSS_CROP1_2_H_OFFS_SHD,
.v_offs_shd = RKVPSS_CROP1_2_V_OFFS_SHD,
.h_size_shd = RKVPSS_CROP1_2_H_SIZE_SHD,
.v_size_shd = RKVPSS_CROP1_2_V_SIZE_SHD,
},
.scale = {
.ctrl = RKVPSS_SCALE2_CTRL,
.update = RKVPSS_SCALE2_UPDATE,
.src_size = RKVPSS_SCALE2_SRC_SIZE,
.dst_size = RKVPSS_SCALE2_DST_SIZE,
.hy_fac = RKVPSS_SCALE2_HY_FAC,
.hc_fac = RKVPSS_SCALE2_HC_FAC,
.vy_fac = RKVPSS_SCALE2_VY_FAC,
.vc_fac = RKVPSS_SCALE2_VC_FAC,
.hy_offs = RKVPSS_SCALE2_HY_OFFS,
.hc_offs = RKVPSS_SCALE2_HC_OFFS,
.vy_offs = RKVPSS_SCALE2_VY_OFFS,
.vc_offs = RKVPSS_SCALE2_VC_OFFS,
.hy_size = RKVPSS_SCALE2_HY_SIZE,
.hc_size = RKVPSS_SCALE2_HC_SIZE,
.hy_offs_mi = RKVPSS_SCALE2_HY_OFFS_MI,
.hc_offs_mi = RKVPSS_SCALE2_HC_OFFS_MI,
.in_crop_offs = RKVPSS_SCALE2_IN_CROP_OFFSET,
.ctrl_shd = RKVPSS_SCALE2_CTRL_SHD,
.src_size_shd = RKVPSS_SCALE2_SRC_SIZE_SHD,
.dst_size_shd = RKVPSS_SCALE2_DST_SIZE_SHD,
.hy_fac_shd = RKVPSS_SCALE2_HY_FAC_SHD,
.hc_fac_shd = RKVPSS_SCALE2_HC_FAC_SHD,
.vy_fac_shd = RKVPSS_SCALE2_VY_FAC_SHD,
.vc_fac_shd = RKVPSS_SCALE2_VC_FAC_SHD,
.hy_offs_shd = RKVPSS_SCALE2_HY_OFFS_SHD,
.hc_offs_shd = RKVPSS_SCALE2_HC_OFFS_SHD,
.vy_offs_shd = RKVPSS_SCALE2_VY_OFFS_SHD,
.vc_offs_shd = RKVPSS_SCALE2_VC_OFFS_SHD,
.hy_size_shd = RKVPSS_SCALE2_HY_SIZE_SHD,
.hc_size_shd = RKVPSS_SCALE2_HC_SIZE_SHD,
.hy_offs_mi_shd = RKVPSS_SCALE2_HY_OFFS_MI_SHD,
.hc_offs_mi_shd = RKVPSS_SCALE2_HC_OFFS_MI_SHD,
.in_crop_offs_shd = RKVPSS_SCALE2_IN_CROP_OFFSET_SHD,
},
.mi = {
.ctrl = RKVPSS_MI_CHN2_WR_CTRL,
.stride = RKVPSS_MI_CHN2_WR_Y_STRIDE,
.y_base = RKVPSS_MI_CHN2_WR_Y_BASE,
.uv_base = RKVPSS_MI_CHN2_WR_CB_BASE,
.y_size = RKVPSS_MI_CHN2_WR_Y_SIZE,
.uv_size = RKVPSS_MI_CHN2_WR_CB_SIZE,
.y_offs_cnt = RKVPSS_MI_CHN2_WR_Y_OFFS_CNT,
.uv_offs_cnt = RKVPSS_MI_CHN2_WR_CB_OFFS_CNT,
.y_pic_width = RKVPSS_MI_CHN2_WR_Y_PIC_WIDTH,
.y_pic_size = RKVPSS_MI_CHN2_WR_Y_PIC_SIZE,
.ctrl_shd = RKVPSS_MI_CHN2_WR_CTRL_SHD,
.y_shd = RKVPSS_MI_CHN2_WR_Y_BASE_SHD,
.uv_shd = RKVPSS_MI_CHN2_WR_CB_BASE_SHD,
},
};
static struct stream_config scl3_config = {
.fmts = scl2_3_fmts,
.fmt_size = ARRAY_SIZE(scl2_3_fmts),
.frame_end_id = RKVPSS_MI_CHN3_FRM_END,
.crop = {
.ctrl = RKVPSS_CROP1_CTRL,
.update = RKVPSS_CROP1_UPDATE,
.h_offs = RKVPSS_CROP1_3_H_OFFS,
.v_offs = RKVPSS_CROP1_3_V_OFFS,
.h_size = RKVPSS_CROP1_3_H_SIZE,
.v_size = RKVPSS_CROP1_3_V_SIZE,
.ctrl_shd = RKVPSS_CROP1_CTRL_SHD,
.h_offs_shd = RKVPSS_CROP1_3_H_OFFS_SHD,
.v_offs_shd = RKVPSS_CROP1_3_V_OFFS_SHD,
.h_size_shd = RKVPSS_CROP1_3_H_SIZE_SHD,
.v_size_shd = RKVPSS_CROP1_3_V_SIZE_SHD,
},
.scale = {
.ctrl = RKVPSS_SCALE3_CTRL,
.update = RKVPSS_SCALE3_UPDATE,
.src_size = RKVPSS_SCALE3_SRC_SIZE,
.dst_size = RKVPSS_SCALE3_DST_SIZE,
.hy_fac = RKVPSS_SCALE3_HY_FAC,
.hc_fac = RKVPSS_SCALE3_HC_FAC,
.vy_fac = RKVPSS_SCALE3_VY_FAC,
.vc_fac = RKVPSS_SCALE3_VC_FAC,
.hy_offs = RKVPSS_SCALE3_HY_OFFS,
.hc_offs = RKVPSS_SCALE3_HC_OFFS,
.vy_offs = RKVPSS_SCALE3_VY_OFFS,
.vc_offs = RKVPSS_SCALE3_VC_OFFS,
.hy_size = RKVPSS_SCALE3_HY_SIZE,
.hc_size = RKVPSS_SCALE3_HC_SIZE,
.hy_offs_mi = RKVPSS_SCALE3_HY_OFFS_MI,
.hc_offs_mi = RKVPSS_SCALE3_HC_OFFS_MI,
.in_crop_offs = RKVPSS_SCALE3_IN_CROP_OFFSET,
.ctrl_shd = RKVPSS_SCALE3_CTRL_SHD,
.src_size_shd = RKVPSS_SCALE3_SRC_SIZE_SHD,
.dst_size_shd = RKVPSS_SCALE3_DST_SIZE_SHD,
.hy_fac_shd = RKVPSS_SCALE3_HY_FAC_SHD,
.hc_fac_shd = RKVPSS_SCALE3_HC_FAC_SHD,
.vy_fac_shd = RKVPSS_SCALE3_VY_FAC_SHD,
.vc_fac_shd = RKVPSS_SCALE3_VC_FAC_SHD,
.hy_offs_shd = RKVPSS_SCALE3_HY_OFFS_SHD,
.hc_offs_shd = RKVPSS_SCALE3_HC_OFFS_SHD,
.vy_offs_shd = RKVPSS_SCALE3_VY_OFFS_SHD,
.vc_offs_shd = RKVPSS_SCALE3_VC_OFFS_SHD,
.hy_size_shd = RKVPSS_SCALE3_HY_SIZE_SHD,
.hc_size_shd = RKVPSS_SCALE3_HC_SIZE_SHD,
.hy_offs_mi_shd = RKVPSS_SCALE3_HY_OFFS_MI_SHD,
.hc_offs_mi_shd = RKVPSS_SCALE3_HC_OFFS_MI_SHD,
.in_crop_offs_shd = RKVPSS_SCALE3_IN_CROP_OFFSET_SHD,
},
.mi = {
.ctrl = RKVPSS_MI_CHN3_WR_CTRL,
.stride = RKVPSS_MI_CHN3_WR_Y_STRIDE,
.y_base = RKVPSS_MI_CHN3_WR_Y_BASE,
.uv_base = RKVPSS_MI_CHN3_WR_CB_BASE,
.y_size = RKVPSS_MI_CHN3_WR_Y_SIZE,
.uv_size = RKVPSS_MI_CHN3_WR_CB_SIZE,
.y_offs_cnt = RKVPSS_MI_CHN3_WR_Y_OFFS_CNT,
.uv_offs_cnt = RKVPSS_MI_CHN3_WR_CB_OFFS_CNT,
.y_pic_width = RKVPSS_MI_CHN3_WR_Y_PIC_WIDTH,
.y_pic_size = RKVPSS_MI_CHN3_WR_Y_PIC_SIZE,
.ctrl_shd = RKVPSS_MI_CHN3_WR_CTRL_SHD,
.y_shd = RKVPSS_MI_CHN3_WR_Y_BASE_SHD,
.uv_shd = RKVPSS_MI_CHN3_WR_CB_BASE_SHD,
},
};
static void calc_unite_scl_params(struct rkvpss_stream *stream)
{
struct rkvpss_online_unite_params *params = &stream->unite_params;
u32 right_scl_need_size_y, right_scl_need_size_c;
u32 left_in_used_size_y, left_in_used_size_c;
u32 right_fst_position_y, right_fst_position_c;
u32 right_y_crop_total;
u32 right_c_crop_total;
params->y_w_fac = (stream->crop.width - 1) * 4096 /
(stream->out_fmt.width - 1);
params->c_w_fac = (stream->crop.width / 2 - 1) * 4096 /
(stream->out_fmt.width / 2 - 1);
params->y_h_fac = (stream->crop.height - 1) * 4096 /
(stream->out_fmt.height - 1);
params->c_h_fac = (stream->crop.height - 1) * 4096 /
(stream->out_fmt.height - 1);
right_fst_position_y = stream->out_fmt.width / 2 *
params->y_w_fac;
right_fst_position_c = stream->out_fmt.width / 2 / 2 *
params->c_w_fac;
left_in_used_size_y = right_fst_position_y >> 12;
left_in_used_size_c = (right_fst_position_c >> 12) * 2;
params->y_w_phase = right_fst_position_y & 0xfff;
params->c_w_phase = right_fst_position_c & 0xfff;
right_scl_need_size_y = stream->crop.width -
left_in_used_size_y;
params->right_scl_need_size_y = right_scl_need_size_y;
right_scl_need_size_c = stream->crop.width -
left_in_used_size_c;
params->right_scl_need_size_c = right_scl_need_size_c;
if (stream->id == 0 && stream->crop.width != stream->out_fmt.width) {
right_y_crop_total = stream->crop.width / 2 +
RKMOUDLE_UNITE_EXTEND_PIXEL -
right_scl_need_size_y - 3;
right_c_crop_total = stream->crop.width / 2 +
RKMOUDLE_UNITE_EXTEND_PIXEL -
right_scl_need_size_c - 6;
} else {
right_y_crop_total = stream->crop.width / 2 +
RKMOUDLE_UNITE_EXTEND_PIXEL -
right_scl_need_size_y;
right_c_crop_total = stream->crop.width / 2 +
RKMOUDLE_UNITE_EXTEND_PIXEL -
right_scl_need_size_c;
}
params->quad_crop_w = ALIGN_DOWN(min(right_y_crop_total, right_c_crop_total), 2);
params->scl_in_crop_w_y = right_y_crop_total - params->quad_crop_w;
params->scl_in_crop_w_c = right_c_crop_total - params->quad_crop_w;
if (rkvpss_debug >= 2) {
v4l2_info(&stream->dev->v4l2_dev,
"%s ch:%d y_w_fac:%u c_w_fac:%u y_h_fac:%u c_h_fac:%u\n",
__func__, stream->id, params->y_w_fac,
params->c_w_fac, params->y_h_fac, params->c_h_fac);
v4l2_info(&stream->dev->v4l2_dev,
"\t%s y_w_phase:%u c_w_phase:%u quad_crop_w:%u scl_in_crop_w_y:%u scl_in_crop_w_c:%u\n",
__func__, params->y_w_phase, params->c_w_phase,
params->quad_crop_w,
params->scl_in_crop_w_y, params->scl_in_crop_w_c);
v4l2_info(&stream->dev->v4l2_dev,
"\t%s right_scl_need_size_y:%u right_scl_need_size_c:%u\n",
__func__, params->right_scl_need_size_y,
params->right_scl_need_size_c);
}
}
int rkvpss_stream_buf_cnt(struct rkvpss_stream *stream)
{
unsigned long lock_flags = 0;
struct rkvpss_buffer *buf, *tmp;
int cnt = 0;
spin_lock_irqsave(&stream->vbq_lock, lock_flags);
list_for_each_entry_safe(buf, tmp, &stream->buf_queue, queue)
cnt++;
if (stream->curr_buf)
cnt++;
if (stream->next_buf && stream->next_buf != stream->curr_buf)
cnt++;
spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
return cnt;
}
static void stream_frame_start(struct rkvpss_stream *stream, u32 irq)
{
struct rkvpss_device *dev = stream->dev;
if (stream->is_crop_upd) {
if (dev->unite_mode)
calc_unite_scl_params(stream);
rkvpss_stream_scale(stream, true, !irq);
rkvpss_stream_crop(stream, true, !irq);
}
if (!irq && !stream->curr_buf &&
!stream->dev->hw_dev->is_single)
stream->ops->update_mi(stream);
}
static void scl_force_update(struct rkvpss_stream *stream)
{
u32 val;
switch (stream->id) {
case RKVPSS_OUTPUT_CH0:
val = RKVPSS_MI_CHN0_FORCE_UPD;
break;
case RKVPSS_OUTPUT_CH1:
val = RKVPSS_MI_CHN1_FORCE_UPD;
break;
case RKVPSS_OUTPUT_CH2:
val = RKVPSS_MI_CHN2_FORCE_UPD;
break;
case RKVPSS_OUTPUT_CH3:
val = RKVPSS_MI_CHN3_FORCE_UPD;
break;
default:
return;
}
/* need update two for online2 mode */
rkvpss_unite_write(stream->dev, RKVPSS_MI_WR_INIT, val);
rkvpss_unite_write(stream->dev, RKVPSS_MI_WR_INIT, val);
}
static void scl_update_mi(struct rkvpss_stream *stream)
{
struct rkvpss_device *dev = stream->dev;
struct rkvpss_buffer *buf = NULL;
unsigned long lock_flags = 0;
u32 y_base, uv_base;
spin_lock_irqsave(&stream->vbq_lock, lock_flags);
if (!list_empty(&stream->buf_queue) && !stream->curr_buf) {
buf = list_first_entry(&stream->buf_queue, struct rkvpss_buffer, queue);
list_del(&buf->queue);
stream->curr_buf = buf;
}
spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
if (buf) {
y_base = buf->dma[0];
uv_base = buf->dma[1];
rkvpss_idx_write(dev, stream->config->mi.y_base, y_base, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, stream->config->mi.uv_base, uv_base, VPSS_UNITE_LEFT);
if (dev->unite_mode) {
y_base = buf->dma[0] + stream->out_fmt.width / 2;
uv_base = buf->dma[1] + stream->out_fmt.width / 2;
rkvpss_idx_write(dev, stream->config->mi.y_base, y_base, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev, stream->config->mi.uv_base, uv_base, VPSS_UNITE_RIGHT);
}
scl_force_update(stream);
if (stream->is_pause) {
stream->is_pause = false;
stream->ops->enable_mi(stream);
}
} else if (!stream->is_pause) {
stream->is_pause = true;
stream->ops->disable_mi(stream);
}
v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev,
"%s id:%d unite_index:%d Y:0x%x UV:0x%x | Y_SHD:0x%x\n",
__func__, stream->id, dev->unite_index,
rkvpss_idx_read(dev, stream->config->mi.y_base, dev->unite_index),
rkvpss_idx_read(dev, stream->config->mi.uv_base, dev->unite_index),
rkvpss_hw_read(dev->hw_dev, stream->config->mi.y_shd));
}
static void scl_config_mi(struct rkvpss_stream *stream)
{
struct rkvpss_device *dev = stream->dev;
struct capture_fmt *fmt = &stream->out_cap_fmt;
struct v4l2_pix_format_mplane *out_fmt = &stream->out_fmt;
u32 reg, val, mask;
val = out_fmt->plane_fmt[0].bytesperline;
reg = stream->config->mi.stride;
rkvpss_unite_write(dev, reg, val);
rkvpss_unite_write(dev, reg, val);
switch (fmt->fourcc) {
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_RGB565X:
val = out_fmt->plane_fmt[0].bytesperline / 2;
break;
case V4L2_PIX_FMT_XBGR32:
case V4L2_PIX_FMT_XRGB32:
val = out_fmt->plane_fmt[0].bytesperline / 4;
break;
default:
val = out_fmt->plane_fmt[0].bytesperline;
break;
}
val = val * out_fmt->height;
reg = stream->config->mi.y_pic_size;
rkvpss_unite_write(dev, reg, val);
val = out_fmt->plane_fmt[0].bytesperline * out_fmt->height;
reg = stream->config->mi.y_size;
rkvpss_unite_write(dev, reg, val);
val = out_fmt->plane_fmt[1].sizeimage;
reg = stream->config->mi.uv_size;
rkvpss_unite_write(dev, reg, val);
reg = stream->config->mi.y_offs_cnt;
rkvpss_unite_write(dev, reg, 0);
reg = stream->config->mi.uv_offs_cnt;
rkvpss_unite_write(dev, reg, 0);
val = fmt->wr_fmt | fmt->output_fmt | fmt->swap |
RKVPSS_MI_CHN_WR_EN | RKVPSS_MI_CHN_WR_AUTO_UPD;
reg = stream->config->mi.ctrl;
rkvpss_unite_write(dev, reg, val);
switch (fmt->fourcc) {
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_VYUY:
mask = RKVPSS_MI_WR_UV_SWAP;
val = RKVPSS_MI_WR_UV_SWAP;
rkvpss_hw_set_bits(dev->hw_dev, RKVPSS_MI_WR_CTRL, mask, val);
break;
case V4L2_PIX_FMT_TILE420:
case V4L2_PIX_FMT_TILE422:
mask = RKVPSS_MI_WR_TILE_SEL(3);
val = RKVPSS_MI_WR_TILE_SEL(stream->id + 1);
rkvpss_hw_set_bits(dev->hw_dev, RKVPSS_MI_WR_CTRL, mask, val);
break;
default:
break;
}
stream->is_mf_upd = true;
rkvpss_frame_end(stream);
}
static void scl_enable_mi(struct rkvpss_stream *stream)
{
struct rkvpss_device *dev = stream->dev;
u32 val, mask;
switch (stream->id) {
case RKVPSS_OUTPUT_CH0:
val = RKVPSS_ISP2VPSS_CHN0_SEL(2);
mask = RKVPSS_ISP2VPSS_CHN0_SEL(3);
break;
case RKVPSS_OUTPUT_CH1:
val = RKVPSS_ISP2VPSS_CHN1_SEL(2);
mask = RKVPSS_ISP2VPSS_CHN1_SEL(3);
break;
case RKVPSS_OUTPUT_CH2:
val = RKVPSS_ISP2VPSS_CHN2_SEL(2);
mask = RKVPSS_ISP2VPSS_CHN2_SEL(3);
break;
case RKVPSS_OUTPUT_CH3:
val = RKVPSS_ISP2VPSS_CHN3_SEL(2);
mask = RKVPSS_ISP2VPSS_CHN3_SEL(3);
break;
default:
return;
}
val |= RKVPSS_ISP2VPSS_ONLINE2;
if (!dev->hw_dev->is_ofl_cmsc)
val |= RKVPSS_ISP2VPSS_ONLINE2_CMSC_EN;
rkvpss_unite_set_bits(dev, RKVPSS_VPSS_ONLINE, mask, val);
val = RKVPSS_ONLINE2_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD;
rkvpss_unite_write(dev, RKVPSS_VPSS_UPDATE, val);
}
static void scl_disable_mi(struct rkvpss_stream *stream)
{
struct rkvpss_device *dev = stream->dev;
u32 val;
switch (stream->id) {
case RKVPSS_OUTPUT_CH0:
val = RKVPSS_ISP2VPSS_CHN0_SEL(3);
break;
case RKVPSS_OUTPUT_CH1:
val = RKVPSS_ISP2VPSS_CHN1_SEL(3);
break;
case RKVPSS_OUTPUT_CH2:
val = RKVPSS_ISP2VPSS_CHN2_SEL(3);
break;
case RKVPSS_OUTPUT_CH3:
val = RKVPSS_ISP2VPSS_CHN3_SEL(3);
break;
default:
return;
}
rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_ONLINE, val);
val = RKVPSS_ONLINE2_CHN_FORCE_UPD | RKVPSS_CFG_GEN_UPD;
rkvpss_unite_write(dev, RKVPSS_VPSS_UPDATE, val);
}
static struct streams_ops scl_stream_ops = {
.config_mi = scl_config_mi,
.enable_mi = scl_enable_mi,
.disable_mi = scl_disable_mi,
.update_mi = scl_update_mi,
.frame_start = stream_frame_start,
};
static void rkvpss_buf_done_task(unsigned long arg)
{
struct rkvpss_stream *stream = (struct rkvpss_stream *)arg;
struct rkvpss_vdev_node *node = &stream->vnode;
struct rkvpss_buffer *buf = NULL;
unsigned long lock_flags = 0;
LIST_HEAD(local_list);
spin_lock_irqsave(&stream->vbq_lock, lock_flags);
list_replace_init(&stream->buf_done_list, &local_list);
spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
while (!list_empty(&local_list)) {
buf = list_first_entry(&local_list, struct rkvpss_buffer, queue);
list_del(&buf->queue);
v4l2_dbg(2, rkvpss_debug, &stream->dev->v4l2_dev,
"%s stream:%d index:%d seq:%d buf:0x%x done\n",
node->vdev.name, stream->id, buf->vb.vb2_buf.index,
buf->vb.sequence, buf->dma[0]);
vb2_buffer_done(&buf->vb.vb2_buf,
stream->streaming ? VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR);
}
}
static void rkvpss_stream_buf_done(struct rkvpss_stream *stream,
struct rkvpss_buffer *buf)
{
unsigned long lock_flags = 0;
if (!stream || !buf)
return;
v4l2_dbg(3, rkvpss_debug, &stream->dev->v4l2_dev,
"stream:%d\n", stream->id);
spin_lock_irqsave(&stream->vbq_lock, lock_flags);
list_add_tail(&buf->queue, &stream->buf_done_list);
spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
tasklet_schedule(&stream->buf_done_tasklet);
}
static void rkvpss_frame_end(struct rkvpss_stream *stream)
{
struct rkvpss_device *dev = stream->dev;
struct rkvpss_subdev *sdev = &dev->vpss_sdev;
struct rkvpss_buffer *buf = NULL;
unsigned long lock_flags = 0;
v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev,
"stream:%d\n", stream->id);
spin_lock_irqsave(&stream->vbq_lock, lock_flags);
if (stream->curr_buf) {
buf = stream->curr_buf;
stream->curr_buf = NULL;
}
spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
if (buf) {
struct vb2_buffer *vb2_buf = &buf->vb.vb2_buf;
struct capture_fmt *fmt = &stream->out_cap_fmt;
u64 ns = sdev->frame_timestamp;
int i;
for (i = 0; i < fmt->mplanes; i++)
vb2_set_plane_payload(vb2_buf, i, stream->out_fmt.plane_fmt[i].sizeimage);
if (!ns)
ns = ktime_get_ns();
buf->vb.vb2_buf.timestamp = ns;
buf->vb.sequence = sdev->frame_seq;
ns = ktime_get_ns();
stream->dbg.frameloss += buf->vb.sequence - stream->dbg.id - 1;
stream->dbg.id = buf->vb.sequence;
stream->dbg.delay = ns - buf->vb.vb2_buf.timestamp;
stream->dbg.interval = ns - stream->dbg.timestamp;
stream->dbg.timestamp = ns;
rkvpss_stream_buf_done(stream, buf);
}
rkvpss_stream_mf(stream);
stream->ops->update_mi(stream);
}
static int rkvpss_queue_setup(struct vb2_queue *queue,
unsigned int *num_buffers,
unsigned int *num_planes,
unsigned int sizes[],
struct device *alloc_ctxs[])
{
struct rkvpss_stream *stream = queue->drv_priv;
struct rkvpss_device *dev = stream->dev;
const struct v4l2_pix_format_mplane *pixm = NULL;
const struct capture_fmt *cap_fmt = NULL;
u32 i;
pixm = &stream->out_fmt;
if (!pixm->width || !pixm->height)
return -EINVAL;
cap_fmt = &stream->out_cap_fmt;
*num_planes = cap_fmt->mplanes;
for (i = 0; i < cap_fmt->mplanes; i++) {
const struct v4l2_plane_pix_format *plane_fmt;
plane_fmt = &pixm->plane_fmt[i];
sizes[i] = plane_fmt->sizeimage / pixm->height * ALIGN(pixm->height, 16);
}
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
"%s stream:%d count %d, size %d\n",
v4l2_type_names[queue->type],
stream->id, *num_buffers, sizes[0]);
return 0;
}
static void rkvpss_buf_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct rkvpss_buffer *vpssbuf = to_rkvpss_buffer(vbuf);
struct vb2_queue *queue = vb->vb2_queue;
struct rkvpss_stream *stream = queue->drv_priv;
struct rkvpss_device *dev = stream->dev;
struct v4l2_pix_format_mplane *pixm = &stream->out_fmt;
struct capture_fmt *cap_fmt = &stream->out_cap_fmt;
unsigned long lock_flags = 0;
struct sg_table *sgt;
u32 height, size;
int i;
for (i = 0; i < cap_fmt->mplanes; i++) {
sgt = vb2_dma_sg_plane_desc(vb, i);
vpssbuf->dma[i] = sg_dma_address(sgt->sgl);
}
/*
* NOTE: plane_fmt[0].sizeimage is total size of all planes for single
* memory plane formats, so calculate the size explicitly.
*/
if (cap_fmt->mplanes == 1) {
for (i = 0; i < cap_fmt->cplanes - 1; i++) {
height = pixm->height;
size = (i == 0) ?
pixm->plane_fmt[i].bytesperline * height :
pixm->plane_fmt[i].sizeimage;
vpssbuf->dma[i + 1] = vpssbuf->dma[i] + size;
}
}
v4l2_dbg(2, rkvpss_debug, &dev->v4l2_dev,
"%s stream:%d index:%d buf:0x%x\n", __func__,
stream->id, vb->index, vpssbuf->dma[0]);
spin_lock_irqsave(&stream->vbq_lock, lock_flags);
list_add_tail(&vpssbuf->queue, &stream->buf_queue);
spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
if (dev->hw_dev->is_single &&
stream->streaming && !stream->curr_buf)
stream->ops->update_mi(stream);
}
static void destroy_buf_queue(struct rkvpss_stream *stream,
enum vb2_buffer_state state)
{
unsigned long lock_flags = 0;
struct rkvpss_buffer *buf;
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;
}
while (!list_empty(&stream->buf_queue)) {
buf = list_first_entry(&stream->buf_queue, struct rkvpss_buffer, queue);
list_del(&buf->queue);
buf->vb.vb2_buf.synced = false;
vb2_buffer_done(&buf->vb.vb2_buf, state);
}
spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
}
static int rkvpss_stream_crop(struct rkvpss_stream *stream, bool on, bool sync)
{
struct rkvpss_device *dev = stream->dev;
struct v4l2_rect *crop = &stream->crop;
u32 reg_ctrl = stream->config->crop.ctrl;
u32 reg_upd = stream->config->crop.update;
u32 reg_h_offs = stream->config->crop.h_offs;
u32 reg_v_offs = stream->config->crop.v_offs;
u32 reg_h_size = stream->config->crop.h_size;
u32 reg_v_size = stream->config->crop.v_size;
u32 val;
val = RKVPSS_CROP_CHN_EN(stream->id);
if (!dev->unite_mode) {
if (on) {
rkvpss_unite_set_bits(dev, reg_ctrl, 0, val);
rkvpss_unite_write(dev, reg_h_offs, crop->left);
rkvpss_unite_write(dev, reg_v_offs, crop->top);
rkvpss_unite_write(dev, reg_h_size, crop->width);
rkvpss_unite_write(dev, reg_v_size, crop->height);
v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev,
"crop left:%d top:%d w:%d h:%d\n",
crop->left, crop->top, crop->width, crop->height);
} else {
rkvpss_unite_clear_bits(dev, reg_ctrl, val);
}
if (sync) {
val = RKVPSS_CROP_CHN_FORCE_UPD(stream->id);
val |= RKVPSS_CROP_GEN_UPD;
rkvpss_unite_write(dev, reg_upd, val);
rkvpss_unite_write(dev, reg_upd, val);
}
} else {
if (on) {
if (crop->left + crop->width / 2 != dev->vpss_sdev.in_fmt.width / 2) {
v4l2_err(&dev->v4l2_dev,
"unite need centered crop left:%d top:%d w:%d h:%d\n",
crop->left, crop->top, crop->width, crop->height);
return -EINVAL;
}
/* unite left */
rkvpss_idx_set_bits(dev, reg_ctrl, 0, val, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, reg_h_offs, crop->left, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, reg_v_offs, crop->top, VPSS_UNITE_LEFT);
/* if no scale left don't enlarge */
if (crop->width == stream->out_fmt.width)
rkvpss_idx_write(dev, reg_h_size, crop->width / 2,
VPSS_UNITE_LEFT);
else
rkvpss_idx_write(dev, reg_h_size, crop->width / 2 +
RKMOUDLE_UNITE_EXTEND_PIXEL, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, reg_v_size, crop->height, VPSS_UNITE_LEFT);
v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev,
"right crop left:%d top:%d w:%d h:%d\n",
rkvpss_idx_read(dev, reg_h_offs, VPSS_UNITE_LEFT),
rkvpss_idx_read(dev, reg_v_offs, VPSS_UNITE_LEFT),
rkvpss_idx_read(dev, reg_h_size, VPSS_UNITE_LEFT),
rkvpss_idx_read(dev, reg_v_size, VPSS_UNITE_LEFT));
/* unite right */
rkvpss_idx_set_bits(dev, reg_ctrl, 0, val, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev, reg_h_offs, stream->unite_params.quad_crop_w,
VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev, reg_v_offs, crop->top, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev, reg_h_size, crop->width / 2 +
RKMOUDLE_UNITE_EXTEND_PIXEL -
stream->unite_params.quad_crop_w, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev, reg_v_size, crop->height, VPSS_UNITE_RIGHT);
v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev,
"right crop left:%d top:%d w:%d h:%d\n",
rkvpss_idx_read(dev, reg_h_offs, VPSS_UNITE_RIGHT),
rkvpss_idx_read(dev, reg_v_offs, VPSS_UNITE_RIGHT),
rkvpss_idx_read(dev, reg_h_size, VPSS_UNITE_RIGHT),
rkvpss_idx_read(dev, reg_v_size, VPSS_UNITE_RIGHT));
} else {
rkvpss_idx_clear_bits(dev, reg_ctrl, val, VPSS_UNITE_LEFT);
rkvpss_idx_clear_bits(dev, reg_ctrl, val, VPSS_UNITE_RIGHT);
}
if (sync) {
val = RKVPSS_CROP_CHN_FORCE_UPD(stream->id);
val |= RKVPSS_CROP_GEN_UPD;
rkvpss_idx_write(dev, reg_upd, val, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, reg_upd, val, VPSS_UNITE_RIGHT);
}
}
stream->is_crop_upd = false;
return 0;
}
static void poly_phase_scale(struct rkvpss_stream *stream, bool on, bool sync)
{
struct rkvpss_device *dev = stream->dev;
u32 out_w = stream->out_fmt.width;
u32 out_h = stream->out_fmt.height;
u32 in_w = stream->crop.width;
u32 in_h = stream->crop.height;
u32 ctrl, y_xscl_fac, y_yscl_fac, uv_xscl_fac, uv_yscl_fac;
u32 i, j, idx, ratio, val, in_div, out_div, factor;
bool dering_en = false, yuv420_in = false, yuv422_to_420 = false;
if (!on) {
rkvpss_unite_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0);
rkvpss_unite_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0);
if (dev->unite_mode) {
rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, 0, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, 0, VPSS_UNITE_RIGHT);
}
return;
}
/*config scl clk gate*/
if (in_w == out_w && in_h == out_h)
rkvpss_unite_clear_bits(dev, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS);
else
rkvpss_unite_set_bits(dev, RKVPSS_VPSS_CLK_GATE, RKVPSS_SCL0_CKG_DIS,
RKVPSS_SCL0_CKG_DIS);
/* TODO diff for input and output format */
if (yuv420_in) {
in_div = 2;
out_div = 2;
} else if (yuv422_to_420) {
in_div = 1;
out_div = 2;
} else {
in_div = 1;
out_div = 1;
}
if (dev->unite_mode) {
/* unite left */
if (in_w == out_w)
val = (in_w / 2 - 1) | ((in_h - 1) << 16);
else
val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL - 1) |
((in_h - 1) << 16);
rkvpss_idx_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val, VPSS_UNITE_LEFT);
/* unite right */
val = (ALIGN(stream->unite_params.right_scl_need_size_y + 3, 2) - 1) |
((in_h - 1) << 16);
rkvpss_idx_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val, VPSS_UNITE_RIGHT);
val = (ALIGN(stream->unite_params.right_scl_need_size_c + 6, 2) - 1) |
((in_h - 1) << 16);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val, VPSS_UNITE_RIGHT);
val = (out_w / 2 - 1) | ((out_h - 1) << 16);
rkvpss_idx_write(dev, RKVPSS_ZME_Y_DST_SIZE, val, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_DST_SIZE, val, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_Y_DST_SIZE, val, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_DST_SIZE, val, VPSS_UNITE_RIGHT);
} else {
val = (in_w - 1) | ((in_h - 1) << 16);
rkvpss_unite_write(dev, RKVPSS_ZME_Y_SRC_SIZE, val);
rkvpss_unite_write(dev, RKVPSS_ZME_UV_SRC_SIZE, val);
val = (out_w - 1) | ((out_h - 1) << 16);
rkvpss_unite_write(dev, RKVPSS_ZME_Y_DST_SIZE, val);
rkvpss_unite_write(dev, RKVPSS_ZME_UV_DST_SIZE, val);
}
ctrl = RKVPSS_ZME_XSCL_MODE | RKVPSS_ZME_YSCL_MODE;
if (dering_en) {
ctrl |= RKVPSS_ZME_DERING_EN;
rkvpss_unite_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410);
rkvpss_unite_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410);
if (dev->unite_mode) {
rkvpss_idx_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_Y_DERING_PARA, 0xd10410, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_DERING_PARA, 0xd10410, VPSS_UNITE_RIGHT);
}
}
if (in_w != out_w) {
if (in_w > out_w) {
factor = 4096;
ctrl |= RKVPSS_ZME_XSD_EN;
} else {
factor = 65536;
ctrl |= RKVPSS_ZME_XSU_EN;
}
y_xscl_fac = (in_w - 1) * factor / (out_w - 1);
uv_xscl_fac = (in_w / 2 - 1) * factor / (out_w / 2 - 1);
ratio = y_xscl_fac * 10000 / factor;
idx = rkvpss_get_zme_tap_coe_index(ratio);
for (i = 0; i < 17; i++) {
for (j = 0; j < 8; j += 2) {
val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap8_coe[idx][i][j],
rkvpss_zme_tap8_coe[idx][i][j + 1]);
rkvpss_unite_write(dev, RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2, val);
rkvpss_unite_write(dev, RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2, val);
if (dev->unite_mode) {
rkvpss_idx_write(dev,
RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2,
val, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev,
RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2,
val, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev,
RKVPSS_ZME_Y_HOR_COE0_10 + i * 16 + j * 2,
val, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev,
RKVPSS_ZME_UV_HOR_COE0_10 + i * 16 + j * 2,
val, VPSS_UNITE_RIGHT);
}
}
}
} else {
y_xscl_fac = 0;
uv_xscl_fac = 0;
}
if (dev->unite_mode) {
/* unite left */
rkvpss_idx_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac, VPSS_UNITE_LEFT);
/* unite right */
val = y_xscl_fac | (stream->unite_params.y_w_phase << 16);
rkvpss_idx_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, val, VPSS_UNITE_RIGHT);
val = uv_xscl_fac | (stream->unite_params.c_w_phase << 16);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, val, VPSS_UNITE_RIGHT);
} else {
rkvpss_unite_write(dev, RKVPSS_ZME_Y_XSCL_FACTOR, y_xscl_fac);
rkvpss_unite_write(dev, RKVPSS_ZME_UV_XSCL_FACTOR, uv_xscl_fac);
}
if (in_h != out_h) {
if (in_h > out_h) {
factor = 4096;
ctrl |= RKVPSS_ZME_YSD_EN;
} else {
factor = 65536;
ctrl |= RKVPSS_ZME_YSU_EN;
}
y_yscl_fac = (in_h - 1) * factor / (out_h - 1);
uv_yscl_fac = (in_h / in_div - 1) * factor / (out_h / out_div - 1);
ratio = y_yscl_fac * 10000 / factor;
idx = rkvpss_get_zme_tap_coe_index(ratio);
for (i = 0; i < 17; i++) {
for (j = 0; j < 8; j += 2) {
val = RKVPSS_ZME_TAP_COE(rkvpss_zme_tap6_coe[idx][i][j],
rkvpss_zme_tap6_coe[idx][i][j + 1]);
rkvpss_unite_write(dev, RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2, val);
rkvpss_unite_write(dev, RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2, val);
if (dev->unite_mode) {
rkvpss_idx_write(dev,
RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2,
val, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev,
RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2,
val, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev,
RKVPSS_ZME_Y_VER_COE0_10 + i * 16 + j * 2,
val, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev,
RKVPSS_ZME_UV_VER_COE0_10 + i * 16 + j * 2,
val, VPSS_UNITE_RIGHT);
}
}
}
} else {
y_yscl_fac = 0;
uv_yscl_fac = 0;
}
if (dev->unite_mode) {
/* unite left */
rkvpss_idx_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl, VPSS_UNITE_LEFT);
/* unite right */
rkvpss_idx_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl, VPSS_UNITE_RIGHT);
} else {
rkvpss_unite_write(dev, RKVPSS_ZME_Y_YSCL_FACTOR, y_yscl_fac);
rkvpss_unite_write(dev, RKVPSS_ZME_UV_YSCL_FACTOR, uv_yscl_fac);
rkvpss_unite_write(dev, RKVPSS_ZME_Y_SCL_CTRL, ctrl);
rkvpss_unite_write(dev, RKVPSS_ZME_UV_SCL_CTRL, ctrl);
}
if (dev->unite_mode) {
/* unite left */
val = out_w / 2;
rkvpss_idx_write(dev, RKVPSS_ZME_H_SIZE, (val << 16) | val, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, 0, VPSS_UNITE_LEFT);
/* unite right */
rkvpss_idx_write(dev, RKVPSS_ZME_H_SIZE, (val << 16) | val, VPSS_UNITE_RIGHT);
rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, 0, VPSS_UNITE_RIGHT);
val = out_w / 2 - ALIGN_DOWN(out_w / 2, 16);
rkvpss_idx_write(dev, RKVPSS_ZME_H_OFFS, (3 << 20) | (3 << 16) |
(stream->unite_params.scl_in_crop_w_y << 8) |
(stream->unite_params.scl_in_crop_w_c << 12) |
(val << 4) | val, VPSS_UNITE_RIGHT);
}
ctrl = RKVPSS_ZME_GATING_EN;
if (yuv420_in)
ctrl |= RKVPSS_ZME_SCL_YUV420_REAL_EN;
if (yuv422_to_420)
ctrl |= RKVPSS_ZME_422TO420_EN;
if (dev->unite_mode) {
/* unite left */
ctrl |= RKVPSS_ZME_CLIP_EN | RKVPSS_ZME_8K_EN;
rkvpss_idx_write(dev, RKVPSS_ZME_CTRL, ctrl, VPSS_UNITE_LEFT);
/* unite left */
ctrl |= RKVPSS_ZME_IN_CLIP_EN;
rkvpss_idx_write(dev, RKVPSS_ZME_CTRL, ctrl, VPSS_UNITE_RIGHT);
} else {
rkvpss_unite_write(dev, RKVPSS_ZME_CTRL, ctrl);
}
val = RKVPSS_ZME_GEN_UPD;
if (sync)
val |= RKVPSS_ZME_FORCE_UPD;
rkvpss_unite_write(dev, RKVPSS_ZME_UPDATE, val);
if (dev->unite_mode) {
rkvpss_idx_write(dev, RKVPSS_ZME_UPDATE, val, VPSS_UNITE_LEFT);
rkvpss_idx_write(dev, RKVPSS_ZME_UPDATE, val, VPSS_UNITE_RIGHT);
}
}
static void bilinear_scale(struct rkvpss_stream *stream, bool on, bool sync)
{
struct rkvpss_device *dev = stream->dev;
u32 out_w = stream->out_fmt.width;
u32 out_h = stream->out_fmt.height;
u32 in_w = stream->crop.width;
u32 in_h = stream->crop.height;
u32 in_div, out_div;
u32 reg, val, ctrl = 0, clk_mask = 0;
bool yuv420_in = false, yuv422_to_420 = false;
if (!on) {
reg = stream->config->scale.ctrl;
rkvpss_unite_write(dev, reg, 0);
return;
}
/*config scl clk gate*/
switch (stream->id) {
case RKVPSS_OUTPUT_CH1:
clk_mask = RKVPSS_SCL1_CKG_DIS;
break;
case RKVPSS_OUTPUT_CH2:
clk_mask = RKVPSS_SCL2_CKG_DIS;
break;
case RKVPSS_OUTPUT_CH3:
clk_mask = RKVPSS_SCL3_CKG_DIS;
break;
default:
return;
}
if (in_w == out_w && in_h == out_h)
rkvpss_unite_clear_bits(dev, RKVPSS_SCL0_CKG_DIS, clk_mask);
else
rkvpss_unite_set_bits(dev, RKVPSS_SCL0_CKG_DIS, clk_mask, clk_mask);
if (!dev->unite_mode) {
/* TODO diff for input and output format */
if (yuv420_in) {
in_div = 2;
out_div = 2;
} else if (yuv422_to_420) {
in_div = 1;
out_div = 2;
} else {
in_div = 1;
out_div = 1;
}
reg = stream->config->scale.hy_offs;
rkvpss_unite_write(dev, reg, 0);
reg = stream->config->scale.hc_offs;
rkvpss_unite_write(dev, reg, 0);
reg = stream->config->scale.vy_offs;
rkvpss_unite_write(dev, reg, 0);
reg = stream->config->scale.vc_offs;
rkvpss_unite_write(dev, reg, 0);
val = in_w | (in_h << 16);
reg = stream->config->scale.src_size;
rkvpss_unite_write(dev, reg, val);
val = out_w | (out_h << 16);
reg = stream->config->scale.dst_size;
rkvpss_unite_write(dev, reg, val);
if (in_w != out_w) {
val = (in_w - 1) * 4096 / (out_w - 1);
reg = stream->config->scale.hy_fac;
rkvpss_unite_write(dev, reg, val);
val = (in_w / 2 - 1) * 4096 / (out_w / 2 - 1);
reg = stream->config->scale.hc_fac;
rkvpss_unite_write(dev, reg, val);
ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN;
}
if (in_h != out_h) {
val = (in_h - 1) * 4096 / (out_h - 1);
reg = stream->config->scale.vy_fac;
rkvpss_unite_write(dev, reg, val);
val = (in_h / in_div - 1) * 4096 / (out_h / out_div - 1);
reg = stream->config->scale.vc_fac;
rkvpss_unite_write(dev, reg, val);
ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN;
}
reg = stream->config->scale.ctrl;
rkvpss_unite_write(dev, reg, ctrl);
val = RKVPSS_SCL_GEN_UPD;
if (sync)
val |= RKVPSS_SCL_FORCE_UPD;
reg = stream->config->scale.update;
rkvpss_unite_write(dev, reg, val);
} else {
/* unite left */
reg = stream->config->scale.in_crop_offs;
rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT);
reg = stream->config->scale.hy_offs;
rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT);
reg = stream->config->scale.hc_offs;
rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT);
reg = stream->config->scale.vy_offs;
rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT);
reg = stream->config->scale.vc_offs;
rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT);
reg = stream->config->scale.hy_offs_mi;
rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT);
reg = stream->config->scale.hc_offs_mi;
rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_LEFT);
if (in_w == out_w)
val = (in_w / 2) | (in_h << 16);
else
val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL) | (in_h << 16);
reg = stream->config->scale.src_size;
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT);
val = (out_w / 2) | (out_h << 16);
reg = stream->config->scale.dst_size;
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT);
ctrl |= RKVPSS_SCL_CLIP_EN;
if (in_w != out_w) {
reg = stream->config->scale.hy_fac;
rkvpss_idx_write(dev, reg, stream->unite_params.y_w_fac, VPSS_UNITE_LEFT);
reg = stream->config->scale.hc_fac;
rkvpss_idx_write(dev, reg, stream->unite_params.c_w_fac, VPSS_UNITE_LEFT);
ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN;
}
if (in_h != out_h) {
reg = stream->config->scale.vy_fac;
rkvpss_idx_write(dev, reg, stream->unite_params.y_h_fac, VPSS_UNITE_LEFT);
reg = stream->config->scale.vc_fac;
rkvpss_idx_write(dev, reg, stream->unite_params.c_h_fac, VPSS_UNITE_LEFT);
ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN;
}
reg = stream->config->scale.ctrl;
rkvpss_idx_write(dev, reg, ctrl, VPSS_UNITE_LEFT);
val = RKVPSS_SCL_GEN_UPD;
if (sync)
val |= RKVPSS_SCL_FORCE_UPD;
reg = stream->config->scale.update;
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_LEFT);
/* unite right */
ctrl = 0;
val = stream->unite_params.scl_in_crop_w_y |
(stream->unite_params.scl_in_crop_w_c << 4);
reg = stream->config->scale.in_crop_offs;
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT);
val = stream->unite_params.y_w_phase;
reg = stream->config->scale.hy_offs;
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT);
val = stream->unite_params.c_w_phase;
reg = stream->config->scale.hc_offs;
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT);
reg = stream->config->scale.vy_offs;
rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_RIGHT);
reg = stream->config->scale.vc_offs;
rkvpss_idx_write(dev, reg, 0, VPSS_UNITE_RIGHT);
val = out_w / 2 - ALIGN_DOWN(out_w / 2, 16);
reg = stream->config->scale.hy_offs_mi;
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT);
reg = stream->config->scale.hc_offs_mi;
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT);
val = (in_w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL) | (in_h << 16);
reg = stream->config->scale.src_size;
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT);
val = (out_w / 2) | (out_h << 16);
reg = stream->config->scale.dst_size;
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT);
ctrl |= RKVPSS_SCL_CLIP_EN | RKVPSS_SCL_IN_CLIP_EN;
if (in_w != out_w) {
reg = stream->config->scale.hy_fac;
rkvpss_idx_write(dev, reg, stream->unite_params.y_w_fac, VPSS_UNITE_RIGHT);
reg = stream->config->scale.hc_fac;
rkvpss_idx_write(dev, reg, stream->unite_params.c_w_fac, VPSS_UNITE_RIGHT);
ctrl |= RKVPSS_SCL_HY_EN | RKVPSS_SCL_HC_EN;
}
if (in_h != out_h) {
reg = stream->config->scale.vy_fac;
rkvpss_idx_write(dev, reg, stream->unite_params.y_h_fac, VPSS_UNITE_RIGHT);
reg = stream->config->scale.vc_fac;
rkvpss_idx_write(dev, reg, stream->unite_params.c_h_fac, VPSS_UNITE_RIGHT);
ctrl |= RKVPSS_SCL_VY_EN | RKVPSS_SCL_VC_EN;
}
reg = stream->config->scale.ctrl;
rkvpss_idx_write(dev, reg, ctrl, VPSS_UNITE_RIGHT);
val = RKVPSS_SCL_GEN_UPD;
if (sync)
val |= RKVPSS_SCL_FORCE_UPD;
reg = stream->config->scale.update;
rkvpss_idx_write(dev, reg, val, VPSS_UNITE_RIGHT);
}
}
static int rkvpss_stream_scale(struct rkvpss_stream *stream, bool on, bool sync)
{
if (stream->id == RKVPSS_OUTPUT_CH0)
poly_phase_scale(stream, on, sync);
else
bilinear_scale(stream, on, sync);
return 0;
}
static void rkvpss_stream_stop(struct rkvpss_stream *stream)
{
struct rkvpss_device *dev = stream->dev;
int ret;
stream->stopping = true;
if (atomic_read(&dev->pipe_stream_cnt) > 0) {
ret = wait_event_timeout(stream->done, !stream->streaming,
msecs_to_jiffies(300));
if (!ret)
v4l2_warn(&dev->v4l2_dev, "%s id:%d timeout\n", __func__, stream->id);
}
stream->stopping = false;
stream->streaming = false;
if (stream->ops->disable_mi)
stream->ops->disable_mi(stream);
rkvpss_stream_crop(stream, false, true);
rkvpss_stream_scale(stream, false, true);
}
static void rkvpss_stop_streaming(struct vb2_queue *queue)
{
struct rkvpss_stream *stream = queue->drv_priv;
struct rkvpss_vdev_node *node = &stream->vnode;
struct rkvpss_device *dev = stream->dev;
struct rkvpss_hw_dev *hw = dev->hw_dev;
if (!stream->streaming)
return;
mutex_lock(&hw->dev_lock);
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
"%s %s id:%d enter\n", __func__,
node->vdev.name, stream->id);
if (atomic_read(&dev->pipe_stream_cnt) == 1) {
rkvpss_pipeline_stream(dev, false);
rkvpss_stream_stop(stream);
} else {
rkvpss_stream_stop(stream);
rkvpss_pipeline_stream(dev, false);
}
destroy_buf_queue(stream, VB2_BUF_STATE_ERROR);
rkvpss_pipeline_close(dev);
tasklet_disable(&stream->buf_done_tasklet);
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
"%s %s id:%d exit\n", __func__,
node->vdev.name, stream->id);
mutex_unlock(&hw->dev_lock);
}
static int check_wr_uvswap(struct rkvpss_stream *stream)
{
struct rkvpss_device *dev = stream->dev;
struct rkvpss_stream *check_stream;
struct capture_fmt *fmt;
bool wr_uv_swap = false;
int i, ret = 0;
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
check_stream = &dev->stream_vdev.stream[i];
if (check_stream->streaming) {
fmt = &check_stream->out_cap_fmt;
switch (fmt->fourcc) {
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_VYUY:
wr_uv_swap = true;
break;
default:
break;
}
}
}
if (wr_uv_swap) {
switch (stream->out_cap_fmt.fourcc) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_UYVY:
v4l2_err(&dev->v4l2_dev, "wr_uv_swap need to be consistent\n");
ret = -EINVAL;
break;
default:
break;
}
}
return ret;
}
static int rkvpss_stream_start(struct rkvpss_stream *stream)
{
struct rkvpss_device *dev = stream->dev;
int ret = 0;
if (dev->unite_mode)
calc_unite_scl_params(stream);
stream->is_crop_upd = true;
ret = rkvpss_stream_scale(stream, true, true);
if (ret < 0)
return ret;
ret = rkvpss_stream_crop(stream, true, true);
if (ret < 0)
return ret;
ret = check_wr_uvswap(stream);
if (ret < 0)
return ret;
if (stream->ops->config_mi)
stream->ops->config_mi(stream);
stream->streaming = true;
stream->dbg.id = -1;
return ret;
}
static int rkvpss_start_streaming(struct vb2_queue *queue, unsigned int count)
{
struct rkvpss_stream *stream = queue->drv_priv;
struct rkvpss_vdev_node *node = &stream->vnode;
struct rkvpss_device *dev = stream->dev;
struct rkvpss_hw_dev *hw = dev->hw_dev;
int ret = -EINVAL;
if (stream->streaming)
return -EBUSY;
mutex_lock(&hw->dev_lock);
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
"%s %s id:%d enter\n", __func__,
node->vdev.name, stream->id);
stream->is_pause = true;
if (!dev->inp || !stream->linked) {
v4l2_err(&dev->v4l2_dev, "no link or invalid input source\n");
goto free_buf_queue;
}
if (hw->is_ofl_ch[stream->id]) {
v4l2_err(&dev->v4l2_dev, "channel[%d] already assigned to offline", stream->id);
goto free_buf_queue;
}
rkvpss_pipeline_open(dev);
ret = rkvpss_stream_start(stream);
if (ret < 0) {
v4l2_err(&dev->v4l2_dev, "start %s failed\n", node->vdev.name);
goto pipe_close;
}
ret = rkvpss_pipeline_stream(dev, true);
if (ret < 0)
goto stop_stream;
tasklet_enable(&stream->buf_done_tasklet);
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
"%s %s id:%d exit\n", __func__,
node->vdev.name, stream->id);
mutex_unlock(&hw->dev_lock);
return 0;
stop_stream:
stream->streaming = false;
rkvpss_stream_stop(stream);
pipe_close:
rkvpss_pipeline_close(dev);
free_buf_queue:
destroy_buf_queue(stream, VB2_BUF_STATE_QUEUED);
mutex_unlock(&hw->dev_lock);
return ret;
}
static const struct vb2_ops stream_vb2_ops = {
.queue_setup = rkvpss_queue_setup,
.buf_queue = rkvpss_buf_queue,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
.stop_streaming = rkvpss_stop_streaming,
.start_streaming = rkvpss_start_streaming,
};
static int rkvpss_init_vb2_queue(struct vb2_queue *q,
struct rkvpss_stream *stream,
enum v4l2_buf_type buf_type)
{
q->type = buf_type;
q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR;
q->drv_priv = stream;
q->ops = &stream_vb2_ops;
q->mem_ops = stream->dev->hw_dev->mem_ops;
q->buf_struct_size = sizeof(struct rkvpss_buffer);
q->min_buffers_needed = STREAM_OUT_REQ_BUFS_MIN;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &stream->dev->apilock;
q->dev = stream->dev->hw_dev->dev;
q->allow_cache_hints = 1;
q->bidirectional = 1;
if (stream->dev->hw_dev->is_dma_contig)
q->dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
q->gfp_flags = GFP_DMA32;
return vb2_queue_init(q);
}
static const
struct capture_fmt *find_fmt(struct rkvpss_stream *stream, u32 pixelfmt)
{
const struct capture_fmt *fmt;
u32 i;
for (i = 0; i < stream->config->fmt_size; i++) {
fmt = &stream->config->fmts[i];
if (fmt->fourcc == pixelfmt)
return fmt;
}
return NULL;
}
static int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs)
{
switch (fcc) {
case V4L2_PIX_FMT_GREY:
case V4L2_PIX_FMT_YUV444M:
*xsubs = 1;
*ysubs = 1;
break;
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_YUV422P:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_YVU422M:
case V4L2_PIX_FMT_FBC2:
*xsubs = 2;
*ysubs = 1;
break;
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21M:
case V4L2_PIX_FMT_NV12M:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_FBC0:
*xsubs = 2;
*ysubs = 2;
break;
default:
return -EINVAL;
}
return 0;
}
static int rkvpss_set_fmt(struct rkvpss_stream *stream,
struct v4l2_pix_format_mplane *pixm,
bool try)
{
struct rkvpss_device *dev = stream->dev;
u32 i, planes, xsubs = 1, ysubs = 1, imagsize = 0;
const struct capture_fmt *fmt;
fmt = find_fmt(stream, pixm->pixelformat);
if (!fmt) {
v4l2_err(&dev->v4l2_dev,
"nonsupport pixelformat:%c%c%c%c\n",
pixm->pixelformat,
pixm->pixelformat >> 8,
pixm->pixelformat >> 16,
pixm->pixelformat >> 24);
return -EINVAL;
}
/* Tile4x4 writing of Channel0 and Channel1 only supports either one.*/
if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422) {
if (stream->id == 0) {
if (dev->stream_vdev.stream[1].streaming &&
(dev->stream_vdev.stream[1].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 ||
dev->stream_vdev.stream[1].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422)) {
v4l2_err(&dev->v4l2_dev,
"Tile4x4 writing of Ch0 and Cl1 only supports either one\n");
return -EINVAL;
}
}
if (stream->id == 1) {
if (dev->stream_vdev.stream[0].streaming &&
(dev->stream_vdev.stream[0].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 ||
dev->stream_vdev.stream[0].out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422)) {
v4l2_err(&dev->v4l2_dev,
"Tile4x4 writing of Ch0 and Cl1 only supports either one\n");
return -EINVAL;
}
}
}
pixm->num_planes = fmt->mplanes;
pixm->field = V4L2_FIELD_NONE;
if (!pixm->quantization)
pixm->quantization = V4L2_QUANTIZATION_FULL_RANGE;
fcc_xysubs(fmt->fourcc, &xsubs, &ysubs);
planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes;
for (i = 0; i < planes; i++) {
struct v4l2_plane_pix_format *plane_fmt;
u32 width, height, bytesperline, w, h;
plane_fmt = pixm->plane_fmt + i;
w = pixm->width;
h = pixm->height;
width = i ? w / xsubs : w;
height = i ? h / ysubs : h;
if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422)
bytesperline = ALIGN(((width / 4) * fmt->bpp[i]), 16);
else
bytesperline = width * DIV_ROUND_UP(fmt->bpp[i], 8);
if (i != 0 || plane_fmt->bytesperline < bytesperline)
plane_fmt->bytesperline = bytesperline;
if (fmt->fourcc == V4L2_PIX_FMT_TILE420 || fmt->fourcc == V4L2_PIX_FMT_TILE422)
plane_fmt->sizeimage = plane_fmt->bytesperline * (height / 4);
else
plane_fmt->sizeimage = plane_fmt->bytesperline * height;
imagsize += plane_fmt->sizeimage;
}
if (fmt->mplanes == 1)
pixm->plane_fmt[0].sizeimage = imagsize;
if (!try) {
stream->out_cap_fmt = *fmt;
stream->out_fmt = *pixm;
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
"%s: stream:%d req(%d, %d) out(%d, %d)\n",
__func__, stream->id, pixm->width, pixm->height,
stream->out_fmt.width, stream->out_fmt.height);
}
return 0;
}
/************************* v4l2_file_operations***************************/
static int rkvpss_fh_open(struct file *file)
{
struct rkvpss_stream *stream = video_drvdata(file);
struct rkvpss_device *dev = stream->dev;
int ret;
if (!dev->is_probe_end)
return -EINVAL;
ret = v4l2_fh_open(file);
if (!ret) {
ret = v4l2_pipeline_pm_get(&stream->vnode.vdev.entity);
if (ret < 0) {
v4l2_err(&dev->v4l2_dev,
"pipeline power on failed %d\n", ret);
vb2_fop_release(file);
}
}
return ret;
}
static int rkvpss_fh_release(struct file *file)
{
struct rkvpss_stream *stream = video_drvdata(file);
int ret;
ret = vb2_fop_release(file);
if (!ret)
v4l2_pipeline_pm_put(&stream->vnode.vdev.entity);
return ret;
}
static const struct v4l2_file_operations rkvpss_fops = {
.open = rkvpss_fh_open,
.release = rkvpss_fh_release,
.unlocked_ioctl = video_ioctl2,
.poll = vb2_fop_poll,
.mmap = vb2_fop_mmap,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = video_ioctl2,
#endif
};
static int rkvpss_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
if (input->index > 0)
return -EINVAL;
input->type = V4L2_INPUT_TYPE_CAMERA;
strscpy(input->name, "Camera", sizeof(input->name));
return 0;
}
static int rkvpss_try_fmt_vid_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct rkvpss_stream *stream = video_drvdata(file);
return rkvpss_set_fmt(stream, &f->fmt.pix_mp, true);
}
static int rkvpss_enum_fmt_vid_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
struct rkvpss_stream *stream = video_drvdata(file);
const struct capture_fmt *fmt = NULL;
if (f->index >= stream->config->fmt_size)
return -EINVAL;
fmt = &stream->config->fmts[f->index];
f->pixelformat = fmt->fourcc;
switch (f->pixelformat) {
case V4L2_PIX_FMT_TILE420:
strscpy(f->description,
"Rockchip yuv420 tile",
sizeof(f->description));
break;
case V4L2_PIX_FMT_TILE422:
strscpy(f->description,
"Rockchip yuv422 tile",
sizeof(f->description));
break;
default:
break;
}
return 0;
}
static int rkvpss_s_fmt_vid_mplane(struct file *file,
void *priv, struct v4l2_format *f)
{
struct rkvpss_stream *stream = video_drvdata(file);
struct video_device *vdev = &stream->vnode.vdev;
struct rkvpss_vdev_node *node = vdev_to_node(vdev);
struct rkvpss_device *dev = stream->dev;
/* Change not allowed if queue is streaming. */
if (vb2_is_streaming(&node->buf_queue)) {
v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
return -EBUSY;
}
return rkvpss_set_fmt(stream, &f->fmt.pix_mp, false);
}
static int rkvpss_g_fmt_vid_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct rkvpss_stream *stream = video_drvdata(file);
f->fmt.pix_mp = stream->out_fmt;
return 0;
}
static int rkvpss_g_selection(struct file *file, void *prv,
struct v4l2_selection *sel)
{
struct rkvpss_stream *stream = video_drvdata(file);
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
sel->r = stream->crop;
break;
default:
return -EINVAL;
}
return 0;
}
static int rkvpss_s_selection(struct file *file, void *prv,
struct v4l2_selection *sel)
{
struct rkvpss_stream *stream = video_drvdata(file);
struct rkvpss_device *dev = stream->dev;
struct v4l2_rect *crop = &stream->crop;
u32 max_w = dev->vpss_sdev.in_fmt.width;
u32 max_h = dev->vpss_sdev.in_fmt.height;
if (sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
if (sel->flags != 0)
return -EINVAL;
sel->r.left = ALIGN(sel->r.left, 2);
sel->r.top = ALIGN(sel->r.top, 2);
sel->r.width = ALIGN(sel->r.width, 2);
sel->r.height = ALIGN(sel->r.height, 2);
if (!sel->r.width || !sel->r.height ||
sel->r.width + sel->r.left > max_w ||
sel->r.height + sel->r.top > max_h) {
v4l2_err(&dev->v4l2_dev,
"invalid crop left:%d top:%d w:%d h:%d for %dx%d\n",
sel->r.left, sel->r.top, sel->r.width, sel->r.height, max_w, max_h);
return -EINVAL;
}
*crop = sel->r;
stream->is_crop_upd = true;
v4l2_dbg(1, rkvpss_debug, &dev->v4l2_dev,
"stream %d crop(%d,%d)/%dx%d\n", stream->id,
crop->left, crop->top, crop->width, crop->height);
return 0;
}
static int rkvpss_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct rkvpss_stream *stream = video_drvdata(file);
struct device *dev = stream->dev->dev;
struct video_device *vdev = video_devdata(file);
strscpy(cap->card, vdev->name, sizeof(cap->card));
snprintf(cap->driver, sizeof(cap->driver),
"%s_v%d", dev->driver->name,
stream->dev->vpss_ver >> 4);
snprintf(cap->bus_info, sizeof(cap->bus_info),
"platform:%s", dev_name(dev));
return 0;
}
static void rkvpss_stream_mf(struct rkvpss_stream *stream)
{
struct rkvpss_device *dev = stream->dev;
u32 val, mask;
if (!stream->is_mf_upd)
return;
stream->is_mf_upd = false;
if (!dev->hw_dev->is_ofl_cmsc) {
mask = RKVPSS_MIR_EN;
val = dev->mir_en ? RKVPSS_MIR_EN : 0;
rkvpss_unite_set_bits(dev, RKVPSS_VPSS_CTRL, mask, val);
}
mask = RKVPSS_MI_CHN_V_FLIP(stream->id);
/* Tile4x4 writing can't flip*/
if (stream->out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE420 ||
stream->out_cap_fmt.fourcc == V4L2_PIX_FMT_TILE422)
stream->flip_en = false;
val = stream->flip_en ? mask : 0;
rkvpss_unite_set_bits(dev, RKVPSS_MI_WR_VFLIP_CTRL, mask, val);
}
static int rkvpss_get_mirror_flip(struct rkvpss_stream *stream,
struct rkvpss_mirror_flip *cfg)
{
cfg->mirror = stream->dev->mir_en;
cfg->flip = stream->flip_en;
return 0;
}
static int rkvpss_set_mirror_flip(struct rkvpss_stream *stream,
struct rkvpss_mirror_flip *cfg)
{
struct rkvpss_device *dev = stream->dev;
if (dev->hw_dev->is_ofl_cmsc && cfg->mirror) {
cfg->mirror = 0;
v4l2_warn(&stream->dev->v4l2_dev,
"mirror mux to vpss offline mode\n");
}
if (dev->mir_en != !!cfg->mirror ||
stream->flip_en != !!cfg->flip)
stream->is_mf_upd = true;
dev->mir_en = !!cfg->mirror;
stream->flip_en = !!cfg->flip;
return 0;
}
static void cmsc_config_hw(struct rkvpss_device *dev, struct rkvpss_cmsc_cfg *cfg, bool sync,
int unite_index)
{
int i, j, k, slope, hor;
u32 win_en, mode, val, ctrl = 0;
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
win_en = cfg->win[i].win_en;
v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev,
"%s unite:%d, unite_index:%d ch:%d win_en:0x%x\n", __func__,
dev->unite_mode, unite_index, i, win_en);
if (win_en)
ctrl |= RKVPSS_CMSC_CHN_EN(i);
rkvpss_idx_write(dev, RKVPSS_CMSC_CHN0_WIN + i * 4, win_en, unite_index);
mode = cfg->win[i].mode;
rkvpss_idx_write(dev, RKVPSS_CMSC_CHN0_MODE + i * 4, mode, unite_index);
for (j = 0; j < RKVPSS_CMSC_WIN_MAX && win_en; j++) {
if (!(win_en & BIT(j)))
continue;
for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) {
val = RKVPSS_CMSC_WIN_VTX(cfg->win[j].point[k].x,
cfg->win[j].point[k].y);
rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_L0_VTX + k * 8 + j * 32, val,
unite_index);
rkvpss_cmsc_slop(&cfg->win[j].point[k],
(k + 1 == RKVPSS_CMSC_POINT_MAX) ?
&cfg->win[j].point[0] : &cfg->win[j].point[k + 1],
&slope, &hor);
val = RKVPSS_CMSC_WIN_SLP(slope, hor);
rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_L0_SLP + k * 8 + j * 32, val,
unite_index);
v4l2_dbg(4, rkvpss_debug, &dev->v4l2_dev,
"%s unite:%d unite_index:%d ch:%d win:%d point:%d x:%u y:%u",
__func__, dev->unite_mode, unite_index, i, j, k,
cfg->win[j].point[k].x,
cfg->win[j].point[k].y);
}
if ((mode & BIT(j)))
continue;
val = RKVPSS_CMSK_WIN_YUV(cfg->win[j].cover_color_y,
cfg->win[j].cover_color_u,
cfg->win[j].cover_color_v);
val |= RKVPSS_CMSC_WIN_ALPHA(cfg->win[j].cover_color_a);
rkvpss_idx_write(dev, RKVPSS_CMSC_WIN0_PARA + j * 4, val, unite_index);
}
}
if (ctrl) {
ctrl |= RKVPSS_CMSC_EN;
ctrl |= RKVPSS_CMSC_BLK_SZIE(cfg->mosaic_block);
}
rkvpss_idx_write(dev, RKVPSS_CMSC_CTRL, ctrl, unite_index);
val = RKVPSS_CMSC_GEN_UPD;
if (sync)
val |= RKVPSS_CMSC_FORCE_UPD;
rkvpss_idx_write(dev, RKVPSS_CMSC_UPDATE, val, unite_index);
}
void rkvpss_cmsc_config(struct rkvpss_device *dev, bool sync)
{
unsigned long lock_flags = 0;
struct rkvpss_cmsc_cfg left_cfg = {0}, right_cfg = {0};
struct rkvpss_cmsc_win *win;
struct rkvpss_cmsc_point *point;
int i, j, k;
u32 mask;
spin_lock_irqsave(&dev->cmsc_lock, lock_flags);
if (dev->hw_dev->is_ofl_cmsc || !dev->cmsc_upd) {
spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags);
return;
}
dev->cmsc_upd = false;
spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags);
if (dev->unite_mode) {
/* unite left */
for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) {
left_cfg.win[i].cover_color_y = dev->cmsc_cfg.win[i].cover_color_y;
left_cfg.win[i].cover_color_u = dev->cmsc_cfg.win[i].cover_color_u;
left_cfg.win[i].cover_color_v = dev->cmsc_cfg.win[i].cover_color_v;
left_cfg.win[i].cover_color_a = dev->cmsc_cfg.win[i].cover_color_a;
for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++)
left_cfg.win[i].point[j] = dev->cmsc_cfg.win[i].point[j];
}
left_cfg.mosaic_block = dev->cmsc_cfg.mosaic_block;
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
left_cfg.win[i].win_en = dev->cmsc_cfg.win[i].win_en;
left_cfg.win[i].mode = dev->cmsc_cfg.win[i].mode;
}
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) {
win = &left_cfg.win[j];
if (!(left_cfg.win[i].win_en & BIT(j)))
continue;
mask = 0;
for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) {
point = &win->point[k];
if (point->x >= dev->vpss_sdev.in_fmt.width / 2)
mask |= BIT(k);
else
mask &= ~BIT(k);
}
if (mask == 0xf) {
/** all right **/
left_cfg.win[i].win_en &= ~BIT(j);
} else if (mask != 0) {
/** middle need avoid pentagon **/
if (win->point[0].x != win->point[3].x ||
win->point[1].x != win->point[2].x) {
left_cfg.win[i].win_en &= ~BIT(j);
} else {
win->point[1].x = dev->vpss_sdev.in_fmt.width / 2;
win->point[2].x = dev->vpss_sdev.in_fmt.width / 2;
}
}
}
}
/* unite right */
for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) {
right_cfg.win[i].cover_color_y = dev->cmsc_cfg.win[i].cover_color_y;
right_cfg.win[i].cover_color_u = dev->cmsc_cfg.win[i].cover_color_u;
right_cfg.win[i].cover_color_v = dev->cmsc_cfg.win[i].cover_color_v;
right_cfg.win[i].cover_color_a = dev->cmsc_cfg.win[i].cover_color_a;
for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++)
right_cfg.win[i].point[j] = dev->cmsc_cfg.win[i].point[j];
}
right_cfg.mosaic_block = dev->cmsc_cfg.mosaic_block;
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
right_cfg.win[i].win_en = dev->cmsc_cfg.win[i].win_en;
right_cfg.win[i].mode = dev->cmsc_cfg.win[i].mode;
}
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
for (j = 0; j < RKVPSS_CMSC_WIN_MAX; j++) {
win = &right_cfg.win[j];
if (!(right_cfg.win[i].win_en & BIT(j)))
continue;
mask = 0;
for (k = 0; k < RKVPSS_CMSC_POINT_MAX; k++) {
point = &win->point[k];
if (point->x <= dev->vpss_sdev.in_fmt.width / 2)
mask |= BIT(k);
else
mask &= ~BIT(k);
}
if (mask == 0xf) {
/** all left **/
right_cfg.win[i].win_en &= ~BIT(j);
} else if (mask != 0) {
/** middle need avoid pentagon **/
if (win->point[0].x != win->point[3].x ||
win->point[1].x != win->point[2].x) {
right_cfg.win[i].win_en &= ~BIT(j);
} else {
win->point[0].x = RKMOUDLE_UNITE_EXTEND_PIXEL;
win->point[3].x = RKMOUDLE_UNITE_EXTEND_PIXEL;
win->point[1].x = win->point[1].x -
(dev->vpss_sdev.in_fmt.width / 2) +
RKMOUDLE_UNITE_EXTEND_PIXEL;
win->point[2].x = win->point[2].x -
(dev->vpss_sdev.in_fmt.width / 2) +
RKMOUDLE_UNITE_EXTEND_PIXEL;
}
} else {
/** all right **/
win->point[0].x = win->point[0].x -
(dev->vpss_sdev.in_fmt.width / 2) +
RKMOUDLE_UNITE_EXTEND_PIXEL;
win->point[1].x = win->point[1].x -
(dev->vpss_sdev.in_fmt.width / 2) +
RKMOUDLE_UNITE_EXTEND_PIXEL;
win->point[2].x = win->point[2].x -
(dev->vpss_sdev.in_fmt.width / 2) +
RKMOUDLE_UNITE_EXTEND_PIXEL;
win->point[3].x = win->point[3].x -
(dev->vpss_sdev.in_fmt.width / 2) +
RKMOUDLE_UNITE_EXTEND_PIXEL;
}
}
}
cmsc_config_hw(dev, &left_cfg, sync, VPSS_UNITE_LEFT);
cmsc_config_hw(dev, &right_cfg, sync, VPSS_UNITE_RIGHT);
} else {
cmsc_config_hw(dev, &dev->cmsc_cfg, sync, VPSS_UNITE_LEFT);
}
}
static int rkvpss_get_cmsc(struct rkvpss_stream *stream, struct rkvpss_cmsc_cfg *cfg)
{
struct rkvpss_device *dev = stream->dev;
unsigned long lock_flags = 0;
u32 i, win_en, mode;
spin_lock_irqsave(&dev->cmsc_lock, lock_flags);
*cfg = dev->cmsc_cfg;
spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags);
win_en = cfg->win[stream->id].win_en;
mode = cfg->win[stream->id].mode;
for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) {
cfg->win[i].win_en = !!(win_en & BIT(i));
cfg->win[i].mode = !!(mode & BIT(i));
}
cfg->width_ro = dev->vpss_sdev.in_fmt.width;
cfg->height_ro = dev->vpss_sdev.in_fmt.height;
return 0;
}
static int rkvpss_set_cmsc(struct rkvpss_stream *stream, struct rkvpss_cmsc_cfg *cfg)
{
struct rkvpss_device *dev = stream->dev;
unsigned long lock_flags = 0;
u16 i, j, k, win_en = 0, mode = 0;
int ret = 0;
if (dev->hw_dev->is_ofl_cmsc)
return -EINVAL;
spin_lock_irqsave(&dev->cmsc_lock, lock_flags);
for (i = 0; i < RKVPSS_CMSC_WIN_MAX; i++) {
if (!cfg->win[i].win_en)
continue;
win_en |= BIT(i);
mode |= cfg->win[i].mode ? BIT(i) : 0;
for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++) {
for (k = j + 1; k < RKVPSS_CMSC_POINT_MAX; k++) {
if (cfg->win[i].point[j].x == cfg->win[i].point[k].x &&
cfg->win[i].point[j].y == cfg->win[i].point[k].y) {
v4l2_warn(&dev->v4l2_dev,
"points should different, P%d(%d,%d) P%d(%d,%d)\n",
j, cfg->win[i].point[j].x, cfg->win[i].point[j].y,
k, cfg->win[i].point[k].x, cfg->win[i].point[k].y);
ret = -EINVAL;
goto unlock;
}
}
}
if (!cfg->win[i].mode) {
dev->cmsc_cfg.win[i].cover_color_y = cfg->win[i].cover_color_y;
dev->cmsc_cfg.win[i].cover_color_u = cfg->win[i].cover_color_u;
dev->cmsc_cfg.win[i].cover_color_v = cfg->win[i].cover_color_v;
dev->cmsc_cfg.win[i].cover_color_a = cfg->win[i].cover_color_a;
if (dev->cmsc_cfg.win[i].cover_color_a > 15)
dev->cmsc_cfg.win[i].cover_color_a = 15;
}
for (j = 0; j < RKVPSS_CMSC_POINT_MAX; j++)
dev->cmsc_cfg.win[i].point[j] = cfg->win[i].point[j];
}
dev->cmsc_cfg.win[stream->id].win_en = win_en;
dev->cmsc_cfg.win[stream->id].mode = mode;
dev->cmsc_cfg.mosaic_block = cfg->mosaic_block;
dev->cmsc_upd = true;
v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev,
"%s ch:%d win_en:0x%x",
__func__, stream->id,
dev->cmsc_cfg.win[stream->id].win_en);
unlock:
spin_unlock_irqrestore(&dev->cmsc_lock, lock_flags);
return ret;
}
static long rkvpss_ioctl_default(struct file *file, void *fh,
bool valid_prio, unsigned int cmd, void *arg)
{
struct rkvpss_stream *stream = video_drvdata(file);
long ret = 0;
if (!arg)
return -EINVAL;
switch (cmd) {
case RKVPSS_CMD_GET_MIRROR_FLIP:
ret = rkvpss_get_mirror_flip(stream, arg);
break;
case RKVPSS_CMD_SET_MIRROR_FLIP:
ret = rkvpss_set_mirror_flip(stream, arg);
break;
case RKVPSS_CMD_GET_CMSC:
ret = rkvpss_get_cmsc(stream, arg);
break;
case RKVPSS_CMD_SET_CMSC:
ret = rkvpss_set_cmsc(stream, arg);
break;
default:
ret = -EINVAL;
}
return ret;
}
static const struct v4l2_ioctl_ops rkvpss_v4l2_ioctl_ops = {
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_enum_input = rkvpss_enum_input,
.vidioc_try_fmt_vid_cap_mplane = rkvpss_try_fmt_vid_mplane,
.vidioc_enum_fmt_vid_cap = rkvpss_enum_fmt_vid_mplane,
.vidioc_s_fmt_vid_cap_mplane = rkvpss_s_fmt_vid_mplane,
.vidioc_g_fmt_vid_cap_mplane = rkvpss_g_fmt_vid_mplane,
.vidioc_s_selection = rkvpss_s_selection,
.vidioc_g_selection = rkvpss_g_selection,
.vidioc_querycap = rkvpss_querycap,
.vidioc_default = rkvpss_ioctl_default,
};
static void rkvpss_unregister_stream_video(struct rkvpss_stream *stream)
{
tasklet_kill(&stream->buf_done_tasklet);
media_entity_cleanup(&stream->vnode.vdev.entity);
video_unregister_device(&stream->vnode.vdev);
}
static int rkvpss_register_stream_video(struct rkvpss_stream *stream)
{
struct rkvpss_device *dev = stream->dev;
struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
struct video_device *vdev = &stream->vnode.vdev;
struct rkvpss_vdev_node *node;
enum v4l2_buf_type buf_type;
int ret = 0;
node = vdev_to_node(vdev);
vdev->release = video_device_release_empty;
vdev->fops = &rkvpss_fops;
vdev->minor = -1;
vdev->v4l2_dev = v4l2_dev;
vdev->lock = &dev->apilock;
video_set_drvdata(vdev, stream);
vdev->ioctl_ops = &rkvpss_v4l2_ioctl_ops;
vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE;
vdev->vfl_dir = VFL_DIR_RX;
node->pad.flags = MEDIA_PAD_FL_SINK;
buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
rkvpss_init_vb2_queue(&node->buf_queue, stream, buf_type);
vdev->queue = &node->buf_queue;
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
v4l2_err(v4l2_dev,
"video register failed with error %d\n", ret);
return ret;
}
ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
if (ret < 0)
goto unreg;
INIT_LIST_HEAD(&stream->buf_done_list);
tasklet_init(&stream->buf_done_tasklet,
rkvpss_buf_done_task, (unsigned long)stream);
tasklet_disable(&stream->buf_done_tasklet);
return 0;
unreg:
video_unregister_device(vdev);
return ret;
}
void rkvpss_stream_default_fmt(struct rkvpss_device *dev, u32 id,
u32 width, u32 height, u32 pixelformat)
{
struct rkvpss_stream *stream = &dev->stream_vdev.stream[id];
struct v4l2_pix_format_mplane pixm = { 0 };
if (pixelformat)
pixm.pixelformat = pixelformat;
else
pixm.pixelformat = stream->out_cap_fmt.fourcc;
if (!pixm.pixelformat)
return;
stream->crop.left = 0;
stream->crop.top = 0;
stream->crop.width = width;
stream->crop.height = height;
pixm.width = width;
pixm.height = height;
rkvpss_set_fmt(stream, &pixm, false);
}
int rkvpss_register_stream_vdevs(struct rkvpss_device *dev)
{
struct rkvpss_stream_vdev *stream_vdev;
struct rkvpss_stream *stream;
struct video_device *vdev;
char *vdev_name;
int i, j, ret = 0;
stream_vdev = &dev->stream_vdev;
memset(stream_vdev, 0, sizeof(*stream_vdev));
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
stream = &stream_vdev->stream[i];
stream->id = i;
stream->dev = dev;
vdev = &stream->vnode.vdev;
INIT_LIST_HEAD(&stream->buf_queue);
init_waitqueue_head(&stream->done);
spin_lock_init(&stream->vbq_lock);
switch (i) {
case RKVPSS_OUTPUT_CH0:
vdev_name = S0_VDEV_NAME;
stream->ops = &scl_stream_ops;
stream->config = &scl0_config;
break;
case RKVPSS_OUTPUT_CH1:
vdev_name = S1_VDEV_NAME;
stream->ops = &scl_stream_ops;
stream->config = &scl1_config;
break;
case RKVPSS_OUTPUT_CH2:
vdev_name = S2_VDEV_NAME;
stream->ops = &scl_stream_ops;
stream->config = &scl2_config;
break;
case RKVPSS_OUTPUT_CH3:
vdev_name = S3_VDEV_NAME;
stream->ops = &scl_stream_ops;
stream->config = &scl3_config;
break;
default:
v4l2_err(&dev->v4l2_dev, "Invalid stream:%d\n", i);
return -EINVAL;
}
strscpy(vdev->name, vdev_name, sizeof(vdev->name));
ret = rkvpss_register_stream_video(stream);
if (ret < 0)
goto err;
}
return 0;
err:
for (j = 0; j < i; j++) {
stream = &stream_vdev->stream[j];
rkvpss_unregister_stream_video(stream);
}
return ret;
}
void rkvpss_unregister_stream_vdevs(struct rkvpss_device *dev)
{
struct rkvpss_stream_vdev *stream_vdev;
struct rkvpss_stream *stream;
int i;
stream_vdev = &dev->stream_vdev;
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
stream = &stream_vdev->stream[i];
rkvpss_unregister_stream_video(stream);
}
}
void rkvpss_isr(struct rkvpss_device *dev, u32 mis_val)
{
v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev,
"isr:0x%x\n", mis_val);
dev->isr_cnt++;
if (mis_val & RKVPSS_ISP_ALL_FRM_END && dev->remote_sd)
rkvpss_check_idle(dev, VPSS_FRAME_END);
}
void rkvpss_mi_isr(struct rkvpss_device *dev, u32 mis_val)
{
struct rkvpss_stream *stream;
int i, ris = readl(dev->hw_dev->base_addr + RKVPSS_MI_RIS);
v4l2_dbg(3, rkvpss_debug, &dev->v4l2_dev,
"mi isr:0x%x, ris:0x%x\n", mis_val, ris);
for (i = 0; i < RKVPSS_OUTPUT_MAX; i++) {
stream = &dev->stream_vdev.stream[i];
if (!stream->streaming ||
!(ris & stream->config->frame_end_id))
continue;
writel(stream->config->frame_end_id, dev->hw_dev->base_addr + RKVPSS_MI_ICR);
if (!dev->unite_mode || dev->unite_index == VPSS_UNITE_RIGHT) {
if (stream->stopping) {
stream->stopping = false;
stream->streaming = false;
stream->ops->disable_mi(stream);
wake_up(&stream->done);
} else {
rkvpss_frame_end(stream);
}
}
}
rkvpss_check_idle(dev, (ris & 0xf) << 3);
}