From patchwork Fri Nov 19 11:09:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 14647 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 85D1CBDB13 for ; Fri, 19 Nov 2021 11:10:16 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D849A6038C; Fri, 19 Nov 2021 12:10:14 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="Z4a8NX8V"; dkim-atps=neutral Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com [IPv6:2a00:1450:4864:20::32d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D197E60233 for ; Fri, 19 Nov 2021 12:10:12 +0100 (CET) Received: by mail-wm1-x32d.google.com with SMTP id 137so4600346wma.1 for ; Fri, 19 Nov 2021 03:10:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=lg6hLkZF5C4o5pvhsXfDsmOyV/0MVmZT0N0mZ4xVufk=; b=Z4a8NX8VZthVpYNo+myqDiXH9sijSiTZOb2+nRCFQEe5ugYwVbpXYPgAAQZrZlXmC7 T6HliBUz29UKEUw855V37q8VuC9HbOYroWUahcrG64A0u7LCrbPhu3e4nLI3LO9yAhW/ wAEAHYhZm5WefUJv56bsn0yQGFSs9WEJUBynqamPMeg481V9+ni13MmlMrRQeU0KIVdh 7AxFgvjt/XRZMEEmw5XvzniGtmlzD11mGlUloCml/kM6ika+jvkEQycsk9uZFlxXRvR0 VVtUxCarp2Q0xPyk9BH+2zqPA+u6bYbOEdd8w92JeQ2ZZQduFIQn52rDa1Q3wOen7/Ld 0Y5w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=lg6hLkZF5C4o5pvhsXfDsmOyV/0MVmZT0N0mZ4xVufk=; b=qoufVv0LokM817HftAjIsUuZdKhGZkvV59MacC66ECkn/usvZIARxSg6LM29Tneen3 rgRubDjaR//umtoBCz//NcVdUgLxUxZ9k5RGchIZ//AbdOCd9OwIgKN+lLhEsvHxsP7r 64MidzMfLJegDHK8zu6IjvxVx8BToDt8SvkruHYev6NWHS5SZWwXB4SA7M6QCj3r6mho ZNTQQE2EuD4Tyr3XLSfY99L92KQGsZ3rcG/6aruePJClbnHvwzMAe9NR0NkCru80/851 bwl5E5UrefcsA1/W/DqnG6Gbgk5QLGUlYdGSYdGmACY7qc4CWV7p+R0Dali6yzKoiwqA tA2A== X-Gm-Message-State: AOAM5315mcL36ZXQoe2V8tapkJrg2NCpqNcqr9qSs8dwdOqIu3Y2t3kC GmT3UwaDaNbj5687CW+uKuB/HW8sBaB4Z9ew X-Google-Smtp-Source: ABdhPJzDowZtO5edeIYqLrjYTjFXT4iB6VU58vugf4CYoGUHCEEavIXhF22Sww1fr0x/JO/J4RNOgg== X-Received: by 2002:a05:600c:35c8:: with SMTP id r8mr5891314wmq.8.1637320212061; Fri, 19 Nov 2021 03:10:12 -0800 (PST) Received: from naush-laptop.pitowers.org ([2a00:1098:3142:14:bad1:6339:b276:e376]) by smtp.gmail.com with ESMTPSA id o5sm2596486wrx.83.2021.11.19.03.10.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 19 Nov 2021 03:10:11 -0800 (PST) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Date: Fri, 19 Nov 2021 11:09:54 +0000 Message-Id: <20211119110955.3137585-2-naush@raspberrypi.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211119110955.3137585-1-naush@raspberrypi.com> References: <20211119110955.3137585-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 2/3] pipeline: raspberrypi: Allow registration of multiple cameras 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" Expand the pipeline handler camera registration to correctly handle multiple cameras attached to the platform. For example, Raspberry Pi Compute Module platforms have two camera connectors, and this change would allow the user to select either of the two cameras to run. There are associated kernel driver changes for both Unicam and the ISP needed to correctly advertise multiple media devices and nodes for multi-camera usage: https://github.com/raspberrypi/linux/pull/4140 https://github.com/raspberrypi/linux/pull/4709 However, this change is backward compatible with kernel builds that do not have these changes for standard single camera usage. Signed-off-by: Naushir Patuck --- .../pipeline/raspberrypi/raspberrypi.cpp | 112 ++++++++++++------ 1 file changed, 77 insertions(+), 35 deletions(-) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 9aa7e9eef5e7..8ed2ebcaafe7 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -311,7 +311,8 @@ private: return static_cast(camera->_d()); } - bool registerCameras(); + int registerCameras(MediaDevice *unicam, MediaDevice *isp, + const std::string &unicamIdStr, const std::string &ispIdStr); int queueAllBuffers(Camera *camera); int prepareBuffers(Camera *camera); void freeBuffers(Camera *camera); @@ -993,49 +994,87 @@ int PipelineHandlerRPi::queueRequestDevice(Camera *camera, Request *request) bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator) { - DeviceMatch unicam("unicam"); - DeviceMatch isp("bcm2835-isp"); + unsigned int numCameras = 0; - unicam.add("unicam-image"); + /* + * String of indexes to append to the entity names when searching for + * the Unican media devices. The first string is empty (un-indexed) to + * to maintain backward compatability with old versions of the Unicam + * kernel driver that did not advertise instance indexes. + */ + for (const std::string &unicamIdStr : { "", "0", "1" }) { + MediaDevice *unicamDevice; - isp.add("bcm2835-isp0-output0"); /* Input */ - isp.add("bcm2835-isp0-capture1"); /* Output 0 */ - isp.add("bcm2835-isp0-capture2"); /* Output 1 */ - isp.add("bcm2835-isp0-capture3"); /* Stats */ + DeviceMatch unicam("unicam"); + unicam.add("unicam" + unicamIdStr + "-image"); + unicamDevice = acquireMediaDevice(enumerator, unicam); - unicam_ = acquireMediaDevice(enumerator, unicam); - if (!unicam_) - return false; + if (!unicamDevice) + continue; - isp_ = acquireMediaDevice(enumerator, isp); - if (!isp_) - return false; + for (const std::string &ispIdStr : { "0", "1" }) { + MediaDevice *ispDevice; + int ret; + + DeviceMatch isp("bcm2835-isp"); + isp.add("bcm2835-isp" + ispIdStr + "-output0"); /* Input */ + isp.add("bcm2835-isp" + ispIdStr + "-capture1"); /* Output 0 */ + isp.add("bcm2835-isp" + ispIdStr + "-capture2"); /* Output 0 */ + isp.add("bcm2835-isp" + ispIdStr + "-capture3"); /* Stats */ + ispDevice = acquireMediaDevice(enumerator, isp); + + if (!ispDevice) + continue; + + ret = registerCameras(unicamDevice, ispDevice, unicamIdStr, ispIdStr); + if (ret) { + LOG(RPI, Error) << "Failed to register camera: " << ret; + ispDevice->release(); + unicamDevice->release(); + } else + numCameras++; - return registerCameras(); + break; + } + } + + return !!numCameras; } -bool PipelineHandlerRPi::registerCameras() +int PipelineHandlerRPi::registerCameras(MediaDevice *unicam, MediaDevice *isp, + const std::string &unicamIdStr, + const std::string &ispIdStr) { std::unique_ptr data = std::make_unique(this); + if (!data->dmaHeap_.isValid()) - return false; + return -ENOMEM; + + MediaEntity *unicamImage = unicam->getEntityByName("unicam" + unicamIdStr + "-image"); + MediaEntity *ispOutput0 = isp->getEntityByName("bcm2835-isp" + ispIdStr + "-output0"); + MediaEntity *ispCapture1 = isp->getEntityByName("bcm2835-isp" + ispIdStr + "-capture1"); + MediaEntity *ispCapture2 = isp->getEntityByName("bcm2835-isp" + ispIdStr + "-capture2"); + MediaEntity *ispCapture3 = isp->getEntityByName("bcm2835-isp" + ispIdStr + "-capture3"); + + if (!unicamImage || !ispOutput0 || !ispCapture1 || !ispCapture2 || !ispCapture3) + return -ENOENT; /* Locate and open the unicam video streams. */ - data->unicam_[Unicam::Image] = RPi::Stream("Unicam Image", unicam_->getEntityByName("unicam-image")); + data->unicam_[Unicam::Image] = RPi::Stream("Unicam Image", unicamImage); /* An embedded data node will not be present if the sensor does not support it. */ - MediaEntity *embeddedEntity = unicam_->getEntityByName("unicam-embedded"); - if (embeddedEntity) { - data->unicam_[Unicam::Embedded] = RPi::Stream("Unicam Embedded", embeddedEntity); + MediaEntity *unicamEmbedded = unicam->getEntityByName("unicam" + unicamIdStr + "-embedded"); + if (unicamEmbedded) { + data->unicam_[Unicam::Embedded] = RPi::Stream("Unicam Embedded", unicamEmbedded); data->unicam_[Unicam::Embedded].dev()->bufferReady.connect(data.get(), &RPiCameraData::unicamBufferDequeue); } /* Tag the ISP input stream as an import stream. */ - data->isp_[Isp::Input] = RPi::Stream("ISP Input", isp_->getEntityByName("bcm2835-isp0-output0"), true); - data->isp_[Isp::Output0] = RPi::Stream("ISP Output0", isp_->getEntityByName("bcm2835-isp0-capture1")); - data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", isp_->getEntityByName("bcm2835-isp0-capture2")); - data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", isp_->getEntityByName("bcm2835-isp0-capture3")); + data->isp_[Isp::Input] = RPi::Stream("ISP Input", ispOutput0, true); + data->isp_[Isp::Output0] = RPi::Stream("ISP Output0", ispCapture1); + data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", ispCapture2); + data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", ispCapture3); /* Wire up all the buffer connections. */ data->unicam_[Unicam::Image].dev()->frameStart.connect(data.get(), &RPiCameraData::frameStarted); @@ -1046,7 +1085,7 @@ bool PipelineHandlerRPi::registerCameras() data->isp_[Isp::Stats].dev()->bufferReady.connect(data.get(), &RPiCameraData::ispOutputDequeue); /* Identify the sensor. */ - for (MediaEntity *entity : unicam_->entities()) { + for (MediaEntity *entity : unicam->entities()) { if (entity->function() == MEDIA_ENT_F_CAM_SENSOR) { data->sensor_ = std::make_unique(entity); break; @@ -1054,23 +1093,23 @@ bool PipelineHandlerRPi::registerCameras() } if (!data->sensor_) - return false; + return -EINVAL; if (data->sensor_->init()) - return false; + return -EINVAL; data->sensorFormats_ = populateSensorFormats(data->sensor_); ipa::RPi::SensorConfig sensorConfig; if (data->loadIPA(&sensorConfig)) { LOG(RPI, Error) << "Failed to load a suitable IPA library"; - return false; + return -EINVAL; } - if (sensorConfig.sensorMetadata ^ !!embeddedEntity) { + if (sensorConfig.sensorMetadata ^ !!unicamEmbedded) { LOG(RPI, Warning) << "Mismatch between Unicam and CamHelper for embedded data usage!"; sensorConfig.sensorMetadata = false; - if (embeddedEntity) + if (unicamEmbedded) data->unicam_[Unicam::Embedded].dev()->bufferReady.disconnect(); } @@ -1091,12 +1130,12 @@ bool PipelineHandlerRPi::registerCameras() for (auto stream : data->streams_) { if (stream->dev()->open()) - return false; + continue; } if (!data->unicam_[Unicam::Image].dev()->caps().hasMediaController()) { LOG(RPI, Error) << "Unicam driver does not use the MediaController, please update your kernel!"; - return false; + return -EINVAL; } /* @@ -1158,7 +1197,7 @@ bool PipelineHandlerRPi::registerCameras() if (!bayerFormat.isValid()) { LOG(RPI, Error) << "No Bayer format found"; - return false; + return -EINVAL; } data->nativeBayerOrder_ = bayerFormat.order; @@ -1178,7 +1217,10 @@ bool PipelineHandlerRPi::registerCameras() Camera::create(std::move(data), id, streams); registerCamera(std::move(camera)); - return true; + LOG(RPI, Info) << "Registered camera " << id + << " to instances \"unicam" << unicamIdStr << "\"" + << " and \"isp" << ispIdStr << "\""; + return 0; } int PipelineHandlerRPi::queueAllBuffers(Camera *camera)