技术标签: # v4l2 video 实例分析
dts源码如下
rkcif_mipi_lvds: rkcif_mipi_lvds {
compatible = "rockchip,rkcif-mipi-lvds";
...
status = "okay";
port {
cif_mipi_in: endpoint {
remote-endpoint = <&mipi_csi2_output>;
data-lanes = <1 2 3 4>;
};
};
};
开始分析驱动代码
static int rkcif_plat_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct rkcif_device *cif_dev;
const struct rkcif_match_data *data;
int ret;
...
cif_dev = devm_kzalloc(dev, sizeof(*cif_dev), GFP_KERNEL);
if (!cif_dev)
return -ENOMEM;
dev_set_drvdata(dev, cif_dev);
cif_dev->dev = dev;
...
ret = rkcif_plat_init(cif_dev, node, data->inf_id);
if (ret) {
rkcif_detach_hw(cif_dev);
return ret;
}
...
return 0;
}
rkcif_plat_probe
-> rkcif_plat_init
int rkcif_plat_init(struct rkcif_device *cif_dev, struct device_node *node, int inf_id)
{
struct device *dev = cif_dev->dev;
struct v4l2_device *v4l2_dev;
int ret;
cif_dev->pipe.open = rkcif_pipeline_open;
cif_dev->pipe.close = rkcif_pipeline_close;
cif_dev->pipe.set_stream = rkcif_pipeline_set_stream;
cif_dev->isr_hdl = rkcif_irq_handler;
/*
* stream和cif_dev进行关联,知道就行
*/
rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID0);
rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID1);
rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID2);
rkcif_stream_init(cif_dev, RKCIF_STREAM_MIPI_ID3);
...
v4l2_dev = &cif_dev->v4l2_dev;
ret = v4l2_device_register(cif_dev->dev, &cif_dev->v4l2_dev);
if (ret < 0)
return ret;
/*
* media暂时不分析
*/
media_device_init(&cif_dev->media_dev);
ret = media_device_register(&cif_dev->media_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "Failed to register media device: %d\n",
ret);
goto err_unreg_v4l2_dev;
}
/* create & register platefom subdev (from of_node) */
ret = rkcif_register_platform_subdevs(cif_dev);
if (ret < 0)
goto err_unreg_media_dev;
...
mutex_lock(&rkcif_dev_mutex);
list_add_tail(&cif_dev->list, &rkcif_device_list);
mutex_unlock(&rkcif_dev_mutex);
return 0;
}
rkcif_plat_probe
-> rkcif_plat_init
-> rkcif_stream_init
这个看一下就行,用于stream和dev的分析
void rkcif_stream_init(struct rkcif_device *dev, u32 id)
{
struct rkcif_stream *stream = &dev->stream[id];
struct v4l2_pix_format_mplane pixm;
int i;
stream->id = id;
stream->cifdev = dev;
/* Set default format */
pixm.pixelformat = V4L2_PIX_FMT_NV12;
pixm.width = RKCIF_DEFAULT_WIDTH;
pixm.height = RKCIF_DEFAULT_HEIGHT;
/*
* stream->cif_fmt_out = fmt;
* stream->pixm = pixm;
*/
rkcif_set_fmt(stream, &pixm, false);
...
}
rkcif_plat_probe
-> rkcif_plat_init
-> rkcif_stream_init
-> rkcif_register_platform_subdevs
static int rkcif_register_platform_subdevs(struct rkcif_device *cif_dev)
{
int stream_num = 0, ret;
/*
* RKCIF_MAX_STREAM_MIPI 值为4
* 会注册4个video节点
*/
stream_num = RKCIF_MAX_STREAM_MIPI;
ret = rkcif_register_stream_vdevs(cif_dev, stream_num,
true);
if (ret < 0) {
dev_err(cif_dev->dev, "cif register stream[%d] failed!\n", stream_num);
return -EINVAL;
}
ret = cif_subdev_notifier(cif_dev);
if (ret < 0) {
v4l2_err(&cif_dev->v4l2_dev,
"Failed to register subdev notifier(%d)\n", ret);
goto err_unreg_stream_vdev;
}
return 0;
}
rkcif_plat_probe
-> rkcif_plat_init
-> rkcif_stream_init
-> rkcif_register_platform_subdevs
-> rkcif_register_stream_vdevs
这部分代码是注册video节点,和vivi驱动大同小异
int rkcif_register_stream_vdevs(struct rkcif_device *dev,
int stream_num,
bool is_multi_input)
{
struct rkcif_stream *stream;
int i, j, ret;
for (i = 0; i < stream_num; i++) {
stream = &dev->stream[i];
stream->cifdev = dev;
ret = rkcif_register_stream_vdev(stream, is_multi_input);
if (ret < 0)
goto err;
}
return 0;
err:
for (j = 0; j < i; j++) {
stream = &dev->stream[j];
rkcif_unregister_stream_vdev(stream);
}
return ret;
}
static int rkcif_register_stream_vdev(struct rkcif_stream *stream,
bool is_multi_input)
{
struct rkcif_device *dev = stream->cifdev;
struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
struct video_device *vdev = &stream->vnode.vdev;
struct rkcif_vdev_node *node;
int ret = 0;
char *vdev_name;
if (is_multi_input) {
switch (stream->id) {
case RKCIF_STREAM_MIPI_ID0:
vdev_name = CIF_MIPI_ID0_VDEV_NAME;
break;
case RKCIF_STREAM_MIPI_ID1:
vdev_name = CIF_MIPI_ID1_VDEV_NAME;
break;
case RKCIF_STREAM_MIPI_ID2:
vdev_name = CIF_MIPI_ID2_VDEV_NAME;
break;
case RKCIF_STREAM_MIPI_ID3:
vdev_name = CIF_MIPI_ID3_VDEV_NAME;
break;
case RKCIF_STREAM_DVP:
vdev_name = CIF_DVP_VDEV_NAME;
break;
default:
v4l2_err(v4l2_dev, "Invalid stream\n");
goto unreg;
}
} else {
vdev_name = CIF_VIDEODEVICE_NAME;
}
strlcpy(vdev->name, vdev_name, sizeof(vdev->name));
node = vdev_to_node(vdev);
mutex_init(&node->vlock);
vdev->ioctl_ops = &rkcif_v4l2_ioctl_ops;
vdev->release = video_device_release_empty;
vdev->fops = &rkcif_fops;
vdev->minor = -1;
vdev->v4l2_dev = v4l2_dev;
vdev->lock = &node->vlock;
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
V4L2_CAP_STREAMING;
video_set_drvdata(vdev, stream);
vdev->vfl_dir = VFL_DIR_RX;
node->pad.flags = MEDIA_PAD_FL_SINK;
rkcif_init_vb2_queue(&node->buf_queue, stream,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
vdev->queue = &node->buf_queue;
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
v4l2_err(v4l2_dev,
"video_register_device failed with error %d\n", ret);
return ret;
}
ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
if (ret < 0)
goto unreg;
return 0;
unreg:
video_unregister_device(vdev);
return ret;
}
列举一下video的一些信息
同样的ops还是v4l2_fops
要注意的是:
和vivi相比,不仅仅支持MMAP还支持DMABUF,另外device_caps类型是V4L2_CAP_VIDEO_CAPTURE_MPLANE,多平面视频格式,vivi中这种格式我们忽略了没有分析
rkcif_plat_probe
-> rkcif_plat_init
-> rkcif_stream_init
-> rkcif_register_platform_subdevs
-> rkcif_register_stream_vdevs
-> cif_subdev_notifier
static int cif_subdev_notifier(struct rkcif_device *cif_dev)
{
struct v4l2_async_notifier *ntf = &cif_dev->notifier;
struct device *dev = cif_dev->dev;
int ret;
/*
* v4l2_async_notifier_parse_fwnode_endpoints和
* v4l2_async_notifier_parse_fwnode_endpoints_by_port类似
* 区别是has_port这个参数是false,而不是true
* true的时候会去看port到reg值,获取port号及id号
* 由于当前对应的dts没有reg
* 所以使用false
* 这样的结果是 rkcif_mipi的notifier->subdevs[0] 存储的数据
* 指向mipi csi dts的节点
* 回调函数 rkcif_fwnode_parse 这里不分析
*/
ret = v4l2_async_notifier_parse_fwnode_endpoints(
dev, ntf, sizeof(struct rkcif_async_subdev), rkcif_fwnode_parse);
if (ret < 0)
return ret;
if (!ntf->num_subdevs)
return -ENODEV; /* no endpoint */
ntf->ops = &subdev_notifier_ops;
return v4l2_async_notifier_register(&cif_dev->v4l2_dev, ntf);
}
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
struct v4l2_async_notifier *notifier)
{
int ret;
if (WARN_ON(!v4l2_dev || notifier->sd))
return -EINVAL;
notifier->v4l2_dev = v4l2_dev;
ret = __v4l2_async_notifier_register(notifier);
if (ret)
notifier->v4l2_dev = NULL;
return ret;
}
对于__v4l2_async_notifier_register看下面的分析
v4l2_async_subdev_notifier_register 分析
一定要看v4l2_async_subdev_notifier_register的分析,否则会不理解怎么注册subdev
看完上面的分析可以知道
所有的subdev现在都挂载在v4l2_dev->subdevs上,现在分析notifier->ops->complete
static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
{
struct rkcif_device *dev;
struct rkcif_sensor_info *sensor;
int ret, index;
dev = container_of(notifier, struct rkcif_device, notifier);
...
ret = v4l2_device_register_subdev_nodes(&dev->v4l2_dev);
if (ret < 0)
goto unregister_lvds;
v4l2_info(&dev->v4l2_dev, "Async subdev notifier completed\n");
return ret;
}
int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
{
struct video_device *vdev;
struct v4l2_subdev *sd;
int err;
/* Register a device node for every subdev marked with the
* V4L2_SUBDEV_FL_HAS_DEVNODE flag.
*/
/*
* 遍历每个sd
*/
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
continue;
if (sd->devnode)
continue;
vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
if (!vdev) {
err = -ENOMEM;
goto clean_up;
}
video_set_drvdata(vdev, sd);
strlcpy(vdev->name, sd->name, sizeof(vdev->name));
vdev->v4l2_dev = v4l2_dev;
vdev->fops = &v4l2_subdev_fops;
vdev->release = v4l2_device_release_subdev_node;
vdev->ctrl_handler = sd->ctrl_handler;
/*
* 注册/dev/v4l-subdev
* 不跟进分析,记住重要的ops就可以了
*/
err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
sd->owner);
if (err < 0) {
kfree(vdev);
goto clean_up;
}
sd->devnode = vdev;
}
return 0;
}}
文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态
文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境
文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn
文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker
文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机
文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk
文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入
文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。 Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。
文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动
文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计
文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;gt;Jni-&amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图
文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法