[RFC,12/12] pipline: rpi: Support memory cameras processing requests
diff mbox series

Message ID 20250827090739.86955-13-david.plowman@raspberrypi.com
State New
Headers show
Series
  • Bayer Re-Processing
Related show

Commit Message

David Plowman Aug. 27, 2025, 9:07 a.m. UTC
This is the final commit in the set to support memory cameras (Bayer
reprocessing).

There are some small fix-ups to allow the camera system to start
without the CFE being involved, and we add runMemoryCamera() which
pushs a buffer from the request into the Back End ISP.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
---
 .../pipeline/rpi/common/pipeline_base.cpp     | 23 ++++----
 src/libcamera/pipeline/rpi/pisp/pisp.cpp      | 53 ++++++++++++++++---
 2 files changed, 61 insertions(+), 15 deletions(-)

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
index 31bacc7c..8ca1e41f 100644
--- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
@@ -727,15 +727,19 @@  int PipelineHandlerBase::start(Camera *camera, const ControlList *controls)
 	/* A good moment to add an initial clock sample. */
 	data->wallClockRecovery_.addSample();
 
-	/*
-	 * Reset the delayed controls with the gain and exposure values set by
-	 * the IPA.
-	 */
-	data->delayedCtrls_->reset(0);
-	data->state_ = CameraData::State::Idle;
+	/* A memory camera wouldn't have a front end device, so you would skip this. */
+	if (data->frontendDevice()) {
+		/*
+		 * Reset the delayed controls with the gain and exposure values set by
+		 * the IPA.
+		 */
+		data->delayedCtrls_->reset(0);
+
+		/* Enable SOF event generation. */
+		data->frontendDevice()->setFrameStartEnabled(true);
+	}
 
-	/* Enable SOF event generation. */
-	data->frontendDevice()->setFrameStartEnabled(true);
+	data->state_ = CameraData::State::Idle;
 
 	data->platformStart();
 
@@ -762,7 +766,8 @@  void PipelineHandlerBase::stopDevice(Camera *camera)
 		stream->dev()->streamOff();
 
 	/* Disable SOF event generation. */
-	data->frontendDevice()->setFrameStartEnabled(false);
+	if (data->frontendDevice())
+		data->frontendDevice()->setFrameStartEnabled(false);
 
 	data->clearIncompleteRequests();
 
diff --git a/src/libcamera/pipeline/rpi/pisp/pisp.cpp b/src/libcamera/pipeline/rpi/pisp/pisp.cpp
index bb22e6d2..a637c6be 100644
--- a/src/libcamera/pipeline/rpi/pisp/pisp.cpp
+++ b/src/libcamera/pipeline/rpi/pisp/pisp.cpp
@@ -780,6 +780,7 @@  private:
 	void prepareCfe();
 	void prepareBe(uint32_t bufferId, bool stitchSwapBuffers);
 
+	void runMemoryCamera();
 	void tryRunPipeline() override;
 
 	struct CfeJob {
@@ -1243,7 +1244,7 @@  PipelineHandlerPiSP::platformCreateCamera(std::unique_ptr<RPi::CameraData> &came
 
 	/* Wire up all the IPA connections. */
 	data->ipa_->prepareIspComplete.connect(data, &PiSPCameraData::prepareIspComplete);
-	data->ipa_->processStatsComplete.connect(data, &PiSPCameraData::processStatsComplete);
+	/* We don't want the processStatsComplete callback, though, for memory cams. */
 
 	/* Create and register the camera. */
 	const std::string &id = cfe ? data->sensor_->id() : "memory";
@@ -1785,8 +1786,11 @@  void PiSPCameraData::platformStart()
 
 	cfeJobQueue_ = {};
 
-	for (unsigned int i = 0; i < config_.numCfeConfigQueue; i++)
-		prepareCfe();
+	/* A memory camera wouldn't be using the CFE. */
+	if (cfe_[Cfe::Output0].dev()) {
+		for (unsigned int i = 0; i < config_.numCfeConfigQueue; i++)
+			prepareCfe();
+	}
 
 	/* Clear the debug dump file history. */
 	last_dump_file_.clear();
@@ -1890,8 +1894,12 @@  void PiSPCameraData::beInputDequeue(FrameBuffer *buffer)
 			<< ", buffer id " << cfe_[Cfe::Output0].getBufferId(buffer)
 			<< ", timestamp: " << buffer->metadata().timestamp;
 
-	/* The ISP input buffer gets re-queued into CFE. */
-	handleStreamBuffer(buffer, &cfe_[Cfe::Output0]);
+	/*
+	 * The ISP input buffer gets re-queued into CFE (but not for memory cameras
+	 * that aren't using the CFE).
+	 */
+	if (cfe_[Cfe::Output0].dev())
+		handleStreamBuffer(buffer, &cfe_[Cfe::Output0]);
 	handleState();
 }
 
@@ -2361,7 +2369,15 @@  void PiSPCameraData::prepareCfe()
 
 void PiSPCameraData::prepareBe(uint32_t bufferId, bool stitchSwapBuffers)
 {
-	FrameBuffer *buffer = cfe_[Cfe::Output0].getBuffers().at(bufferId).buffer;
+	FrameBuffer *buffer;
+	if (!sensor_->isMemory())
+		buffer = cfe_[Cfe::Output0].getBuffers().at(bufferId).buffer;
+	else {
+		/* Memory camera. Back end input buffer is in the request. */
+		Request *request = requestQueue_.front();
+		const libcamera::Stream *rawStream = &isp_[Isp::Input];
+		buffer = request->findBuffer(rawStream);
+	}
 
 	LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << bufferId
 			<< ", timestamp: " << buffer->metadata().timestamp;
@@ -2412,8 +2428,33 @@  void PiSPCameraData::prepareBe(uint32_t bufferId, bool stitchSwapBuffers)
 	isp_[Isp::Config].queueBuffer(config.buffer);
 }
 
+void PiSPCameraData::runMemoryCamera()
+{
+	Request *request = requestQueue_.front();
+
+	applyScalerCrop(request->controls());
+
+	request->metadata().clear();
+
+	state_ = State::Busy;
+
+	ipa::RPi::PrepareParams params;
+	params.buffers.embedded = 0;
+	params.ipaContext = requestQueue_.front()->sequence();
+	params.requestControls = request->controls();
+
+	LOG(RPI, Debug) << "runMemoryCamera: call prepareIsp";
+	ipa_->prepareIsp(params);
+}
+
 void PiSPCameraData::tryRunPipeline()
 {
+	if (state_ == State::Idle && !requestQueue_.empty() && sensor_->isMemory()) {
+		runMemoryCamera();
+
+		return;
+	}
+
 	/* If any of our request or buffer queues are empty, we cannot proceed. */
 	if (state_ != State::Idle || requestQueue_.empty() || !cfeJobComplete())
 		return;