工作:
sensor
LIST_HEAD(notifier_list)----+ LIST_HEAD(subdev_list)----+
| |
| |
| 挂载 | 挂载
| |
struct techpoint | |
struct v4l2_async_notifier *subdev_notifier; -----------+ |
struct v4l2_subdev *sd; ---------+ |
| bind |
struct v4l2_subdev subdev;-----------+ ---------------------------------------------------------------+
const struct v4l2_subdev_ops *ops(init!!!)
struct v4l2_device *v4l2_dev; (NULL)
struct i2c_client *client;
struct device dev;
struct device_node *of_node; // A 如节点 tp2855_1@44
struct device_node *child; //Aa 如节点 ports
+-------struct fwnode_handle *fwnode;
| +---struct device_node *child; // Aa-1 如节点 ucam_out0
| |
| |
| |
| | mipi_csi_dphy
| |
| | LIST_HEAD(notifier_list)-----+ LIST_HEAD(subdev_list)-----+
| | | |
| | |挂载 |挂载
| | struct mipidphy_priv | |
| | +-------struct v4l2_async_notifier notifier; --------------------+ |
| | | const struct v4l2_async_notifier_operations *ops; (init!!!) |
| | | +-------struct list_head waiting;//等待通知链表 |
| | | | struct v4l2_async_subdev **subdevs;//数组 |
| | | +-----------|//subdevs[n] 将远端目标节点封装为 v4l2_async_subdev 结构 保存在该数组 |
| | | |enum v4l2_async_match_type match_type = V4L2_ASYNC_MATCH_FWNODE |
| | | |union match |
+---|-------|--------------------struct fwnode_handle *fwnode; |
| | struct v4l2_subdev *sd ------------+ |
| | | |
| | struct v4l2_subdev sd;-----------------+ -----------------------------------------------------+
| | const struct v4l2_subdev_ops *ops
| | struct v4l2_device *v4l2_dev; (NULL)
| +------------struct v4l2_async_notifier *subdev_notifier;
|
| struct device *dev;
| struct device_node *of_node; //A associated device tree node 如 节点 csi_dphy0
| struct device_node *child; //Aa 第一个子节点 如 节点 ports
| struct device_node *child; // Aa-1 第一个子节点 如 节点 port@0
| struct device_node *child; //Aa-1-1 第一个子节点 如 节点 csi_dphy1_input: endpoint@1
|------------------------------------remote-endpoint = <&ucam_out0> 设备树属性
struct device_node *sibling; // Aa-2 第二个子节点 如 节点 port@1
+-----------------------------------struct fwnode_handle *fwnode;
| +---------------------------struct device_node *child; //Aa-2-1 第一个子节点 如 节点 csi_dphy1_output: endpoint@0
| | remote-endpoint = <&mipi_csi2_input> 设备树属性
| |
| |
| |
| |
| |
| | mipi_csi
| | LIST_HEAD(notifier_list)-----+ LIST_HEAD(subdev_list)----+
| | | |
| | | |
| | struct csi2_dev | |
| | struct device *dev;== platform_device *pdev->dev | |
| | +-----------struct v4l2_async_notifier notifier;-------------------------------------+ |
| | | const struct v4l2_async_notifier_operations *ops; (init !!!!) |
| | | +-------struct list_head waiting;//等待通知链表 |
| | | | struct v4l2_async_subdev **subdevs;//subdevs数组 |
| | | +-----------|//subdevs[n] 将远端目标节点封装为 v4l2_async_subdev 结构 保存在该数组 |
| | | |enum v4l2_async_match_type match_type = V4L2_ASYNC_MATCH_FWNODE 设备树fwnode匹配方式 |
| | | |union match |
+-------|---------------|-----------------------struct fwnode_handle *fwnode; |
| | struct v4l2_subdev *sd -----+ |
| | | |
| | | |
| | struct v4l2_subdev sd; --------+--------------------------------------------------------------------------+
| | const struct v4l2_subdev_ops *ops;
| | struct v4l2_device *v4l2_dev; (NULL)
| | struct device *dev;== platform_device *pdev->dev
| +---------------struct v4l2_async_notifier notifier;
|
| struct device *dev;
| (-->)struct device_node *of_node; //A associated device tree node 如 节点 mipi_csi2
| struct device_node *child; //Aa 第一个子节点 如 节点 ports
| struct device_node *child; // Aa-1 第一个子节点 如 节点 port@0
| struct device_node *child; //Aa-1-1 第一个子节点 如 节点 endpoint@1
+---------------------------------------------------remote-endpoint = <&csi_dphy0_output>; 设备树属性
&mipi_csi2 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
mipi_csi2_input: endpoint@1 {
reg = <1>;
remote-endpoint = <&csi_dphy0_output>;
data-lanes = <1 2 3 4>;
};
};
port@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
mipi_csi2_output: endpoint@0 {
reg = <0>;
remote-endpoint = <&cif_mipi_in>;
data-lanes = <1 2 3 4>;
};
};
};
};
kernel\drivers\media\platform\rockchip\cif\mipi-csi2.c
struct csi2_dev {
struct device *dev;
struct v4l2_subdev sd;
struct media_pad pad[CSI2_NUM_PADS];
struct clk_bulk_data *clks_bulk;
int clks_num;
struct reset_control *rsts_bulk;
void __iomem *base;
struct v4l2_async_notifier notifier;
struct v4l2_fwnode_bus_mipi_csi2 bus;
/* lock to protect all members below */
struct mutex lock;
struct v4l2_mbus_framefmt format_mbus;
struct v4l2_rect crop;
int stream_count;
struct v4l2_subdev *src_sd;
bool sink_linked[CSI2_NUM_SRC_PADS];
struct csi2_sensor sensors[MAX_CSI2_SENSORS];
const struct csi2_match_data *match_data;
int num_sensors;
atomic_t frm_sync_seq;
struct csi2_err_stats err_list[RK_CSI2_ERR_MAX];
};
static const struct
v4l2_async_notifier_operations csi2_async_ops = {
.bound = csi2_notifier_bound,
.unbind = csi2_notifier_unbind,
};
static const struct v4l2_subdev_core_ops csi2_core_ops = {
.subscribe_event = rkcif_csi2_subscribe_event,
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
static const struct v4l2_subdev_video_ops csi2_video_ops = {
.g_mbus_config = csi2_g_mbus_config,
.s_stream = csi2_s_stream,
};
static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
.get_fmt = csi2_get_set_fmt,
.set_fmt = csi2_get_set_fmt,
.get_selection = csi2_get_selection,
.set_selection = csi2_set_selection,
};
static const struct v4l2_subdev_ops csi2_subdev_ops = {
.core = &csi2_core_ops,
.video = &csi2_video_ops,
.pad = &csi2_pad_ops,
};
static const struct csi2_match_data rv1126_csi2_match_data = {
.chip_id = CHIP_RV1126_CSI2,
.num_pads = CSI2_NUM_PADS,//5
};
static const struct of_device_id csi2_dt_ids[] = {
{
.compatible = "rockchip,rk1808-mipi-csi2",
.data = &rk1808_csi2_match_data,
},
{
.compatible = "rockchip,rk3288-mipi-csi2",
.data = &rk3288_csi2_match_data,
},
{
.compatible = "rockchip,rk3568-mipi-csi2",
.data = &rk3568_csi2_match_data,
},
{
.compatible = "rockchip,rv1126-mipi-csi2",
.data = &rv1126_csi2_match_data,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, csi2_dt_ids);
static const struct media_entity_operations csi2_entity_ops = {
.link_setup = csi2_link_setup,
.link_validate = v4l2_subdev_link_validate,
};
static int csi2_notifier(struct csi2_dev *csi2)
{
struct v4l2_async_notifier *ntf = &csi2->notifier;
int ret;
/* 设备树解析
参数1 csi2_dev->device
参数2 csi2_dev->v4l2_async_notifier
参数3 size_t v4l2_async_subdev
参数4 0
参数5 函数指针 csi2_parse_endpoint
*/
/*
解析 endpoint1 节点对应的远程端点(csi_dphy0的 子节点 port@1 ),并记录到 mipi_csi2->v4l2_async_notifier->subdevs[]数组
工作
1 通过 mipi_csi2 节点 找到 最底层孙设备 mipi_csi2_input: endpoint@1 节点
struct mipi_csi2
struct device *dev;
(-->)struct device_node *of_node; //A associated device tree node 如 节点 mipi_csi2
struct device_node *child; //Aa 第一个子节点 如 节点 ports
struct device_node *child; // Aa-1 第一个子节点 如 节点 port@0
struct device_node *child; //Aa-1-1 第一个子节点 如 节点 endpoint@1
(<--)struct fwnode_handle fwnode;
2 通过 最底层孙设备 endpoint@1 节点 的 "remote-endpoint" 属性 找到 远程关联节点 csi_dphy0_output 的 父节点 port@1
3 将 远程关联节点 csi_dphy0_output 的 父节点 port@1 信息 组织成 struct v4l2_async_subdev 数据结构。作为子设备 添加到 struct mipi_csi2 中 。
这样 就将 mipi_csi2 信息与 mipidphy 信息 关联了起来
struct mipi_csi2
struct device *dev;
(-->)struct device_node *of_node; //A associated device tree node 如 节点 mipi_csi2
struct device_node *child; //Aa 第一个子节点 如 节点 ports
struct device_node *child; // Aa-1 第一个子节点 如 节点 port@0
struct device_node *child; //Aa-1-1 第一个子节点 如 节点 endpoint@1
(<--)struct fwnode_handle fwnode;
struct v4l2_async_notifier notifier;
struct v4l2_async_subdev **subdevs;//subdevs数组
enum v4l2_async_match_type match_type = V4L2_ASYNC_MATCH_FWNODE 设备树fwnode匹配方式
union match
struct fwnode_handle *fwnode;-----------------------------------------------------相连接的远程节点csi_dphy0_output 的 父节点 port@1 的 fwnode
*/
ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(csi2->dev,
&csi2->notifier,
sizeof(struct v4l2_async_subdev), 0,
csi2_parse_endpoint);
if (ret < 0)
return ret;
if (!ntf->num_subdevs)
return -ENODEV; /* no endpoint */
csi2->sd.subdev_notifier = &csi2->notifier;
csi2->notifier.ops = &csi2_async_ops;
/* 主要就是将异步通知notifier挂载到 全局notifier_list链表上
1 记录v4l2_subdev 到 异步通知notifier notifier->sd = sd
2 将异步通知notifier挂载到 全局notifier_list链表上
3 对于mipi-csi 驱动 还有 将 远端关联节点 挂载到 struct csi2_dev -> v4l2_async_notifier->waiting 等待链表上
v4l2_subdev
v4l2_async_notifier
*/
ret = v4l2_async_subdev_notifier_register(&csi2->sd, &csi2->notifier);
if (ret) {
v4l2_err(&csi2->sd,
"failed to register async notifier : %d\n",
ret);
v4l2_async_notifier_cleanup(&csi2->notifier);
return ret;
}
/*
* 这里会有v4l2_dev = NULL不执行
* 只是将当前subdev挂载到 全局链表subdev_list上
*/
ret = v4l2_async_register_subdev(&csi2->sd);
return ret;
}
static int csi2_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct csi2_dev *csi2 = NULL;
struct resource *res;
const struct csi2_match_data *data;
int ret, irq;
match = of_match_node(csi2_dt_ids, node);
if (IS_ERR(match))
return PTR_ERR(match);
data = match->data;
csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL);
if (!csi2)
return -ENOMEM;
csi2->dev = &pdev->dev;
csi2->match_data = data;
/*
初始化 csi2_dev->v4l2_subdev->ops
*/
v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops);
v4l2_set_subdevdata(&csi2->sd, &pdev->dev);
csi2->sd.entity.ops = &csi2_entity_ops;
csi2->sd.dev = &pdev->dev;
csi2->sd.owner = THIS_MODULE;
csi2->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
ret = strscpy(csi2->sd.name, DEVICE_NAME, sizeof(csi2->sd.name));
if (ret < 0)
v4l2_err(&csi2->sd, "failed to copy name\n");
platform_set_drvdata(pdev, &csi2->sd);
...
mutex_init(&csi2->lock);
...
/*
参数 struct csi2_dev *csi2
*/
ret = csi2_notifier(csi2);
if (ret)
goto rmmutex;
...
}
static struct platform_driver csi2_driver = {
.driver = {
.name = DEVICE_NAME,
.of_match_table = csi2_dt_ids,
},
.probe = csi2_probe,
.remove = csi2_remove,
};
int __init rkcif_csi2_plat_drv_init(void)
{
return platform_driver_register(&csi2_driver);
}
void __exit rkcif_csi2_plat_drv_exit(void)
{
platform_driver_unregister(&csi2_driver);
}
kernel\drivers\media\v4l2-core\v4l2-async.c
int v4l2_async_register_subdev(struct v4l2_subdev *sd)
{
struct v4l2_async_notifier *subdev_notifier;
struct v4l2_async_notifier *notifier;
int ret;
if (!sd->fwnode && sd->dev)
sd->fwnode = dev_fwnode(sd->dev);
mutex_lock(&list_lock);
INIT_LIST_HEAD(&sd->async_list);
...
/*
* 没有匹配到相关的信息
* 将当前subdev挂载到 全局链表subdev_list上
*/
list_add(&sd->async_list, &subdev_list);
...
return ret;
}