在rk 9.0之前还没有实现这个子系统,为了解决多媒体设备的复杂性和流动性,我的理解是把多媒体各个模块树状链接,可以很方便的操作各个链接点的格式分辨率等。但是这样需要代码来支持,所以就有media子系统,虽然增加了大段代码,但是这样操作更加灵活。
由于media子系统在网上很多,这里只做简单讲述,后面自己看看代码。
Media子系统是一种pipeline形式
Rk从sensor到ddr设定了4个设备如下
sensor —> DPHY —> isp —> stream
其中每个设备都认为是一个entity,是一个节点。每个都包含一个或者多个pads,可以认为是各个分叉,link就是将各个分叉pad链接起来,也就是谁链接谁。谁是源端,谁是目的端。
根据上图:
sensor有ov5695和ov2659,它们只有一个pad,因为只能输出
DPHY 就是mipi dphy 接收sensor数据,输出输出到isp,所有有2个pads
Isp 因为isp除了要接收dphy进来的数据,还要接收input参数,输出统计信息,最后将数据输出到 mainpath和selfpath(这个不清楚是否rkisp独有),4个pads
Mp和sp 接收isp处理好的数据 一个pad。
下面看下media设备的注册,以及相关操作函数
Media设备结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23struct media_device { /* dev->driver_data points to this struct. */ struct device *dev; struct media_devnode devnode; //设备节点 char model[32]; //名字以及相关信息 char serial[40]; char bus_info[32]; u32 hw_revision; u32 driver_version; u32 entity_id; struct list_head entities; //entity 列表 /* Protects the entities list */ spinlock_t lock; /* Serializes graph operations. */ struct mutex graph_mutex; int (*link_notify)(struct media_link *link, u32 flags, unsigned int notification);//link改变后的callback函数 };
在rkisp1_plat_probe函数是初始化rkisp1_device结构体的,里面包含media_device结构,
1
2
3
4
5
6
7
8
9
10static int rkisp1_plat_probe(struct platform_device *pdev) { …… strlcpy(isp_dev->media_dev.model, "rkisp1", sizeof(isp_dev->media_dev.model)); isp_dev->media_dev.dev = &pdev->dev; isp_dev->media_dev.link_notify = rkisp1_pipeline_link_notify; media_device_register(&isp_dev->media_dev); rkisp1_register_platform_subdevs (isp_dev);
media_device_register这个是公共函数,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30int __must_check __media_device_register(struct media_device *mdev, struct module *owner) { int ret; if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0)) return -EINVAL; mdev->entity_id = 1; INIT_LIST_HEAD(&mdev->entities); spin_lock_init(&mdev->lock); mutex_init(&mdev->graph_mutex); /* Register the device node. */ mdev->devnode.fops = &media_device_fops;//ioctl函数就在这里 mdev->devnode.parent = mdev->dev; mdev->devnode.release = media_device_release; ret = media_devnode_register(&mdev->devnode, owner); if (ret < 0) return ret; ret = device_create_file(&mdev->devnode.dev, &dev_attr_model); if (ret < 0) { media_devnode_unregister(&mdev->devnode); return ret; } return 0; }
创建节点和文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15static int rkisp1_register_platform_subdevs(struct rkisp1_device *dev) { ret = rkisp1_register_isp_subdev(dev, &dev->v4l2_dev);//ispsubdev节点 if (ret < 0) goto err_cleanup_ctx; ret = rkisp1_register_stream_vdevs(dev);//video节点 if (ret < 0) goto err_unreg_isp_subdev; …… ret = rkisp1_register_stats_vdev(&dev->stats_vdev, &dev->v4l2_dev, dev); ret = rkisp1_register_params_vdev(&dev->params_vdev, &dev->v4l2_dev,dev); ret = isp_subdev_notifier(dev); ……
isp_subdev_notifier。
调用v4l2_async_notifier_parse_fwnode_endpoints
(里面包含endpoints的解析)和v4l2_async_notifier_register。
异步注册,就是父设备中调用v4l2_async_notifier_register(),并实现v4l2_async_notifier_operations结构体中的bound(),complete(),unbound()三个函数,当子设备调用v4l2_device_register_subdev()进行注册的时候,会根据match_type去进行匹配,如果匹配成功,则父设备就会收到通知,并且父设备中实现的bound函数就会被调用(子设备有几次匹配成功,该bound就会被调用几次),当所有的子设备都注册并匹配成功后,父设备中的complete()函数就会被调用,为所有的子设备生成设备节点的函数v4l2_device_register_subdev_nodes()一般都是在父设备的complete()函数中来调用的,所以complete函数打印rkisp1: Async subdev notifier completed说明注册成功
看注册isp subdev和初始化它的entity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24int rkisp1_register_isp_subdev(struct rkisp1_device *isp_dev, struct v4l2_device *v4l2_dev) { struct rkisp1_isp_subdev *isp_sdev = &isp_dev->isp_sdev; struct v4l2_subdev *sd = &isp_sdev->sd; int ret; v4l2_subdev_init(sd, &rkisp1_isp_sd_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.ops = &rkisp1_isp_sd_media_ops;//entity的操作函数, snprintf(sd->name, sizeof(sd->name), "rkisp1-isp-subdev"); isp_sdev->pads[RKISP1_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; isp_sdev->pads[RKISP1_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK; isp_sdev->pads[RKISP1_ISP_PAD_SOURCE_PATH].flags = MEDIA_PAD_FL_SOURCE; isp_sdev->pads[RKISP1_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_init(&sd->entity, RKISP1_ISP_PAD_MAX, isp_sdev->pads, 0); ret = v4l2_device_register_subdev(v4l2_dev, sd);//注册subdev //注册input参数和统计设备 }
这部分是初始化rkisp1-isp-subdev这个entity和里面的pad,里面links分配结构空间。
再看isp vdev部分
1
2
3
4
5
6
7
8
9
10
11
12
13int rkisp1_register_stream_vdevs(struct rkisp1_device *dev) { struct rkisp1_stream *stream; int i, j, ret; for (i = 0; i < RKISP1_MAX_STREAM; i++) { stream = &dev->stream[i]; stream->ispdev = dev; ret = rkisp1_register_stream_vdev(stream); …… } rkisp1_register_stream_vdev里面注册了mp,sp和raw 3个video节点,对应有操作函数和ioctl。初始化它们的pad,这几个只有一个pad。
上面注册了isp mp和sp的entity ,然后看看其他dphy和sensor怎么注册成为subdev
以ov5695为例:
在驱动probe函数调用
media_entity_init(&sd->entity, 1, &ov5695->pad, 0);//初始化entity,里面一个pad
v4l2_async_register_subdev_sensor_common //异步注册
phy-rockchip-mipi-rx.c,mipi 驱动也是也同样注册方式,注册2个pad。
当前问题,这些entity怎么链接起来的,看dts定义了一些remote-endpoint,
上面看v4l2_async_notifier_parse_fwnode_endpoints会解析这些端点。
异步注册绑定。
1
2
3
4
5
6
7
8
9v4l2_async_notifier_operations rockchip_mipidphy_async_ops = { .bound = rockchip_mipidphy_notifier_bound, .unbind = rockchip_mipidphy_notifier_unbind, }; static const struct v4l2_async_notifier_operations subdev_notifier_ops = { .bound = subdev_notifier_bound,//子设备匹配成功 .complete = subdev_notifier_complete,//所有子设备匹配成功 };
子设备绑定成功后会调用bound函数,也就是camera绑定mipiphy,mipiphy绑定isp。Bound函数创建了link。
当所有的子设备绑定完之后会调用subdev_notifier_complete函数
1
2
3
4
5
6
7
8
9
10static int subdev_notifier_complete(struct v4l2_async_notifier *notifier) { ret = rkisp1_create_links(dev);// media_entity_create_link,enable相关link ret = v4l2_device_register_subdev_nodes(&dev->v4l2_dev);//生成设备节点 ret = rkisp1_update_sensor_info(dev); ret = _set_pipeline_default_fmt(dev); v4l2_info(&dev->v4l2_dev, "Async subdev notifier completedn"); }
接下来看下media有哪些ioctl
1
2
3
4
5#define MEDIA_IOC_DEVICE_INFO _IOWR('|', 0x00, struct media_device_info) #define MEDIA_IOC_ENUM_ENTITIES _IOWR('|', 0x01, struct media_entity_desc) #define MEDIA_IOC_ENUM_LINKS _IOWR('|', 0x02, struct media_links_enum) #define MEDIA_IOC_SETUP_LINK _IOWR('|', 0x03, struct media_link_desc)
1
2
3
4
5
6
7
8
9
10static const struct media_file_operations media_device_fops = { .owner = THIS_MODULE, .open = media_device_open, .ioctl = media_device_ioctl, //所以只需关注这个函数 #ifdef CONFIG_COMPAT .compat_ioctl = media_device_compat_ioctl, #endif /* CONFIG_COMPAT */ .release = media_device_close, };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33static long media_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct media_devnode *devnode = media_devnode_data(filp); struct media_device *dev = to_media_device(devnode); long ret; switch (cmd) { case MEDIA_IOC_DEVICE_INFO: ret = media_device_get_info(dev, (struct media_device_info __user *)arg); break;//直接从media_device结构里获取信息返回 case MEDIA_IOC_ENUM_ENTITIES: ret = media_device_enum_entities(dev, (struct media_entity_desc __user *)arg); break;//只是找到对应entity case MEDIA_IOC_ENUM_LINKS: mutex_lock(&dev->graph_mutex); ret = media_device_enum_links(dev, (struct media_links_enum __user *)arg); mutex_unlock(&dev->graph_mutex); break;//这个是枚举一个entity的pads和links, //__media_device_enum_links函数里会判断links->pads,links->links决定枚举单个还是两个一起。注意links里面不会返回backlink到应用。(在创建link的时候,我在源端和sink端都会创建links。这样就会多一个backlink,创建时候当前entity的源端entity不一致,就说明是backlink,也就返回应用的link都是本地到外部的link) case MEDIA_IOC_SETUP_LINK: mutex_lock(&dev->graph_mutex); ret = media_device_setup_link(dev, (struct media_link_desc __user *)arg); mutex_unlock(&dev->graph_mutex); break;//先是从参数找到source和sink的entity,再从source中找到link和当前要设置的链接匹配然后调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14int __media_entity_setup_link(struct media_link *link, u32 flags) { …… mdev = source->parent; if (mdev->link_notify) { ret = mdev->link_notify(link, flags, MEDIA_DEV_NOTIFY_PRE_LINK_CH); if (ret < 0) return ret; } ret = __media_entity_setup_link_notify(link, flags); if (mdev->link_notify) mdev->link_notify(link, flags, MEDIA_DEV_NOTIFY_POST_LINK_CH);
1
2
3
4
5
6
7
8
9
10
11static int __media_entity_setup_link_notify(struct media_link *link, u32 flags) { …… ret = media_entity_call(link->source->entity, link_setup, link->source, link->sink, flags); ret = media_entity_call(link->sink->entity, link_setup, link->sink, link->source, flags); 分辨执行source和sink的ops的link_setup函数 sd->entity.ops = &rkisp1_isp_sd_media_ops;//只有isp有entity的操作函数 实际只判断如果是dma设备置了变量,没做什么
Setup前后调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33static int rkisp1_pipeline_link_notify(struct media_link *link, u32 flags, unsigned int notification) { struct media_entity *source = link->source->entity; struct media_entity *sink = link->sink->entity; int source_use = rkisp1_pipeline_pm_use_count(source); int sink_use = rkisp1_pipeline_pm_use_count(sink); int ret; if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && !(flags & MEDIA_LNK_FL_ENABLED)) { /* Powering off entities is assumed to never fail. */ rkisp1_pipeline_pm_power(source, -sink_use); rkisp1_pipeline_pm_power(sink, -source_use); return 0; } if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && (flags & MEDIA_LNK_FL_ENABLED)) { ret = rkisp1_pipeline_pm_power(source, sink_use); if (ret < 0) return ret; ret = rkisp1_pipeline_pm_power(sink, source_use); if (ret < 0) rkisp1_pipeline_pm_power(source, -sink_use); return ret; } return 0; }
看着名字似乎是只是电源管理,其实也是,使能链接就是把对应的电源供上。
遍历调用s_power函数
.s_power = rkisp1_isp_sd_s_power, isp的power函数
.s_power = mipidphy_s_power, phy的power函数
.s_power = ov13850_s_power camera的power函数,比如ov13580
小结: media子系统相对来说比较简单,只是对media设备和entity pad link的相关操作结构描述。
最后
以上就是落寞金针菇最近收集整理的关于camera_kernel之---media子系统(3)的全部内容,更多相关camera_kernel之---media子系统(3)内容请搜索靠谱客的其他文章。
发表评论 取消回复