mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-02-01 07:42:18 +00:00
04c1822c0a
Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey!
1441 lines
40 KiB
C
1441 lines
40 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Rockchip ISP1 Driver - V4l capture device
|
|
*
|
|
* Copyright (C) 2019 Collabora, Ltd.
|
|
*
|
|
* Based on Rockchip ISP1 driver by Rockchip Electronics Co., Ltd.
|
|
* Copyright (C) 2017 Rockchip Electronics Co., Ltd.
|
|
*/
|
|
|
|
#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-mc.h>
|
|
#include <media/v4l2-subdev.h>
|
|
#include <media/videobuf2-dma-contig.h>
|
|
|
|
#include "rkisp1-common.h"
|
|
|
|
/*
|
|
* NOTE: There are two capture video devices in rkisp1, selfpath and mainpath.
|
|
*
|
|
* differences between selfpath 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 RKISP1_SP_DEV_NAME RKISP1_DRIVER_NAME "_selfpath"
|
|
#define RKISP1_MP_DEV_NAME RKISP1_DRIVER_NAME "_mainpath"
|
|
|
|
#define RKISP1_MIN_BUFFERS_NEEDED 3
|
|
|
|
enum rkisp1_plane {
|
|
RKISP1_PLANE_Y = 0,
|
|
RKISP1_PLANE_CB = 1,
|
|
RKISP1_PLANE_CR = 2
|
|
};
|
|
|
|
/*
|
|
* @fourcc: pixel format
|
|
* @fmt_type: helper filed for pixel format
|
|
* @uv_swap: if cb cr swapped, for yuv
|
|
* @write_format: defines how YCbCr self picture data is written to memory
|
|
* @output_format: defines sp output format
|
|
* @mbus: the mbus code on the src resizer pad that matches the pixel format
|
|
*/
|
|
struct rkisp1_capture_fmt_cfg {
|
|
u32 fourcc;
|
|
u8 uv_swap;
|
|
u32 write_format;
|
|
u32 output_format;
|
|
u32 mbus;
|
|
};
|
|
|
|
struct rkisp1_capture_ops {
|
|
void (*config)(struct rkisp1_capture *cap);
|
|
void (*stop)(struct rkisp1_capture *cap);
|
|
void (*enable)(struct rkisp1_capture *cap);
|
|
void (*disable)(struct rkisp1_capture *cap);
|
|
void (*set_data_path)(struct rkisp1_capture *cap);
|
|
bool (*is_stopped)(struct rkisp1_capture *cap);
|
|
};
|
|
|
|
struct rkisp1_capture_config {
|
|
const struct rkisp1_capture_fmt_cfg *fmts;
|
|
int fmt_size;
|
|
struct {
|
|
u32 y_size_init;
|
|
u32 cb_size_init;
|
|
u32 cr_size_init;
|
|
u32 y_base_ad_init;
|
|
u32 cb_base_ad_init;
|
|
u32 cr_base_ad_init;
|
|
u32 y_offs_cnt_init;
|
|
u32 cb_offs_cnt_init;
|
|
u32 cr_offs_cnt_init;
|
|
} mi;
|
|
};
|
|
|
|
/*
|
|
* The supported pixel formats for mainpath. NOTE, pixel formats with identical 'mbus'
|
|
* are grouped together. This is assumed and used by the function rkisp1_cap_enum_mbus_codes
|
|
*/
|
|
static const struct rkisp1_capture_fmt_cfg rkisp1_mp_fmts[] = {
|
|
/* yuv422 */
|
|
{
|
|
.fourcc = V4L2_PIX_FMT_YUYV,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUVINT,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_YUV422P,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_NV16,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_NV61,
|
|
.uv_swap = 1,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_YVU422M,
|
|
.uv_swap = 1,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
},
|
|
/* yuv400 */
|
|
{
|
|
.fourcc = V4L2_PIX_FMT_GREY,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
},
|
|
/* yuv420 */
|
|
{
|
|
.fourcc = V4L2_PIX_FMT_NV21,
|
|
.uv_swap = 1,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_NV12,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_NV21M,
|
|
.uv_swap = 1,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_NV12M,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_SPLA,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_YUV420,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_YVU420,
|
|
.uv_swap = 1,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
|
|
},
|
|
/* raw */
|
|
{
|
|
.fourcc = V4L2_PIX_FMT_SRGGB8,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
|
|
.mbus = MEDIA_BUS_FMT_SRGGB8_1X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_SGRBG8,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
|
|
.mbus = MEDIA_BUS_FMT_SGRBG8_1X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_SGBRG8,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
|
|
.mbus = MEDIA_BUS_FMT_SGBRG8_1X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_SBGGR8,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
|
|
.mbus = MEDIA_BUS_FMT_SBGGR8_1X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_SRGGB10,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
|
|
.mbus = MEDIA_BUS_FMT_SRGGB10_1X10,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_SGRBG10,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
|
|
.mbus = MEDIA_BUS_FMT_SGRBG10_1X10,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_SGBRG10,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
|
|
.mbus = MEDIA_BUS_FMT_SGBRG10_1X10,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_SBGGR10,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
|
|
.mbus = MEDIA_BUS_FMT_SBGGR10_1X10,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_SRGGB12,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
|
|
.mbus = MEDIA_BUS_FMT_SRGGB12_1X12,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_SGRBG12,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
|
|
.mbus = MEDIA_BUS_FMT_SGRBG12_1X12,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_SGBRG12,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
|
|
.mbus = MEDIA_BUS_FMT_SGBRG12_1X12,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_SBGGR12,
|
|
.write_format = RKISP1_MI_CTRL_MP_WRITE_RAW12,
|
|
.mbus = MEDIA_BUS_FMT_SBGGR12_1X12,
|
|
},
|
|
};
|
|
|
|
/*
|
|
* The supported pixel formats for selfpath. NOTE, pixel formats with identical 'mbus'
|
|
* are grouped together. This is assumed and used by the function rkisp1_cap_enum_mbus_codes
|
|
*/
|
|
static const struct rkisp1_capture_fmt_cfg rkisp1_sp_fmts[] = {
|
|
/* yuv422 */
|
|
{
|
|
.fourcc = V4L2_PIX_FMT_YUYV,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_INT,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_YUV422P,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_NV16,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_NV61,
|
|
.uv_swap = 1,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_YVU422M,
|
|
.uv_swap = 1,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
},
|
|
/* yuv400 */
|
|
{
|
|
.fourcc = V4L2_PIX_FMT_GREY,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV400,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
},
|
|
/* rgb */
|
|
{
|
|
.fourcc = V4L2_PIX_FMT_XBGR32,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_RGB888,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_RGB565,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_RGB565,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
|
|
},
|
|
/* yuv420 */
|
|
{
|
|
.fourcc = V4L2_PIX_FMT_NV21,
|
|
.uv_swap = 1,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV420,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_NV12,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV420,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_NV21M,
|
|
.uv_swap = 1,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV420,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_NV12M,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_SPLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV420,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_YUV420,
|
|
.uv_swap = 0,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV420,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
|
|
}, {
|
|
.fourcc = V4L2_PIX_FMT_YVU420,
|
|
.uv_swap = 1,
|
|
.write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
|
|
.output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV420,
|
|
.mbus = MEDIA_BUS_FMT_YUYV8_1_5X8,
|
|
},
|
|
};
|
|
|
|
static const struct rkisp1_capture_config rkisp1_capture_config_mp = {
|
|
.fmts = rkisp1_mp_fmts,
|
|
.fmt_size = ARRAY_SIZE(rkisp1_mp_fmts),
|
|
.mi = {
|
|
.y_size_init = RKISP1_CIF_MI_MP_Y_SIZE_INIT,
|
|
.cb_size_init = RKISP1_CIF_MI_MP_CB_SIZE_INIT,
|
|
.cr_size_init = RKISP1_CIF_MI_MP_CR_SIZE_INIT,
|
|
.y_base_ad_init = RKISP1_CIF_MI_MP_Y_BASE_AD_INIT,
|
|
.cb_base_ad_init = RKISP1_CIF_MI_MP_CB_BASE_AD_INIT,
|
|
.cr_base_ad_init = RKISP1_CIF_MI_MP_CR_BASE_AD_INIT,
|
|
.y_offs_cnt_init = RKISP1_CIF_MI_MP_Y_OFFS_CNT_INIT,
|
|
.cb_offs_cnt_init = RKISP1_CIF_MI_MP_CB_OFFS_CNT_INIT,
|
|
.cr_offs_cnt_init = RKISP1_CIF_MI_MP_CR_OFFS_CNT_INIT,
|
|
},
|
|
};
|
|
|
|
static const struct rkisp1_capture_config rkisp1_capture_config_sp = {
|
|
.fmts = rkisp1_sp_fmts,
|
|
.fmt_size = ARRAY_SIZE(rkisp1_sp_fmts),
|
|
.mi = {
|
|
.y_size_init = RKISP1_CIF_MI_SP_Y_SIZE_INIT,
|
|
.cb_size_init = RKISP1_CIF_MI_SP_CB_SIZE_INIT,
|
|
.cr_size_init = RKISP1_CIF_MI_SP_CR_SIZE_INIT,
|
|
.y_base_ad_init = RKISP1_CIF_MI_SP_Y_BASE_AD_INIT,
|
|
.cb_base_ad_init = RKISP1_CIF_MI_SP_CB_BASE_AD_INIT,
|
|
.cr_base_ad_init = RKISP1_CIF_MI_SP_CR_BASE_AD_INIT,
|
|
.y_offs_cnt_init = RKISP1_CIF_MI_SP_Y_OFFS_CNT_INIT,
|
|
.cb_offs_cnt_init = RKISP1_CIF_MI_SP_CB_OFFS_CNT_INIT,
|
|
.cr_offs_cnt_init = RKISP1_CIF_MI_SP_CR_OFFS_CNT_INIT,
|
|
},
|
|
};
|
|
|
|
static inline struct rkisp1_vdev_node *
|
|
rkisp1_vdev_to_node(struct video_device *vdev)
|
|
{
|
|
return container_of(vdev, struct rkisp1_vdev_node, vdev);
|
|
}
|
|
|
|
int rkisp1_cap_enum_mbus_codes(struct rkisp1_capture *cap,
|
|
struct v4l2_subdev_mbus_code_enum *code)
|
|
{
|
|
const struct rkisp1_capture_fmt_cfg *fmts = cap->config->fmts;
|
|
/*
|
|
* initialize curr_mbus to non existing mbus code 0 to ensure it is
|
|
* different from fmts[0].mbus
|
|
*/
|
|
u32 curr_mbus = 0;
|
|
int i, n = 0;
|
|
|
|
for (i = 0; i < cap->config->fmt_size; i++) {
|
|
if (fmts[i].mbus == curr_mbus)
|
|
continue;
|
|
|
|
curr_mbus = fmts[i].mbus;
|
|
if (n++ == code->index) {
|
|
code->code = curr_mbus;
|
|
return 0;
|
|
}
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* Stream operations for self-picture path (sp) and main-picture path (mp)
|
|
*/
|
|
|
|
static void rkisp1_mi_config_ctrl(struct rkisp1_capture *cap)
|
|
{
|
|
u32 mi_ctrl = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL);
|
|
|
|
mi_ctrl &= ~GENMASK(17, 16);
|
|
mi_ctrl |= RKISP1_CIF_MI_CTRL_BURST_LEN_LUM_64;
|
|
|
|
mi_ctrl &= ~GENMASK(19, 18);
|
|
mi_ctrl |= RKISP1_CIF_MI_CTRL_BURST_LEN_CHROM_64;
|
|
|
|
mi_ctrl |= RKISP1_CIF_MI_CTRL_INIT_BASE_EN |
|
|
RKISP1_CIF_MI_CTRL_INIT_OFFSET_EN;
|
|
|
|
rkisp1_write(cap->rkisp1, mi_ctrl, RKISP1_CIF_MI_CTRL);
|
|
}
|
|
|
|
static u32 rkisp1_pixfmt_comp_size(const struct v4l2_pix_format_mplane *pixm,
|
|
unsigned int component)
|
|
{
|
|
/*
|
|
* If packed format, then plane_fmt[0].sizeimage is the sum of all
|
|
* components, so we need to calculate just the size of Y component.
|
|
* See rkisp1_fill_pixfmt().
|
|
*/
|
|
if (!component && pixm->num_planes == 1)
|
|
return pixm->plane_fmt[0].bytesperline * pixm->height;
|
|
return pixm->plane_fmt[component].sizeimage;
|
|
}
|
|
|
|
static void rkisp1_irq_frame_end_enable(struct rkisp1_capture *cap)
|
|
{
|
|
u32 mi_imsc = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_IMSC);
|
|
|
|
mi_imsc |= RKISP1_CIF_MI_FRAME(cap);
|
|
rkisp1_write(cap->rkisp1, mi_imsc, RKISP1_CIF_MI_IMSC);
|
|
}
|
|
|
|
static void rkisp1_mp_config(struct rkisp1_capture *cap)
|
|
{
|
|
const struct v4l2_pix_format_mplane *pixm = &cap->pix.fmt;
|
|
struct rkisp1_device *rkisp1 = cap->rkisp1;
|
|
u32 reg;
|
|
|
|
rkisp1_write(rkisp1, rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_Y),
|
|
cap->config->mi.y_size_init);
|
|
rkisp1_write(rkisp1, rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CB),
|
|
cap->config->mi.cb_size_init);
|
|
rkisp1_write(rkisp1, rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CR),
|
|
cap->config->mi.cr_size_init);
|
|
|
|
rkisp1_irq_frame_end_enable(cap);
|
|
|
|
/* set uv swapping for semiplanar formats */
|
|
if (cap->pix.info->comp_planes == 2) {
|
|
reg = rkisp1_read(rkisp1, RKISP1_CIF_MI_XTD_FORMAT_CTRL);
|
|
if (cap->pix.cfg->uv_swap)
|
|
reg |= RKISP1_CIF_MI_XTD_FMT_CTRL_MP_CB_CR_SWAP;
|
|
else
|
|
reg &= ~RKISP1_CIF_MI_XTD_FMT_CTRL_MP_CB_CR_SWAP;
|
|
rkisp1_write(rkisp1, reg, RKISP1_CIF_MI_XTD_FORMAT_CTRL);
|
|
}
|
|
|
|
rkisp1_mi_config_ctrl(cap);
|
|
|
|
reg = rkisp1_read(rkisp1, RKISP1_CIF_MI_CTRL);
|
|
reg &= ~RKISP1_MI_CTRL_MP_FMT_MASK;
|
|
reg |= cap->pix.cfg->write_format;
|
|
rkisp1_write(rkisp1, reg, RKISP1_CIF_MI_CTRL);
|
|
|
|
reg = rkisp1_read(rkisp1, RKISP1_CIF_MI_CTRL);
|
|
reg |= RKISP1_CIF_MI_MP_AUTOUPDATE_ENABLE;
|
|
rkisp1_write(rkisp1, reg, RKISP1_CIF_MI_CTRL);
|
|
}
|
|
|
|
static void rkisp1_sp_config(struct rkisp1_capture *cap)
|
|
{
|
|
const struct v4l2_pix_format_mplane *pixm = &cap->pix.fmt;
|
|
struct rkisp1_device *rkisp1 = cap->rkisp1;
|
|
u32 mi_ctrl, reg;
|
|
|
|
rkisp1_write(rkisp1, rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_Y),
|
|
cap->config->mi.y_size_init);
|
|
rkisp1_write(rkisp1, rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CB),
|
|
cap->config->mi.cb_size_init);
|
|
rkisp1_write(rkisp1, rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CR),
|
|
cap->config->mi.cr_size_init);
|
|
|
|
rkisp1_write(rkisp1, pixm->width, RKISP1_CIF_MI_SP_Y_PIC_WIDTH);
|
|
rkisp1_write(rkisp1, pixm->height, RKISP1_CIF_MI_SP_Y_PIC_HEIGHT);
|
|
rkisp1_write(rkisp1, cap->sp_y_stride, RKISP1_CIF_MI_SP_Y_LLENGTH);
|
|
|
|
rkisp1_irq_frame_end_enable(cap);
|
|
|
|
/* set uv swapping for semiplanar formats */
|
|
if (cap->pix.info->comp_planes == 2) {
|
|
reg = rkisp1_read(rkisp1, RKISP1_CIF_MI_XTD_FORMAT_CTRL);
|
|
if (cap->pix.cfg->uv_swap)
|
|
reg |= RKISP1_CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP;
|
|
else
|
|
reg &= ~RKISP1_CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP;
|
|
rkisp1_write(rkisp1, reg, RKISP1_CIF_MI_XTD_FORMAT_CTRL);
|
|
}
|
|
|
|
rkisp1_mi_config_ctrl(cap);
|
|
|
|
mi_ctrl = rkisp1_read(rkisp1, RKISP1_CIF_MI_CTRL);
|
|
mi_ctrl &= ~RKISP1_MI_CTRL_SP_FMT_MASK;
|
|
mi_ctrl |= cap->pix.cfg->write_format |
|
|
RKISP1_MI_CTRL_SP_INPUT_YUV422 |
|
|
cap->pix.cfg->output_format |
|
|
RKISP1_CIF_MI_SP_AUTOUPDATE_ENABLE;
|
|
rkisp1_write(rkisp1, mi_ctrl, RKISP1_CIF_MI_CTRL);
|
|
}
|
|
|
|
static void rkisp1_mp_disable(struct rkisp1_capture *cap)
|
|
{
|
|
u32 mi_ctrl = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL);
|
|
|
|
mi_ctrl &= ~(RKISP1_CIF_MI_CTRL_MP_ENABLE |
|
|
RKISP1_CIF_MI_CTRL_RAW_ENABLE);
|
|
rkisp1_write(cap->rkisp1, mi_ctrl, RKISP1_CIF_MI_CTRL);
|
|
}
|
|
|
|
static void rkisp1_sp_disable(struct rkisp1_capture *cap)
|
|
{
|
|
u32 mi_ctrl = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL);
|
|
|
|
mi_ctrl &= ~RKISP1_CIF_MI_CTRL_SP_ENABLE;
|
|
rkisp1_write(cap->rkisp1, mi_ctrl, RKISP1_CIF_MI_CTRL);
|
|
}
|
|
|
|
static void rkisp1_mp_enable(struct rkisp1_capture *cap)
|
|
{
|
|
u32 mi_ctrl;
|
|
|
|
rkisp1_mp_disable(cap);
|
|
|
|
mi_ctrl = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL);
|
|
if (v4l2_is_format_bayer(cap->pix.info))
|
|
mi_ctrl |= RKISP1_CIF_MI_CTRL_RAW_ENABLE;
|
|
/* YUV */
|
|
else
|
|
mi_ctrl |= RKISP1_CIF_MI_CTRL_MP_ENABLE;
|
|
|
|
rkisp1_write(cap->rkisp1, mi_ctrl, RKISP1_CIF_MI_CTRL);
|
|
}
|
|
|
|
static void rkisp1_sp_enable(struct rkisp1_capture *cap)
|
|
{
|
|
u32 mi_ctrl = rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL);
|
|
|
|
mi_ctrl |= RKISP1_CIF_MI_CTRL_SP_ENABLE;
|
|
rkisp1_write(cap->rkisp1, mi_ctrl, RKISP1_CIF_MI_CTRL);
|
|
}
|
|
|
|
static void rkisp1_mp_sp_stop(struct rkisp1_capture *cap)
|
|
{
|
|
if (!cap->is_streaming)
|
|
return;
|
|
rkisp1_write(cap->rkisp1,
|
|
RKISP1_CIF_MI_FRAME(cap), RKISP1_CIF_MI_ICR);
|
|
cap->ops->disable(cap);
|
|
}
|
|
|
|
static bool rkisp1_mp_is_stopped(struct rkisp1_capture *cap)
|
|
{
|
|
u32 en = RKISP1_CIF_MI_CTRL_SHD_MP_IN_ENABLED |
|
|
RKISP1_CIF_MI_CTRL_SHD_RAW_OUT_ENABLED;
|
|
|
|
return !(rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL_SHD) & en);
|
|
}
|
|
|
|
static bool rkisp1_sp_is_stopped(struct rkisp1_capture *cap)
|
|
{
|
|
return !(rkisp1_read(cap->rkisp1, RKISP1_CIF_MI_CTRL_SHD) &
|
|
RKISP1_CIF_MI_CTRL_SHD_SP_IN_ENABLED);
|
|
}
|
|
|
|
static void rkisp1_mp_set_data_path(struct rkisp1_capture *cap)
|
|
{
|
|
u32 dpcl = rkisp1_read(cap->rkisp1, RKISP1_CIF_VI_DPCL);
|
|
|
|
dpcl = dpcl | RKISP1_CIF_VI_DPCL_CHAN_MODE_MP |
|
|
RKISP1_CIF_VI_DPCL_MP_MUX_MRSZ_MI;
|
|
rkisp1_write(cap->rkisp1, dpcl, RKISP1_CIF_VI_DPCL);
|
|
}
|
|
|
|
static void rkisp1_sp_set_data_path(struct rkisp1_capture *cap)
|
|
{
|
|
u32 dpcl = rkisp1_read(cap->rkisp1, RKISP1_CIF_VI_DPCL);
|
|
|
|
dpcl |= RKISP1_CIF_VI_DPCL_CHAN_MODE_SP;
|
|
rkisp1_write(cap->rkisp1, dpcl, RKISP1_CIF_VI_DPCL);
|
|
}
|
|
|
|
static const struct rkisp1_capture_ops rkisp1_capture_ops_mp = {
|
|
.config = rkisp1_mp_config,
|
|
.enable = rkisp1_mp_enable,
|
|
.disable = rkisp1_mp_disable,
|
|
.stop = rkisp1_mp_sp_stop,
|
|
.set_data_path = rkisp1_mp_set_data_path,
|
|
.is_stopped = rkisp1_mp_is_stopped,
|
|
};
|
|
|
|
static const struct rkisp1_capture_ops rkisp1_capture_ops_sp = {
|
|
.config = rkisp1_sp_config,
|
|
.enable = rkisp1_sp_enable,
|
|
.disable = rkisp1_sp_disable,
|
|
.stop = rkisp1_mp_sp_stop,
|
|
.set_data_path = rkisp1_sp_set_data_path,
|
|
.is_stopped = rkisp1_sp_is_stopped,
|
|
};
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* Frame buffer operations
|
|
*/
|
|
|
|
static int rkisp1_dummy_buf_create(struct rkisp1_capture *cap)
|
|
{
|
|
const struct v4l2_pix_format_mplane *pixm = &cap->pix.fmt;
|
|
struct rkisp1_dummy_buffer *dummy_buf = &cap->buf.dummy;
|
|
|
|
dummy_buf->size = max3(rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_Y),
|
|
rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CB),
|
|
rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CR));
|
|
|
|
/* The driver never access vaddr, no mapping is required */
|
|
dummy_buf->vaddr = dma_alloc_attrs(cap->rkisp1->dev,
|
|
dummy_buf->size,
|
|
&dummy_buf->dma_addr,
|
|
GFP_KERNEL,
|
|
DMA_ATTR_NO_KERNEL_MAPPING);
|
|
if (!dummy_buf->vaddr)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rkisp1_dummy_buf_destroy(struct rkisp1_capture *cap)
|
|
{
|
|
dma_free_attrs(cap->rkisp1->dev,
|
|
cap->buf.dummy.size, cap->buf.dummy.vaddr,
|
|
cap->buf.dummy.dma_addr, DMA_ATTR_NO_KERNEL_MAPPING);
|
|
}
|
|
|
|
static void rkisp1_set_next_buf(struct rkisp1_capture *cap)
|
|
{
|
|
cap->buf.curr = cap->buf.next;
|
|
cap->buf.next = NULL;
|
|
|
|
if (!list_empty(&cap->buf.queue)) {
|
|
u32 *buff_addr;
|
|
|
|
cap->buf.next = list_first_entry(&cap->buf.queue, struct rkisp1_buffer, queue);
|
|
list_del(&cap->buf.next->queue);
|
|
|
|
buff_addr = cap->buf.next->buff_addr;
|
|
|
|
rkisp1_write(cap->rkisp1,
|
|
buff_addr[RKISP1_PLANE_Y],
|
|
cap->config->mi.y_base_ad_init);
|
|
rkisp1_write(cap->rkisp1,
|
|
buff_addr[RKISP1_PLANE_CB],
|
|
cap->config->mi.cb_base_ad_init);
|
|
rkisp1_write(cap->rkisp1,
|
|
buff_addr[RKISP1_PLANE_CR],
|
|
cap->config->mi.cr_base_ad_init);
|
|
} else {
|
|
/*
|
|
* Use the dummy space allocated by dma_alloc_coherent to
|
|
* throw data if there is no available buffer.
|
|
*/
|
|
rkisp1_write(cap->rkisp1,
|
|
cap->buf.dummy.dma_addr,
|
|
cap->config->mi.y_base_ad_init);
|
|
rkisp1_write(cap->rkisp1,
|
|
cap->buf.dummy.dma_addr,
|
|
cap->config->mi.cb_base_ad_init);
|
|
rkisp1_write(cap->rkisp1,
|
|
cap->buf.dummy.dma_addr,
|
|
cap->config->mi.cr_base_ad_init);
|
|
}
|
|
|
|
/* Set plane offsets */
|
|
rkisp1_write(cap->rkisp1, 0, cap->config->mi.y_offs_cnt_init);
|
|
rkisp1_write(cap->rkisp1, 0, cap->config->mi.cb_offs_cnt_init);
|
|
rkisp1_write(cap->rkisp1, 0, cap->config->mi.cr_offs_cnt_init);
|
|
}
|
|
|
|
/*
|
|
* This function is called when a frame end comes. The next frame
|
|
* is processing and we should set up buffer for next-next frame,
|
|
* otherwise it will overflow.
|
|
*/
|
|
static void rkisp1_handle_buffer(struct rkisp1_capture *cap)
|
|
{
|
|
struct rkisp1_isp *isp = &cap->rkisp1->isp;
|
|
struct rkisp1_buffer *curr_buf;
|
|
|
|
spin_lock(&cap->buf.lock);
|
|
curr_buf = cap->buf.curr;
|
|
|
|
if (curr_buf) {
|
|
curr_buf->vb.sequence = isp->frame_sequence;
|
|
curr_buf->vb.vb2_buf.timestamp = ktime_get_boottime_ns();
|
|
curr_buf->vb.field = V4L2_FIELD_NONE;
|
|
vb2_buffer_done(&curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
|
|
} else {
|
|
cap->rkisp1->debug.frame_drop[cap->id]++;
|
|
}
|
|
|
|
rkisp1_set_next_buf(cap);
|
|
spin_unlock(&cap->buf.lock);
|
|
}
|
|
|
|
void rkisp1_capture_isr(struct rkisp1_device *rkisp1)
|
|
{
|
|
unsigned int i;
|
|
u32 status;
|
|
|
|
status = rkisp1_read(rkisp1, RKISP1_CIF_MI_MIS);
|
|
rkisp1_write(rkisp1, status, RKISP1_CIF_MI_ICR);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(rkisp1->capture_devs); ++i) {
|
|
struct rkisp1_capture *cap = &rkisp1->capture_devs[i];
|
|
|
|
if (!(status & RKISP1_CIF_MI_FRAME(cap)))
|
|
continue;
|
|
if (!cap->is_stopping) {
|
|
rkisp1_handle_buffer(cap);
|
|
continue;
|
|
}
|
|
/*
|
|
* 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() takes effect at the next
|
|
* frame end that sync the configurations to shadow
|
|
* regs.
|
|
*/
|
|
if (!cap->ops->is_stopped(cap)) {
|
|
cap->ops->stop(cap);
|
|
continue;
|
|
}
|
|
cap->is_stopping = false;
|
|
cap->is_streaming = false;
|
|
wake_up(&cap->done);
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* Vb2 operations
|
|
*/
|
|
|
|
static int rkisp1_vb2_queue_setup(struct vb2_queue *queue,
|
|
unsigned int *num_buffers,
|
|
unsigned int *num_planes,
|
|
unsigned int sizes[],
|
|
struct device *alloc_devs[])
|
|
{
|
|
struct rkisp1_capture *cap = queue->drv_priv;
|
|
const struct v4l2_pix_format_mplane *pixm = &cap->pix.fmt;
|
|
unsigned int i;
|
|
|
|
if (*num_planes) {
|
|
if (*num_planes != pixm->num_planes)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < pixm->num_planes; i++)
|
|
if (sizes[i] < pixm->plane_fmt[i].sizeimage)
|
|
return -EINVAL;
|
|
} else {
|
|
*num_planes = pixm->num_planes;
|
|
for (i = 0; i < pixm->num_planes; i++)
|
|
sizes[i] = pixm->plane_fmt[i].sizeimage;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rkisp1_vb2_buf_init(struct vb2_buffer *vb)
|
|
{
|
|
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
|
struct rkisp1_buffer *ispbuf =
|
|
container_of(vbuf, struct rkisp1_buffer, vb);
|
|
struct rkisp1_capture *cap = vb->vb2_queue->drv_priv;
|
|
const struct v4l2_pix_format_mplane *pixm = &cap->pix.fmt;
|
|
unsigned int i;
|
|
|
|
memset(ispbuf->buff_addr, 0, sizeof(ispbuf->buff_addr));
|
|
for (i = 0; i < pixm->num_planes; i++)
|
|
ispbuf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
|
|
|
|
/* Convert to non-MPLANE */
|
|
if (pixm->num_planes == 1) {
|
|
ispbuf->buff_addr[RKISP1_PLANE_CB] =
|
|
ispbuf->buff_addr[RKISP1_PLANE_Y] +
|
|
rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_Y);
|
|
ispbuf->buff_addr[RKISP1_PLANE_CR] =
|
|
ispbuf->buff_addr[RKISP1_PLANE_CB] +
|
|
rkisp1_pixfmt_comp_size(pixm, RKISP1_PLANE_CB);
|
|
}
|
|
|
|
/*
|
|
* uv swap can be supported for planar formats by switching
|
|
* the address of cb and cr
|
|
*/
|
|
if (cap->pix.info->comp_planes == 3 && cap->pix.cfg->uv_swap)
|
|
swap(ispbuf->buff_addr[RKISP1_PLANE_CR],
|
|
ispbuf->buff_addr[RKISP1_PLANE_CB]);
|
|
return 0;
|
|
}
|
|
|
|
static void rkisp1_vb2_buf_queue(struct vb2_buffer *vb)
|
|
{
|
|
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
|
struct rkisp1_buffer *ispbuf =
|
|
container_of(vbuf, struct rkisp1_buffer, vb);
|
|
struct rkisp1_capture *cap = vb->vb2_queue->drv_priv;
|
|
|
|
spin_lock_irq(&cap->buf.lock);
|
|
list_add_tail(&ispbuf->queue, &cap->buf.queue);
|
|
spin_unlock_irq(&cap->buf.lock);
|
|
}
|
|
|
|
static int rkisp1_vb2_buf_prepare(struct vb2_buffer *vb)
|
|
{
|
|
struct rkisp1_capture *cap = vb->vb2_queue->drv_priv;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < cap->pix.fmt.num_planes; i++) {
|
|
unsigned long size = cap->pix.fmt.plane_fmt[i].sizeimage;
|
|
|
|
if (vb2_plane_size(vb, i) < size) {
|
|
dev_err(cap->rkisp1->dev,
|
|
"User buffer too small (%ld < %ld)\n",
|
|
vb2_plane_size(vb, i), size);
|
|
return -EINVAL;
|
|
}
|
|
vb2_set_plane_payload(vb, i, size);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rkisp1_return_all_buffers(struct rkisp1_capture *cap,
|
|
enum vb2_buffer_state state)
|
|
{
|
|
struct rkisp1_buffer *buf;
|
|
|
|
spin_lock_irq(&cap->buf.lock);
|
|
if (cap->buf.curr) {
|
|
vb2_buffer_done(&cap->buf.curr->vb.vb2_buf, state);
|
|
cap->buf.curr = NULL;
|
|
}
|
|
if (cap->buf.next) {
|
|
vb2_buffer_done(&cap->buf.next->vb.vb2_buf, state);
|
|
cap->buf.next = NULL;
|
|
}
|
|
while (!list_empty(&cap->buf.queue)) {
|
|
buf = list_first_entry(&cap->buf.queue,
|
|
struct rkisp1_buffer, queue);
|
|
list_del(&buf->queue);
|
|
vb2_buffer_done(&buf->vb.vb2_buf, state);
|
|
}
|
|
spin_unlock_irq(&cap->buf.lock);
|
|
}
|
|
|
|
/*
|
|
* Most registers inside the rockchip ISP1 have shadow register since
|
|
* they must not be changed while processing a frame.
|
|
* Usually, each sub-module updates its shadow register after
|
|
* processing the last pixel of a frame.
|
|
*/
|
|
static void rkisp1_cap_stream_enable(struct rkisp1_capture *cap)
|
|
{
|
|
struct rkisp1_device *rkisp1 = cap->rkisp1;
|
|
struct rkisp1_capture *other = &rkisp1->capture_devs[cap->id ^ 1];
|
|
|
|
cap->ops->set_data_path(cap);
|
|
cap->ops->config(cap);
|
|
|
|
/* Setup a buffer for the next frame */
|
|
spin_lock_irq(&cap->buf.lock);
|
|
rkisp1_set_next_buf(cap);
|
|
cap->ops->enable(cap);
|
|
/* It's safe to configure ACTIVE and SHADOW registers for the
|
|
* first stream. While when the second is starting, do NOT
|
|
* force update because it also updates the first one.
|
|
*
|
|
* The latter case would drop one more buffer(that is 2) since
|
|
* there's no buffer in a shadow register when the second FE received.
|
|
* This's also required because the second FE maybe corrupt
|
|
* especially when run at 120fps.
|
|
*/
|
|
if (!other->is_streaming) {
|
|
/* force cfg update */
|
|
rkisp1_write(rkisp1,
|
|
RKISP1_CIF_MI_INIT_SOFT_UPD, RKISP1_CIF_MI_INIT);
|
|
rkisp1_set_next_buf(cap);
|
|
}
|
|
spin_unlock_irq(&cap->buf.lock);
|
|
cap->is_streaming = true;
|
|
}
|
|
|
|
static void rkisp1_cap_stream_disable(struct rkisp1_capture *cap)
|
|
{
|
|
int ret;
|
|
|
|
/* Stream should stop in interrupt. If it doesn't, stop it by force. */
|
|
cap->is_stopping = true;
|
|
ret = wait_event_timeout(cap->done,
|
|
!cap->is_streaming,
|
|
msecs_to_jiffies(1000));
|
|
if (!ret) {
|
|
cap->rkisp1->debug.stop_timeout[cap->id]++;
|
|
cap->ops->stop(cap);
|
|
cap->is_stopping = false;
|
|
cap->is_streaming = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* rkisp1_pipeline_stream_disable - disable nodes in the pipeline
|
|
*
|
|
* Call s_stream(false) in the reverse order from
|
|
* rkisp1_pipeline_stream_enable() and disable the DMA engine.
|
|
* Should be called before media_pipeline_stop()
|
|
*/
|
|
static void rkisp1_pipeline_stream_disable(struct rkisp1_capture *cap)
|
|
__must_hold(&cap->rkisp1->stream_lock)
|
|
{
|
|
struct rkisp1_device *rkisp1 = cap->rkisp1;
|
|
|
|
rkisp1_cap_stream_disable(cap);
|
|
|
|
/*
|
|
* If the other capture is streaming, isp and sensor nodes shouldn't
|
|
* be disabled, skip them.
|
|
*/
|
|
if (rkisp1->pipe.streaming_count < 2) {
|
|
v4l2_subdev_call(rkisp1->active_sensor->sd, video, s_stream,
|
|
false);
|
|
v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, false);
|
|
}
|
|
|
|
v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video, s_stream,
|
|
false);
|
|
}
|
|
|
|
/*
|
|
* rkisp1_pipeline_stream_enable - enable nodes in the pipeline
|
|
*
|
|
* Enable the DMA Engine and call s_stream(true) through the pipeline.
|
|
* Should be called after media_pipeline_start()
|
|
*/
|
|
static int rkisp1_pipeline_stream_enable(struct rkisp1_capture *cap)
|
|
__must_hold(&cap->rkisp1->stream_lock)
|
|
{
|
|
struct rkisp1_device *rkisp1 = cap->rkisp1;
|
|
int ret;
|
|
|
|
rkisp1_cap_stream_enable(cap);
|
|
|
|
ret = v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video,
|
|
s_stream, true);
|
|
if (ret)
|
|
goto err_disable_cap;
|
|
|
|
/*
|
|
* If the other capture is streaming, isp and sensor nodes are already
|
|
* enabled, skip them.
|
|
*/
|
|
if (rkisp1->pipe.streaming_count > 1)
|
|
return 0;
|
|
|
|
ret = v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, true);
|
|
if (ret)
|
|
goto err_disable_rsz;
|
|
|
|
ret = v4l2_subdev_call(rkisp1->active_sensor->sd, video, s_stream,
|
|
true);
|
|
if (ret)
|
|
goto err_disable_isp;
|
|
|
|
return 0;
|
|
|
|
err_disable_isp:
|
|
v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, false);
|
|
err_disable_rsz:
|
|
v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video, s_stream,
|
|
false);
|
|
err_disable_cap:
|
|
rkisp1_cap_stream_disable(cap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue)
|
|
{
|
|
struct rkisp1_capture *cap = queue->drv_priv;
|
|
struct rkisp1_vdev_node *node = &cap->vnode;
|
|
struct rkisp1_device *rkisp1 = cap->rkisp1;
|
|
int ret;
|
|
|
|
mutex_lock(&cap->rkisp1->stream_lock);
|
|
|
|
rkisp1_pipeline_stream_disable(cap);
|
|
|
|
rkisp1_return_all_buffers(cap, VB2_BUF_STATE_ERROR);
|
|
|
|
v4l2_pipeline_pm_put(&node->vdev.entity);
|
|
ret = pm_runtime_put(rkisp1->dev);
|
|
if (ret < 0)
|
|
dev_err(rkisp1->dev, "power down failed error:%d\n", ret);
|
|
|
|
rkisp1_dummy_buf_destroy(cap);
|
|
|
|
media_pipeline_stop(&node->vdev.entity);
|
|
|
|
mutex_unlock(&cap->rkisp1->stream_lock);
|
|
}
|
|
|
|
static int
|
|
rkisp1_vb2_start_streaming(struct vb2_queue *queue, unsigned int count)
|
|
{
|
|
struct rkisp1_capture *cap = queue->drv_priv;
|
|
struct media_entity *entity = &cap->vnode.vdev.entity;
|
|
int ret;
|
|
|
|
mutex_lock(&cap->rkisp1->stream_lock);
|
|
|
|
ret = media_pipeline_start(entity, &cap->rkisp1->pipe);
|
|
if (ret) {
|
|
dev_err(cap->rkisp1->dev, "start pipeline failed %d\n", ret);
|
|
goto err_ret_buffers;
|
|
}
|
|
|
|
ret = rkisp1_dummy_buf_create(cap);
|
|
if (ret)
|
|
goto err_pipeline_stop;
|
|
|
|
ret = pm_runtime_resume_and_get(cap->rkisp1->dev);
|
|
if (ret < 0) {
|
|
dev_err(cap->rkisp1->dev, "power up failed %d\n", ret);
|
|
goto err_destroy_dummy;
|
|
}
|
|
ret = v4l2_pipeline_pm_get(entity);
|
|
if (ret) {
|
|
dev_err(cap->rkisp1->dev, "open cif pipeline failed %d\n", ret);
|
|
goto err_pipe_pm_put;
|
|
}
|
|
|
|
ret = rkisp1_pipeline_stream_enable(cap);
|
|
if (ret)
|
|
goto err_v4l2_pm_put;
|
|
|
|
mutex_unlock(&cap->rkisp1->stream_lock);
|
|
|
|
return 0;
|
|
|
|
err_v4l2_pm_put:
|
|
v4l2_pipeline_pm_put(entity);
|
|
err_pipe_pm_put:
|
|
pm_runtime_put(cap->rkisp1->dev);
|
|
err_destroy_dummy:
|
|
rkisp1_dummy_buf_destroy(cap);
|
|
err_pipeline_stop:
|
|
media_pipeline_stop(entity);
|
|
err_ret_buffers:
|
|
rkisp1_return_all_buffers(cap, VB2_BUF_STATE_QUEUED);
|
|
mutex_unlock(&cap->rkisp1->stream_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct vb2_ops rkisp1_vb2_ops = {
|
|
.queue_setup = rkisp1_vb2_queue_setup,
|
|
.buf_init = rkisp1_vb2_buf_init,
|
|
.buf_queue = rkisp1_vb2_buf_queue,
|
|
.buf_prepare = rkisp1_vb2_buf_prepare,
|
|
.wait_prepare = vb2_ops_wait_prepare,
|
|
.wait_finish = vb2_ops_wait_finish,
|
|
.stop_streaming = rkisp1_vb2_stop_streaming,
|
|
.start_streaming = rkisp1_vb2_start_streaming,
|
|
};
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* IOCTLs operations
|
|
*/
|
|
|
|
static const struct v4l2_format_info *
|
|
rkisp1_fill_pixfmt(struct v4l2_pix_format_mplane *pixm,
|
|
enum rkisp1_stream_id id)
|
|
{
|
|
struct v4l2_plane_pix_format *plane_y = &pixm->plane_fmt[0];
|
|
const struct v4l2_format_info *info;
|
|
unsigned int i;
|
|
u32 stride;
|
|
|
|
memset(pixm->plane_fmt, 0, sizeof(pixm->plane_fmt));
|
|
info = v4l2_format_info(pixm->pixelformat);
|
|
pixm->num_planes = info->mem_planes;
|
|
stride = info->bpp[0] * pixm->width;
|
|
/* Self path supports custom stride but Main path doesn't */
|
|
if (id == RKISP1_MAINPATH || plane_y->bytesperline < stride)
|
|
plane_y->bytesperline = stride;
|
|
plane_y->sizeimage = plane_y->bytesperline * pixm->height;
|
|
|
|
/* normalize stride to pixels per line */
|
|
stride = DIV_ROUND_UP(plane_y->bytesperline, info->bpp[0]);
|
|
|
|
for (i = 1; i < info->comp_planes; i++) {
|
|
struct v4l2_plane_pix_format *plane = &pixm->plane_fmt[i];
|
|
|
|
/* bytesperline for other components derive from Y component */
|
|
plane->bytesperline = DIV_ROUND_UP(stride, info->hdiv) *
|
|
info->bpp[i];
|
|
plane->sizeimage = plane->bytesperline *
|
|
DIV_ROUND_UP(pixm->height, info->vdiv);
|
|
}
|
|
|
|
/*
|
|
* If pixfmt is packed, then plane_fmt[0] should contain the total size
|
|
* considering all components. plane_fmt[i] for i > 0 should be ignored
|
|
* by userspace as mem_planes == 1, but we are keeping information there
|
|
* for convenience.
|
|
*/
|
|
if (info->mem_planes == 1)
|
|
for (i = 1; i < info->comp_planes; i++)
|
|
plane_y->sizeimage += pixm->plane_fmt[i].sizeimage;
|
|
|
|
return info;
|
|
}
|
|
|
|
static const struct rkisp1_capture_fmt_cfg *
|
|
rkisp1_find_fmt_cfg(const struct rkisp1_capture *cap, const u32 pixelfmt)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < cap->config->fmt_size; i++) {
|
|
if (cap->config->fmts[i].fourcc == pixelfmt)
|
|
return &cap->config->fmts[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void rkisp1_try_fmt(const struct rkisp1_capture *cap,
|
|
struct v4l2_pix_format_mplane *pixm,
|
|
const struct rkisp1_capture_fmt_cfg **fmt_cfg,
|
|
const struct v4l2_format_info **fmt_info)
|
|
{
|
|
const struct rkisp1_capture_config *config = cap->config;
|
|
const struct rkisp1_capture_fmt_cfg *fmt;
|
|
const struct v4l2_format_info *info;
|
|
const unsigned int max_widths[] = { RKISP1_RSZ_MP_SRC_MAX_WIDTH,
|
|
RKISP1_RSZ_SP_SRC_MAX_WIDTH };
|
|
const unsigned int max_heights[] = { RKISP1_RSZ_MP_SRC_MAX_HEIGHT,
|
|
RKISP1_RSZ_SP_SRC_MAX_HEIGHT};
|
|
|
|
fmt = rkisp1_find_fmt_cfg(cap, pixm->pixelformat);
|
|
if (!fmt) {
|
|
fmt = config->fmts;
|
|
pixm->pixelformat = fmt->fourcc;
|
|
}
|
|
|
|
pixm->width = clamp_t(u32, pixm->width,
|
|
RKISP1_RSZ_SRC_MIN_WIDTH, max_widths[cap->id]);
|
|
pixm->height = clamp_t(u32, pixm->height,
|
|
RKISP1_RSZ_SRC_MIN_HEIGHT, max_heights[cap->id]);
|
|
|
|
pixm->field = V4L2_FIELD_NONE;
|
|
pixm->colorspace = V4L2_COLORSPACE_DEFAULT;
|
|
pixm->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
|
|
pixm->quantization = V4L2_QUANTIZATION_DEFAULT;
|
|
|
|
info = rkisp1_fill_pixfmt(pixm, cap->id);
|
|
|
|
if (fmt_cfg)
|
|
*fmt_cfg = fmt;
|
|
if (fmt_info)
|
|
*fmt_info = info;
|
|
}
|
|
|
|
static void rkisp1_set_fmt(struct rkisp1_capture *cap,
|
|
struct v4l2_pix_format_mplane *pixm)
|
|
{
|
|
rkisp1_try_fmt(cap, pixm, &cap->pix.cfg, &cap->pix.info);
|
|
cap->pix.fmt = *pixm;
|
|
|
|
/* SP supports custom stride in number of pixels of the Y plane */
|
|
if (cap->id == RKISP1_SELFPATH)
|
|
cap->sp_y_stride = pixm->plane_fmt[0].bytesperline /
|
|
cap->pix.info->bpp[0];
|
|
}
|
|
|
|
static int rkisp1_try_fmt_vid_cap_mplane(struct file *file, void *fh,
|
|
struct v4l2_format *f)
|
|
{
|
|
struct rkisp1_capture *cap = video_drvdata(file);
|
|
|
|
rkisp1_try_fmt(cap, &f->fmt.pix_mp, NULL, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rkisp1_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
|
|
struct v4l2_fmtdesc *f)
|
|
{
|
|
struct rkisp1_capture *cap = video_drvdata(file);
|
|
const struct rkisp1_capture_fmt_cfg *fmt = NULL;
|
|
unsigned int i, n = 0;
|
|
|
|
if (!f->mbus_code) {
|
|
if (f->index >= cap->config->fmt_size)
|
|
return -EINVAL;
|
|
|
|
fmt = &cap->config->fmts[f->index];
|
|
f->pixelformat = fmt->fourcc;
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < cap->config->fmt_size; i++) {
|
|
if (cap->config->fmts[i].mbus != f->mbus_code)
|
|
continue;
|
|
|
|
if (n++ == f->index) {
|
|
f->pixelformat = cap->config->fmts[i].fourcc;
|
|
return 0;
|
|
}
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int rkisp1_s_fmt_vid_cap_mplane(struct file *file,
|
|
void *priv, struct v4l2_format *f)
|
|
{
|
|
struct rkisp1_capture *cap = video_drvdata(file);
|
|
struct rkisp1_vdev_node *node =
|
|
rkisp1_vdev_to_node(&cap->vnode.vdev);
|
|
|
|
if (vb2_is_busy(&node->buf_queue))
|
|
return -EBUSY;
|
|
|
|
rkisp1_set_fmt(cap, &f->fmt.pix_mp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rkisp1_g_fmt_vid_cap_mplane(struct file *file, void *fh,
|
|
struct v4l2_format *f)
|
|
{
|
|
struct rkisp1_capture *cap = video_drvdata(file);
|
|
|
|
f->fmt.pix_mp = cap->pix.fmt;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
rkisp1_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
|
|
{
|
|
struct rkisp1_capture *cap_dev = video_drvdata(file);
|
|
struct rkisp1_device *rkisp1 = cap_dev->rkisp1;
|
|
|
|
strscpy(cap->driver, rkisp1->dev->driver->name, sizeof(cap->driver));
|
|
strscpy(cap->card, rkisp1->dev->driver->name, sizeof(cap->card));
|
|
strscpy(cap->bus_info, RKISP1_BUS_INFO, sizeof(cap->bus_info));
|
|
|
|
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_try_fmt_vid_cap_mplane = rkisp1_try_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_enum_fmt_vid_cap = rkisp1_enum_fmt_vid_cap_mplane,
|
|
.vidioc_querycap = rkisp1_querycap,
|
|
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
|
|
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
|
|
};
|
|
|
|
static int rkisp1_capture_link_validate(struct media_link *link)
|
|
{
|
|
struct video_device *vdev =
|
|
media_entity_to_video_device(link->sink->entity);
|
|
struct v4l2_subdev *sd =
|
|
media_entity_to_v4l2_subdev(link->source->entity);
|
|
struct rkisp1_capture *cap = video_get_drvdata(vdev);
|
|
const struct rkisp1_capture_fmt_cfg *fmt =
|
|
rkisp1_find_fmt_cfg(cap, cap->pix.fmt.pixelformat);
|
|
struct v4l2_subdev_format sd_fmt;
|
|
int ret;
|
|
|
|
sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
|
sd_fmt.pad = link->source->index;
|
|
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (sd_fmt.format.height != cap->pix.fmt.height ||
|
|
sd_fmt.format.width != cap->pix.fmt.width ||
|
|
sd_fmt.format.code != fmt->mbus)
|
|
return -EPIPE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* core functions
|
|
*/
|
|
|
|
static const struct media_entity_operations rkisp1_media_ops = {
|
|
.link_validate = rkisp1_capture_link_validate,
|
|
};
|
|
|
|
static const struct v4l2_file_operations rkisp1_fops = {
|
|
.open = v4l2_fh_open,
|
|
.release = vb2_fop_release,
|
|
.unlocked_ioctl = video_ioctl2,
|
|
.poll = vb2_fop_poll,
|
|
.mmap = vb2_fop_mmap,
|
|
};
|
|
|
|
static void rkisp1_unregister_capture(struct rkisp1_capture *cap)
|
|
{
|
|
media_entity_cleanup(&cap->vnode.vdev.entity);
|
|
vb2_video_unregister_device(&cap->vnode.vdev);
|
|
}
|
|
|
|
void rkisp1_capture_devs_unregister(struct rkisp1_device *rkisp1)
|
|
{
|
|
struct rkisp1_capture *mp = &rkisp1->capture_devs[RKISP1_MAINPATH];
|
|
struct rkisp1_capture *sp = &rkisp1->capture_devs[RKISP1_SELFPATH];
|
|
|
|
rkisp1_unregister_capture(mp);
|
|
rkisp1_unregister_capture(sp);
|
|
}
|
|
|
|
static int rkisp1_register_capture(struct rkisp1_capture *cap)
|
|
{
|
|
const char * const dev_names[] = {RKISP1_MP_DEV_NAME,
|
|
RKISP1_SP_DEV_NAME};
|
|
struct v4l2_device *v4l2_dev = &cap->rkisp1->v4l2_dev;
|
|
struct video_device *vdev = &cap->vnode.vdev;
|
|
struct rkisp1_vdev_node *node;
|
|
struct vb2_queue *q;
|
|
int ret;
|
|
|
|
strscpy(vdev->name, dev_names[cap->id], sizeof(vdev->name));
|
|
node = rkisp1_vdev_to_node(vdev);
|
|
mutex_init(&node->vlock);
|
|
|
|
vdev->ioctl_ops = &rkisp1_v4l2_ioctl_ops;
|
|
vdev->release = video_device_release_empty;
|
|
vdev->fops = &rkisp1_fops;
|
|
vdev->minor = -1;
|
|
vdev->v4l2_dev = v4l2_dev;
|
|
vdev->lock = &node->vlock;
|
|
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
|
|
V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
|
|
vdev->entity.ops = &rkisp1_media_ops;
|
|
video_set_drvdata(vdev, cap);
|
|
vdev->vfl_dir = VFL_DIR_RX;
|
|
node->pad.flags = MEDIA_PAD_FL_SINK;
|
|
|
|
q = &node->buf_queue;
|
|
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
|
q->io_modes = VB2_MMAP | VB2_DMABUF;
|
|
q->drv_priv = cap;
|
|
q->ops = &rkisp1_vb2_ops;
|
|
q->mem_ops = &vb2_dma_contig_memops;
|
|
q->buf_struct_size = sizeof(struct rkisp1_buffer);
|
|
q->min_buffers_needed = RKISP1_MIN_BUFFERS_NEEDED;
|
|
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
|
|
q->lock = &node->vlock;
|
|
q->dev = cap->rkisp1->dev;
|
|
ret = vb2_queue_init(q);
|
|
if (ret) {
|
|
dev_err(cap->rkisp1->dev,
|
|
"vb2 queue init failed (err=%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
vdev->queue = q;
|
|
|
|
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
|
|
if (ret) {
|
|
dev_err(cap->rkisp1->dev,
|
|
"failed to register %s, ret=%d\n", vdev->name, ret);
|
|
return ret;
|
|
}
|
|
v4l2_info(v4l2_dev, "registered %s as /dev/video%d\n", vdev->name,
|
|
vdev->num);
|
|
|
|
ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
|
|
if (ret) {
|
|
video_unregister_device(vdev);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
rkisp1_capture_init(struct rkisp1_device *rkisp1, enum rkisp1_stream_id id)
|
|
{
|
|
struct rkisp1_capture *cap = &rkisp1->capture_devs[id];
|
|
struct v4l2_pix_format_mplane pixm;
|
|
|
|
memset(cap, 0, sizeof(*cap));
|
|
cap->id = id;
|
|
cap->rkisp1 = rkisp1;
|
|
|
|
INIT_LIST_HEAD(&cap->buf.queue);
|
|
init_waitqueue_head(&cap->done);
|
|
spin_lock_init(&cap->buf.lock);
|
|
if (cap->id == RKISP1_SELFPATH) {
|
|
cap->ops = &rkisp1_capture_ops_sp;
|
|
cap->config = &rkisp1_capture_config_sp;
|
|
} else {
|
|
cap->ops = &rkisp1_capture_ops_mp;
|
|
cap->config = &rkisp1_capture_config_mp;
|
|
}
|
|
|
|
cap->is_streaming = false;
|
|
|
|
memset(&pixm, 0, sizeof(pixm));
|
|
pixm.pixelformat = V4L2_PIX_FMT_YUYV;
|
|
pixm.width = RKISP1_DEFAULT_WIDTH;
|
|
pixm.height = RKISP1_DEFAULT_HEIGHT;
|
|
rkisp1_set_fmt(cap, &pixm);
|
|
}
|
|
|
|
int rkisp1_capture_devs_register(struct rkisp1_device *rkisp1)
|
|
{
|
|
struct rkisp1_capture *cap;
|
|
unsigned int i, j;
|
|
int ret;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(rkisp1->capture_devs); i++) {
|
|
rkisp1_capture_init(rkisp1, i);
|
|
cap = &rkisp1->capture_devs[i];
|
|
cap->rkisp1 = rkisp1;
|
|
ret = rkisp1_register_capture(cap);
|
|
if (ret)
|
|
goto err_unreg_capture_devs;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_unreg_capture_devs:
|
|
for (j = 0; j < i; j++) {
|
|
cap = &rkisp1->capture_devs[j];
|
|
rkisp1_unregister_capture(cap);
|
|
}
|
|
|
|
return ret;
|
|
}
|