1.デバイスツリーの構造
xilinxプラットフォーム側
vcap_csi {
compatible = "xlnx,video";
dmas = <&Video_IN_1ch_v_frmbuf_wr_0 0>;
dma-names = "port0";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
direction = "input";
vcap_csi_in: endpoint {
remote-endpoint = <&sensor_out>;
};
};
};
};
PLサイドフレームバッファー
Video_IN_1ch_v_frmbuf_wr_0: v_frmbuf_wr@b0020000 {
#dma-cells = <1>;
clock-names = "ap_clk";
clocks = <&clk 72>;
compatible = "xlnx,v-frmbuf-wr-2.1", "xlnx,axi-frmbuf-wr-v2.1";
interrupt-names = "interrupt";
interrupt-parent = <&gic>;
interrupts = <0 104 4>;
reg = <0x0 0xb0020000 0x0 0x10000>;
reset-gpios = <&gpio 80 1>;
xlnx,dma-addr-width = <64>;
xlnx,dma-align = <8>;
xlnx,max-height = <2160>;
xlnx,max-width = <3840>;
xlnx,pixels-per-clock = <1>;
xlnx,s-axi-ctrl-addr-width = <0x7>;
xlnx,s-axi-ctrl-data-width = <0x20>;
xlnx,vid-formats = "yuyv";
xlnx,video-width = <8>;
};
サブデバイスドライバーの終わり:
nvp6124: sensor@1a{
compatible = "nextchip,nvp6124";
reg = <0x1a>;
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
sensor_out: endpoint {
remote-endpoint = <&vcap_csi_in>;
};
};
};
2.ソースコード分析
1)プラットフォーム側
プラットフォームデバイスドライバがデバイスツリーと一致したら、プローブ機能を実行して次の手順を実行します。
static int xvip_graph_init(struct xvip_composite_device *xdev)
{
struct xvip_graph_entity *entity;
struct v4l2_async_subdev **subdevs = NULL;
unsigned int num_subdevs;
unsigned int i;
int ret;
/* Init the DMA channels. */
ret = xvip_graph_dma_init(xdev);
if (ret < 0) {
dev_err(xdev->dev, "DMA initialization failed\n");
goto done;
}
/* Parse the graph to extract a list of subdevice DT nodes. */
ret = xvip_graph_parse(xdev);
if (ret < 0) {
dev_err(xdev->dev, "graph parsing failed\n");
goto done;
}
if (!xdev->num_subdevs) {
dev_err(xdev->dev, "no subdev found in graph\n");
goto done;
}
/* Register the subdevices notifier. */
num_subdevs = xdev->num_subdevs;
subdevs = devm_kzalloc(xdev->dev, sizeof(*subdevs) * num_subdevs,
GFP_KERNEL);
if (subdevs == NULL) {
ret = -ENOMEM;
goto done;
}
i = 0;
list_for_each_entry(entity, &xdev->entities, list)
subdevs[i++] = &entity->asd;
xdev->notifier.subdevs = subdevs;
xdev->notifier.num_subdevs = num_subdevs;
xdev->notifier.bound = xvip_graph_notify_bound;
xdev->notifier.complete = xvip_graph_notify_complete;
ret = v4l2_async_notifier_register(&xdev->v4l2_dev, &xdev->notifier);
if (ret < 0) {
dev_err(xdev->dev, "notifier registration failed\n");
goto done;
}
ret = 0;
done:
if (ret < 0)
xvip_graph_cleanup(xdev);
return ret;
}
主にDMAチャネルの初期化とv4l2_async_notifier_registerを行いました
v4l2_async_notifier_registerの機能を見てください。
int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
struct v4l2_async_notifier *notifier)
{
struct v4l2_subdev *sd, *tmp;
struct v4l2_async_subdev *asd;
int i;
if (!v4l2_dev || !notifier->num_subdevs ||
notifier->num_subdevs > V4L2_MAX_SUBDEVS)
return -EINVAL;
notifier->v4l2_dev = v4l2_dev;
INIT_LIST_HEAD(¬ifier->waiting);
INIT_LIST_HEAD(¬ifier->done);
for (i = 0; i < notifier->num_subdevs; i++) {
asd = notifier->subdevs[i];
switch (asd->match_type) {
case V4L2_ASYNC_MATCH_CUSTOM:
case V4L2_ASYNC_MATCH_DEVNAME:
case V4L2_ASYNC_MATCH_I2C:
case V4L2_ASYNC_MATCH_FWNODE:
break;
default:
dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
"Invalid match type %u on %p\n",
asd->match_type, asd);
return -EINVAL;
}
list_add_tail(&asd->list, ¬ifier->waiting);
}
mutex_lock(&list_lock);
//获取是否有注册的subdev
list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
int ret;
printk("___________________v4l2_async_notifier_register___________notifier_list\n");
asd = v4l2_async_belongs(notifier, sd);
if (!asd)
continue;
ret = v4l2_async_test_notify(notifier, sd, asd);
if (ret < 0) {
mutex_unlock(&list_lock);
return ret;
}
}
printk("______________________________notifier_list\n");
/* Keep also completed notifiers on the list */
list_add(¬ifier->list, ¬ifier_list);
mutex_unlock(&list_lock);
return 0;
}
子デバイスが後で一致すると仮定すると、リンクリストで既存の子デバイス登録を取得できません。このとき、notifier_listリンクリストに通知メッセージが追加され、子デバイスドライバ側が取得します。
登録されているサブデバイスがある場合、v4l2_async_test_notify関数が実行されます。
static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *sd,
struct v4l2_async_subdev *asd)
{
int ret;
if (notifier->bound) {
ret = notifier->bound(notifier, sd, asd);
if (ret < 0)
return ret;
}
ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
if (ret < 0) {
if (notifier->unbind)
notifier->unbind(notifier, sd, asd);
return ret;
}
/* Remove from the waiting list */
list_del(&asd->list);
sd->asd = asd;
sd->notifier = notifier;
/* Move from the global subdevice list to notifier's done */
list_move(&sd->async_list, ¬ifier->done);
if (list_empty(¬ifier->waiting) && notifier->complete)
return notifier->complete(notifier);
return 0;
}
ここで、コールバックがバインドされ、v4l2_device_register_subdevを実行して、サブデバイスをV4L2コアレイヤーに登録します。
2)サブデバイス側
v4l2_async_register_subdevを介して登録します。
int v4l2_async_register_subdev(struct v4l2_subdev *sd)
{
struct v4l2_async_notifier *notifier;
/*
* No reference taken. The reference is held by the device
* (struct v4l2_subdev.dev), and async sub-device does not
* exist independently of the device at any point of time.
*/
if (!sd->fwnode && sd->dev)
sd->fwnode = dev_fwnode(sd->dev);
mutex_lock(&list_lock);
INIT_LIST_HEAD(&sd->async_list);
//查找是否平台已注册了通知subdev事件,紧接着将执行注册v4l2_device_register_subdev
list_for_each_entry(notifier, ¬ifier_list, list) {
printk("______________________v4l2_async_register_subdev___________notifier_list\n");
struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
if (asd) {
int ret = v4l2_async_test_notify(notifier, sd, asd);
mutex_unlock(&list_lock);
return ret;
}
}
/* None matched, wait for hot-plugging */
list_add(&sd->async_list, &subdev_list);
mutex_unlock(&list_lock);
return 0;
}
この操作は、上記のv4l2_async_notifier_registerを補完します。誰が最初に登録し、誰が登録しても、v4l2_async_test_notify関数操作は最後に実装されます。