现在继续分析 mipi csi 的 bound()函数
工作:
drivers\media\platform\rockchip\cif\mipi-csi2.c
static const struct v4l2_async_notifier_operations csi2_async_ops = {
.bound = csi2_notifier_bound,
.unbind = csi2_notifier_unbind,
};
/* The .bound() notifier callback when a match is found */
static int
csi2_notifier_bound(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *sd,
struct v4l2_async_subdev *asd)
{
struct csi2_dev *csi2 = container_of(notifier,
struct csi2_dev,
notifier);
struct csi2_sensor *sensor;
struct media_link *link;
unsigned int pad, ret;
if (csi2->num_sensors == ARRAY_SIZE(csi2->sensors)) {
v4l2_err(&csi2->sd,
"%s: the num of sd is beyond:%d\n",
__func__, csi2->num_sensors);
return -EBUSY;
}
sensor = &csi2->sensors[csi2->num_sensors++];
/*
* 这里的sd是mipi csi phy的subdev
*/
sensor->sd = sd;
/*
* mipi csi phy 的entity有2个pad
* 1个sink pad,1个source pad
* 这里是要找到source pad
* 为什么要找source呢?
* 因为source才是下一级需要的
* 可以看到这里只考虑了一个pad
* 因为写驱动的人知道source media 及 sink media的pad的个数
*/
for (pad = 0; pad < sd->entity.num_pads; pad++)
if (sensor->sd->entity.pads[pad].flags
& MEDIA_PAD_FL_SOURCE)
break;
if (pad == sensor->sd->entity.num_pads) {
dev_err(csi2->dev,
"failed to find src pad for %s\n",
sd->name);
return -ENXIO;
}
/*
参数1 mipi csi phy的 entity 子设备的entity
参数2 mipi csi phy的 entity 中的 source pad 下标编号 子设备的entity中的 source pad 下标编号
参数3 mipi csi的 entity 当前设备的entity
参数4 RK_CSI2_PAD_SINK RK_CSI2_PAD_SINK
0
工作
mipi csi
|----------------------------------|
mipi csi phy mipi csi phy link | entity |
|----------------------------------| | #-------------# |
|----------------------------------| | #-------------# #-------------# | | | source0 pad |--|-
| entity | +--|->| source pad | | sink pad |-|--+ | #-------------# |
| #-------------# #-------------#| | | #-------------# #-------------# | | | #-------------# |
数据源 -------|-->| sink pad | | source1 pad ||--+ |----------------------------------| | | #----------# | source1 pad |--|-
| #-------------# #-------------#| | mipi csi link +----|-->| sink pad | #-------------# |
|----------------------------------| | |----------------------------------| | | #----------# #-------------# |
| | #-------------# #-------------# | | | | source2 pad |--|-
+--|->| source pad | | sink pad |-|--+ | #-------------# |
| #-------------# #-------------# | | #-------------# |
|----------------------------------| | | source2 pad |--|-
| #-------------# |
|----------------------------------|
绑定 子设备mipidphy_priv ----- link ----- 多媒体设备
绑定 设备 mipi csi link ----- 多媒体设备
*/
ret = media_create_pad_link(&sensor->sd->entity, pad,
&csi2->sd.entity, RK_CSI2_PAD_SINK,
0/* csi2->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED */);
if (ret) {
dev_err(csi2->dev,
"failed to create link for %s\n",
sd->name);
return ret;
}
/*
media_create_pad_link中已经设置:
struct csi2_dev
struct v4l2_subdev sd; (backlink)
struct media_entity entity; struct media_link {
struct list_head links; ---创建绑定---> struct list_head list;
struct media_gobj graph_obj------+
struct list_head list; ---|------+
| |
struct media_gobj graph_obj 指定同一个 多媒体设备 | |
struct media_device *mdev; --------------------------------------------------+ | 关联
struct list_head links ---------------------------------------------------------+
所以这里就是 获取 csi2_dev模块抽象的 反向链接 backlink !!!
*/
link = list_first_entry(&csi2->sd.entity.links, struct media_link, list);
/*
参数1 csi2_dev模块抽象的 反向链接backlink !!!
参数2 MEDIA_LNK_FL_ENABLED
*/
/*
工作 :
主要就是调用 mipi csi 的 link_setup == csi2_link_setup。
在V4L2设备层面,即 v4l2_subdev层面 建立数据源之间的关系, 指定 mipi csi 的V4L2子设备 数据源 是 mipi-csi-phy的V4L2子设备
*/
ret = media_entity_setup_link(link, MEDIA_LNK_FL_ENABLED);
if (ret) {
dev_err(csi2->dev,
"failed to create link for %s\n",
sensor->sd->name);
return ret;
}
return 0;
}
/*
参数1 mipi csi phy 的 entity 子设备的entity
参数2 mipi csi phy 的 entity 中的 source pad 下标编号 子设备的entity中的 source pad 下标编号
参数3 mipi csi的 entity 当前设备的entity
参数4 RK_CSI2_PAD_SINK RK_CSI2_PAD_SINK
0
*/
/*
工作
mipi csi
|----------------------------------|
mipi csi phy mipi csi phy link | entity |
|----------------------------------| | #-------------# |
|----------------------------------| | #-------------# #-------------# | | | source0 pad |--|-
| entity | +--|->| source pad | | sink pad |-|--+ | #-------------# |
| #-------------# #-------------#| | | #-------------# #-------------# | | | #-------------# |
数据源 -------|-->| sink pad | | source1 pad ||--+ |----------------------------------| | | #----------# | source1 pad |--|-
| #-------------# #-------------#| | mipi csi link +----|-->| sink pad | #-------------# |
|----------------------------------| | |----------------------------------| | | #----------# #-------------# |
| | #-------------# #-------------# | | | | source2 pad |--|-
+--|->| source pad | | sink pad |-|--+ | #-------------# |
| #-------------# #-------------# | | #-------------# |
|----------------------------------| | | source2 pad |--|-
| #-------------# |
|----------------------------------|
绑定 子设备mipidphy_priv ----- link ----- 多媒体设备
绑定 设备 mipi csi link ----- 多媒体设备
此时
mipi csi phy设备抽象 ---- mipi csi phy 的 抽象链接 ---- mipi csi设备抽象
---- mipi csi的 抽象链接 ----
关联 这两个link : link和backlink
*/
int media_create_pad_link(struct media_entity *source, u16 source_pad,
struct media_entity *sink, u16 sink_pad, u32 flags)
{
struct media_link *link;
struct media_link *backlink;
BUG_ON(source == NULL || sink == NULL);
BUG_ON(source_pad >= source->num_pads);
BUG_ON(sink_pad >= sink->num_pads);
/*
参数 子设备 mipi csi phy 的entity中的 links
创建子设备的 entity 的连接抽象(media_link)
struct media_entity { struct media_link {
struct list_head links; -----------创建绑定---------->struct list_head list;
并返回 media_link
*/
link = media_add_link(&source->links);
if (link == NULL)
return -ENOMEM;
//初始化 子设备的entity中的 连接抽象(media_link)
/*
链接管道 子设备mipidphy_priv ----- 正向link ----- mipi csi phy
mipi csi
|----------------------------------|
| entity |
mipi csi phy | #-------------# |
| | source0 pad |--|-
|----------------------------------| mipi csi phy link | #-------------# |
| entity | |----------------------------------| | #-------------# |
| #-------------# #-------------#| | #-------------# #-------------# | | #----------# | source1 pad |--|-
数据源 -------|-->| sink pad | | source1 pad || ----|->| source pad | | | sink pad | | ------|-->| sink pad | #-------------# |
| #-------------# #-------------#| | #-------------# #-------------# | | #----------# #-------------# |
|----------------------------------| |----------------------------------| | | source2 pad |--|-
| #-------------# |
| #-------------# |
| | source2 pad |--|-
| #-------------# |
|----------------------------------|
*/
link->source = &source->pads[source_pad];
link->sink = &sink->pads[sink_pad];
link->flags = flags & ~MEDIA_LNK_FL_INTERFACE_LINK;
/* Initialize graph object embedded at the new link
绑定 子设备mipidphy_priv ----- link ----- 多媒体设备
struct mipidphy_priv
struct v4l2_subdev sd; 绑定 entity---link
struct media_entity entity; | struct media_link
struct list_head links; ---------> struct list_head list;
struct media_gobj graph_obj;
struct media_device *mdev------+
struct list_head list; -----|--->-+
| |
struct media_gobj graph_obj; 绑定 多媒体设备--link | |
struct media_device *mdev; -----------------------------------+ | 挂载到多媒体设备中的 links 链表
struct list_head links; ------------------<----------------------+
*/
media_gobj_create(source->graph_obj.mdev, MEDIA_GRAPH_LINK,
&link->graph_obj);
//关于反向链接
/*
struct csi2_dev
struct v4l2_subdev sd; 反向link
struct media_entity entity; struct media_link {
struct list_head links;-----创建绑定-----> struct list_head list;
*/
backlink = media_add_link(&sink->links);
if (backlink == NULL) {
__media_entity_remove_link(source, link);
return -ENOMEM;
}
/*
链接管道 子设备 mipi csi phy ----- link(mipi csi link) ----- mipi csi
mipi csi
|----------------------------------|
| entity |
mipi csi phy | #-------------# |
| | source0 pad |--|-
|----------------------------------| mipi csi back_link | #-------------# |
| entity | |----------------------------------| | #-------------# |
| #-------------# #-------------#| | #-------------# #-------------# | | #----------# | source1 pad |--|-
数据源 -------|-->| sink pad | | source1 pad || ----|->| source pad | | | sink pad | | ------|-->| sink pad | #-------------# |
| #-------------# #-------------#| | #-------------# #-------------# | | #----------# #-------------# |
|----------------------------------| |----------------------------------| | | source2 pad |--|-
mipi csi link | #-------------# |
| #-------------# |
| | source2 pad |--|-
| #-------------# |
|----------------------------------|
*/
backlink->source = &source->pads[source_pad];
backlink->sink = &sink->pads[sink_pad];
backlink->flags = flags;
backlink->is_backlink = true;
/*
绑定 设备 mipi csi link ----- 多媒体设备
struct csi2_dev
struct v4l2_subdev sd; 绑定 entity---backlink
struct media_entity entity; | struct media_link {
struct list_head links; ---创建绑定---> struct list_head list;
struct media_gobj graph_obj------+
struct list_head list; ---|------+
| |
struct media_gobj graph_obj 绑定 多媒体设备--backlink | |
struct media_device *mdev; --------------------------------------------------+ | 挂载到多媒体设备中的 links 链表
struct list_head links ---------------------------------------------------------+
*/
media_gobj_create(sink->graph_obj.mdev, MEDIA_GRAPH_LINK,
&backlink->graph_obj);
/*
* link和backlink关联起来
*/
link->reverse = backlink;
backlink->reverse = link;
sink->num_backlinks++;
sink->num_links++;
source->num_links++;
return 0;
}
EXPORT_SYMBOL_GPL(media_create_pad_link);
/*
参数1 csi2_dev模块抽象的 反向链接backlink !!!
参数2 MEDIA_LNK_FL_ENABLED
*/
int media_entity_setup_link(struct media_link *link, u32 flags)
{
int ret;
mutex_lock(&link->graph_obj.mdev->graph_mutex);
ret = __media_entity_setup_link(link, flags);
mutex_unlock(&link->graph_obj.mdev->graph_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(media_entity_setup_link);
/*
参数1 mipi csi 模块抽象的 反向链接backlink !!!
在前面的 media_create_pad_link 中已经设置了 mipi csi 模块抽象的 反向连接(backlink) 的连接方式 : 上一级是 mipi csi phy 下一级是mipi csi
参数2 MEDIA_LNK_FL_ENABLED
*/
int __media_entity_setup_link(struct media_link *link, u32 flags)
{
const u32 mask = MEDIA_LNK_FL_ENABLED;
struct media_device *mdev;
struct media_entity *source, *sink;
int ret = -EBUSY;
...
/*
在前面的 media_create_pad_link 中已经设置了 mipi csi 模块抽象的 反向连接(backlink) 的连接方式 : 上一级是 mipi csi phy 下一级是mipi csi
source entity : mipi csi phy的 模块抽象 entity
sink entity : mipi csi的 模块抽象 entity
*/
source = link->source->entity;
sink = link->sink->entity;
// 找到 mipi csi phy的 模块抽象 entity 所属的 多媒体设备
mdev = source->graph_obj.mdev;
/*
medv没有ops,跳过
*/
if (mdev->ops && mdev->ops->link_notify) {
ret = mdev->ops->link_notify(link, flags,
MEDIA_DEV_NOTIFY_PRE_LINK_CH);
if (ret < 0)
return ret;
}
/*
参数1 mipi csi 模块抽象的 反向链接backlink !!!
参数2 MEDIA_LNK_FL_ENABLED
*/
ret = __media_entity_setup_link_notify(link, flags);
if (mdev->ops && mdev->ops->link_notify)
mdev->ops->link_notify(link, flags,
MEDIA_DEV_NOTIFY_POST_LINK_CH);
return ret;
}
EXPORT_SYMBOL_GPL(__media_entity_setup_link);
/*
参数1 mipi csi 模块抽象的 反向链接backlink !!!
参数2 MEDIA_LNK_FL_ENABLED
*/
static int __media_entity_setup_link_notify(struct media_link *link, u32 flags)
{
int ret;
/* Notify both entities. */
/*
对于csi2_dev模块抽象的 反向链接backlink !!!
source entity : mipi csi phy的 模块抽象 entity
sink entity : mipi csi的 模块抽象 entity
*/
//mipi csi phy的 模块抽象 entity 没有 ops
ret = media_entity_call(link->source->entity, link_setup,
link->source, link->sink, flags);
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
//mipi csi的 模块抽象 entity 有 ops
/*
调用 ops:
csi2_entity_ops = {
.link_setup = csi2_link_setup,
参数1 mipi csi的 模块抽象 entity
参数2 mipi csi 模块抽象的 反向链接backlink 所连接的 下一级pad 即 pad(mipi csi)
参数3 mipi csi phy 模块抽象的 反向链接backlink 所连接的 上一级pad 即 pad(mipi csi phy)
参数4 MEDIA_LNK_FL_ENABLED
工作:csi2_link_setup
在V4L2设备层面,即 v4l2_subdev层面 建立数据源之间的关系, 指定 mipi csi 的V4L2子设备 数据源 是 mipi-csi-phy的V4L2子设备
struct csi2_dev struct mipidphy_priv
struct v4l2_subdev *src_sd;------绑定-----struct v4l2_subdev sd
*/
ret = media_entity_call(link->sink->entity, link_setup,
link->sink, link->source, flags);
if (ret < 0 && ret != -ENOIOCTLCMD) {
media_entity_call(link->source->entity, link_setup,
link->source, link->sink, link->flags);
return ret;
}
link->flags = flags;
link->reverse->flags = link->flags;
return 0;
}