From patchwork Thu Aug 5 22:24:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 13238 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 1C1EFC323C for ; Thu, 5 Aug 2021 22:25:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 073D0688CA; Fri, 6 Aug 2021 00:25:02 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="kwEcaz9A"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 127186884E for ; Fri, 6 Aug 2021 00:24:58 +0200 (CEST) Received: from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id AF7DE4FB for ; Fri, 6 Aug 2021 00:24:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1628202297; bh=yLlL0DLW9bNfQFknGVf71ywNPPHIqC9SNrVsLNOSPlE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=kwEcaz9AjdHgHb2J3Ajr3UhbyNK29Hj/VgIzkzniKeDnmNRVd9vSfya4DYJVbBYEY dRkat+FoMs4tLr2kZb9s0vcGwUzVdFSk88olw5UbRvhpbmDCO1jcPaq/zWeGhqnMvA Qx861dVKFq0rnzu71x2uXYAt+efbSRSZHCNjvt0k= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Fri, 6 Aug 2021 01:24:33 +0300 Message-Id: <20210805222436.6263-8-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210805222436.6263-1-laurent.pinchart@ideasonboard.com> References: <20210805222436.6263-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 07/10] libcamera: pipeline: simple: Add pipeline pad reservation mechanism X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 --- src/libcamera/pipeline/simple/simple.cpp | 95 +++++++++++++++++++++++- 1 file changed, 93 insertions(+), 2 deletions(-) diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index 17ef3f43ad41..b32369cae701 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -121,6 +121,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; @@ -266,6 +288,7 @@ private: struct EntityData { std::unique_ptr video; std::unique_ptr subdev; + std::map owners; }; SimpleCameraData *cameraData(const Camera *camera) @@ -276,6 +299,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); @@ -841,6 +867,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 @@ -853,8 +887,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) { @@ -892,6 +928,8 @@ void SimplePipelineHandler::stop(Camera *camera) data->converterBuffers_.clear(); activeCamera_ = nullptr; + + releasePipeline(data); } int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request) @@ -1101,7 +1139,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. */ @@ -1144,6 +1182,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 */