Análisis de captura de video basado en plataforma heterogénea xilinx

1. Estructura de árbol de dispositivos

lado de la plataforma 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>;
					};
				};
			};
		};

Amortiguador de marco lateral 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>;
		};

Extremo del controlador del subdispositivo: 

			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. Análisis del código fuente

1) Lado de la plataforma

Después de que el controlador del dispositivo de la plataforma coincida con el árbol del dispositivo, ejecute la función de sonda y haga lo siguiente:

 
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;
}

Realiza principalmente la inicialización del canal DMA y v4l2_async_notifier_register

Eche un vistazo a lo que hace 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(&notifier->waiting);
	INIT_LIST_HEAD(&notifier->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, &notifier->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(&notifier->list, &notifier_list);
 
	mutex_unlock(&list_lock);
 
	return 0;
}

Suponiendo que el dispositivo secundario coincide más tarde, el registro del dispositivo secundario existente no se puede obtener en la lista vinculada. En este momento, el mensaje de notificación se agrega a la lista vinculada notifier_list, y el lado del controlador del dispositivo secundario lo obtendrá;

Si hay subdispositivos registrados, se ejecutará la función 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, &notifier->done);
 
	if (list_empty(&notifier->waiting) && notifier->complete)
		return notifier->complete(notifier);
 
	return 0;
}

Aquí se enlaza la devolución de llamada y se ejecuta v4l2_device_register_subdev para registrar el subdispositivo en la capa central de V4L2;

 

2) Extremo del subdispositivo

Regístrese a través de 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, &notifier_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;
}

La operación aquí es complementaria a la anterior v4l2_async_notifier_register; no importa quién se registre primero, la operación de la función v4l2_async_test_notify se implementará al final;

Supongo que te gusta

Origin blog.csdn.net/a8039974/article/details/107726115
Recomendado
Clasificación