我是靠谱客的博主 凶狠果汁,这篇文章主要介绍v4l2_async_notifier_parse_fwnode_endpoints_by_port 分析Linux v4l2架构学习总链接,现在分享给大家,希望可以做个参考。

Linux v4l2架构学习总链接

这个函数比较大,这里一部分一部分分析

v4l2_async_notifier_parse_fwnode_endpoints_by_port

-> __v4l2_async_notifier_parse_fwnode_endpoints


has_port = TRUE

port = 0

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static int __v4l2_async_notifier_parse_fwnode_endpoints( struct device *dev, struct v4l2_async_notifier *notifier, size_t asd_struct_size, unsigned int port, bool has_port, int (*parse_endpoint)(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd)) { struct fwnode_handle *fwnode; unsigned int max_subdevs = notifier->max_subdevs; int ret; if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev))) return -EINVAL; /* 首先fwnode_graph_get_next_endpoint 要分析一下 */ for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint( dev_fwnode(dev), fwnode)); ) { ... }
复制代码
1
2
fwnode_graph_get_next_endpoint(dev_fwnode(dev), fwnode)

其中dev_fwnode 就是dev->of_node->fwnode,

of_node和fwnode区别,看这里Linux fwnode和device_node的区别_不想好好取名字的博客-CSDN博客_fwnode

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
#define fwnode_has_op(fwnode, op) ((fwnode) && (fwnode)->ops && (fwnode)->ops->op) #define fwnode_call_ptr_op(fwnode, op, ...) (fwnode_has_op(fwnode, op) ? (fwnode)->ops->op(fwnode, ## __VA_ARGS__) : NULL) struct fwnode_handle * fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode, struct fwnode_handle *prev) { return fwnode_call_ptr_op(fwnode, graph_get_next_endpoint, prev); }

最终就是调用 fwnode->ops->graph_get_next_endpoint

fwnode_operation的回调函数的设置是在内核初始化的时候设置的

复制代码
1
2
3
4
5
static inline void of_node_init(struct device_node *node) { kobject_init(&node->kobj, &of_node_ktype); node->fwnode.ops = &of_fwnode_ops; }

graph_get_next_endpoint 对应 of_fwnode_graph_get_next_endpoint

复制代码
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
static inline bool is_of_node(const struct fwnode_handle *fwnode) { return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &of_fwnode_ops; } #define to_of_node(__fwnode) ({ typeof(__fwnode) __to_of_node_fwnode = (__fwnode); is_of_node(__to_of_node_fwnode) ? container_of(__to_of_node_fwnode, struct device_node, fwnode) : NULL; }) #define of_fwnode_handle(node) ({ typeof(node) __of_fwnode_handle_node = (node); __of_fwnode_handle_node ? &__of_fwnode_handle_node->fwnode : NULL; }) static struct fwnode_handle * of_fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode, struct fwnode_handle *prev) { return of_fwnode_handle(of_graph_get_next_endpoint(to_of_node(fwnode), to_of_node(prev))); }

找到一个具体的dts分析

复制代码
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
csi_dphy0: csi-dphy@ff4b0000 { compatible = "rockchip,rv1126-csi-dphy"; ... status = "okay"; ... ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; #address-cells = <1>; #size-cells = <0>; mipi_in_ucam0: endpoint@1 { reg = <1>; /* imx291 */ remote-endpoint = <&ucam_out2>; data-lanes = <1 2 3 4>; }; }; port@1 { reg = <1>; #address-cells = <1>; #size-cells = <0>; csidphy0_out: endpoint@0 { reg = <0>; remote-endpoint = <&mipi_csi2_input>; }; }; }; };

其中of_graph_get_next_endpoint

复制代码
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, struct device_node *prev) { struct device_node *endpoint; struct device_node *port; if (!parent) return NULL; /* * Start by locating the port node. If no previous endpoint is specified * search for the first port node, otherwise get the previous endpoint * parent port node. */ /* * prev在首次for循环中是NULL */ if (!prev) { struct device_node *node; /* * 当前node指向ports节点 * 这里会将node作为当前的parent,用于分析下面的port节点 */ node = of_get_child_by_name(parent, "ports"); if (node) parent = node; /* * 主意对于port@0节点其名字是port * 所以port指向port@0节点 */ port = of_get_child_by_name(parent, "port"); of_node_put(node); if (!port) { pr_err("graph: no port node found in %pOFn", parent); return NULL; } } else { /* * prev不为NULL * 说明当前prev是指向port下面的一个endpoint * 这里获取port,用于下面获取其他endpoint */ port = of_get_parent(prev); if (WARN_ONCE(!port, "%s(): endpoint %pOF has no parent noden", __func__, prev)) return NULL; } while (1) { /* * Now that we have a port node, get the next endpoint by * getting the next child. If the previous endpoint is NULL this * will return the first child. */ /* * 这里的endpoint对应 endpoint@1节点 * 主意如果prev是NULL * 这里获取第一个endpoint * 如果prev不为NULL,是第一个endpoint * 那么这里将获取第二个endpoint */ endpoint = of_get_next_child(port, prev); /* * 找到了一个endpoint就返回 * 不要忘记目的,这个复杂的函数就是为了找到一个endpoint */ if (endpoint) { of_node_put(port); return endpoint; } /* * 当前port下面已经没有enpoint * 所以要找其parent也就是ports * 然后找到ports下面的下一个port */ /* No more endpoints under this port, try the next one. */ prev = NULL; do { port = of_get_next_child(parent, port); if (!port) return NULL; } while (of_node_cmp(port->name, "port")); } }

回到 __v4l2_async_notifier_parse_fwnode_endpoint


复制代码
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
static int __v4l2_async_notifier_parse_fwnode_endpoints( struct device *dev, struct v4l2_async_notifier *notifier, size_t asd_struct_size, unsigned int port, bool has_port, int (*parse_endpoint)(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd)) { struct fwnode_handle *fwnode; unsigned int max_subdevs = notifier->max_subdevs; int ret; if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev))) return -EINVAL; /* 首先fwnode_graph_get_next_endpoint 要分析一下 */ for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint( dev_fwnode(dev), fwnode)); ) { struct fwnode_handle *dev_fwnode; bool is_available; /* * 这里已经找到了一个endpoint * fwnode_graph_get_port_parent * 这个函数获取到parent并不是想象的endpoint的parent即port * 而是ports的parent * csi_dphy0: csi-dphy@ff4b0000 { * status = "okay"; * ports { * port@0 { * mipi_in_ucam0: endpoint@1 { * }; * }; * port@1 { * csidphy0_out: endpoint@0 { * }; * }; * }; * }; * 以这个为例 * dev_fwnode指向的是csi-dphy */ dev_fwnode = fwnode_graph_get_port_parent(fwnode); /* * is_availabel的判断条件是 * status = "okay" 或者 "ok" */ is_available = fwnode_device_is_available(dev_fwnode); fwnode_handle_put(dev_fwnode); if (!is_available) continue; /* * 对于这里的has_port有点疑惑 * 只有指定了has_port才会去去解析endpoint的端点编号 * 以下面的为例 * ports { * #address-cells = <1>; * #size-cells = <0>; * port@0 { * reg = <0>; * #address-cells = <1>; * #size-cells = <0>; * * mipi_in_ucam0: endpoint@1 { * reg = <1>; * /* imx291 */ * remote-endpoint = <&ucam_out2>; * }; * }; * ep.port = 0 * 对应port0下面的reg的值 * ep.id = 1 * 对应endpoint下面的reg的值 */ if (has_port) { struct fwnode_endpoint ep; ret = fwnode_graph_parse_endpoint(fwnode, &ep); if (ret) { fwnode_handle_put(fwnode); return ret; } if (ep.port != port) continue; } /* * 对于假设的条件 * has_port = 1 * port = 0 * 只能匹配一个endpoint,这里的max_subdev只会加1操作 */ max_subdevs++; } /* * max_subdevs == notifier->max_subdevs * 说明没有找到endpoint * 所以直接返回 */ /* No subdevs to add? Return here. */ if (max_subdevs == notifier->max_subdevs) return 0; /* * 找到了endpoint * 1. 更新notifier->subdevs * 重新申请匹配maxsubdevs个数的内存空间,并将数据拷贝过来,然后释放以前的 * 2. 更新notifier->max_subdevs的个数 */ ret = v4l2_async_notifier_realloc(notifier, max_subdevs); if (ret) return ret; /* * 下面的代码和上面的很相似,似乎又操作了一遍 * 上面的代码具体的作用就是查找合适的enpoint * 并更新notifier的信息,但是并没有记录endpoint的信息 * 有没有什么好的方法? */ for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint( dev_fwnode(dev), fwnode)); ) { struct fwnode_handle *dev_fwnode; bool is_available; dev_fwnode = fwnode_graph_get_port_parent(fwnode); is_available = fwnode_device_is_available(dev_fwnode); fwnode_handle_put(dev_fwnode); if (!is_available) continue; if (has_port) { struct fwnode_endpoint ep; ret = fwnode_graph_parse_endpoint(fwnode, &ep); if (ret) break; if (ep.port != port) continue; } if (WARN_ON(notifier->num_subdevs >= notifier->max_subdevs)) { ret = -EINVAL; break; } /* * 这个循环中上面的代码已经分析过了 * 这里直接分析v4l2_async_notifier_fwnode_parse_endpoint */ ret = v4l2_async_notifier_fwnode_parse_endpoint( dev, notifier, fwnode, asd_struct_size, parse_endpoint); if (ret < 0) break; } fwnode_handle_put(fwnode); return ret; }

v4l2_async_notifier_parse_fwnode_endpoints_by_port

-> __v4l2_async_notifier_parse_fwnode_endpoints

        -> v4l2_async_notifier_fwnode_parse_endpoint


复制代码
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
static int v4l2_async_notifier_fwnode_parse_endpoint( struct device *dev, struct v4l2_async_notifier *notifier, struct fwnode_handle *endpoint, unsigned int asd_struct_size, int (*parse_endpoint)(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd)) { struct v4l2_async_subdev *asd; struct v4l2_fwnode_endpoint *vep; int ret = 0; /* * 上面代码分析过 * notifier->subdevs已经申请了空间,这里会为其填充数据 * 数据对应下面的asd */ asd = kzalloc(asd_struct_size, GFP_KERNEL); if (!asd) return -ENOMEM; asd->match_type = V4L2_ASYNC_MATCH_FWNODE; /* * fwnode_graph_get_remote_port_parent * 分2步 * 1. 获取endpoint的数据节点 * 2. 根据节点找到对应的parent * 同样的这个parent并不是port@x 或者ports * 实例如下 * 解析下面的remote-endpoint * remote-endpoint = <&ucam_out2>; * imx291: imx291@1a { * compatible = "sony,imx291"; * status = "okay"; * reg = <0x1a>; * * ... * * port { * ucam_out2: endpoint { * remote-endpoint = <&mipi_in_ucam0>; * data-lanes = <1 2 3 4>; * }; * }; * }; * 这里得到的就是imx291这个节点的fwnode值 */ asd->match.fwnode = fwnode_graph_get_remote_port_parent(endpoint); if (!asd->match.fwnode) { dev_warn(dev, "bad remote port parentn"); ret = -EINVAL; goto out_err; } /* * v4l2_fwnode_endpoint_alloc_parse * 这个函数的主要作用就是解析endpoint端点找到接口类型 * 比如mipi csi , bt656等等 * csi2 :vep->bus_type = V4L2_MBUS_CSI2 * parallel : vep->bus_type = V4L2_MBUS_BT656 */ vep = v4l2_fwnode_endpoint_alloc_parse(endpoint); if (IS_ERR(vep)) { ret = PTR_ERR(vep); dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)n", ret); goto out_err; } /* * 调用传入的回调函数 * 根据实际情况解析 */ ret = parse_endpoint ? parse_endpoint(dev, vep, asd) : 0; if (ret == -ENOTCONN) dev_dbg(dev, "ignoring port@%u/endpoint@%un", vep->base.port, vep->base.id); else if (ret < 0) dev_warn(dev, "driver could not parse port@%u/endpoint@%u (%d)n", vep->base.port, vep->base.id, ret); v4l2_fwnode_endpoint_free(vep); if (ret < 0) goto out_err; /* * 将asd保存 * 更新num_subdevs的值 * 注意还有一个max_subdevs * num_subdevs <= max_subdevs * 整理一下asd保存了什么 * 1. asd->match_type = V4L2_ASYNC_MATCH_FWNODE * 2. asd->match.fwnode的值存储remote-endpoint的parent * 也就是说asd主要是用于解析remote-endpoint */ notifier->subdevs[notifier->num_subdevs] = asd; notifier->num_subdevs++; return 0; out_err: fwnode_handle_put(asd->match.fwnode); kfree(asd); return ret == -ENOTCONN ? 0 : ret; }

最后

以上就是凶狠果汁最近收集整理的关于v4l2_async_notifier_parse_fwnode_endpoints_by_port 分析Linux v4l2架构学习总链接的全部内容,更多相关v4l2_async_notifier_parse_fwnode_endpoints_by_port内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(99)

评论列表共有 0 条评论

立即
投稿
返回
顶部