[libcamera-devel,v2,14/14] libcamera: pipeline: rkisp1: Attach to an IPA

Message ID 20190829232653.13214-15-niklas.soderlund@ragnatech.se
State Superseded
Headers show
Series
  • libcamera: ipa: Add basic IPA support
Related show

Commit Message

Niklas Söderlund Aug. 29, 2019, 11:26 p.m. UTC
Add the plumbing to the pipeline handler to interact with an IPA module.
To support this parameter and statistic buffers needs to be associated
with every request queued. The parameters buffer needs to be passed to
the IPA before any buffer in the request is queued to hardware and the
statistics buffer needs to be passed to the IPA for inspection as soon
as it's ready.

This change makes the usage of an IPA module mandatory for the rkisp1
pipeline.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
 src/libcamera/pipeline/rkisp1/rkisp1.cpp | 263 ++++++++++++++++++++++-
 1 file changed, 252 insertions(+), 11 deletions(-)

Patch

diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index de4ab523d0e4fe36..2bebf0eaf3877641 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -9,7 +9,7 @@ 
 #include <array>
 #include <iomanip>
 #include <memory>
-#include <vector>
+#include <queue>
 
 #include <linux/media-bus-format.h>
 
@@ -34,7 +34,7 @@  class RkISP1CameraData : public CameraData
 {
 public:
 	RkISP1CameraData(PipelineHandler *pipe)
-		: CameraData(pipe), sensor_(nullptr)
+		: CameraData(pipe, 1, 1), sensor_(nullptr)
 	{
 	}
 
@@ -43,8 +43,21 @@  public:
 		delete sensor_;
 	}
 
+	int initIPA() override;
+
 	Stream stream_;
 	CameraSensor *sensor_;
+
+private:
+	void updateSensor(V4L2ControlList controls);
+	void queueRequestHardware(const void *cookie);
+};
+
+class RkISP1RequestData : public RequestData
+{
+public:
+	Buffer *stat;
+	Buffer *param;
 };
 
 class RkISP1CameraConfiguration : public CameraConfiguration
@@ -99,18 +112,69 @@  private:
 			PipelineHandler::cameraData(camera));
 	}
 
+	friend RkISP1CameraData;
+
 	int initLinks();
 	int createCamera(MediaEntity *sensor);
+	void tryCompleteRequest(Request *request);
 	void bufferReady(Buffer *buffer);
+	void statReady(Buffer *buffer);
+	void paramReady(Buffer *buffer);
 
 	MediaDevice *media_;
 	V4L2Subdevice *dphy_;
 	V4L2Subdevice *isp_;
 	V4L2VideoDevice *video_;
+	V4L2VideoDevice *stat_;
+	V4L2VideoDevice *param_;
+
+	BufferPool statPool_;
+	BufferPool paramPool_;
+
+	std::queue<Buffer *> statBuffers_;
+	std::queue<Buffer *> paramBuffers_;
 
 	Camera *activeCamera_;
 };
 
+int RkISP1CameraData::initIPA()
+{
+	ipa_->updateSensor.connect(this,
+				   &RkISP1CameraData::updateSensor);
+	ipa_->queueRequest.connect(this,
+				   &RkISP1CameraData::queueRequestHardware);
+	return 0;
+}
+
+void RkISP1CameraData::updateSensor(V4L2ControlList controls)
+{
+	sensor_->setControls(&controls);
+}
+
+void RkISP1CameraData::queueRequestHardware(const void *cookie)
+{
+	/* Translate cookie to request. */
+	Request *request = reinterpret_cast<Request *>(const_cast<void *>(cookie));
+	PipelineHandlerRkISP1 *pipe =
+		static_cast<PipelineHandlerRkISP1 *>(pipe_);
+	RkISP1RequestData *reqData =
+		static_cast<RkISP1RequestData *>(request->data);
+	Buffer *buffer = request->findBuffer(&stream_);
+	int ret;
+
+	ret = pipe->param_->queueBuffer(reqData->param);
+	if (ret < 0)
+		LOG(RkISP1, Error) << "Failed to queue parameters";
+
+	ret = pipe->stat_->queueBuffer(reqData->stat);
+	if (ret < 0)
+		LOG(RkISP1, Error) << "Failed to queue statistics";
+
+	ret = pipe->video_->queueBuffer(buffer);
+	if (ret < 0)
+		LOG(RkISP1, Error) << "Failed to queue video";
+}
+
 RkISP1CameraConfiguration::RkISP1CameraConfiguration(Camera *camera,
 						     RkISP1CameraData *data)
 	: CameraConfiguration()
@@ -202,12 +266,14 @@  CameraConfiguration::Status RkISP1CameraConfiguration::validate()
 
 PipelineHandlerRkISP1::PipelineHandlerRkISP1(CameraManager *manager)
 	: PipelineHandler(manager), dphy_(nullptr), isp_(nullptr),
-	  video_(nullptr)
+	  video_(nullptr), stat_(nullptr), param_(nullptr)
 {
 }
 
 PipelineHandlerRkISP1::~PipelineHandlerRkISP1()
 {
+	delete param_;
+	delete stat_;
 	delete video_;
 	delete isp_;
 	delete dphy_;
@@ -317,6 +383,20 @@  int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
 	if (ret)
 		return ret;
 
+	V4L2DeviceFormat statFormat = {};
+	statFormat.fourcc = V4L2_META_FMT_RK_ISP1_STAT_3A;
+
+	ret = stat_->setFormat(&statFormat);
+	if (ret)
+		return ret;
+
+	V4L2DeviceFormat paramFormat = {};
+	paramFormat.fourcc = V4L2_META_FMT_RK_ISP1_PARAMS;
+
+	ret = param_->setFormat(&paramFormat);
+	if (ret)
+		return ret;
+
 	if (outputFormat.size != cfg.size ||
 	    outputFormat.fourcc != cfg.pixelFormat) {
 		LOG(RkISP1, Error)
@@ -333,30 +413,92 @@  int PipelineHandlerRkISP1::allocateBuffers(Camera *camera,
 					   const std::set<Stream *> &streams)
 {
 	Stream *stream = *streams.begin();
+	int ret;
 
 	if (stream->memoryType() == InternalMemory)
-		return video_->exportBuffers(&stream->bufferPool());
+		ret = video_->exportBuffers(&stream->bufferPool());
 	else
-		return video_->importBuffers(&stream->bufferPool());
+		ret = video_->importBuffers(&stream->bufferPool());
+
+	if (ret)
+		return ret;
+
+	statPool_.createBuffers(stream->configuration().bufferCount);
+	ret = stat_->exportBuffers(&statPool_);
+	if (ret) {
+		video_->releaseBuffers();
+		return ret;
+	}
+
+	paramPool_.createBuffers(stream->configuration().bufferCount);
+	ret = param_->exportBuffers(&paramPool_);
+	if (ret) {
+		stat_->releaseBuffers();
+		video_->releaseBuffers();
+		return ret;
+	}
+
+	for (unsigned int i = 0; i < stream->configuration().bufferCount; i++) {
+		statBuffers_.push(new Buffer(i));
+		paramBuffers_.push(new Buffer(i));
+	}
+
+	return ret;
 }
 
 int PipelineHandlerRkISP1::freeBuffers(Camera *camera,
 				       const std::set<Stream *> &streams)
 {
+	while (!paramBuffers_.empty())
+		paramBuffers_.pop();
+
+	while (!statBuffers_.empty())
+		statBuffers_.pop();
+
+	if (param_->releaseBuffers())
+		LOG(RkISP1, Error) << "Failed to release parameters buffers";
+
+	if (stat_->releaseBuffers())
+		LOG(RkISP1, Error) << "Failed to release stat buffers";
+
 	if (video_->releaseBuffers())
-		LOG(RkISP1, Error) << "Failed to release buffers";
+		LOG(RkISP1, Error) << "Failed to release video buffers";
 
 	return 0;
 }
 
 int PipelineHandlerRkISP1::start(Camera *camera)
 {
+	RkISP1CameraData *data = cameraData(camera);
 	int ret;
 
+	ret = data->ipa_->initSensor(data->sensor_->controls());
+	if (ret)
+		return ret;
+
+	ret = param_->streamOn();
+	if (ret) {
+		LOG(RkISP1, Error)
+			<< "Failed to start parameters " << camera->name();
+		return ret;
+	}
+
+	ret = stat_->streamOn();
+	if (ret) {
+		param_->streamOff();
+		LOG(RkISP1, Error)
+			<< "Failed to start statistics " << camera->name();
+		return ret;
+	}
+
 	ret = video_->streamOn();
-	if (ret)
+	if (ret) {
+		param_->streamOff();
+		stat_->streamOff();
+
 		LOG(RkISP1, Error)
 			<< "Failed to start camera " << camera->name();
+	}
 
 	activeCamera_ = camera;
 
@@ -372,6 +514,16 @@  void PipelineHandlerRkISP1::stop(Camera *camera)
 		LOG(RkISP1, Warning)
 			<< "Failed to stop camera " << camera->name();
 
+	ret = stat_->streamOff();
+	if (ret)
+		LOG(RkISP1, Warning)
+			<< "Failed to stop statistics " << camera->name();
+
+	ret = param_->streamOff();
+	if (ret)
+		LOG(RkISP1, Warning)
+			<< "Failed to stop parameters " << camera->name();
+
 	activeCamera_ = nullptr;
 }
 
@@ -380,6 +532,16 @@  int PipelineHandlerRkISP1::queueRequest(Camera *camera, Request *request)
 	RkISP1CameraData *data = cameraData(camera);
 	Stream *stream = &data->stream_;
 
+	if (paramBuffers_.empty()) {
+		LOG(RkISP1, Error) << "Parameters buffer underrun";
+		return -ENOENT;
+	}
+
+	if (statBuffers_.empty()) {
+		LOG(RkISP1, Error) << "Statistic buffer underrun";
+		return -ENOENT;
+	}
+
 	Buffer *buffer = request->findBuffer(stream);
 	if (!buffer) {
 		LOG(RkISP1, Error)
@@ -387,12 +549,24 @@  int PipelineHandlerRkISP1::queueRequest(Camera *camera, Request *request)
 		return -ENOENT;
 	}
 
-	int ret = video_->queueBuffer(buffer);
-	if (ret < 0)
-		return ret;
+	RkISP1RequestData *reqData = new RkISP1RequestData();
+	request->data = reqData;
+	reqData->param = paramBuffers_.front();
+	reqData->stat = statBuffers_.front();
+
+	prepareInternalBuffer(reqData->param, request,
+			      &paramPool_.buffers()[reqData->param->index()]);
+	prepareInternalBuffer(reqData->stat, request,
+			      &statPool_.buffers()[reqData->stat->index()]);
+
+	paramBuffers_.pop();
+	statBuffers_.pop();
 
 	PipelineHandler::queueRequest(camera, request);
 
+	data->ipa_->processRequest(request, request->controls(),
+				   *reqData->param);
+
 	return 0;
 }
 
@@ -435,6 +609,10 @@  int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
 	std::unique_ptr<RkISP1CameraData> data =
 		utils::make_unique<RkISP1CameraData>(this);
 
+	data->controlInfo_.emplace(std::piecewise_construct,
+				   std::forward_as_tuple(AeEnable),
+				   std::forward_as_tuple(AeEnable, false, true));
+
 	data->sensor_ = new CameraSensor(sensor);
 	ret = data->sensor_->init();
 	if (ret)
@@ -478,7 +656,17 @@  bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
 	if (video_->open() < 0)
 		return false;
 
+	stat_ = V4L2VideoDevice::fromEntityName(media_, "rkisp1-statistics");
+	if (stat_->open() < 0)
+		return false;
+
+	param_ = V4L2VideoDevice::fromEntityName(media_, "rkisp1-input-params");
+	if (param_->open() < 0)
+		return false;
+
 	video_->bufferReady.connect(this, &PipelineHandlerRkISP1::bufferReady);
+	stat_->bufferReady.connect(this, &PipelineHandlerRkISP1::statReady);
+	param_->bufferReady.connect(this, &PipelineHandlerRkISP1::paramReady);
 
 	/* Configure default links. */
 	if (initLinks() < 0) {
@@ -504,13 +692,66 @@  bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
  * Buffer Handling
  */
 
+void PipelineHandlerRkISP1::tryCompleteRequest(Request *request)
+{
+	RkISP1RequestData *reqData =
+		static_cast<RkISP1RequestData *>(request->data);
+
+	if (reqData->param)
+		return;
+
+	if (reqData->stat)
+		return;
+
+	if (request->hasPendingBuffers())
+		return;
+
+	delete reqData;
+	request->data = nullptr;
+
+	completeRequest(activeCamera_, request);
+}
+
 void PipelineHandlerRkISP1::bufferReady(Buffer *buffer)
 {
 	ASSERT(activeCamera_);
 	Request *request = buffer->request();
 
 	completeBuffer(activeCamera_, request, buffer);
-	completeRequest(activeCamera_, request);
+	tryCompleteRequest(request);
+}
+
+void PipelineHandlerRkISP1::statReady(Buffer *buffer)
+{
+	ASSERT(activeCamera_);
+	RkISP1CameraData *data = cameraData(activeCamera_);
+	Request *request = buffer->request();
+	RkISP1RequestData *reqData =
+		static_cast<RkISP1RequestData *>(request->data);
+
+	data->ipa_->updateStatistics(request, *buffer);
+
+	/* TODO: Fetch libcamera status controls from IPA */
+
+	reqData->stat = nullptr;
+
+	statBuffers_.push(buffer);
+
+	tryCompleteRequest(request);
+}
+
+void PipelineHandlerRkISP1::paramReady(Buffer *buffer)
+{
+	ASSERT(activeCamera_);
+	Request *request = buffer->request();
+	RkISP1RequestData *reqData =
+		static_cast<RkISP1RequestData *>(request->data);
+
+	reqData->param = nullptr;
+
+	paramBuffers_.push(buffer);
+
+	tryCompleteRequest(request);
 }
 
 REGISTER_PIPELINE_HANDLER(PipelineHandlerRkISP1);