// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
/*
* Copyright (C) 2024 Amlogic, Inc. All rights reserved
*/
#include <linux/media/amlogic/c3-isp-config.h>
#include <linux/pm_runtime.h>
#include <media/v4l2-event.h>
#include "c3-isp-common.h"
#include "c3-isp-regs.h"
#define C3_ISP_CORE_SUBDEV_NAME "c3-isp-core"
#define C3_ISP_PHASE_OFFSET_0 0
#define C3_ISP_PHASE_OFFSET_1 1
#define C3_ISP_PHASE_OFFSET_NONE 0xff
#define C3_ISP_CORE_DEF_SINK_PAD_FMT MEDIA_BUS_FMT_SRGGB10_1X10
#define C3_ISP_CORE_DEF_SRC_PAD_FMT MEDIA_BUS_FMT_YUV10_1X30
/*
* struct c3_isp_core_format_info - ISP core format information
*
* @mbus_code: the mbus code
* @pads: bitmask detailing valid pads for this mbus_code
* @xofst: horizontal phase offset of hardware
* @yofst: vertical phase offset of hardware
* @is_raw: the raw format flag of mbus code
*/
struct c3_isp_core_format_info {
u32 mbus_code;
u32 pads;
u8 xofst;
u8 yofst;
bool is_raw;
};
static const struct c3_isp_core_format_info c3_isp_core_fmts[] = {
/* RAW formats */
{
.mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
.pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
.xofst = C3_ISP_PHASE_OFFSET_0,
.yofst = C3_ISP_PHASE_OFFSET_1,
.is_raw = true ,
}, {
.mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
.pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
.xofst = C3_ISP_PHASE_OFFSET_1,
.yofst = C3_ISP_PHASE_OFFSET_1,
.is_raw = true ,
}, {
.mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
.pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
.xofst = C3_ISP_PHASE_OFFSET_0,
.yofst = C3_ISP_PHASE_OFFSET_0,
.is_raw = true ,
}, {
.mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
.pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
.xofst = C3_ISP_PHASE_OFFSET_1,
.yofst = C3_ISP_PHASE_OFFSET_0,
.is_raw = true ,
}, {
.mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
.pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
.xofst = C3_ISP_PHASE_OFFSET_0,
.yofst = C3_ISP_PHASE_OFFSET_1,
.is_raw = true ,
}, {
.mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
.pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
.xofst = C3_ISP_PHASE_OFFSET_1,
.yofst = C3_ISP_PHASE_OFFSET_1,
.is_raw = true ,
}, {
.mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
.pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
.xofst = C3_ISP_PHASE_OFFSET_0,
.yofst = C3_ISP_PHASE_OFFSET_0,
.is_raw = true ,
}, {
.mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
.pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
.xofst = C3_ISP_PHASE_OFFSET_1,
.yofst = C3_ISP_PHASE_OFFSET_0,
.is_raw = true ,
}, {
.mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16,
.pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0)
| BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1)
| BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
.xofst = C3_ISP_PHASE_OFFSET_NONE,
.yofst = C3_ISP_PHASE_OFFSET_NONE,
.is_raw = true ,
}, {
.mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16,
.pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0)
| BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1)
| BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
.xofst = C3_ISP_PHASE_OFFSET_NONE,
.yofst = C3_ISP_PHASE_OFFSET_NONE,
.is_raw = true ,
}, {
.mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16,
.pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0)
| BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1)
| BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
.xofst = C3_ISP_PHASE_OFFSET_NONE,
.yofst = C3_ISP_PHASE_OFFSET_NONE,
.is_raw = true ,
}, {
.mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16,
.pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0)
| BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1)
| BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
.xofst = C3_ISP_PHASE_OFFSET_NONE,
.yofst = C3_ISP_PHASE_OFFSET_NONE,
.is_raw = true ,
},
/* YUV formats */
{
.mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
.pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0) |
BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1) |
BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
.xofst = C3_ISP_PHASE_OFFSET_NONE,
.yofst = C3_ISP_PHASE_OFFSET_NONE,
.is_raw = false ,
},
};
static const struct c3_isp_core_format_info
*core_find_format_by_code(u32 code, u32 pad)
{
for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_core_fmts); i++) {
const struct c3_isp_core_format_info *info =
&c3_isp_core_fmts[i];
if (info->mbus_code == code && info->pads & BIT(pad))
return info;
}
return NULL;
}
static const struct c3_isp_core_format_info
*core_find_format_by_index(u32 index, u32 pad)
{
for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_core_fmts); i++) {
const struct c3_isp_core_format_info *info =
&c3_isp_core_fmts[i];
if (!(info->pads & BIT(pad)))
continue ;
if (!index)
return info;
index--;
}
return NULL;
}
static void c3_isp_core_enable(struct c3_isp_device *isp)
{
c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_END_MASK,
ISP_TOP_IRQ_EN_FRM_END_EN);
c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_RST_MASK,
ISP_TOP_IRQ_EN_FRM_RST_EN);
/* Enable image data to ISP core */
c3_isp_update_bits(isp, ISP_TOP_PATH_SEL, ISP_TOP_PATH_SEL_CORE_MASK,
ISP_TOP_PATH_SEL_CORE_MIPI_CORE);
}
static void c3_isp_core_disable(struct c3_isp_device *isp)
{
/* Disable image data to ISP core */
c3_isp_update_bits(isp, ISP_TOP_PATH_SEL, ISP_TOP_PATH_SEL_CORE_MASK,
ISP_TOP_PATH_SEL_CORE_CORE_DIS);
c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_END_MASK,
ISP_TOP_IRQ_EN_FRM_END_DIS);
c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_RST_MASK,
ISP_TOP_IRQ_EN_FRM_RST_DIS);
}
/* Set the phase offset of blc, wb and lns */
static void c3_isp_core_lswb_ofst(struct c3_isp_device *isp,
u8 xofst, u8 yofst)
{
c3_isp_update_bits(isp, ISP_LSWB_BLC_PHSOFST,
ISP_LSWB_BLC_PHSOFST_HORIZ_OFST_MASK,
ISP_LSWB_BLC_PHSOFST_HORIZ_OFST(xofst));
c3_isp_update_bits(isp, ISP_LSWB_BLC_PHSOFST,
ISP_LSWB_BLC_PHSOFST_VERT_OFST_MASK,
ISP_LSWB_BLC_PHSOFST_VERT_OFST(yofst));
c3_isp_update_bits(isp, ISP_LSWB_WB_PHSOFST,
ISP_LSWB_WB_PHSOFST_HORIZ_OFST_MASK,
ISP_LSWB_WB_PHSOFST_HORIZ_OFST(xofst));
c3_isp_update_bits(isp, ISP_LSWB_WB_PHSOFST,
ISP_LSWB_WB_PHSOFST_VERT_OFST_MASK,
ISP_LSWB_WB_PHSOFST_VERT_OFST(yofst));
c3_isp_update_bits(isp, ISP_LSWB_LNS_PHSOFST,
ISP_LSWB_LNS_PHSOFST_HORIZ_OFST_MASK,
ISP_LSWB_LNS_PHSOFST_HORIZ_OFST(xofst));
c3_isp_update_bits(isp, ISP_LSWB_LNS_PHSOFST,
ISP_LSWB_LNS_PHSOFST_VERT_OFST_MASK,
ISP_LSWB_LNS_PHSOFST_VERT_OFST(yofst));
}
/* Set the phase offset of af, ae and awb */
static void c3_isp_core_3a_ofst(struct c3_isp_device *isp,
u8 xofst, u8 yofst)
{
c3_isp_update_bits(isp, ISP_AF_CTRL, ISP_AF_CTRL_HORIZ_OFST_MASK,
ISP_AF_CTRL_HORIZ_OFST(xofst));
c3_isp_update_bits(isp, ISP_AF_CTRL, ISP_AF_CTRL_VERT_OFST_MASK,
ISP_AF_CTRL_VERT_OFST(yofst));
c3_isp_update_bits(isp, ISP_AE_CTRL, ISP_AE_CTRL_HORIZ_OFST_MASK,
ISP_AE_CTRL_HORIZ_OFST(xofst));
c3_isp_update_bits(isp, ISP_AE_CTRL, ISP_AE_CTRL_VERT_OFST_MASK,
ISP_AE_CTRL_VERT_OFST(yofst));
c3_isp_update_bits(isp, ISP_AWB_CTRL, ISP_AWB_CTRL_HORIZ_OFST_MASK,
ISP_AWB_CTRL_HORIZ_OFST(xofst));
c3_isp_update_bits(isp, ISP_AWB_CTRL, ISP_AWB_CTRL_VERT_OFST_MASK,
ISP_AWB_CTRL_VERT_OFST(yofst));
}
/* Set the phase offset of demosaic */
static void c3_isp_core_dms_ofst(struct c3_isp_device *isp,
u8 xofst, u8 yofst)
{
c3_isp_update_bits(isp, ISP_DMS_COMMON_PARAM0,
ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST_MASK,
ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST(xofst));
c3_isp_update_bits(isp, ISP_DMS_COMMON_PARAM0,
ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST_MASK,
ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST(yofst));
}
static void c3_isp_core_cfg_format(struct c3_isp_device *isp,
struct v4l2_subdev_state *state)
{
struct v4l2_mbus_framefmt *fmt;
const struct c3_isp_core_format_info *isp_fmt;
fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SINK_VIDEO);
isp_fmt = core_find_format_by_code(fmt->code,
C3_ISP_CORE_PAD_SINK_VIDEO);
c3_isp_write(isp, ISP_TOP_INPUT_SIZE,
ISP_TOP_INPUT_SIZE_HORIZ_SIZE(fmt->width) |
ISP_TOP_INPUT_SIZE_VERT_SIZE(fmt->height));
c3_isp_write(isp, ISP_TOP_FRM_SIZE,
ISP_TOP_FRM_SIZE_CORE_HORIZ_SIZE(fmt->width) |
ISP_TOP_FRM_SIZE_CORE_VERT_SIZE(fmt->height));
c3_isp_update_bits(isp, ISP_TOP_HOLD_SIZE,
ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE_MASK,
ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE(fmt->width));
c3_isp_write(isp, ISP_AF_HV_SIZE,
ISP_AF_HV_SIZE_GLB_WIN_XSIZE(fmt->width) |
ISP_AF_HV_SIZE_GLB_WIN_YSIZE(fmt->height));
c3_isp_write(isp, ISP_AE_HV_SIZE,
ISP_AE_HV_SIZE_HORIZ_SIZE(fmt->width) |
ISP_AE_HV_SIZE_VERT_SIZE(fmt->height));
c3_isp_write(isp, ISP_AWB_HV_SIZE,
ISP_AWB_HV_SIZE_HORIZ_SIZE(fmt->width) |
ISP_AWB_HV_SIZE_VERT_SIZE(fmt->height));
c3_isp_core_lswb_ofst(isp, isp_fmt->xofst, isp_fmt->yofst);
c3_isp_core_3a_ofst(isp, isp_fmt->xofst, isp_fmt->yofst);
c3_isp_core_dms_ofst(isp, isp_fmt->xofst, isp_fmt->yofst);
}
static bool c3_isp_core_streams_ready(struct c3_isp_core *core)
{
unsigned int n_links = 0;
struct media_link *link;
for_each_media_entity_data_link(&core->sd.entity, link) {
if ((link->source->index == C3_ISP_CORE_PAD_SOURCE_VIDEO_0 ||
link->source->index == C3_ISP_CORE_PAD_SOURCE_VIDEO_1 ||
link->source->index == C3_ISP_CORE_PAD_SOURCE_VIDEO_2) &&
link->flags == MEDIA_LNK_FL_ENABLED)
n_links++;
}
return n_links == core->isp->pipe.start_count;
}
static int c3_isp_core_enable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
u32 pad, u64 streams_mask)
{
struct c3_isp_core *core = v4l2_get_subdevdata(sd);
struct media_pad *sink_pad;
struct v4l2_subdev *src_sd;
int ret;
if (!c3_isp_core_streams_ready(core))
return 0;
core->isp->frm_sequence = 0;
c3_isp_core_cfg_format(core->isp, state);
c3_isp_core_enable(core->isp);
sink_pad = &core->pads[C3_ISP_CORE_PAD_SINK_VIDEO];
core->src_pad = media_pad_remote_pad_unique(sink_pad);
if (IS_ERR(core->src_pad)) {
dev_dbg(core->isp->dev,
"Failed to get source pad for ISP core\n" );
return -EPIPE;
}
src_sd = media_entity_to_v4l2_subdev(core->src_pad->entity);
ret = v4l2_subdev_enable_streams(src_sd, core->src_pad->index, BIT(0));
if (ret) {
c3_isp_core_disable(core->isp);
return ret;
}
return 0;
}
static int c3_isp_core_disable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
u32 pad, u64 streams_mask)
{
struct c3_isp_core *core = v4l2_get_subdevdata(sd);
struct v4l2_subdev *src_sd;
if (core->isp->pipe.start_count != 1)
return 0;
if (core->src_pad) {
src_sd = media_entity_to_v4l2_subdev(core->src_pad->entity);
v4l2_subdev_disable_streams(src_sd, core->src_pad->index,
BIT(0));
}
core->src_pad = NULL;
c3_isp_core_disable(core->isp);
return 0;
}
static int c3_isp_core_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum *code)
{
const struct c3_isp_core_format_info *info;
switch (code->pad) {
case C3_ISP_CORE_PAD_SINK_VIDEO:
case C3_ISP_CORE_PAD_SOURCE_VIDEO_0:
case C3_ISP_CORE_PAD_SOURCE_VIDEO_1:
case C3_ISP_CORE_PAD_SOURCE_VIDEO_2:
info = core_find_format_by_index(code->index, code->pad);
if (!info)
return -EINVAL;
code->code = info->mbus_code;
break ;
case C3_ISP_CORE_PAD_SINK_PARAMS:
case C3_ISP_CORE_PAD_SOURCE_STATS:
if (code->index)
return -EINVAL;
code->code = MEDIA_BUS_FMT_METADATA_FIXED;
break ;
default :
return -EINVAL;
}
return 0;
}
static void c3_isp_core_set_sink_fmt(struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
struct v4l2_mbus_framefmt *sink_fmt;
struct v4l2_mbus_framefmt *src_fmt;
const struct c3_isp_core_format_info *isp_fmt;
sink_fmt = v4l2_subdev_state_get_format(state, format->pad);
isp_fmt = core_find_format_by_code(format->format.code, format->pad);
if (!isp_fmt)
sink_fmt->code = C3_ISP_CORE_DEF_SINK_PAD_FMT;
else
sink_fmt->code = format->format.code;
sink_fmt->width = clamp_t(u32, format->format.width,
C3_ISP_MIN_WIDTH, C3_ISP_MAX_WIDTH);
sink_fmt->height = clamp_t(u32, format->format.height,
C3_ISP_MIN_HEIGHT, C3_ISP_MAX_HEIGHT);
sink_fmt->field = V4L2_FIELD_NONE;
sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
for (unsigned int i = C3_ISP_CORE_PAD_SOURCE_VIDEO_0;
i < C3_ISP_CORE_PAD_MAX; i++) {
src_fmt = v4l2_subdev_state_get_format(state, i);
src_fmt->width = sink_fmt->width;
src_fmt->height = sink_fmt->height;
}
format->format = *sink_fmt;
}
static void c3_isp_core_set_source_fmt(struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
const struct c3_isp_core_format_info *isp_fmt;
struct v4l2_mbus_framefmt *src_fmt;
struct v4l2_mbus_framefmt *sink_fmt;
sink_fmt = v4l2_subdev_state_get_format(state,
C3_ISP_CORE_PAD_SINK_VIDEO);
src_fmt = v4l2_subdev_state_get_format(state, format->pad);
isp_fmt = core_find_format_by_code(format->format.code, format->pad);
if (!isp_fmt)
src_fmt->code = C3_ISP_CORE_DEF_SRC_PAD_FMT;
else
src_fmt->code = format->format.code;
src_fmt->width = sink_fmt->width;
src_fmt->height = sink_fmt->height;
src_fmt->field = V4L2_FIELD_NONE;
src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
if (isp_fmt && isp_fmt->is_raw) {
src_fmt->colorspace = V4L2_COLORSPACE_RAW;
src_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
src_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
} else {
src_fmt->colorspace = V4L2_COLORSPACE_SRGB;
src_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
}
format->format = *src_fmt;
}
static int c3_isp_core_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
if (format->pad == C3_ISP_CORE_PAD_SINK_VIDEO)
c3_isp_core_set_sink_fmt(state, format);
else if (format->pad == C3_ISP_CORE_PAD_SOURCE_VIDEO_0 ||
format->pad == C3_ISP_CORE_PAD_SOURCE_VIDEO_1 ||
format->pad == C3_ISP_CORE_PAD_SOURCE_VIDEO_2)
c3_isp_core_set_source_fmt(state, format);
else
format->format =
*v4l2_subdev_state_get_format(state, format->pad);
return 0;
}
static int c3_isp_core_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
struct v4l2_mbus_framefmt *fmt;
/* Video sink pad */
fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SINK_VIDEO);
fmt->width = C3_ISP_DEFAULT_WIDTH;
fmt->height = C3_ISP_DEFAULT_HEIGHT;
fmt->field = V4L2_FIELD_NONE;
fmt->code = C3_ISP_CORE_DEF_SINK_PAD_FMT;
fmt->colorspace = V4L2_COLORSPACE_RAW;
fmt->xfer_func = V4L2_XFER_FUNC_NONE;
fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
/* Video source pad */
for (unsigned int i = C3_ISP_CORE_PAD_SOURCE_VIDEO_0;
i < C3_ISP_CORE_PAD_MAX; i++) {
fmt = v4l2_subdev_state_get_format(state, i);
fmt->width = C3_ISP_DEFAULT_WIDTH;
fmt->height = C3_ISP_DEFAULT_HEIGHT;
fmt->field = V4L2_FIELD_NONE;
fmt->code = C3_ISP_CORE_DEF_SRC_PAD_FMT;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
}
/* Parameters pad */
fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SINK_PARAMS);
fmt->width = 0;
fmt->height = 0;
fmt->field = V4L2_FIELD_NONE;
fmt->code = MEDIA_BUS_FMT_METADATA_FIXED;
/* Statistics pad */
fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SOURCE_STATS);
fmt->width = 0;
fmt->height = 0;
fmt->field = V4L2_FIELD_NONE;
fmt->code = MEDIA_BUS_FMT_METADATA_FIXED;
return 0;
}
static int c3_isp_core_subscribe_event(struct v4l2_subdev *sd,
struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
if (sub->type != V4L2_EVENT_FRAME_SYNC)
return -EINVAL;
/* V4L2_EVENT_FRAME_SYNC doesn't need id, so should set 0 */
if (sub->id != 0)
return -EINVAL;
return v4l2_event_subscribe(fh, sub, 0, NULL);
}
static const struct v4l2_subdev_pad_ops c3_isp_core_pad_ops = {
.enum_mbus_code = c3_isp_core_enum_mbus_code,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = c3_isp_core_set_fmt,
.enable_streams = c3_isp_core_enable_streams,
.disable_streams = c3_isp_core_disable_streams,
};
static const struct v4l2_subdev_core_ops c3_isp_core_core_ops = {
.subscribe_event = c3_isp_core_subscribe_event,
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
static const struct v4l2_subdev_ops c3_isp_core_subdev_ops = {
.core = &c3_isp_core_core_ops,
.pad = &c3_isp_core_pad_ops,
};
static const struct v4l2_subdev_internal_ops c3_isp_core_internal_ops = {
.init_state = c3_isp_core_init_state,
};
static int c3_isp_core_link_validate(struct media_link *link)
{
if (link->sink->index == C3_ISP_CORE_PAD_SINK_PARAMS)
return 0;
return v4l2_subdev_link_validate(link);
}
/* Media entity operations */
static const struct media_entity_operations c3_isp_core_entity_ops = {
.link_validate = c3_isp_core_link_validate,
};
void c3_isp_core_queue_sof(struct c3_isp_device *isp)
{
struct v4l2_event event = {
.type = V4L2_EVENT_FRAME_SYNC,
};
event.u.frame_sync.frame_sequence = isp->frm_sequence;
v4l2_event_queue(isp->core.sd.devnode, &event);
}
int c3_isp_core_register(struct c3_isp_device *isp)
{
struct c3_isp_core *core = &isp->core;
struct v4l2_subdev *sd = &core->sd;
int ret;
v4l2_subdev_init(sd, &c3_isp_core_subdev_ops);
sd->owner = THIS_MODULE;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
sd->internal_ops = &c3_isp_core_internal_ops;
snprintf(sd->name, sizeof (sd->name), "%s" , C3_ISP_CORE_SUBDEV_NAME);
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
sd->entity.ops = &c3_isp_core_entity_ops;
core->isp = isp;
sd->dev = isp->dev;
v4l2_set_subdevdata(sd, core);
core->pads[C3_ISP_CORE_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK;
core->pads[C3_ISP_CORE_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
core->pads[C3_ISP_CORE_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
core->pads[C3_ISP_CORE_PAD_SOURCE_VIDEO_0].flags = MEDIA_PAD_FL_SOURCE;
core->pads[C3_ISP_CORE_PAD_SOURCE_VIDEO_1].flags = MEDIA_PAD_FL_SOURCE;
core->pads[C3_ISP_CORE_PAD_SOURCE_VIDEO_2].flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&sd->entity, C3_ISP_CORE_PAD_MAX,
core->pads);
if (ret)
return ret;
ret = v4l2_subdev_init_finalize(sd);
if (ret)
goto err_entity_cleanup;
ret = v4l2_device_register_subdev(&isp->v4l2_dev, sd);
if (ret)
goto err_subdev_cleanup;
return 0;
err_subdev_cleanup:
v4l2_subdev_cleanup(sd);
err_entity_cleanup:
media_entity_cleanup(&sd->entity);
return ret;
}
void c3_isp_core_unregister(struct c3_isp_device *isp)
{
struct c3_isp_core *core = &isp->core;
struct v4l2_subdev *sd = &core->sd;
v4l2_device_unregister_subdev(sd);
v4l2_subdev_cleanup(sd);
media_entity_cleanup(&sd->entity);
}
Messung V0.5 C=97 H=91 G=93
¤ Dauer der Verarbeitung: 0.5 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland