From patchwork Fri Sep 18 07:59:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 28329 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 42FECC335E for ; Fri, 18 Sep 2026 08:08:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5E5C768757; Fri, 18 Sep 2026 10:08:08 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="P1zdt1eI"; dkim-atps=neutral Received: from mail-wr2-x10.google.com (mail-wr2-x10.google.com [IPv6:2a00:1450:4864:30::10]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A6B1568715 for ; Fri, 18 Sep 2026 10:07:55 +0200 (CEST) Received: by mail-wr2-x10.google.com with SMTP id ffacd0b85a97d-482e1b55da9so52039f8f.2 for ; Fri, 18 Sep 2026 01:07:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1789718875; x=1790323675; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=pFv9ns3rAhbj1IiOwzZSokLG0z3FvA5oTFaFhxT6bQQ=; b=P1zdt1eINwLtYADnQCY4xWnXwCxUtv+x0ow78uGJx4sf4Bb2l4mm+C4guHN+DfbqNM MlzYbR+QZO8okDnhdiJ2PnCurfK+Cv+fRXSpNNN9flfjgJurZwgm5Mr58pUD5VJTYPNu 1giLgfqa4nkPYUf8u9hZmT+/QBLkB0TvqPyv6DdcQj/1HRQiGFEyKvcBabjCLhH1SIob 7eZ+GhOTTh7F9ViawVnTXO7ZUuQIVVFB8MBguv64B2QSYy9S06w9eMkddmpC3Nu4WrbF JNG+Nsw78f3lsoTo6WGrBpClXGCGhfbVWLa/fqvh7PaFi8MGw7alcf4HkSZw8+5LDwJn XhPw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789718875; x=1790323675; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=pFv9ns3rAhbj1IiOwzZSokLG0z3FvA5oTFaFhxT6bQQ=; b=s+fSfpvjpJ/obqFFVTLw2y/0L2DQaTZxJrOJ6/5ZmIserUqa9Z7GkeSpZhG7hp4f/y APJCb0doBHaXGzmpBRCKcuxnnvoHBPOuiw2uXpVRSUSzJdTxo4GFlcBiO/8OhsJLcyz9 A89GpBhgwMyvxUI8ZXIy0i/1tr2hNwC61bxgbjsC4RtsyuyTMulhOx5Obu9unE1qldsM J7t4eGUqePkoozQChEXxsfSlx7lBeVvAZB79HhW6GVB1eMxSpMEh53Qgj+hwlEp4EYOA XdCyd3qCSGfukmMkW8Ob+VkZpmKEfWBnCEb3MVQBFTkIYH08BIhCjBUiHe7qOTX1qJtP KBbQ== X-Gm-Message-State: AFuF++l7QeBd3K2IQXKZY+Keq6UfgcjRie+oAXyFMZnSEgZnXW8CotwV GeUzgJIgjeg/IrDZbhSGAMQsgUMEjoGUx2hmJ3NKDb+lGhH+Y2xoAQWOzgeEfV5+IwklU9gnYMP cJ8mG+So= X-Gm-Gg: AYBFou2yC7D5ZRbXMn41eDu/ymbGvqX2j1n1lN47nS9MieT5fx3OQLh7qsbnmUL065m UJQDl7uHTwNDRh9U4BZfIkLr1hfxXYntZK5/1i4VipDy5LeFFVM87m9HiGB0ww2oLml9cUcWI+j 4+VNB8bZrAJSp1nrvie0Uz4zSWFkCOst+sHNOEOLNdU0zJrJA3DYhc1uthLo6zJk+it5+BvHzAT CPTSH7gCzPNcHuxgqswrK5o4NS+76hrIohDmqk94ntFhijstSKovABQpeWyasmQPMXp8b2PrtCk 9erdM/74zxngXuDfY12KZHqe+nSbXXPWoY2OsMS9cDmQfl7JC01a0B0iT87iaTmBnkdrpkRMwLM rUxF6Xf/g+YyL4qfaVP/8bQWhnaz1FhKHNwYU+HLIJO2Yn2rlOU/vNJj3VS7MMgTBbhwcvGJx+0 hwB6QnlFxotydKtF6rRw3BWSg45tktm+gNtQ3I8MaLqArQKdYCkFZaF416FQ8GxiL2LwtMyUx9D p4ggixLHvzxqDX59UoL2JSN4cpHrIRfg5gWvBTbgMde9o5Wpv+/ttT1cJK8jMyvr78FCEa9ih/C MUwDUXTGd3HjDRcxt6hNiK0xQZsL3dtzu00ZXRkNU/dJ6fg5/Dx1e0OxHk2DcOkemjjQL1l0gt2 NyF9aWl6lt7yh3cSzi8ZXmpb+aGB/LlmIw/XsdXDyd8zZIw== X-Received: by 2002:a05:6000:4283:b0:487:aff:995e with SMTP id ffacd0b85a97d-4871fa7433fmr1336913f8f.3.1789718875004; Fri, 18 Sep 2026 01:07:55 -0700 (PDT) Received: from naush-dell.pitowers.org ([2a00:1098:3142:1f:45e3:df8b:2b18:6253]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-487203f077fsm1742230f8f.26.2026.09.18.01.07.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Sep 2026 01:07:54 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Cc: Naushir Patuck Subject: [RFC PATCH v1 17/20] pipeline: rpi: Implement survey() and createCamera() Date: Fri, 18 Sep 2026 08:59:55 +0100 Message-ID: <20260918080734.1228227-18-naush@raspberrypi.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260918080734.1228227-1-naush@raspberrypi.com> References: <20260918080734.1228227-1-naush@raspberrypi.com> MIME-Version: 1.0 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" Implement survey() and createCamera() for the vc4 and pisp platforms. survey() pairs each frontend with a backend in match order and reports a descriptor for every sensor entity on the frontend. The camera ID comes from sysfs and the model from the entity name, so no device is acquired or opened. createCamera() creates a single camera from a descriptor. It acquires the media devices unless this pipeline handler instance already holds them, so that cameras behind a shared frontend end up in the same instance. The vc4 and pisp match() implementations are no longer used and removed. Signed-off-by: Naushir Patuck --- .../pipeline/rpi/common/pipeline_base.cpp | 99 +++++++++++++++++++ .../pipeline/rpi/common/pipeline_base.h | 4 + src/libcamera/pipeline/rpi/pisp/pisp.cpp | 90 ----------------- src/libcamera/pipeline/rpi/vc4/vc4.cpp | 57 ----------- 4 files changed, 103 insertions(+), 147 deletions(-) diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp index 71f3f26a4ffe..57188b8343e3 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp @@ -15,11 +15,15 @@ #include #include +#include #include #include #include +#include "libcamera/internal/camera_descriptor.h" #include "libcamera/internal/camera_lens.h" +#include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/v4l2_subdevice.h" #include "libcamera/internal/yaml_parser.h" @@ -793,6 +797,101 @@ int PipelineHandlerBase::queueRequestDevice(Camera *camera, Request *request) return 0; } +int PipelineHandlerBase::survey(const DeviceEnumerator *enumerator, + std::vector> *descriptors) +{ + /* + * Pair each available frontend instance with a backend instance, in + * the same order as match() acquires them, and report a descriptor + * for every sensor entity attached to the frontend. No device is + * acquired or opened. + */ + std::vector> frontends = + enumerator->searchAll(frontendMatch()); + std::vector> backends = + enumerator->searchAll(backendMatch()); + + for (unsigned int i = 0; i < frontends.size() && i < backends.size(); i++) { + MediaDevice *frontendDevice = frontends[i].get(); + MediaDevice *backendDevice = backends[i].get(); + + if (!platformSupported(frontendDevice, backendDevice)) + continue; + + for (MediaEntity *entity : frontendDevice->entities()) { + if (entity->function() != MEDIA_ENT_F_CAM_SENSOR) + continue; + + std::string id = CameraSensorFactoryBase::generateId(entity); + if (id.empty()) { + LOG(RPI, Warning) + << "Failed to generate an ID for sensor " + << entity->name(); + continue; + } + + std::string model = + V4L2Subdevice::modelFromEntityName(entity->name()); + + auto data = std::make_unique(); + data->id_ = std::move(id); + data->properties_.set(properties::Model, + utils::toAscii(model)); + data->mediaDevices_ = { frontends[i], backends[i] }; + data->entityName_ = entity->name(); + + descriptors->push_back(CameraDescriptor::create(std::move(data))); + } + } + + return 0; +} + +int PipelineHandlerBase::createCamera(const CameraDescriptor *descriptor) +{ + const CameraDescriptor::Private *data = descriptor->_d(); + + if (data->mediaDevices_.size() != 2) + return -EINVAL; + + const std::shared_ptr &frontendDevice = data->mediaDevices_[0]; + const std::shared_ptr &backendDevice = data->mediaDevices_[1]; + + /* + * Acquire the media devices, unless this pipeline handler instance + * already holds them because a camera behind the same frontend has + * been created before. + */ + if (!usesMediaDevice(frontendDevice.get())) { + if (!acquireMediaDevice(frontendDevice)) + return -EBUSY; + if (!acquireMediaDevice(backendDevice)) + return -EBUSY; + } + + MediaEntity *sensorEntity = nullptr; + for (MediaEntity *entity : frontendDevice->entities()) { + if (entity->name() == data->entityName_) { + sensorEntity = entity; + break; + } + } + + if (!sensorEntity) { + LOG(RPI, Error) << "Sensor entity '" << data->entityName_ + << "' not found"; + return -ENODEV; + } + + std::unique_ptr cameraData = + allocateCameraData(frontendDevice.get(), backendDevice.get()); + if (!cameraData) + return -EINVAL; + + return registerCamera(cameraData, frontendDevice, frontendLinkName(), + backendDevice, sensorEntity); +} + int PipelineHandlerBase::registerCamera(std::unique_ptr &cameraData, std::shared_ptr frontend, const std::string &frontendName, diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h index 16c509d6a769..6cc1684c74b0 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.h +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h @@ -223,6 +223,10 @@ public: const V4L2SubdeviceFormat &format, BayerFormat::Packing packingReq); + int survey(const DeviceEnumerator *enumerator, + std::vector> *descriptors) override; + int createCamera(const CameraDescriptor *descriptor) override; + std::unique_ptr generateConfiguration(Camera *camera, std::span roles) override; int configure(Camera *camera, CameraConfiguration *config) override; diff --git a/src/libcamera/pipeline/rpi/pisp/pisp.cpp b/src/libcamera/pipeline/rpi/pisp/pisp.cpp index 34de291f4092..221e058222ea 100644 --- a/src/libcamera/pipeline/rpi/pisp/pisp.cpp +++ b/src/libcamera/pipeline/rpi/pisp/pisp.cpp @@ -861,8 +861,6 @@ public: { } - bool match(DeviceEnumerator *enumerator) override; - private: PiSPCameraData *cameraData(Camera *camera) { @@ -944,94 +942,6 @@ PipelineHandlerPiSP::allocateCameraData(MediaDevice *frontend, MediaDevice *back return cameraData; } -bool PipelineHandlerPiSP::match(DeviceEnumerator *enumerator) -{ - constexpr unsigned int numCfeDevices = 2; - - /* - * Loop over all CFE instances, but return out once a match is found. - * This is to ensure we correctly enumerate the camera when an instance - * of the CFE has registered with media controller, but has not registered - * device nodes due to a sensor subdevice failure. - */ - for (unsigned int i = 0; i < numCfeDevices; i++) { - DeviceMatch cfe("rp1-cfe"); - cfe.add("rp1-cfe-fe-image0"); - cfe.add("rp1-cfe-fe-stats"); - cfe.add("rp1-cfe-fe-config"); - std::shared_ptr cfeDevice = acquireMediaDevice(enumerator, cfe); - - if (!cfeDevice) { - LOG(RPI, Debug) << "Unable to acquire a CFE instance"; - break; - } - - DeviceMatch isp("pispbe"); - isp.add("pispbe-input"); - isp.add("pispbe-config"); - isp.add("pispbe-output0"); - isp.add("pispbe-output1"); - isp.add("pispbe-tdn_output"); - isp.add("pispbe-tdn_input"); - isp.add("pispbe-stitch_output"); - isp.add("pispbe-stitch_input"); - std::shared_ptr ispDevice = acquireMediaDevice(enumerator, isp); - - if (!ispDevice) { - LOG(RPI, Debug) << "Unable to acquire ISP instance"; - break; - } - - /* - * The loop below is used to register multiple cameras behind - * one or more video mux devices that are attached to a - * particular CFE instance. Obviously these cameras cannot be - * used simultaneously. - */ - unsigned int numCameras = 0; - for (MediaEntity *entity : cfeDevice->entities()) { - if (entity->function() != MEDIA_ENT_F_CAM_SENSOR) - continue; - - const libpisp::PiSPVariant &variant = - libpisp::get_variant(cfeDevice->hwRevision(), - ispDevice->hwRevision()); - if (!variant.NumFrontEnds() || !variant.NumBackEnds()) { - LOG(RPI, Error) << "Unsupported PiSP variant"; - break; - } - - std::unique_ptr cameraData = - std::make_unique(this, variant); - PiSPCameraData *pisp = - static_cast(cameraData.get()); - - pisp->fe_ = SharedMemObject - ("pisp_frontend", true, pisp->pispVariant_); - pisp->be_ = SharedMemObject - ("pisp_backend", BackEnd::Config({}), pisp->pispVariant_); - - if (!pisp->fe_.fd().isValid() || !pisp->be_.fd().isValid()) { - LOG(RPI, Error) << "Failed to create ISP shared objects"; - break; - } - - int ret = registerCamera(cameraData, cfeDevice, "csi2", - ispDevice, entity); - if (ret) - LOG(RPI, Error) << "Failed to register camera " - << entity->name() << ": " << ret; - else - numCameras++; - } - - if (numCameras) - return true; - } - - return false; -} - int PipelineHandlerPiSP::allocateBuffers(Camera *camera) { PiSPCameraData *data = cameraData(camera); diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp index f6af31072eee..a638fe6de052 100644 --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp @@ -150,8 +150,6 @@ public: { } - bool match(DeviceEnumerator *enumerator) override; - private: Vc4CameraData *cameraData(Camera *camera) { @@ -186,61 +184,6 @@ private: std::shared_ptr isp) override; }; -bool PipelineHandlerVc4::match(DeviceEnumerator *enumerator) -{ - constexpr unsigned int numUnicamDevices = 2; - - /* - * Loop over all Unicam instances, but return out once a match is found. - * This is to ensure we correctly enumrate the camera when an instance - * of Unicam has registered with media controller, but has not registered - * device nodes due to a sensor subdevice failure. - */ - for (unsigned int i = 0; i < numUnicamDevices; i++) { - DeviceMatch unicam("unicam"); - std::shared_ptr unicamDevice = acquireMediaDevice(enumerator, unicam); - - if (!unicamDevice) { - LOG(RPI, Debug) << "Unable to acquire a Unicam instance"; - continue; - } - - DeviceMatch isp("bcm2835-isp"); - std::shared_ptr ispDevice = acquireMediaDevice(enumerator, isp); - - if (!ispDevice) { - LOG(RPI, Debug) << "Unable to acquire ISP instance"; - continue; - } - - /* - * The loop below is used to register multiple cameras behind one or more - * video mux devices that are attached to a particular Unicam instance. - * Obviously these cameras cannot be used simultaneously. - */ - unsigned int numCameras = 0; - for (MediaEntity *entity : unicamDevice->entities()) { - if (entity->function() != MEDIA_ENT_F_CAM_SENSOR) - continue; - - std::unique_ptr cameraData = std::make_unique(this); - int ret = RPi::PipelineHandlerBase::registerCamera(cameraData, - unicamDevice, "unicam-image", - ispDevice, entity); - if (ret) - LOG(RPI, Error) << "Failed to register camera " - << entity->name() << ": " << ret; - else - numCameras++; - } - - if (numCameras) - return true; - } - - return false; -} - int PipelineHandlerVc4::allocateBuffers(Camera *camera) { Vc4CameraData *data = cameraData(camera);