[10/10] libcamera: mali-c55: Match for memory input media entities
diff mbox series

Message ID 20250724065256.75175-11-dan.scally@ideasonboard.com
State New
Headers show
Series
  • Support memory input mode in mali-c55
Related show

Commit Message

Dan Scally July 24, 2025, 6:52 a.m. UTC
Updating the Pipeline Handler's match() function to search for the
entities necessary for memory input mode and create a CameraData
instance for that camera if one is found.

Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
---
 src/libcamera/pipeline/mali-c55/mali-c55.cpp | 100 ++++++++++++++++---
 1 file changed, 85 insertions(+), 15 deletions(-)

Comments

Kieran Bingham July 24, 2025, 8:53 a.m. UTC | #1
Quoting Daniel Scally (2025-07-24 07:52:56)
> Updating the Pipeline Handler's match() function to search for the
> entities necessary for memory input mode and create a CameraData
> instance for that camera if one is found.
> 
> Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
> ---
>  src/libcamera/pipeline/mali-c55/mali-c55.cpp | 100 ++++++++++++++++---
>  1 file changed, 85 insertions(+), 15 deletions(-)
> 
> diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp
> index 2a396950..e2f31e77 100644
> --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp
> +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp
> @@ -1817,6 +1817,45 @@ bool PipelineHandlerMaliC55::registerSensorCamera(MediaLink *ispLink)
>         return true;
>  }
>  
> +bool PipelineHandlerMaliC55::registerMemoryInputCamera()
> +{
> +       MediaEntity *sensorEntity;
> +       int ret;
> +
> +       std::unique_ptr<MaliC55CameraData> data = std::make_unique<MaliC55CameraData>(this);
> +
> +       data->cru_ = std::make_unique<RZG2LCRU>();
> +       ret = data->cru_->init(cruMedia_, &sensorEntity);
> +       if (ret)
> +               return false;
> +
> +       if (data->init(sensorEntity))
> +               return false;
> +
> +       data->cru_->setSensorAndCSI2Pointers(data->sensor_, data->csi_);
> +
> +       data->properties_ = data->sensor_->properties();
> +
> +       const CameraSensorProperties::SensorDelays &delays = data->sensor_->sensorDelays();
> +       std::unordered_map<uint32_t, DelayedControls::ControlParams> params = {
> +               { V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },
> +               { V4L2_CID_EXPOSURE, { delays.exposureDelay, false } },
> +       };
> +
> +       data->delayedCtrls_ =
> +               std::make_unique<DelayedControls>(data->sensor_->device(),
> +                                                 params);
> +       isp_->frameStart.connect(data->delayedCtrls_.get(),
> +                                &DelayedControls::applyControls);
> +       data->cru_->output()->bufferReady.connect(this, &PipelineHandlerMaliC55::cruBufferReady);
> +       input_->bufferReady.connect(data->cru_.get(), &RZG2LCRU::cruReturnBuffer);
> +
> +       if (!registerMaliCamera(std::move(data), sensorEntity->name()))
> +               return false;
> +
> +       return true;
> +}
> +
>  bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)
>  {
>         const MediaPad *ispSink;
> @@ -1826,14 +1865,14 @@ bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)
>          * The TPG and the downscale pipe are both optional blocks and may not
>          * be fitted.
>          */
> -       DeviceMatch dm("mali-c55");
> -       dm.add("mali-c55 isp");
> -       dm.add("mali-c55 resizer fr");
> -       dm.add("mali-c55 fr");
> -       dm.add("mali-c55 3a stats");
> -       dm.add("mali-c55 3a params");
> -
> -       media_ = acquireMediaDevice(enumerator, dm);
> +       DeviceMatch c55_dm("mali-c55");
> +       c55_dm.add("mali-c55 isp");
> +       c55_dm.add("mali-c55 resizer fr");
> +       c55_dm.add("mali-c55 fr");
> +       c55_dm.add("mali-c55 3a stats");
> +       c55_dm.add("mali-c55 3a params");
> +
> +       media_ = acquireMediaDevice(enumerator, c55_dm);
>         if (!media_)
>                 return false;
>  
> @@ -1893,6 +1932,22 @@ bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)
>         stats_->bufferReady.connect(this, &PipelineHandlerMaliC55::statsBufferReady);
>         params_->bufferReady.connect(this, &PipelineHandlerMaliC55::paramsBufferReady);
>  
> +       /*
> +        * We also need to search for the rzg2l-cru CSI-2 receiver. If we find
> +        * that then we need to work in memory input mode instead of the inline
> +        * mode. The absence of this match is not necessarily a failure at this
> +        * point...it depends on the media links that we investigate momentarily.
> +        *
> +        * This is a bit hacky, because there could be multiple of these media
> +        * devices and we're just taking the first. We need modular pipelines to
> +        * properly solve the issue.
> +        */

Interesting - I'd almost switch it around sometime - as the CRU is what's going
to define the existing of actual cameras. So if we find a CRU - then we
find a suitable C55 memory input device and construct a pipeline from
that.

Then we construct independent C55 pipelines from whatevers left (which
presumably would be only the inline operational versions - even if there
was a hypothetical system with both combinations...)


I guess that's how we move to modular pipelines too "First find media
devices that can support sensors, then find compatible ISPs (including
Soft/GPU) to pair up to the camera devices and register the pipeline"
(but yes - that's later).


> +       DeviceMatch cru_dm("rzg2l_cru");
> +       cru_dm.add(std::regex("csi-[0-9a-f]{8}.csi2"));
> +       cru_dm.add(std::regex("cru-ip-[0-9a-f]{8}.cru[0-9]"));
> +       cru_dm.add("CRU output");
> +       cruMedia_ = acquireMediaDevice(enumerator, cru_dm);
> +
>         ispSink = isp_->entity()->getPadByIndex(0);
>         if (!ispSink || ispSink->links().empty()) {
>                 LOG(MaliC55, Error) << "ISP sink pad error";
> @@ -1907,12 +1962,13 @@ bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)
>          * MEDIA_ENT_F_VID_IF_BRIDGE - A CSI-2 receiver
>          * MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER - An input device
>          *
> -        * The last one will be unsupported for now. The TPG is relatively easy,
> -        * we just register a Camera for it. If we have a CSI-2 receiver we need
> -        * to check its sink pad and register Cameras for anything connected to
> -        * it (probably...there are some complex situations in which that might
> -        * not be true but let's pretend they don't exist until we come across
> -        * them)
> +        * The TPG is relatively easy, we just register a Camera for it. If we
> +        * have a CSI-2 receiver we need to check its sink pad and register
> +        * Cameras for anything connected to it (probably...there are some
> +        * complex situations in which that might not be true but let's pretend
> +        * they don't exist until we come across them). If we have an input
> +        * device then we need to acquire the V4L2 infrastructure for it and
> +        * confirm that we found the rzg2l-cru media device too.
>          */
>         bool registered;
>         for (MediaLink *link : ispSink->links()) {
> @@ -1932,7 +1988,21 @@ bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)
>  
>                         break;
>                 case MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER:
> -                       LOG(MaliC55, Warning) << "Memory input not yet supported";
> +                       if (!cruMedia_)
> +                               return false;
> +
> +                       ivc_ = V4L2Subdevice::fromEntityName(media_, "rzv2h ivc block");
> +                       if (ivc_->open() < 0)
> +                               return false;
> +
> +                       input_ = V4L2VideoDevice::fromEntityName(media_, "rzv2h-ivc");
> +                       if (input_->open() < 0)
> +                               return false;
> +
> +                       registered = registerMemoryInputCamera();
> +                       if (!registered)
> +                               return registered;
> +
>                         break;
>                 default:
>                         LOG(MaliC55, Error) << "Unsupported entity function";
> -- 
> 2.30.2
>

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp
index 2a396950..e2f31e77 100644
--- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp
+++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp
@@ -1817,6 +1817,45 @@  bool PipelineHandlerMaliC55::registerSensorCamera(MediaLink *ispLink)
 	return true;
 }
 
+bool PipelineHandlerMaliC55::registerMemoryInputCamera()
+{
+	MediaEntity *sensorEntity;
+	int ret;
+
+	std::unique_ptr<MaliC55CameraData> data = std::make_unique<MaliC55CameraData>(this);
+
+	data->cru_ = std::make_unique<RZG2LCRU>();
+	ret = data->cru_->init(cruMedia_, &sensorEntity);
+	if (ret)
+		return false;
+
+	if (data->init(sensorEntity))
+		return false;
+
+	data->cru_->setSensorAndCSI2Pointers(data->sensor_, data->csi_);
+
+	data->properties_ = data->sensor_->properties();
+
+	const CameraSensorProperties::SensorDelays &delays = data->sensor_->sensorDelays();
+	std::unordered_map<uint32_t, DelayedControls::ControlParams> params = {
+		{ V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },
+		{ V4L2_CID_EXPOSURE, { delays.exposureDelay, false } },
+	};
+
+	data->delayedCtrls_ =
+		std::make_unique<DelayedControls>(data->sensor_->device(),
+						  params);
+	isp_->frameStart.connect(data->delayedCtrls_.get(),
+				 &DelayedControls::applyControls);
+	data->cru_->output()->bufferReady.connect(this, &PipelineHandlerMaliC55::cruBufferReady);
+	input_->bufferReady.connect(data->cru_.get(), &RZG2LCRU::cruReturnBuffer);
+
+	if (!registerMaliCamera(std::move(data), sensorEntity->name()))
+		return false;
+
+	return true;
+}
+
 bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)
 {
 	const MediaPad *ispSink;
@@ -1826,14 +1865,14 @@  bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)
 	 * The TPG and the downscale pipe are both optional blocks and may not
 	 * be fitted.
 	 */
-	DeviceMatch dm("mali-c55");
-	dm.add("mali-c55 isp");
-	dm.add("mali-c55 resizer fr");
-	dm.add("mali-c55 fr");
-	dm.add("mali-c55 3a stats");
-	dm.add("mali-c55 3a params");
-
-	media_ = acquireMediaDevice(enumerator, dm);
+	DeviceMatch c55_dm("mali-c55");
+	c55_dm.add("mali-c55 isp");
+	c55_dm.add("mali-c55 resizer fr");
+	c55_dm.add("mali-c55 fr");
+	c55_dm.add("mali-c55 3a stats");
+	c55_dm.add("mali-c55 3a params");
+
+	media_ = acquireMediaDevice(enumerator, c55_dm);
 	if (!media_)
 		return false;
 
@@ -1893,6 +1932,22 @@  bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)
 	stats_->bufferReady.connect(this, &PipelineHandlerMaliC55::statsBufferReady);
 	params_->bufferReady.connect(this, &PipelineHandlerMaliC55::paramsBufferReady);
 
+	/*
+	 * We also need to search for the rzg2l-cru CSI-2 receiver. If we find
+	 * that then we need to work in memory input mode instead of the inline
+	 * mode. The absence of this match is not necessarily a failure at this
+	 * point...it depends on the media links that we investigate momentarily.
+	 *
+	 * This is a bit hacky, because there could be multiple of these media
+	 * devices and we're just taking the first. We need modular pipelines to
+	 * properly solve the issue.
+	 */
+	DeviceMatch cru_dm("rzg2l_cru");
+	cru_dm.add(std::regex("csi-[0-9a-f]{8}.csi2"));
+	cru_dm.add(std::regex("cru-ip-[0-9a-f]{8}.cru[0-9]"));
+	cru_dm.add("CRU output");
+	cruMedia_ = acquireMediaDevice(enumerator, cru_dm);
+
 	ispSink = isp_->entity()->getPadByIndex(0);
 	if (!ispSink || ispSink->links().empty()) {
 		LOG(MaliC55, Error) << "ISP sink pad error";
@@ -1907,12 +1962,13 @@  bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)
 	 * MEDIA_ENT_F_VID_IF_BRIDGE - A CSI-2 receiver
 	 * MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER - An input device
 	 *
-	 * The last one will be unsupported for now. The TPG is relatively easy,
-	 * we just register a Camera for it. If we have a CSI-2 receiver we need
-	 * to check its sink pad and register Cameras for anything connected to
-	 * it (probably...there are some complex situations in which that might
-	 * not be true but let's pretend they don't exist until we come across
-	 * them)
+	 * The TPG is relatively easy, we just register a Camera for it. If we
+	 * have a CSI-2 receiver we need to check its sink pad and register
+	 * Cameras for anything connected to it (probably...there are some
+	 * complex situations in which that might not be true but let's pretend
+	 * they don't exist until we come across them). If we have an input
+	 * device then we need to acquire the V4L2 infrastructure for it and
+	 * confirm that we found the rzg2l-cru media device too.
 	 */
 	bool registered;
 	for (MediaLink *link : ispSink->links()) {
@@ -1932,7 +1988,21 @@  bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator)
 
 			break;
 		case MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER:
-			LOG(MaliC55, Warning) << "Memory input not yet supported";
+			if (!cruMedia_)
+				return false;
+
+			ivc_ = V4L2Subdevice::fromEntityName(media_, "rzv2h ivc block");
+			if (ivc_->open() < 0)
+				return false;
+
+			input_ = V4L2VideoDevice::fromEntityName(media_, "rzv2h-ivc");
+			if (input_->open() < 0)
+				return false;
+
+			registered = registerMemoryInputCamera();
+			if (!registered)
+				return registered;
+
 			break;
 		default:
 			LOG(MaliC55, Error) << "Unsupported entity function";