【Raspberry PI】Raspberry PiSP摄像头前端(rpl-cfe)

news/2025/1/12 23:17:05/文章来源:https://www.cnblogs.com/Wangzx000/p/18667580

1.PiSP相机前端

PiSP 摄像头前端 (CFE) 是一个将 CSI-2 接收器与 一个简单的 ISP,称为前端 (FE)。
CFE 有四个 DMA 引擎,可以从四个单独的流写入帧 从 CSI-2 接收到内存。也可以路由其中一个流 直接给 FE 做最少的图片处理,写两个版本 (例如,未缩放和缩小版本)将接收到的帧保存到内存中,并且 提供接收到的帧的统计信息。

FE寄存器参考:https://datasheets.raspberrypi.com/camera/raspberry-pi-image-signal-processor-specification.pdf

FE的示例代码:https://github.com/raspberrypi/libpisp.git

参考资料:https://www.kernel.org/doc/html/latest/admin-guide/media/index.html
https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/v4l2.html

树莓派Linux源码仓库:https://github.com/raspberrypi/linux.git

2.rpl-cfe驱动程序

2.1 raspberrypi camera驱动框图

Raspberry PiSP 摄像头前端 (rp1-cfe) 驱动程序位于 drivers/media/platform/raspberrypi/rp1-cfe 的 Drivers/Media/Platform/RaspberryPi/RP1-CFE 中。它使用 V4L2 API 注册 多个视频采集和输出设备,V4L2 subdev API 注册 接收的 CSI-2 的子设备以及连接视频设备的 FE 使用媒体控制器 (MC) API 实现的单个媒体图。

rp1-cfe 驱动程序注册的媒体拓扑,在此特定 连接到 IMX219 传感器的示例如下:

image

媒体图包含以下视频设备节点:
● rp1-cfe-csi2-ch0:第一个 CSI-2 流的捕获设备
● rp1-cfe-csi2-ch1:第二个 CSI-2 流的捕获设备
● rp1-cfe-csi2-ch2:第三个 CSI-2 流的捕获设备
● rp1-cfe-csi2-ch3:第四个 CSI-2 流的捕获设备
● rp1-cfe-fe-image0:第一个 FE 输出的捕获设备
● rp1-cfe-fe-image1:第二个 FE 输出的捕获设备
● rp1-cfe-fe-stats:用于 FE 统计信息的捕获设备
● rp1-cfe-fe-config:FE 配置的输出设备

rp1-cfe-csi2-chX 捕获设备是普通的 V4L2 捕获设备,它 可用于捕获从 CSI-2 接收的视频帧或元数据。

rp1-cfe-fe-image0 和 rp1-cfe-fe-image1 捕获设备用于写入 将处理的帧保存到内存中。

FE 统计缓冲区的格式由 C 结构定义,每个参数的含义为 在 PiSP 规范文档中进行了描述。pisp_statistics

FE 配置缓冲区的格式由 C 结构定义,每个参数的含义为 在 PiSP 规范文档中进行了描述。

2.2 rpl-cfe驱动架构

rpl-cfe的目录如下:

PS A:\raspberrypi\linux\drivers\media\platform\raspberrypi\rp1_cfe> dirMode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          2025/1/7     21:48          65117 cfe.c
-a----          2025/1/7     21:48            941 cfe.h
-a----          2025/1/7     21:48           8298 cfe_fmts.h
-a----          2025/1/7     21:48          17757 csi2.c
-a----          2025/1/7     21:48           2307 csi2.h
-a----          2025/1/7     21:48           5041 dphy.c
-a----          2025/1/7     21:48            452 dphy.h
-a----          2025/1/7     21:48            409 Kconfig
-a----          2025/1/7     21:48            175 Makefile
-a----          2025/1/7     21:48           1548 pisp_common.h
-a----          2025/1/7     21:48          18627 pisp_fe.c
-a----          2025/1/7     21:48           1323 pisp_fe.h
-a----          2025/1/7     21:48           6514 pisp_fe_config.h
-a----          2025/1/7     21:48           1678 pisp_statistics.h
-a----          2025/1/7     21:48           6487 pisp_types.h
PS A:\raspberrypi\linux\drivers\media\platform\raspberrypi\pisp_be> dirMode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          2025/1/7     21:48            407 Kconfig
-a----          2025/1/7     21:48            172 Makefile
-a----          2025/1/7     21:48          55988 pisp_be.c
-a----          2025/1/7     21:48          15149 pisp_be_formats.h

从源文件的文件名分析:

文件名 功能
cfe.c CSI-2数据流的捕获设备驱动
csi2.c MIPI-CSI-2的驱动文件
dphy.c dphy是mipi接口的物理层标准
pisp_fe.c isp的前端处理(具体可参考背景知识)
pisp_be.c isp的后端处理

数据流图:

image

2.3.1 D-PHY

D-PHY驱动中对外提供了3个接口:

void dphy_probe(struct dphy_data *dphy);
void dphy_start(struct dphy_data *dphy);
void dphy_stop(struct dphy_data *dphy);
static void dw_csi2_host_write(struct dphy_data *dphy, u32 offset, u32 data)
{writel(data, dphy->base + offset);
}....static void dphy_init(struct dphy_data *dphy)
{dw_csi2_host_write(dphy, PHY_RSTZ, 0);dw_csi2_host_write(dphy, PHY_SHUTDOWNZ, 0);set_tstclk(dphy, 1);set_testen(dphy, 0);set_tstclr(dphy, 1);usleep_range(15, 20);set_tstclr(dphy, 0);usleep_range(15, 20);dphy_set_hsfreqrange(dphy, dphy->dphy_rate);usleep_range(5, 10);dw_csi2_host_write(dphy, PHY_SHUTDOWNZ, 1);usleep_range(5, 10);dw_csi2_host_write(dphy, PHY_RSTZ, 1);
}void dphy_start(struct dphy_data *dphy)
{dw_csi2_host_write(dphy, N_LANES, (dphy->active_lanes - 1));dphy_init(dphy);dw_csi2_host_write(dphy, RESETN, 0xffffffff);usleep_range(10, 50);
}void dphy_stop(struct dphy_data *dphy)
{/* Set only one lane (lane 0) as active (ON) */dw_csi2_host_write(dphy, N_LANES, 0);dw_csi2_host_write(dphy, RESETN, 0);
}void dphy_probe(struct dphy_data *dphy)
{u32 host_ver;u8 host_ver_major, host_ver_minor;host_ver = dw_csi2_host_read(dphy, VERSION);host_ver_major = (u8)((host_ver >> 24) - '0');host_ver_minor = (u8)((host_ver >> 16) - '0');host_ver_minor = host_ver_minor * 10;host_ver_minor += (u8)((host_ver >> 8) - '0');dphy_info("DW dphy Host HW v%u.%u\n", host_ver_major, host_ver_minor);
}

2.3.2 CSI-2

CSI-2是一种应用层协议,通过D-PHY这个物理层传输介质来传输。

CSI-2外露了以下接口:

void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof);
void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,dma_addr_t dmaaddr, unsigned int stride,unsigned int size);
void csi2_set_compression(struct csi2_device *csi2, unsigned int channel,enum csi2_compression_mode mode, unsigned int shift,unsigned int offset);
void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,enum csi2_mode mode, bool auto_arm,bool pack_bytes, unsigned int width,unsigned int height);
void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel);
void csi2_open_rx(struct csi2_device *csi2);
void csi2_close_rx(struct csi2_device *csi2);
int csi2_init(struct csi2_device *csi2, struct dentry *debugfs);
void csi2_uninit(struct csi2_device *csi2);

以下为csi-2的初始化函数,tx函数和rx函数的实现:

int csi2_init(struct csi2_device *csi2, struct dentry *debugfs)
{unsigned int i, ret;spin_lock_init(&csi2->errors_lock);csi2->dphy.dev = csi2->v4l2_dev->dev;dphy_probe(&csi2->dphy);debugfs_create_file("csi2_regs", 0444, debugfs, csi2, &csi2_regs_fops);if (csi2_track_errors)debugfs_create_file("csi2_errors", 0444, debugfs, csi2,&csi2_errors_fops);for (i = 0; i < CSI2_NUM_CHANNELS * 2; i++)csi2->pad[i].flags = i < CSI2_NUM_CHANNELS ?MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;ret = media_entity_pads_init(&csi2->sd.entity, ARRAY_SIZE(csi2->pad),csi2->pad);if (ret)return ret;/* Initialize subdev */v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops);csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;csi2->sd.entity.ops = &csi2_entity_ops;csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;csi2->sd.owner = THIS_MODULE;snprintf(csi2->sd.name, sizeof(csi2->sd.name), "csi2");ret = v4l2_subdev_init_finalize(&csi2->sd);if (ret)goto err_entity_cleanup;ret = v4l2_device_register_subdev(csi2->v4l2_dev, &csi2->sd);if (ret) {csi2_err("Failed register csi2 subdev (%d)\n", ret);goto err_subdev_cleanup;}return 0;err_subdev_cleanup:v4l2_subdev_cleanup(&csi2->sd);
err_entity_cleanup:media_entity_cleanup(&csi2->sd.entity);return ret;
}void csi2_open_rx(struct csi2_device *csi2)
{csi2_reg_write(csi2, CSI2_IRQ_MASK,csi2_track_errors ? CSI2_IRQ_MASK_IRQ_ALL : 0);dphy_start(&csi2->dphy);csi2_reg_write(csi2, CSI2_CTRL,csi2->multipacket_line ? 0 : EOP_IS_EOL);
}void csi2_close_rx(struct csi2_device *csi2)
{dphy_stop(&csi2->dphy);csi2_reg_write(csi2, CSI2_IRQ_MASK, 0);
}static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = {.init_cfg = csi2_init_cfg,.get_fmt = v4l2_subdev_get_fmt,.set_fmt = csi2_pad_set_fmt,.link_validate = v4l2_subdev_link_validate_default,
};static const struct media_entity_operations csi2_entity_ops = {.link_validate = v4l2_subdev_link_validate,
};static const struct v4l2_subdev_ops csi2_subdev_ops = {.pad = &csi2_subdev_pad_ops,
};

2.3.3 CFE

CFE是CSI-2流的捕获设备

const struct cfe_fmt *find_format_by_code(u32 code);
const struct cfe_fmt *find_format_by_pix(u32 pixelformat);
u32 cfe_find_16bit_code(u32 code);
u32 cfe_find_compressed_code(u32 code);
static int cfe_probe(struct platform_device *pdev)
{struct cfe_device *cfe;char debugfs_name[32];int ret;cfe = kzalloc(sizeof(*cfe), GFP_KERNEL);if (!cfe)return -ENOMEM;platform_set_drvdata(pdev, cfe);kref_init(&cfe->kref);cfe->pdev = pdev;cfe->fe_csi2_channel = -1;spin_lock_init(&cfe->state_lock);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) {dev_err(&pdev->dev, "No IRQ resource\n");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;}/* TODO: Enable clock only when running. */cfe->clk = devm_clk_get(&pdev->dev, NULL);if (IS_ERR(cfe->clk))return dev_err_probe(&pdev->dev, PTR_ERR(cfe->clk),"clock not found\n");cfe->mdev.dev = &pdev->dev;cfe->mdev.ops = &cfe_media_device_ops;strscpy(cfe->mdev.model, CFE_MODULE_NAME, sizeof(cfe->mdev.model));strscpy(cfe->mdev.serial, "", sizeof(cfe->mdev.serial));snprintf(cfe->mdev.bus_info, sizeof(cfe->mdev.bus_info), "platform:%s",dev_name(&pdev->dev));media_device_init(&cfe->mdev);cfe->v4l2_dev.mdev = &cfe->mdev;ret = v4l2_device_register(&pdev->dev, &cfe->v4l2_dev);if (ret) {cfe_err("Unable to register v4l2 device.\n");goto err_cfe_put;}snprintf(debugfs_name, sizeof(debugfs_name), "rp1-cfe:%s",dev_name(&pdev->dev));cfe->debugfs = debugfs_create_dir(debugfs_name, NULL);debugfs_create_file("format", 0444, cfe->debugfs, cfe, &format_fops);debugfs_create_file("regs", 0444, cfe->debugfs, cfe,&mipi_cfg_regs_fops);/* Enable the block power domain */pm_runtime_enable(&pdev->dev);ret = pm_runtime_resume_and_get(&cfe->pdev->dev);if (ret)goto err_runtime_disable;cfe->csi2.v4l2_dev = &cfe->v4l2_dev;ret = csi2_init(&cfe->csi2, cfe->debugfs);if (ret) {cfe_err("Failed to init csi2 (%d)\n", ret);goto err_runtime_put;}cfe->fe.v4l2_dev = &cfe->v4l2_dev;ret = pisp_fe_init(&cfe->fe, cfe->debugfs);if (ret) {cfe_err("Failed to init pisp fe (%d)\n", ret);goto err_csi2_uninit;}cfe->mdev.hw_revision = cfe->fe.hw_revision;ret = media_device_register(&cfe->mdev);if (ret < 0) {cfe_err("Unable to register media-controller device.\n");goto err_pisp_fe_uninit;}ret = of_cfe_connect_subdevs(cfe);if (ret) {cfe_err("Failed to connect subdevs\n");goto err_media_unregister;}pm_runtime_put(&cfe->pdev->dev);return 0;err_media_unregister:media_device_unregister(&cfe->mdev);
err_pisp_fe_uninit:pisp_fe_uninit(&cfe->fe);
err_csi2_uninit:csi2_uninit(&cfe->csi2);
err_runtime_put:pm_runtime_put(&cfe->pdev->dev);
err_runtime_disable:pm_runtime_disable(&pdev->dev);debugfs_remove(cfe->debugfs);v4l2_device_unregister(&cfe->v4l2_dev);
err_cfe_put:cfe_put(cfe);return ret;
}static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count)
{struct v4l2_mbus_config mbus_config = { 0 };struct cfe_node *node = vb2_get_drv_priv(vq);struct cfe_device *cfe = node->cfe;int ret;cfe_dbg("%s: [%s] begin.\n", __func__, node_desc[node->id].name);if (!check_state(cfe, NODE_ENABLED, node->id)) {cfe_err("%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("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("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("Failed to start media pipeline: %d\n", ret);goto err_pm_put;}clear_state(cfe, FS_INT | FE_INT, node->id);set_state(cfe, NODE_STREAMING, node->id);node->fs_count = 0;cfe_start_channel(node);if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) {cfe_dbg("Not all nodes are set to streaming yet!\n");return 0;}cfg_reg_write(cfe, MIPICFG_CFG, MIPICFG_CFG_SEL_CSI);cfg_reg_write(cfe, MIPICFG_INTE, MIPICFG_INT_CSI_DMA | MIPICFG_INT_PISP_FE);ret = v4l2_subdev_call(cfe->sensor, pad, get_mbus_config, 0,&mbus_config);if (ret < 0 && ret != -ENOIOCTLCMD) {cfe_err("g_mbus_config failed\n");goto err_pm_put;}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("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_disable_cfe;}cfe_dbg("Configuring CSI-2 block - %u data lanes\n", cfe->csi2.dphy.active_lanes);cfe->csi2.dphy.dphy_rate = sensor_link_rate(cfe) / 1000000UL;csi2_open_rx(&cfe->csi2);cfe_dbg("Starting sensor streaming\n");ret = v4l2_subdev_call(cfe->sensor, video, s_stream, 1);if (ret < 0) {cfe_err("stream on failed in subdev\n");goto err_disable_cfe;}cfe_dbg("%s: [%s] end.\n", __func__, node_desc[node->id].name);return 0;err_disable_cfe:csi2_close_rx(&cfe->csi2);cfe_stop_channel(node, true);media_pipeline_stop(&node->pad);
err_pm_put:pm_runtime_put(&cfe->pdev->dev);
err_streaming:cfe_return_buffers(node, VB2_BUF_STATE_QUEUED);clear_state(cfe, NODE_STREAMING, node->id);return ret;
}static void cfe_stop_streaming(struct vb2_queue *vq)
{struct cfe_node *node = vb2_get_drv_priv(vq);struct cfe_device *cfe = node->cfe;unsigned long flags;bool fe_stop;cfe_dbg("%s: [%s] begin.\n", __func__, node_desc[node->id].name);spin_lock_irqsave(&cfe->state_lock, flags);fe_stop = is_fe_enabled(cfe) &&test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);cfe->job_ready = false;clear_state(cfe, NODE_STREAMING, node->id);spin_unlock_irqrestore(&cfe->state_lock, flags);cfe_stop_channel(node, fe_stop);if (!test_any_node(cfe, NODE_STREAMING)) {/* Stop streaming the sensor and disable the peripheral. */if (v4l2_subdev_call(cfe->sensor, video, s_stream, 0) < 0)cfe_err("stream off failed in subdev\n");csi2_close_rx(&cfe->csi2);cfg_reg_write(cfe, MIPICFG_INTE, 0);}media_pipeline_stop(&node->pad);/* Clear all queued buffers for the node */cfe_return_buffers(node, VB2_BUF_STATE_ERROR);pm_runtime_put(&cfe->pdev->dev);cfe_dbg("%s: [%s] end.\n", __func__, node_desc[node->id].name);
}static const struct vb2_ops cfe_video_qops = {.wait_prepare = vb2_ops_wait_prepare,.wait_finish = vb2_ops_wait_finish,.queue_setup = cfe_queue_setup,.buf_prepare = cfe_buffer_prepare,.buf_queue = cfe_buffer_queue,.start_streaming = cfe_start_streaming,.stop_streaming = cfe_stop_streaming,
};// 此函数用于创建cfe与csi或isp_fe的连接pad(垫)
static int cfe_link_node_pads(struct cfe_device *cfe)
{unsigned int i, source_pad = 0;int ret;for (i = 0; i < CSI2_NUM_CHANNELS; i++) {struct cfe_node *node = &cfe->node[i];if (!check_state(cfe, NODE_REGISTERED, i))continue;/* Find next source pad */while (source_pad < cfe->sensor->entity.num_pads &&!(cfe->sensor->entity.pads[source_pad].flags &MEDIA_PAD_FL_SOURCE))source_pad++;if (source_pad < cfe->sensor->entity.num_pads) {/* Sensor -> CSI2 */ret = media_create_pad_link(&cfe->sensor->entity, source_pad,&cfe->csi2.sd.entity, i,MEDIA_LNK_FL_IMMUTABLE |MEDIA_LNK_FL_ENABLED);if (ret)return ret;/* Dealt with that source_pad, look at the next one next time */source_pad++;}/* 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 (; i < NUM_NODES; i++) {struct cfe_node *node = &cfe->node[i];struct media_entity *src, *dst;unsigned int src_pad, dst_pad;if (node_desc[i].pad_flags & MEDIA_PAD_FL_SINK) {/* FE -> /dev/video# */src = &cfe->fe.sd.entity;src_pad = node_desc[i].link_pad;dst = &node->video_dev.entity;dst_pad = 0;} else {/* /dev/video# -> FE */dst = &cfe->fe.sd.entity;dst_pad = node_desc[i].link_pad;src = &node->video_dev.entity;src_pad = 0;}ret = media_create_pad_link(src, src_pad, dst, dst_pad, 0);if (ret)return ret;}return 0;
}

2.3.4 ISP_FE

ISP_FE是图像信号处理前端

void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof);
int pisp_fe_validate_config(struct pisp_fe_device *fe,struct pisp_fe_config *cfg,struct v4l2_format const *f0,struct v4l2_format const *f1);
void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs,struct pisp_fe_config *cfg);
void pisp_fe_start(struct pisp_fe_device *fe);
void pisp_fe_stop(struct pisp_fe_device *fe);
int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs);
void pisp_fe_uninit(struct pisp_fe_device *fe);
int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs)
{int ret;debugfs_create_file("pisp_regs", 0444, debugfs, fe, &pisp_regs_fops);fe->hw_revision = pisp_fe_reg_read(fe, FE_VERSION);pisp_fe_info("PiSP FE HW v%u.%u\n",(fe->hw_revision >> 24) & 0xff,(fe->hw_revision >> 20) & 0x0f);fe->pad[FE_STREAM_PAD].flags =MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;fe->pad[FE_CONFIG_PAD].flags = MEDIA_PAD_FL_SINK;fe->pad[FE_OUTPUT0_PAD].flags = MEDIA_PAD_FL_SOURCE;fe->pad[FE_OUTPUT1_PAD].flags = MEDIA_PAD_FL_SOURCE;fe->pad[FE_STATS_PAD].flags = MEDIA_PAD_FL_SOURCE;ret = media_entity_pads_init(&fe->sd.entity, ARRAY_SIZE(fe->pad),fe->pad);if (ret)return ret;/* Initialize subdev */v4l2_subdev_init(&fe->sd, &pisp_fe_subdev_ops);fe->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;fe->sd.entity.ops = &pisp_fe_entity_ops;fe->sd.entity.name = "pisp-fe";fe->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;fe->sd.owner = THIS_MODULE;snprintf(fe->sd.name, sizeof(fe->sd.name), "pisp-fe");ret = v4l2_subdev_init_finalize(&fe->sd);if (ret)goto err_entity_cleanup;ret = v4l2_device_register_subdev(fe->v4l2_dev, &fe->sd);if (ret) {pisp_fe_err("Failed register pisp fe subdev (%d)\n", ret);goto err_subdev_cleanup;}/* Must be in IDLE state (STATUS == 0) here. */WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));return 0;err_subdev_cleanup:v4l2_subdev_cleanup(&fe->sd);
err_entity_cleanup:media_entity_cleanup(&fe->sd.entity);return ret;
}void pisp_fe_start(struct pisp_fe_device *fe)
{pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_RESET);pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF | FE_INT_LINES0 | FE_INT_LINES1);fe->inframe_count = 0;
}void pisp_fe_stop(struct pisp_fe_device *fe)
{pisp_fe_reg_write(fe, FE_INT_EN, 0);pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_ABORT);usleep_range(1000, 2000);WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);
}

背景知识

ISP

在图像信号处理(ISP)领域,ISP_FEISP_BE分别代表图像信号处理的前端(Front End)和后端(Back End)。以下是它们的详细解释:

ISP_FE(图像信号处理前端)

定义:

ISP_FE是图像信号处理流水线中前端部分,主要负责从图像传感器接收原始图像数据(通常是 Bayer 格式)并进行初步处理。

主要功能:

• 图像裁剪(Crop):对输入图像进行裁剪,去除不需要的边缘部分。

• 坏点校正(Defect Pixel Correction,DPC):检测和校正图像中的坏点。

• 黑电平校正(Black Level Compensation,BLC):校正图像的黑电平,提高图像质量。

• 镜头阴影校正(Lens Shading Correction,LSC):校正由于镜头光学特性导致的图像阴影。

• 自动曝光控制(Auto Exposure Control,AEC):根据环境光线自动调整曝光参数,以获得最佳图像亮度。

• 自动白平衡(Auto White Balance,AWB):自动调整图像的白平衡,使图像颜色更自然。

ISP_BE(图像信号处理后端)

定义:

ISP_BE是图像信号处理流水线中后端部分,主要负责对前端处理后的图像数据进行进一步的处理,生成最终的输出图像(通常是 YUV 或 RGB 格式)。

主要功能:

• 去噪(Noise Reduction,NR):在 Bayer 域或 RGB 域进行去噪处理,减少图像噪声。

• 色彩校正(Color Correction Matrix,CCM):通过矩阵变换调整图像的色彩平衡。

• 伽马校正(Gamma Correction):调整图像的伽马曲线,改善图像的对比度和亮度。

• 锐化(Sharpening):增强图像的边缘,提高图像的清晰度。

• 动态范围压缩(Dynamic Range Compression,DRC):压缩图像的动态范围,使图像在不同光照条件下都能保持良好的视觉效果。

• 多帧合成宽动态(Multi-Frame Wide Dynamic Range,WDR):通过多帧合成技术,提高图像在高动态范围场景下的表现。

• 色彩空间转换(Color Space Conversion):将图像从 Bayer 格式转换为 YUV 或 RGB 格式,以便后续处理和显示。

示例架构

以下是一个典型的 ISP 架构示例,展示了 ISP_FEISP_BE的位置和功能:

图像传感器 (Sensor) --> ISP_FE --> ISP_BE --> 输出图像 (YUV/RGB)

• ISP_FE:

• 接收 Bayer 格式的原始图像数据。

• 进行裁剪、坏点校正、黑电平校正、镜头阴影校正等初步处理。

• 输出处理后的 Bayer 图像数据。

• ISP_BE:

• 接收处理后的 Bayer 图像数据。

• 进行去噪、色彩校正、伽马校正、锐化、动态范围压缩等高级处理。

• 输出最终的 YUV 或 RGB 图像数据。

总结

• ISP_FE:主要负责从图像传感器接收原始图像数据并进行初步处理。

• ISP_BE:主要负责对初步处理后的图像数据进行进一步的处理,生成最终的输出图像。

通过这种分工,ISP 能够高效地处理图像数据,提供高质量的图像输出。

CSI-2,C-PHY,D-PHY

D-PHY:

  • D-PHY 是 MIPI 接口的一种物理层标准,主要用于摄像头(CSI)和显示屏(DSI)的数据传输。

  • D 是罗马数字中的 500,表示 D-PHY 最初设计目标是支持 500 Mbits/s 的数据传输速率。

C-PHY:

• C-PHY 是 MIPI 接口的另一种物理层标准,旨在提供更高的数据传输速率,适用于高带宽应用。

• C 代表 Channel-limited,表示 C-PHY 适用于通道受限的应用场景。

CSI-2:

  • CSI-2 是 MIPI 接口的一种应用层协议,主要用于摄像头模块与处理器之间的高速数据传输。
  • CSI-2 定义了数据传输的格式、协议和控制机制,确保摄像头采集的图像数据能够高效、准确地传输到处理器。
特性 D-PHY C-PHY CSI-2
类型 物理层标准 物理层标准 应用层协议
主要功能 数据传输的物理介质定义 数据传输的物理介质定义 数据传输的格式和控制机制
信号传输 差分信号对,每条 lane 两根线 三相信号 定义数据包格式和传输协议
时钟支持 独立时钟线 C-PHY 没有单独的时钟线,时钟信号嵌入在数据传输的时序中,减少了引脚数量 通过 LLP 和 Lane Management 管理时钟
传输速率 最高 1.5 Gbps/通道 高达 2.5 Gbps 依赖于物理层(D-PHY 或 C-PHY)
数据线配置 1/2/4 条数据 lane 三条信号线 多通道数据传输,每个通道可以是 D-PHY 或 C-PHY
应用场景 摄像头和显示屏的数据传输 高端显示器、相机模块 摄像头模块与处理器之间的数据传输
控制接口 包含 CCI(基于 I2C)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/868401.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一文看懂“高大上”的边缘计算!!

一、边缘计算(Edge Computing) 边缘计算是一种分布式计算架构,其核心思想是将计算、存储和数据处理任务从云端推到靠近数据源的设备或网络边缘。通过这种方式,数据可以在本地(离数据源更近的地方)进行处理,从而减少延迟、减轻带宽负担、提高处理效率,并降低对远程数据中…

使用 CompletableFuture 实现异步编程

使用 CompletableFuture 实现异步编程 在现代 Java 开发中,异步编程是一项重要使用 CompletableFuture 实现异步编程 在现代 Java 开发中,异步编程是一项重要技能。而 CompletableFuture 是从 Java 8 开始提供的一个功能强大的工具,用于简化异步任务的编写和组合。本文将详细…

PCIe总线-存储器域和PCIe总线域访问流程分析(二)

1.概述 PCIe总线的最大特点是像CPU访问DDR一样,可以直接使用地址访问PCIe设备(桥),但不同的是DDR和CPU同属于存储器域,而CPU和PCIe设备属于两个不同的域,PCIe设备(桥)的地址空间属于PCIe总线域。存储器域访问PCIe总线域或者PCIe总线域访问存储器域,需要经过一系列的转…

在Windows上调试iOS Safari中的H5页面

本次开发的web页面需要适配移动端,第一个版本在发布到线上,发现ios设备一直打不开网页,无论是自带的safari浏览器还是其他浏览器,页面经常出现“***页面重复出现错误”,但是在本地以及使用PC浏览器模拟都没有复现该问题。为了进行线上的调试查询了一下如何在windows上调试…

大模型AI应用场景及产品汇总(持续更新)

一、文生图 1. Napkin AINapkin AI 可以基于输入的文本生成各种图示,例如流程图、逻辑关系图等等。https://app.napkin.ai/

大模型AI应用场景汇总(持续更新)

一、文生图 1. Napkin AINapkin AI 可以基于输入的文本生成各种图示,例如流程图、逻辑关系图等等。https://app.napkin.ai/

G1原理—5.G1垃圾回收过程之Mixed GC

大纲 1.Mixed GC混合回收是什么 2.YGC可作为Mixed GC的初始标记阶段 3.Mixed GC并发标记算法详解(一) 4.Mixed GC并发标记算法详解(二) 5.Mixed GC并发标记算法详解(三) 6.并发标记的三色标记法 7.三色标记法如何解决错标漏标问题 8.SATB如何解决错标漏标问题 9.重新梳理Mixed …

AlexNet文献阅读与代码实现

目录AlexNet文献阅读与代码实现文献内容介绍代码实现内容总结 AlexNet文献阅读与代码实现前言:笔者目前研一,刚开始入门深度学习,所以想记录一下自己学习的过程,接下来的时间里,我会定期阅读深度学习领域的经典文献,并尝试用代码实现它们,也欢迎大家积极评论。注:博客本…

c语言的基本类型及输入输出占位符

基本类型 c语言的基本类型:int 【long、short、unsigned、signed(c90)】、char、float、double、_Bool【布尔值】、_complex、_Imaginary【复数和虚数】 输入输出 printf标志字符标志 含义- 转换的结果在字段内左对齐+ 有符号的转换结果总是以+或者-开始空格 如果有符号转换的…

LeetCode:65.有效数字

LeetCode:65.有效数字解题步骤构建一个表示状态的图。遍历字符串,并沿着图走,如果到了某个节点无路可走就返false。遍历结束,如走到3/5/6,就返回true,否则返回false。 extend 2 8 10 16进制 /*** 检查一个字符串是否可以表示为一个有效的数字* @param {string} s - 待检查的…

vs2019项目报错:文件中的类都不能进行设计,解决办法_无法设计基类system.void

文件中的类都不能进行设计,因此未能为该文件显示设计器。设计器检查出文件中有以下类: Form1 --- 无法设计基类“System.Void”。 出现上述错误,解决办法如下: 关闭所有设计窗口(例如form1.cs(设计)),菜单栏生成,清理解决方案,关闭vs2022,重新启动vs2022打开你的项目…

关于VSCode的c/c++环境配置

适用于Windows的VSCode的c/c++环境配置c/c++环境配置写给 初学C/C++的 保姆级 VSCode环境配置第一步 下载VScode 找到官网点下载就好啦下载安装完成之后,启动!点击左侧“拓展”图标下载中文拓展安装好后重启 别关 留着备用 第二步 下载c/c++编译器 这里我使用MinGw实际上 Mi…