struct cfe_node { /* Node id */ enum node_ids id; /* Pointer pointing to current v4l2_buffer */ struct cfe_buffer *cur_frm; /* Pointer pointing to next v4l2_buffer */ struct cfe_buffer *next_frm; /* Used to store current pixel format */ struct v4l2_format vid_fmt; /* Used to store current meta format */ struct v4l2_format meta_fmt; /* Buffer queue used in video-buf */ struct vb2_queue buffer_queue; /* Queue of filled frames */ struct list_head dma_queue; /* lock used to access this structure */ struct mutex lock; /* Identifies video device for this channel */ struct video_device video_dev; /* Pointer to the parent handle */ struct cfe_device *cfe; /* Media pad for this node */ struct media_pad pad; /* Frame-start counter */ unsignedint fs_count; /* Timestamp of the current buffer */
u64 ts;
};
static bool test_any_node(struct cfe_device *cfe, unsigned long cond)
{ for (unsigned int i = 0; i < NUM_NODES; i++) { if (check_state(cfe, cond, i))
return true;
}
return false;
}
static bool test_all_nodes(struct cfe_device *cfe, unsigned long precond,
unsigned long cond)
{ for (unsigned int i = 0; i < NUM_NODES; i++) { if (check_state(cfe, precond, i)) { if (!check_state(cfe, cond, i))
return false;
}
}
return true;
}
static int mipi_cfg_regs_show(struct seq_file *s, void *data)
{
struct cfe_device *cfe = s->private;
int ret;
ret = pm_runtime_resume_and_get(&cfe->pdev->dev); if (ret)
return ret;
/* Format setup functions */
const struct cfe_fmt *find_format_by_code(u32 code)
{ for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { if (formats[i].code == code)
return &formats[i];
}
return NULL;
}
const struct cfe_fmt *find_format_by_pix(u32 pixelformat)
{ for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { if (formats[i].fourcc == pixelformat)
return &formats[i];
}
return NULL;
}
static const struct cfe_fmt *find_format_by_code_and_fourcc(u32 code,
u32 fourcc)
{ for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) { if (formats[i].code == code && formats[i].fourcc == fourcc)
return &formats[i];
}
return NULL;
}
/*
* Given the mbus code, find the 16 bit remapped code. Returns 0if no remap
* possible.
*/
u32 cfe_find_16bit_code(u32 code)
{
const struct cfe_fmt *cfe_fmt;
cfe_fmt = find_format_by_code(code);
if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_16BIT])
return 0;
cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_16BIT]); if (!cfe_fmt)
return 0;
return cfe_fmt->code;
}
/*
* Given the mbus code, find the 8 bit compressed code. Returns 0if no remap
* possible.
*/
u32 cfe_find_compressed_code(u32 code)
{
const struct cfe_fmt *cfe_fmt;
cfe_fmt = find_format_by_code(code);
if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_COMPRESSED])
return 0;
cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_COMPRESSED]); if (!cfe_fmt)
return 0;
/*
* If the sensor is producing unexpected frame event ordering over a
* sustained period of time, guard against the possibility of coming
* here and orphaning the cur_frm if it's not been dequeued already.
* Unfortunately, there is not enough hardware state to tell if this
* may have occurred.
*/ if (WARN(node->cur_frm, "%s: [%s] Orphaned frame at seq %u\n",
__func__, node_desc[node->id].name, node->fs_count))
cfe_process_buffer_complete(node, VB2_BUF_STATE_ERROR);
node->ts = ktime_get_ns(); for (unsigned int i = 0; i < NUM_NODES; i++) { if (!check_state(cfe, NODE_STREAMING, i) || i == node->id)
continue;
/*
* This checks if any other node has seen a FS. If yes, use the
* same timestamp, eventually across all node buffers.
*/ if (cfe->node[i].fs_count >= node->fs_count)
node->ts = cfe->node[i].ts;
/*
* This checks ifall other node have seen a matching FS. If
* yes, we can flag another job to be queued.
*/ if (matching_fs && cfe->node[i].fs_count != node->fs_count)
matching_fs = false;
}
if (matching_fs)
cfe->job_queued = false;
if (node->cur_frm)
node->cur_frm->vb.vb2_buf.timestamp = node->ts;
if (sts & MIPICFG_INT_CSI_DMA)
csi2_isr(&cfe->csi2, sof, eof);
if (sts & MIPICFG_INT_PISP_FE)
pisp_fe_isr(&cfe->fe, sof + CSI2_NUM_CHANNELS,
eof + CSI2_NUM_CHANNELS);
spin_lock(&cfe->state_lock);
for (unsigned int i = 0; i < NUM_NODES; i++) {
struct cfe_node *node = &cfe->node[i];
/*
* The check_state(NODE_STREAMING) is to ensure we do not loop
* over the CSI2_CHx nodes when the FE is active since they
* generate interrupts even though the node is not streaming.
*/ if (!check_state(cfe, NODE_STREAMING, i) || !(sof[i] || eof[i]))
continue;
/*
* There are 3 cases where we could get FS + FE_ACK at
* the same time:
* 1) FE of the current frame, and FS of the next frame.
* 2) FS + FE of the same frame.
* 3) FE of the current frame, and FS + FE of the next
* frame. To handle this, see the sof handler below.
*
* (1) is handled implicitly by the ordering of the FE and FS
* handlers below.
*/ if (eof[i]) {
/*
* The condition below tests for (2). Run the FS handler
* first before the FE handler, both for the current
* frame.
*/ if (sof[i] && !check_state(cfe, FS_INT, i)) {
cfe_sof_isr(node);
sof[i] = false;
}
cfe_eof_isr(node);
}
if (sof[i]) {
/*
* The condition below tests for (3). In such cases, we
* come in here with FS flag set in the node state from
* the previous frame since it only gets cleared in
* cfe_eof_isr(). Handle the FE for the previous
* frame first before the FS handler for the current
* frame.
*/ if (check_state(cfe, FS_INT, node->id) &&
!check_state(cfe, FE_INT, node->id)) {
cfe_dbg(cfe, "%s: [%s] Handling missing previous FE interrupt\n",
__func__, node_desc[node->id].name);
cfe_eof_isr(node);
}
cfe_sof_isr(node);
}
if (!cfe->job_queued && cfe->job_ready)
cfe_prepare_next_job(cfe);
}
state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd);
fmt = v4l2_subdev_state_get_format(state, CSI2_PAD_SINK, 0); if (!fmt)
return -EINVAL;
cfe_fmt = find_format_by_code(fmt->code); if (!cfe_fmt)
return -EINVAL;
*vc = 0;
*dt = cfe_fmt->csi_dt;
return 0;
}
static int cfe_get_vc_dt(struct cfe_device *cfe, unsigned int channel, u8 *vc,
u8 *dt)
{
struct v4l2_mbus_frame_desc remote_desc;
struct v4l2_subdev_state *state;
u32 sink_stream;
unsigned int i;
int ret;
state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd);
ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
CSI2_PAD_FIRST_SOURCE + channel, 0, NULL, &sink_stream); if (ret)
return ret;
ret = v4l2_subdev_call(cfe->source_sd, pad, get_frame_desc,
cfe->source_pad, &remote_desc); if (ret == -ENOIOCTLCMD) {
cfe_dbg(cfe, "source does not support get_frame_desc, use fallback\n");
return cfe_get_vc_dt_fallback(cfe, vc, dt);
} elseif (ret) {
cfe_err(cfe, "Failed to get frame descriptor\n");
return ret;
}
if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
cfe_err(cfe, "Frame descriptor does not describe CSI-2 link");
return -EINVAL;
}
for (i = 0; i < remote_desc.num_entries; i++) { if (remote_desc.entry[i].stream == sink_stream)
break;
}
if (i == remote_desc.num_entries) {
cfe_err(cfe, "Stream %u not found in remote frame desc\n",
sink_stream);
return -EINVAL;
}
/* Must have a valid CSI2 datatype. */
WARN_ON(!fmt->csi_dt);
/*
* Start the associated CSI2 Channel as well.
*
* Must write to the ADDR register to latch the ctrl values
* even if we are connected to the front end. Once running,
* this is handled by the CSI2 AUTO_ARM mode.
*/
csi2_start_channel(&cfe->csi2, cfe->fe_csi2_channel,
CSI2_MODE_FE_STREAMING,
true, false, width, height, vc, dt);
csi2_set_buffer(&cfe->csi2, cfe->fe_csi2_channel, 0, 0, -1);
pisp_fe_start(&cfe->fe);
}
if (is_csi2_node(node)) {
unsigned int width = 0, height = 0;
u8 vc, dt;
ret = cfe_get_vc_dt(cfe, node->id, &vc, &dt); if (ret) { if (start_fe) {
csi2_stop_channel(&cfe->csi2,
cfe->fe_csi2_channel);
pisp_fe_stop(&cfe->fe);
}
state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd);
/*
* v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back
* to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available.
*
* With multistream input there is no single pixel rate, and thus we
* cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which
* causes v4l2_get_link_freq() to return an error if it falls back to
* V4L2_CID_PIXEL_RATE.
*/
fmt = find_format_by_code(source_fmt->code); if (!fmt)
return -EINVAL;
bpp = fmt->depth;
} else {
bpp = 0;
}
link_freq = v4l2_get_link_freq(src_pad, bpp, 2 * cfe->csi2.dphy.active_lanes); if (link_freq < 0)
cfe_err(cfe, "failed to get link freq for subdev '%s'\n",
cfe->source_sd->name);
if (!check_state(cfe, NODE_ENABLED, node->id)) {
cfe_err(cfe, "%s node link is not enabled.\n",
node_desc[node->id].name);
ret = -EINVAL;
goto err_streaming;
}
ret = pm_runtime_resume_and_get(&cfe->pdev->dev); if (ret < 0) {
cfe_err(cfe, "pm_runtime_resume_and_get failed\n");
goto err_streaming;
}
/* When using the Frontend, we must enable the FE_CONFIG node. */ if (is_fe_enabled(cfe) &&
!check_state(cfe, NODE_ENABLED, cfe->node[FE_CONFIG].id)) {
cfe_err(cfe, "FE enabled, but FE_CONFIG node is not\n");
ret = -EINVAL;
goto err_pm_put;
}
ret = media_pipeline_start(&node->pad, &cfe->pipe); if (ret < 0) {
cfe_err(cfe, "Failed to start media pipeline: %d\n", ret);
goto err_pm_put;
}
state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd);
ret = cfe_start_channel(node); if (ret)
goto err_unlock_state;
if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) {
cfe_dbg(cfe, "Streaming on hold, as all nodes are not set to streaming yet\n");
v4l2_subdev_unlock_state(state);
return 0;
}
ret = v4l2_subdev_call(cfe->source_sd, pad, get_mbus_config, 0,
&mbus_config); if (ret < 0 && ret != -ENOIOCTLCMD) {
cfe_err(cfe, "g_mbus_config failed\n");
goto err_clear_inte;
}
cfe->csi2.dphy.active_lanes = mbus_config.bus.mipi_csi2.num_data_lanes; if (!cfe->csi2.dphy.active_lanes)
cfe->csi2.dphy.active_lanes = cfe->csi2.dphy.max_lanes; if (cfe->csi2.dphy.active_lanes > cfe->csi2.dphy.max_lanes) {
cfe_err(cfe, "Device has requested %u data lanes, which is >%u configured in DT\n",
cfe->csi2.dphy.active_lanes, cfe->csi2.dphy.max_lanes);
ret = -EINVAL;
goto err_clear_inte;
}
link_freq = cfe_get_source_link_freq(cfe); if (link_freq < 0)
goto err_clear_inte;
ret = v4l2_subdev_enable_streams(cfe->source_sd, cfe->source_pad,
cfe->streams_mask); if (ret) {
cfe_err(cfe, "stream on failed in subdev\n");
goto err_disable_cfe;
}
if (!node_supports_image_output(node))
return -EINVAL;
/*
* Default to a format that works for both CSI2 and FE.
*/
fmt = find_format_by_pix(f->fmt.pix.pixelformat); if (!fmt)
fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10);
if (source_fmt->width != pix_fmt->width ||
source_fmt->height != pix_fmt->height) {
cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n",
pix_fmt->width, pix_fmt->height,
source_fmt->width, source_fmt->height);
ret = -EINVAL;
goto out;
}
if (source_fmt->width != meta_fmt->width ||
source_fmt->height != meta_fmt->height) {
cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n",
meta_fmt->width, meta_fmt->height,
source_fmt->width, source_fmt->height);
ret = -EINVAL;
goto out;
}
fmt = find_format_by_code_and_fourcc(source_fmt->code,
meta_fmt->dataformat); if (!fmt) {
cfe_err(cfe, "Format mismatch!\n");
ret = -EINVAL;
goto out;
}
}
for (unsigned int i = 0; i < NUM_NODES; i++) { if (link->sink->entity != &cfe->node[i].video_dev.entity &&
link->source->entity != &cfe->node[i].video_dev.entity)
continue;
if (!node_supports_image(node)) {
v4l2_disable_ioctl(&node->video_dev,
VIDIOC_ENUM_FRAMEINTERVALS);
v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES);
}
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) {
cfe_err(cfe, "Unable to register video device %s\n",
vdev->name);
return ret;
}
cfe_info(cfe, "Registered [%s] node id %d as /dev/video%u\n",
vdev->name, id, vdev->num);
/*
* Acquire a reference to cfe, which will be released when the video
* device will be unregistered and userspace will have closed all open
* file handles.
*/
cfe_get(cfe);
set_state(cfe, NODE_REGISTERED, id);
return 0;
}
static void cfe_unregister_nodes(struct cfe_device *cfe)
{ for (unsigned int i = 0; i < NUM_NODES; i++) {
struct cfe_node *node = &cfe->node[i];
static int cfe_link_node_pads(struct cfe_device *cfe)
{
struct media_pad *remote_pad;
int ret;
/* Source -> CSI2 */
ret = v4l2_create_fwnode_links_to_pad(cfe->source_sd,
&cfe->csi2.pad[CSI2_PAD_SINK],
MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
if (ret) {
cfe_err(cfe, "Failed to create links to the source: %d\n", ret);
return ret;
}
remote_pad = media_pad_remote_pad_unique(&cfe->csi2.pad[CSI2_PAD_SINK]); if (IS_ERR(remote_pad)) {
ret = PTR_ERR(remote_pad);
cfe_err(cfe, "Failed to get unique remote source pad: %d\n",
ret);
return ret;
}
cfe->source_pad = remote_pad->index;
for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) {
struct cfe_node *node = &cfe->node[i];
if (!check_state(cfe, NODE_REGISTERED, i))
continue;
/* CSI2 channel # -> /dev/video# */
ret = media_create_pad_link(&cfe->csi2.sd.entity,
node_desc[i].link_pad,
&node->video_dev.entity, 0, 0); if (ret)
return ret;
if (node_supports_image(node)) {
/* CSI2 channel # -> FE Input */
ret = media_create_pad_link(&cfe->csi2.sd.entity,
node_desc[i].link_pad,
&cfe->fe.sd.entity,
FE_STREAM_PAD, 0); if (ret)
return ret;
}
}
for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) {
struct cfe_node *node = &cfe->node[i];
struct media_entity *src, *dst;
unsigned int src_pad, dst_pad;
ret = media_create_pad_link(src, src_pad, dst, dst_pad, 0); if (ret)
return ret;
}
return 0;
}
static int cfe_probe_complete(struct cfe_device *cfe)
{
int ret;
cfe->v4l2_dev.notify = cfe_notify;
for (unsigned int i = 0; i < NUM_NODES; i++) {
ret = cfe_register_node(cfe, i); if (ret) {
cfe_err(cfe, "Unable to register video node %u.\n", i);
goto unregister;
}
}
ret = cfe_link_node_pads(cfe); if (ret) {
cfe_err(cfe, "Unable to link node pads.\n");
goto unregister;
}
ret = v4l2_device_register_subdev_nodes(&cfe->v4l2_dev); if (ret) {
cfe_err(cfe, "Unable to register subdev nodes.\n");
goto unregister;
}
static int cfe_register_async_nf(struct cfe_device *cfe)
{
struct platform_device *pdev = cfe->pdev;
struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
struct fwnode_handle *local_ep_fwnode;
struct v4l2_async_connection *asd;
int ret;
local_ep_fwnode = fwnode_graph_get_endpoint_by_id(pdev->dev.fwnode, 0, 0, 0); if (!local_ep_fwnode) {
cfe_err(cfe, "Failed to find local endpoint fwnode\n");
return -ENODEV;
}
/* Parse the local endpoint and validate its configuration. */
ret = v4l2_fwnode_endpoint_parse(local_ep_fwnode, &ep); if (ret) {
cfe_err(cfe, "Failed to find remote endpoint fwnode\n");
goto err_put_local_fwnode;
}
for (unsigned int lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes;
lane++) { if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) {
cfe_err(cfe, "Data lanes reordering not supported\n");
ret = -EINVAL;
goto err_put_local_fwnode;
}
}
cfe->csi2.base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(cfe->csi2.base)) {
dev_err(&pdev->dev, "Failed to get dma io block\n");
ret = PTR_ERR(cfe->csi2.base);
goto err_cfe_put;
}
cfe->csi2.dphy.base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(cfe->csi2.dphy.base)) {
dev_err(&pdev->dev, "Failed to get host io block\n");
ret = PTR_ERR(cfe->csi2.dphy.base);
goto err_cfe_put;
}
cfe->mipi_cfg_base = devm_platform_ioremap_resource(pdev, 2); if (IS_ERR(cfe->mipi_cfg_base)) {
dev_err(&pdev->dev, "Failed to get mipi cfg io block\n");
ret = PTR_ERR(cfe->mipi_cfg_base);
goto err_cfe_put;
}
cfe->fe.base = devm_platform_ioremap_resource(pdev, 3); if (IS_ERR(cfe->fe.base)) {
dev_err(&pdev->dev, "Failed to get pisp fe io block\n");
ret = PTR_ERR(cfe->fe.base);
goto err_cfe_put;
}
ret = platform_get_irq(pdev, 0); if (ret <= 0) {
ret = -EINVAL;
goto err_cfe_put;
}
ret = devm_request_irq(&pdev->dev, ret, cfe_isr, 0, "rp1-cfe", cfe); if (ret) {
dev_err(&pdev->dev, "Unable to request interrupt\n");
ret = -EINVAL;
goto err_cfe_put;
}
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); if (ret) {
dev_err(&pdev->dev, "DMA enable failed\n");
goto err_cfe_put;
}
ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, UINT_MAX); if (ret)
goto err_cfe_put;
/* TODO: Enable clock only when running. */
cfe->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(cfe->clk)) {
ret = dev_err_probe(&pdev->dev, PTR_ERR(cfe->clk), "clock not found\n");
goto err_cfe_put;
}
MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>");
MODULE_DESCRIPTION("Raspberry Pi RP1 Camera Front End driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(CFE_VERSION);
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.37 Sekunden
(vorverarbeitet am 2026-10-11)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.