From 8c9e1926b9cd5719768f7f1035e31ab8efec96fc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 3 Jul 2021 18:33:55 +0300 Subject: libcamera: pipeline: simple: Add pipeline pad reservation mechanism The cameras created by the same pipeline handler instance may share hardware resources, prohibiting usage of multiple cameras concurrently. Implement a heuristic to reserve resources and handle mutual exclusiong in a generic way. Signed-off-by: Laurent Pinchart Tested-by: Martin Kepplinger --- src/libcamera/pipeline/simple/simple.cpp | 95 +++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 2 deletions(-) (limited to 'src/libcamera/pipeline') diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index 1c53de5c..0930626b 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -122,6 +122,28 @@ LOG_DEFINE_CATEGORY(SimplePipeline) * the pixel formats and sizes that the converter can produce for the output of * the capture video node, and stores the information in the outputFormats and * outputSizes of the SimpleCameraData::Configuration structure. + * + * Concurrent Access to Cameras + * ---------------------------- + * + * The cameras created by the same pipeline handler instance may share hardware + * resources. For instances, a platform may have multiple CSI-2 receivers but a + * single DMA engine, prohibiting usage of multiple cameras concurrently. This + * depends heavily on the hardware architecture, which the simple pipeline + * handler has no a priori knowledge of. The pipeline handler thus implements a + * heuristic to handle sharing of hardware resources in a generic fashion. + * + * Two cameras are considered to be mutually exclusive if their share common + * pads along the pipeline from the camera sensor to the video node. An entity + * can thus be used concurrently by multiple cameras, as long as pads are + * distinct. + * + * A resource reservation mechanism is implemented by the SimplePipelineHandler + * acquirePipeline() and releasePipeline() functions to manage exclusive access + * to pads. A camera reserves all the pads present in its pipeline when it is + * started, and the start() function returns an error if any of the required + * pads is already in use. When the camera is stopped, the pads it has reserved + * are released. */ class SimplePipelineHandler; @@ -268,6 +290,7 @@ private: struct EntityData { std::unique_ptr video; std::unique_ptr subdev; + std::map owners; }; SimpleCameraData *cameraData(Camera *camera) @@ -277,6 +300,9 @@ private: std::vector locateSensors(); + const MediaPad *acquirePipeline(SimpleCameraData *data); + void releasePipeline(SimpleCameraData *data); + void bufferReady(FrameBuffer *buffer); void converterInputDone(FrameBuffer *buffer); void converterOutputDone(FrameBuffer *buffer); @@ -846,6 +872,14 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL V4L2VideoDevice *video = data->video_; int ret; + const MediaPad *pad = acquirePipeline(data); + if (pad) { + LOG(SimplePipeline, Info) + << "Failed to acquire pipeline, entity " + << pad->entity()->name() << " in use"; + return -EBUSY; + } + if (data->useConverter_) { /* * When using the converter allocate a fixed number of internal @@ -858,8 +892,10 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL Stream *stream = &data->streams_[0]; ret = video->importBuffers(stream->configuration().bufferCount); } - if (ret < 0) + if (ret < 0) { + releasePipeline(data); return ret; + } ret = video->streamOn(); if (ret < 0) { @@ -897,6 +933,8 @@ void SimplePipelineHandler::stop(Camera *camera) data->converterBuffers_.clear(); activeCamera_ = nullptr; + + releasePipeline(data); } int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request) @@ -1106,7 +1144,7 @@ bool SimplePipelineHandler::match(DeviceEnumerator *enumerator) break; } - entities_[entity] = { std::move(video), std::move(subdev) }; + entities_[entity] = { std::move(video), std::move(subdev), {} }; } /* Initialize each pipeline and register a corresponding camera. */ @@ -1150,6 +1188,59 @@ V4L2Subdevice *SimplePipelineHandler::subdev(const MediaEntity *entity) return iter->second.subdev.get(); } +/** + * \brief Acquire all resources needed by the camera pipeline + * \return nullptr on success, a pointer to the contended pad on error + */ +const MediaPad *SimplePipelineHandler::acquirePipeline(SimpleCameraData *data) +{ + for (const SimpleCameraData::Entity &entity : data->entities_) { + const EntityData &edata = entities_[entity.entity]; + + if (entity.sink) { + auto iter = edata.owners.find(entity.sink); + if (iter != edata.owners.end() && iter->second != data) + return entity.sink; + } + + if (entity.source) { + auto iter = edata.owners.find(entity.source); + if (iter != edata.owners.end() && iter->second != data) + return entity.source; + } + } + + for (const SimpleCameraData::Entity &entity : data->entities_) { + EntityData &edata = entities_[entity.entity]; + + if (entity.sink) + edata.owners[entity.sink] = data; + if (entity.source) + edata.owners[entity.source] = data; + } + + return nullptr; +} + +void SimplePipelineHandler::releasePipeline(SimpleCameraData *data) +{ + for (const SimpleCameraData::Entity &entity : data->entities_) { + EntityData &edata = entities_[entity.entity]; + + if (entity.sink) { + auto iter = edata.owners.find(entity.sink); + ASSERT(iter->second == data); + edata.owners.erase(iter); + } + + if (entity.source) { + auto iter = edata.owners.find(entity.source); + ASSERT(iter->second == data); + edata.owners.erase(iter); + } + } +} + /* ----------------------------------------------------------------------------- * Buffer Handling */ -- cgit v1.2.1