Raspberry Pi 5 Support
diff mbox series

Message ID ae369612-0d35-47b9-8796-d3670bfd9086@gmx.de
State Rejected
Headers show
Series
  • Raspberry Pi 5 Support
Related show

Commit Message

Christian Rauch May 17, 2026, 10:38 a.m. UTC
Dear all,

A while ago (actually nearly a year to this date) I was asking about the 
Raspberry Pi 5 support in upstream libcamera and Naush mentioned that 
Raspberry Pi is using a workaround in the CFE kernel driver to make this 
work. I assume that Naush was talking about the kernel module "rp1_cfe", 
which is shipped with the workaround as "rp1_cfe_downstream" in 
Raspberry Pi OS.

Ubuntu 26.04 with its kernel "7.0.0-1010-raspi" ships the "rp1_cfe" 
module with workaround as "rp1_cfe_downstream" too, and also patched 
(patch attached) the upstream libcamera sources with a fix to detect the 
camera when either of those kernel modules is loaded.

Since the Raspberry Pi 5 support is still not available with upstream 
libcamera and the "rp1_cfe" kernel module, and it seems it is going to 
stay that way for a while, would you accept the attached patch to 
replicate the behaviour in the Raspberry Pi libcamera fork?

Given that the "rp1_cfe" module does not work with the upstream 
"rpi/pisp" pipeline, how do you know that this pipeline handler is 
actually working? Are you carrying custom patches on top of "rp1_cfe" to 
make the upstream "rpi/pisp" work? Once the required features are merged 
into upstream module "rp1_cfe", how do you know that the "rpi/pisp" 
pipeline will still be compatible?

Alternatively, couldn't the patches to the pipeline handler and kernel 
modules be upstreamed in order to resolve this issue?

Best,
Christian

Comments

Laurent Pinchart June 11, 2026, 7:10 p.m. UTC | #1
Hi Christian,

On Sun, May 17, 2026 at 12:38:52PM +0200, Christian Rauch wrote:
> Dear all,
> 
> A while ago (actually nearly a year to this date) I was asking about the 
> Raspberry Pi 5 support in upstream libcamera and Naush mentioned that 
> Raspberry Pi is using a workaround in the CFE kernel driver to make this 
> work. I assume that Naush was talking about the kernel module "rp1_cfe", 
> which is shipped with the workaround as "rp1_cfe_downstream" in 
> Raspberry Pi OS.
> 
> Ubuntu 26.04 with its kernel "7.0.0-1010-raspi" ships the "rp1_cfe" 
> module with workaround as "rp1_cfe_downstream" too, and also patched 
> (patch attached) the upstream libcamera sources with a fix to detect the 
> camera when either of those kernel modules is loaded.
> 
> Since the Raspberry Pi 5 support is still not available with upstream 
> libcamera and the "rp1_cfe" kernel module, and it seems it is going to 
> stay that way for a while, would you accept the attached patch to 
> replicate the behaviour in the Raspberry Pi libcamera fork?
> 
> Given that the "rp1_cfe" module does not work with the upstream 
> "rpi/pisp" pipeline, how do you know that this pipeline handler is 
> actually working? Are you carrying custom patches on top of "rp1_cfe" to 
> make the upstream "rpi/pisp" work? Once the required features are merged 
> into upstream module "rp1_cfe", how do you know that the "rpi/pisp" 
> pipeline will still be compatible?

I've just tested the latest libcamera release with the upstream kernel
(v7.1-rc7). I had to add an overlay for the IMX219 camera module as that
part is not upstream (and the downstream overlays are not compatible
with upstream), but apart from that, the kernel is unmodified. The cam
application runs fine and displays frames on the screen (which also
means that HDMI output is working :-)).

> Alternatively, couldn't the patches to the pipeline handler and kernel 
> modules be upstreamed in order to resolve this issue?

There are currently two features missing upstream for the Raspberry Pi 5
camera pipeline:

- Embedded data is not supported. This will require landing the "[PATCH
  v12 00/86] Generic line based metadata support, internal pads" ([1])
  series.

  Lack of embedded data doesn't prevent the ISP from working.

- Time-multiplexing of the ISP is not supported. This will require
  landing the "[PATCH v2 00/27] media: Add support for multi-context
  operations" ([2]) series.

  Lack of multi-context support prevents running multiple cameras
  concurrently. 

Once those series land, we will adapt the rp1-cfe driver (for [1]) and
the pisp driver (for [2]) and the libcamera rpi5 pipeline handler. The
two missing features are independent from each other, we can adaptat the
kernel drivers and libcamera once one of the series lands without
waiting for the other one. The plan is for the Raspberry Pi downstream
kernel and libcamera to use the upstream APIs when they will be
available.

[1] https://lore.kernel.org/linux-media/20260409201501.975242-1-sakari.ailus@linux.intel.com/
[2] https://lore.kernel.org/linux-media/20250724-multicontext-mainline-2025-v2-0-c9b316773486@ideasonboard.com

> Author: Pragyansh Chaturvedi <r41k0u@ubuntu.com>
> Forwarded: not-needed
> Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/libcamera/+bug/2110144
> Last-Update: 2026-02-18
> Description: Fix PiSP CFE entity match patterns
>  libcamera0.5 includes the PiSP drivers for Raspberry Pi. But
>  while registering the cameras, it is using different match
>  strings for CFE entities than what are present in Raspberry Pi's
>  fork of libcamera and the current device-tree overlays. This leads
>  to camera sensors not being detected on Raspberry Pi 5, which
>  used to work in libcamera0.4.
>  .
>  This indicates an upcoming change in these device-tree overlays,
>  as Raspberry Pi themselves upstreamed these changes to libcamera.
>  So this patch allows both the old and current match strings for
>  searching for CFE devices. This delta can be removed once our kernel
>  catches up and introduces the new strings.
> 
> ---
> --- a/include/libcamera/internal/device_enumerator.h
> +++ b/include/libcamera/internal/device_enumerator.h
> @@ -28,6 +28,8 @@
>  
>  	bool match(const MediaDevice *device) const;
>  
> +	void clear();
> +
>  private:
>  	std::string driver_;
>  	std::vector<std::string> entities_;
> --- a/src/libcamera/device_enumerator.cpp
> +++ b/src/libcamera/device_enumerator.cpp
> @@ -157,6 +157,14 @@
>  }
>  
>  /**
> + * \brief Clear the entities search pattern vector
> + */
> +void DeviceMatch::clear()
> +{
> +	entities_.clear();
> +}
> +
> +/**
>   * \class DeviceEnumerator
>   * \brief Enumerate, store and search media devices
>   *
> --- a/src/libcamera/pipeline/rpi/pisp/pisp.cpp
> +++ b/src/libcamera/pipeline/rpi/pisp/pisp.cpp
> @@ -888,7 +888,17 @@
>  		std::shared_ptr<MediaDevice> cfeDevice = acquireMediaDevice(enumerator, cfe);
>  
>  		if (!cfeDevice) {
> -			LOG(RPI, Debug) << "Unable to acquire a CFE instance";
> +			LOG(RPI, Debug) << "Trying old entity search patterns for CFE instance";
> +		}
> +
> +		cfe.clear();
> +		cfe.add("rp1-cfe-fe_image0");
> +		cfe.add("rp1-cfe-fe_stats");
> +		cfe.add("rp1-cfe-fe_config");
> +		cfeDevice = acquireMediaDevice(enumerator, cfe);
> +
> +		if (!cfeDevice) {
> +			LOG(RPI, Debug) << "Unable to acquire CFE instance";
>  			break;
>  		}
>  
> @@ -1073,9 +1083,26 @@
>  	int ret;
>  
>  	MediaEntity *cfeImage = cfe->getEntityByName("rp1-cfe-fe-image0");
> +	if (!cfeImage)
> +	{
> +		cfeImage = cfe->getEntityByName("rp1-cfe-fe_image0");
> +	}
>  	MediaEntity *cfeEmbedded = cfe->getEntityByName("rp1-cfe-csi2-ch1");
> +	if (!cfeEmbedded)
> +	{
> +		cfeEmbedded = cfe->getEntityByName("rp1-cfe-embedded");
> +	}
>  	MediaEntity *cfeStats = cfe->getEntityByName("rp1-cfe-fe-stats");
> +	if (!cfeStats)
> +	{
> +		cfeStats = cfe->getEntityByName("rp1-cfe-fe_stats");
> +	}
>  	MediaEntity *cfeConfig = cfe->getEntityByName("rp1-cfe-fe-config");
> +	if (!cfeConfig)
> +	{
> +		cfeConfig = cfe->getEntityByName("rp1-cfe-fe_config");
> +	}
> +
>  	MediaEntity *ispInput = isp->getEntityByName("pispbe-input");
>  	MediaEntity *IpaPrepare = isp->getEntityByName("pispbe-config");
>  	MediaEntity *ispOutput0 = isp->getEntityByName("pispbe-output0");
> @@ -2144,8 +2171,9 @@
>  	int ret = 0;
>  
>  	constexpr unsigned int csiVideoSinkPad = 0;
> -	constexpr unsigned int csiVideoSourcePad = 1;
> -	constexpr unsigned int csiMetaSourcePad = 2;
> +	constexpr unsigned int csiMetaSinkPad = 1;
> +	constexpr unsigned int csiVideoSourcePad = 4;
> +	constexpr unsigned int csiMetaSourcePad = 5;
>  
>  	constexpr unsigned int feVideoSinkPad = 0;
>  	constexpr unsigned int feConfigSinkPad = 1;
> @@ -2156,48 +2184,29 @@
>  	const MediaEntity *csi2 = csi2Subdev_->entity();
>  	const MediaEntity *fe = feSubdev_->entity();
>  
> -	for (MediaLink *link : csi2->pads()[csiVideoSourcePad]->links()) {
> -		if (link->sink()->entity()->name() == "rp1-cfe-csi2-ch0")
> +	for (MediaLink *link : csi2->getPadByIndex(csiVideoSourcePad)->links()) {
> +		if (link->sink()->entity()->name() == "rp1-cfe-csi2-ch0" || link->sink()->entity()->name() == "rp1-cfe-csi2_ch0")
>  			link->setEnabled(false);
>  		else if (link->sink()->entity()->name() == "pisp-fe")
>  			link->setEnabled(true);
>  	}
>  
> -	csi2->pads()[csiMetaSourcePad]->links()[0]->setEnabled(sensorMetadata_);
> +	csi2->getPadByIndex(csiMetaSourcePad)->links()[0]->setEnabled(sensorMetadata_);
>  
> -	fe->pads()[feConfigSinkPad]->links()[0]->setEnabled(true);
> -	fe->pads()[feVideo0SourcePad]->links()[0]->setEnabled(true);
> -	fe->pads()[feVideo1SourcePad]->links()[0]->setEnabled(false);
> -	fe->pads()[feStatsSourcePad]->links()[0]->setEnabled(true);
> -
> -	const V4L2Subdevice::Stream imageStream{
> -		csiVideoSinkPad,
> -		sensor_->imageStream().stream
> -	};
> -	const V4L2Subdevice::Stream embeddedDataStream{
> -		csiVideoSinkPad,
> -		sensor_->embeddedDataStream().value_or(V4L2Subdevice::Stream{}).stream
> -	};
> -
> -	V4L2Subdevice::Routing routing;
> -	routing.emplace_back(imageStream, V4L2Subdevice::Stream{ csiVideoSourcePad, 0 },
> -			     V4L2_SUBDEV_ROUTE_FL_ACTIVE);
> -
> -	if (sensorMetadata_)
> -		routing.emplace_back(embeddedDataStream,
> -				     V4L2Subdevice::Stream{ csiMetaSourcePad, 0 },
> -				     V4L2_SUBDEV_ROUTE_FL_ACTIVE);
> -
> -	ret = csi2Subdev_->setRouting(&routing);
> -	if (ret)
> -		return ret;
> +	fe->getPadByIndex(feConfigSinkPad)->links()[0]->setEnabled(true);
> +	fe->getPadByIndex(feVideo0SourcePad)->links()[0]->setEnabled(true);
> +	fe->getPadByIndex(feVideo1SourcePad)->links()[0]->setEnabled(false);
> +	fe->getPadByIndex(feStatsSourcePad)->links()[0]->setEnabled(true);
>  
> -	ret = csi2Subdev_->setFormat(imageStream, &sensorFormat);
> +	ret = csi2Subdev_->setFormat(csiVideoSinkPad, &sensorFormat);
>  	if (ret)
>  		return ret;
>  
>  	if (sensorMetadata_) {
> -		ret = csi2Subdev_->setFormat(embeddedDataStream, &embeddedFormat);
> +		ret = csi2Subdev_->setFormat(csiMetaSinkPad, &embeddedFormat);
> +		if (ret)
> +			return ret;
> +		ret = csi2Subdev_->setFormat(csiMetaSourcePad, &embeddedFormat);
>  		if (ret)
>  			return ret;
>  	}

Patch
diff mbox series

Author: Pragyansh Chaturvedi <r41k0u@ubuntu.com>
Forwarded: not-needed
Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/libcamera/+bug/2110144
Last-Update: 2026-02-18
Description: Fix PiSP CFE entity match patterns
 libcamera0.5 includes the PiSP drivers for Raspberry Pi. But
 while registering the cameras, it is using different match
 strings for CFE entities than what are present in Raspberry Pi's
 fork of libcamera and the current device-tree overlays. This leads
 to camera sensors not being detected on Raspberry Pi 5, which
 used to work in libcamera0.4.
 .
 This indicates an upcoming change in these device-tree overlays,
 as Raspberry Pi themselves upstreamed these changes to libcamera.
 So this patch allows both the old and current match strings for
 searching for CFE devices. This delta can be removed once our kernel
 catches up and introduces the new strings.

---
--- a/include/libcamera/internal/device_enumerator.h
+++ b/include/libcamera/internal/device_enumerator.h
@@ -28,6 +28,8 @@ 
 
 	bool match(const MediaDevice *device) const;
 
+	void clear();
+
 private:
 	std::string driver_;
 	std::vector<std::string> entities_;
--- a/src/libcamera/device_enumerator.cpp
+++ b/src/libcamera/device_enumerator.cpp
@@ -157,6 +157,14 @@ 
 }
 
 /**
+ * \brief Clear the entities search pattern vector
+ */
+void DeviceMatch::clear()
+{
+	entities_.clear();
+}
+
+/**
  * \class DeviceEnumerator
  * \brief Enumerate, store and search media devices
  *
--- a/src/libcamera/pipeline/rpi/pisp/pisp.cpp
+++ b/src/libcamera/pipeline/rpi/pisp/pisp.cpp
@@ -888,7 +888,17 @@ 
 		std::shared_ptr<MediaDevice> cfeDevice = acquireMediaDevice(enumerator, cfe);
 
 		if (!cfeDevice) {
-			LOG(RPI, Debug) << "Unable to acquire a CFE instance";
+			LOG(RPI, Debug) << "Trying old entity search patterns for CFE instance";
+		}
+
+		cfe.clear();
+		cfe.add("rp1-cfe-fe_image0");
+		cfe.add("rp1-cfe-fe_stats");
+		cfe.add("rp1-cfe-fe_config");
+		cfeDevice = acquireMediaDevice(enumerator, cfe);
+
+		if (!cfeDevice) {
+			LOG(RPI, Debug) << "Unable to acquire CFE instance";
 			break;
 		}
 
@@ -1073,9 +1083,26 @@ 
 	int ret;
 
 	MediaEntity *cfeImage = cfe->getEntityByName("rp1-cfe-fe-image0");
+	if (!cfeImage)
+	{
+		cfeImage = cfe->getEntityByName("rp1-cfe-fe_image0");
+	}
 	MediaEntity *cfeEmbedded = cfe->getEntityByName("rp1-cfe-csi2-ch1");
+	if (!cfeEmbedded)
+	{
+		cfeEmbedded = cfe->getEntityByName("rp1-cfe-embedded");
+	}
 	MediaEntity *cfeStats = cfe->getEntityByName("rp1-cfe-fe-stats");
+	if (!cfeStats)
+	{
+		cfeStats = cfe->getEntityByName("rp1-cfe-fe_stats");
+	}
 	MediaEntity *cfeConfig = cfe->getEntityByName("rp1-cfe-fe-config");
+	if (!cfeConfig)
+	{
+		cfeConfig = cfe->getEntityByName("rp1-cfe-fe_config");
+	}
+
 	MediaEntity *ispInput = isp->getEntityByName("pispbe-input");
 	MediaEntity *IpaPrepare = isp->getEntityByName("pispbe-config");
 	MediaEntity *ispOutput0 = isp->getEntityByName("pispbe-output0");
@@ -2144,8 +2171,9 @@ 
 	int ret = 0;
 
 	constexpr unsigned int csiVideoSinkPad = 0;
-	constexpr unsigned int csiVideoSourcePad = 1;
-	constexpr unsigned int csiMetaSourcePad = 2;
+	constexpr unsigned int csiMetaSinkPad = 1;
+	constexpr unsigned int csiVideoSourcePad = 4;
+	constexpr unsigned int csiMetaSourcePad = 5;
 
 	constexpr unsigned int feVideoSinkPad = 0;
 	constexpr unsigned int feConfigSinkPad = 1;
@@ -2156,48 +2184,29 @@ 
 	const MediaEntity *csi2 = csi2Subdev_->entity();
 	const MediaEntity *fe = feSubdev_->entity();
 
-	for (MediaLink *link : csi2->pads()[csiVideoSourcePad]->links()) {
-		if (link->sink()->entity()->name() == "rp1-cfe-csi2-ch0")
+	for (MediaLink *link : csi2->getPadByIndex(csiVideoSourcePad)->links()) {
+		if (link->sink()->entity()->name() == "rp1-cfe-csi2-ch0" || link->sink()->entity()->name() == "rp1-cfe-csi2_ch0")
 			link->setEnabled(false);
 		else if (link->sink()->entity()->name() == "pisp-fe")
 			link->setEnabled(true);
 	}
 
-	csi2->pads()[csiMetaSourcePad]->links()[0]->setEnabled(sensorMetadata_);
+	csi2->getPadByIndex(csiMetaSourcePad)->links()[0]->setEnabled(sensorMetadata_);
 
-	fe->pads()[feConfigSinkPad]->links()[0]->setEnabled(true);
-	fe->pads()[feVideo0SourcePad]->links()[0]->setEnabled(true);
-	fe->pads()[feVideo1SourcePad]->links()[0]->setEnabled(false);
-	fe->pads()[feStatsSourcePad]->links()[0]->setEnabled(true);
-
-	const V4L2Subdevice::Stream imageStream{
-		csiVideoSinkPad,
-		sensor_->imageStream().stream
-	};
-	const V4L2Subdevice::Stream embeddedDataStream{
-		csiVideoSinkPad,
-		sensor_->embeddedDataStream().value_or(V4L2Subdevice::Stream{}).stream
-	};
-
-	V4L2Subdevice::Routing routing;
-	routing.emplace_back(imageStream, V4L2Subdevice::Stream{ csiVideoSourcePad, 0 },
-			     V4L2_SUBDEV_ROUTE_FL_ACTIVE);
-
-	if (sensorMetadata_)
-		routing.emplace_back(embeddedDataStream,
-				     V4L2Subdevice::Stream{ csiMetaSourcePad, 0 },
-				     V4L2_SUBDEV_ROUTE_FL_ACTIVE);
-
-	ret = csi2Subdev_->setRouting(&routing);
-	if (ret)
-		return ret;
+	fe->getPadByIndex(feConfigSinkPad)->links()[0]->setEnabled(true);
+	fe->getPadByIndex(feVideo0SourcePad)->links()[0]->setEnabled(true);
+	fe->getPadByIndex(feVideo1SourcePad)->links()[0]->setEnabled(false);
+	fe->getPadByIndex(feStatsSourcePad)->links()[0]->setEnabled(true);
 
-	ret = csi2Subdev_->setFormat(imageStream, &sensorFormat);
+	ret = csi2Subdev_->setFormat(csiVideoSinkPad, &sensorFormat);
 	if (ret)
 		return ret;
 
 	if (sensorMetadata_) {
-		ret = csi2Subdev_->setFormat(embeddedDataStream, &embeddedFormat);
+		ret = csi2Subdev_->setFormat(csiMetaSinkPad, &embeddedFormat);
+		if (ret)
+			return ret;
+		ret = csi2Subdev_->setFormat(csiMetaSourcePad, &embeddedFormat);
 		if (ret)
 			return ret;
 	}