From patchwork Wed Dec 10 14:39:17 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25458 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 69FC5BD1F1 for ; Wed, 10 Dec 2025 14:39:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 96A0C6148A; Wed, 10 Dec 2025 15:39:35 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="A+Lr0vVW"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 51D8F613CB for ; Wed, 10 Dec 2025 15:39:32 +0100 (CET) Received: from [192.168.1.106] (mob-5-90-55-146.net.vodafone.it [5.90.55.146]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E310710C4; Wed, 10 Dec 2025 15:39:30 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1765377571; bh=Ss8dxmx3lkh/4SqMeS6BCfbpP2JQefxDDyPK+lQmOLo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=A+Lr0vVWN9uI5pk/nbXbrj/qqpMRua+Jo0hgjTG9jrqpOQkJkQSDnMq2l9fVaXEKj dGEluBMHvaMEfLJMz5nweppfP9q7sjxsLGkjXYRZQSa+Ztz9s/AGdHG+JHAeyOGd8h uczFy+sjt4UxT7iuvxMssGzYCxLnrRR6K9X2F8y0= From: Jacopo Mondi Date: Wed, 10 Dec 2025 15:39:17 +0100 Subject: [PATCH v2 1/7] libcamera: mali-c55: Add RZG2LCRU class MIME-Version: 1.0 Message-Id: <20251210-mali-cru-v2-1-e26421de202b@ideasonboard.com> References: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> In-Reply-To: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> To: Daniel Scally , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=10089; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=2vl1cyHt7A6ItMi3RNcn23hoKgxZHq1QVFa4P2a3sa8=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpOYYi4DVhRz9GCG+YHcLFpKjuGAkLPXlV6s3r9 26c+Tqmgn+JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaTmGIgAKCRByNAaPFqFW PL7NEACwPzRkhUievQvNks37dIdbl8z3RR3wZPq7SRYh6e1VjK3Ufz3VgH11J6y/kOUf2dAcP/S lt8D6rIN4aO91zdc0N5abQmWAcMhebW9sM8raQqkL3M1z/zihoTIuS9C2rdpic3HVn4SGso318J FrkQGBibe52TzZwLEv580DCkrGVw8QcRUTGYSircGuDGTolxIJ3jz3jVrfRN03RCiEF3Bg16vnk XS41u4qYpByDSwzj+QBfkghQjKoPrDDTwMSOC+CmCGBeGCUavKClXWK49N8zH8TzlYALUoic/6S hKwR/yshIqRs494h9tyaT61kEZFhCsTVjh8GuBW2LuQz+MhUqhKfiJ1R8dTO/qPfxsGdMsPORsL BEC4ibwg2bN0MdjVVbnkDz+qI65wGeEUkqAp6c5ww7GhUYQKU636QKLFGfRWh03oXIBEqhhsJDS 8zqaGzZw7VdLdadeQlNQksEsqGsGYGVt3rdu5QpuaOjXRmiONNRi3FVXlO1STzLIRdvpiehC8jH Ogkv94oyjQF0eEMSBRMBQCNK0zRETfw3UveWi1xo0HZXM9z3t3GSH4Iv6z86BOcnJGa0XIXRo4k 2ExBubpqDBo/xtHipFGIg6uiDKwV0xQ+RbJka0gUhOVDUh5VybUT73YOUnTyJ2DGCefkJH/IKBf yZGLxpq9cznV+jg== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B 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" From: Daniel Scally Add a class allowing us to control the RZ/G2L Camera Receiver Unit. The class is named after the media device it handles, which is called "rzg2l_cru" but the CRU is actually found in other SoCs than the RZ/G2L one, such as the RZ/V2H(P). The RZG2LCRU class models the CSI-2 receiver found on those SoCs and allows to add support for memory-to-memory operations on the RZ/V2H(P) SoC which integrates a Mali-C55 ISP and a specialized DMA engine named IVC that feeds images from memory, where the CRU has saved them. Signed-off-by: Daniel Scally Signed-off-by: Jacopo Mondi --- src/libcamera/pipeline/mali-c55/meson.build | 3 +- src/libcamera/pipeline/mali-c55/rzg2l-cru.cpp | 259 ++++++++++++++++++++++++++ src/libcamera/pipeline/mali-c55/rzg2l-cru.h | 75 ++++++++ 3 files changed, 336 insertions(+), 1 deletion(-) diff --git a/src/libcamera/pipeline/mali-c55/meson.build b/src/libcamera/pipeline/mali-c55/meson.build index eba8e5a3905469297ade6ed5bf523473448cb9de..4e768242602e563612dca7ccbd0d11f5e5ab92a5 100644 --- a/src/libcamera/pipeline/mali-c55/meson.build +++ b/src/libcamera/pipeline/mali-c55/meson.build @@ -1,5 +1,6 @@ # SPDX-License-Identifier: CC0-1.0 libcamera_internal_sources += files([ - 'mali-c55.cpp' + 'mali-c55.cpp', + 'rzg2l-cru.cpp', ]) diff --git a/src/libcamera/pipeline/mali-c55/rzg2l-cru.cpp b/src/libcamera/pipeline/mali-c55/rzg2l-cru.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44bbf102cd25df301572393fe344e8074fa284b8 --- /dev/null +++ b/src/libcamera/pipeline/mali-c55/rzg2l-cru.cpp @@ -0,0 +1,259 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas on Board Oy + * + * Pipine handler element for the Renesas RZ/G2L Camera Receiver Unit + */ + +#include "rzg2l-cru.h" + +#include +#include + +#include + +#include +#include + +#include +#include + +#include "libcamera/internal/bayer_format.h" +#include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/framebuffer.h" +#include "libcamera/internal/media_device.h" +#include "libcamera/internal/request.h" + +namespace libcamera { + +static const std::map bitDepthToFmt{ + { 10, V4L2PixelFormat(V4L2_PIX_FMT_RAW_CRU10) }, + { 12, V4L2PixelFormat(V4L2_PIX_FMT_RAW_CRU12) }, + { 14, V4L2PixelFormat(V4L2_PIX_FMT_RAW_CRU14) }, +}; + +LOG_DEFINE_CATEGORY(RZG2LCRU) + +FrameBuffer *RZG2LCRU::queueBuffer(Request *request) +{ + FrameBuffer *buffer; + + if (availableBuffers_.empty()) { + LOG(RZG2LCRU, Debug) << "CRU buffer underrun"; + return nullptr; + } + + buffer = availableBuffers_.front(); + + int ret = output_->queueBuffer(buffer); + if (ret) { + LOG(RZG2LCRU, Error) << "Failed to queue buffer to CRU"; + return nullptr; + } + + availableBuffers_.pop(); + buffer->_d()->setRequest(request); + + return buffer; +} + +void RZG2LCRU::cruReturnBuffer(FrameBuffer *buffer) +{ + auto it = std::find_if(buffers_.begin(), buffers_.end(), + [&](const auto& b) { return b.get() == buffer; }); + ASSERT(it != buffers_.end()); + + availableBuffers_.push(buffer); +} + +int RZG2LCRU::start() +{ + int ret = output_->exportBuffers(kBufferCount, &buffers_); + if (ret < 0) + return ret; + + ret = output_->importBuffers(kBufferCount); + if (ret) + return ret; + + for (std::unique_ptr &buffer : buffers_) + availableBuffers_.push(buffer.get()); + + ret = output_->streamOn(); + if (ret) { + freeBuffers(); + return ret; + } + + return 0; +} + +int RZG2LCRU::stop() +{ + int ret; + + csi2_->setFrameStartEnabled(false); + + ret = output_->streamOff(); + + freeBuffers(); + + return ret; +} + +void RZG2LCRU::freeBuffers() +{ + availableBuffers_ = {}; + buffers_.clear(); + + if (output_->releaseBuffers()) + LOG(RZG2LCRU, Error) << "Failed to release CRU buffers"; +} + +int RZG2LCRU::configure(V4L2SubdeviceFormat *subdevFormat, V4L2DeviceFormat *inputFormat) +{ + /* + * Set format on the sensor and propagate it up to the CRU video + * device. + */ + + int ret = sensor_->setFormat(subdevFormat); + if (ret) + return ret; + + ret = csi2_->setFormat(0, subdevFormat); + if (ret) + return ret; + + ret = csi2_->getFormat(1, subdevFormat); + if (ret) + return ret; + + ret = cru_->setFormat(0, subdevFormat); + if (ret) + return ret; + + ret = cru_->getFormat(1, subdevFormat); + if (ret) + return ret; + + /* + * The capture device needs to be set with a format that can be produced + * from the mbus code of the subdevFormat. The CRU and IVC use bayer + * order agnostic pixel formats, so all we need to do is find the right + * bitdepth and select the appropriate format. + */ + BayerFormat bayerFormat = BayerFormat::fromMbusCode(subdevFormat->code); + if (!bayerFormat.isValid()) + return -EINVAL; + + V4L2DeviceFormat captureFormat; + captureFormat.fourcc = bitDepthToFmt.at(bayerFormat.bitDepth); + captureFormat.size = subdevFormat->size; + + ret = output_->setFormat(&captureFormat); + if (ret) + return ret; + + /* + * We return the format that we set against the output device, as the + * same format will also need to be set against the Input Video Control + * Block device. + */ + *inputFormat = captureFormat; + + return 0; +} + +void RZG2LCRU::initCRUSizes() +{ + Size maxCSI2Size; + + /* + * Get the maximum supported size on the CSI-2 receiver. We need to + * query the kernel interface as the size limits differ from RZ/G2L + * (2800x4095) and RZ/V2H (4096x4096). + */ + V4L2Subdevice::Formats csi2Formats = csi2_->formats(0); + if (csi2Formats.empty()) + return; + + for (const auto &format : csi2Formats) { + for (const auto &range : format.second) { + if (range.max > maxCSI2Size) + maxCSI2Size = range.max; + } + } + + /* + * Enumerate the sensor supported resolutions and filter out the ones + * larger than the maximum supported CSI-2 receiver input size. + */ + V4L2Subdevice::Formats formats = sensor_->device()->formats(0); + if (formats.empty()) + return; + + for (const auto &format : formats) { + for (const auto &range : format.second) { + const Size &max = range.max; + + if (max.width > maxCSI2Size.width || + max.height > maxCSI2Size.height) + continue; + + csi2Sizes_.push_back(max); + } + } + + /* Sort in increasing order and remove duplicates. */ + std::sort(csi2Sizes_.begin(), csi2Sizes_.end()); + auto last = std::unique(csi2Sizes_.begin(), csi2Sizes_.end()); + csi2Sizes_.erase(last, csi2Sizes_.end()); + + csi2Resolution_ = csi2Sizes_.back(); +} + +int RZG2LCRU::init(const MediaDevice *media) +{ + static const std::regex cruRegex("cru-ip-[0-9a-f]{8}.cru[0-9]"); + static const std::regex csi2Regex("csi-[0-9a-f]{8}.csi2"); + + csi2_ = V4L2Subdevice::fromEntityName(media, csi2Regex); + if (!csi2_) + return -ENODEV; + + int ret = csi2_->open(); + if (ret) + return ret; + + const std::vector &pads = csi2_->entity()->pads(); + if (pads.empty()) + return -ENODEV; + + /* The receiver has a single sink pad at index 0 */ + MediaPad *sink = pads[0]; + const std::vector &links = sink->links(); + if (links.empty()) + return -ENODEV; + + MediaLink *link = links[0]; + sensor_ = CameraSensorFactoryBase::create(link->source()->entity()); + if (!sensor_) + return -ENODEV; + + cru_ = V4L2Subdevice::fromEntityName(media, cruRegex); + ret = cru_->open(); + if (ret) + return ret; + + output_ = V4L2VideoDevice::fromEntityName(media, "CRU output"); + ret = output_->open(); + if (ret) + return ret; + + initCRUSizes(); + + return 0; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/mali-c55/rzg2l-cru.h b/src/libcamera/pipeline/mali-c55/rzg2l-cru.h new file mode 100644 index 0000000000000000000000000000000000000000..be06e979ac22e03897fd0de0fbbdc08083323425 --- /dev/null +++ b/src/libcamera/pipeline/mali-c55/rzg2l-cru.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas on Board Oy + * + * Pipine handler element for the Renesas RZ/G2L Camera Receiver Unit + */ + +#pragma once + +#include +#include +#include + +#include + +#include "libcamera/internal/v4l2_subdevice.h" +#include "libcamera/internal/v4l2_videodevice.h" + +namespace libcamera { + +class CameraSensor; +class FrameBuffer; +class MediaDevice; +class Request; +class Size; + +class RZG2LCRU +{ +public: + static constexpr unsigned int kBufferCount = 4; + + RZG2LCRU() = default; + + const std::vector &sizes() const + { + return csi2Sizes_; + } + + int init(const MediaDevice *media); + const Size &resolution() const + { + return csi2Resolution_; + } + + CameraSensor *sensor() const { return sensor_.get(); } + V4L2Subdevice *csi2() const { return csi2_.get(); } + V4L2Subdevice *cru() const { return cru_.get(); } + + int configure(V4L2SubdeviceFormat *subdevFormat, V4L2DeviceFormat *inputFormat); + FrameBuffer *queueBuffer(Request *request); + void cruReturnBuffer(FrameBuffer *buffer); + V4L2VideoDevice *output() { return output_.get(); } + int start(); + int stop(); + +private: + void freeBuffers(); + + void cruBufferReady(FrameBuffer *buffer); + + void initCRUSizes(); + + std::unique_ptr sensor_; + std::unique_ptr csi2_; + std::unique_ptr cru_; + std::unique_ptr output_; + + std::vector> buffers_; + std::queue availableBuffers_; + + std::vector csi2Sizes_; + Size csi2Resolution_; +}; + +} /* namespace libcamera */ From patchwork Wed Dec 10 14:39:18 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25459 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 E075DBD1F1 for ; Wed, 10 Dec 2025 14:39:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 44ABE61499; Wed, 10 Dec 2025 15:39:36 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="CCF/5ziw"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B459D613CB for ; Wed, 10 Dec 2025 15:39:32 +0100 (CET) Received: from [192.168.1.106] (mob-5-90-55-146.net.vodafone.it [5.90.55.146]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 4BB3A6DC; Wed, 10 Dec 2025 15:39:31 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1765377571; bh=oENpKKBBloUwF+GHLDNipl1ATndf6nx/0uDbs5A8PEI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=CCF/5ziwnB6wyXXr66/67bunUss0wOPkz11sRTNYWGS3WlATtAY5AyqdCEmWGiwHc 2gRtCmxcYB9ksblg66hn4fvNiYUUTKlu4LWyJRtlYkPG9JD+tAPPuA3SMdbclOliho pGr0z1boY7P1O4AEosDE1BVreGp5Zyo1TNxygO6Q= From: Jacopo Mondi Date: Wed, 10 Dec 2025 15:39:18 +0100 Subject: [PATCH v2 2/7] libcamera: mali-c55: Split TPG and Inline camera handling MIME-Version: 1.0 Message-Id: <20251210-mali-cru-v2-2-e26421de202b@ideasonboard.com> References: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> In-Reply-To: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> To: Daniel Scally , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=16136; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=oENpKKBBloUwF+GHLDNipl1ATndf6nx/0uDbs5A8PEI=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpOYYi2XLm3gXdBdevdGRzIaG88oVIm3bHDKZqR ZqCNUO9F+WJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaTmGIgAKCRByNAaPFqFW PBlwEACs2DORDzMrXJIV+Yyn21+m/FGV6gexhjA3WkM95nwQcBimWEcCpWec6WGJqj3AgRynuTE tRgYqfV/PITnKy7QW/B4G7Uc4Y5/z/YQmFxJFp203ITOHJ/ta+kEPH9ruDNl5xlZSr5O1Juploh J6dxv0mXqcS2OXd3qmPuHMlm++XwEL1mUryln6VCfqk3Ujw/h9iUeSonVc3CbZOcS33ay+wnKeD ui5drq/V7hgqo4cC0o55GTj8w+Edt4dsnONB+9oSpoX3913AcWmchO4PcajyUPSuG867uwaPBwm AlK5VP+0GzWyAfoli2ZeLwJbMxaqkDNU2lE78B5iuaOQnraNpq+kjg/PD1LFxoH4w9ltbi41Xbe hHFko1jMsrgak+SKaQp28lH0WMEN46W3mdiHUH64TWCYBK81d0EEpzApagGjGrhmNuQ4iDfv658 mhwmpemCX1e9rJ7soE28zgAiidIRp2GpD1oxhxR2wK/vH91oxjjDugB8CLXUKMiKRbAM4xvAjMt XPKv1qEEygqMVgc/QLjU8qZwSBEsxZ0Zn+PGHbOW3cnmOTJKmSsoOERYD7x1d5zIwJgquomINGY bcLFOJrejZQHMHHerkHTdvrK1f5wggyzuaRjyKv4OF4Qz5tEROkhKIAnF3z3oIn1aNGaOTEKo4o Odx1u+9ipqaAj3g== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B 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" In order to prepare to support memory input cameras, split the handling of the TPG and Inline camera cases. The Mali C55 pipeline handler uses the entity and subdevice stored in the CameraData to support both the handling of the TPG and of the [CSI-2 + sensor] use cases. Adding support for memory cameras by using the CRU unit would add yet-another special case making the code harder to follow and more prone to errors. Split the handling of the TPG and inline cameras by introducing the MaliC55CameraData class hierarchy, to deflect functions called on the camera data to the correct entities. Signed-off-by: Jacopo Mondi Reviewed-by: Daniel Scally --- src/libcamera/pipeline/mali-c55/mali-c55.cpp | 287 +++++++++++++++++---------- 1 file changed, 185 insertions(+), 102 deletions(-) diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp index cf0cb15f8bb39143eea38aa8acb8d2b1268f5530..552a258a6b849a2518fa6c83226cf9ab4e657717 100644 --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp @@ -92,17 +92,28 @@ struct MaliC55FrameInfo { class MaliC55CameraData : public Camera::Private { public: - MaliC55CameraData(PipelineHandler *pipe, MediaEntity *entity) - : Camera::Private(pipe), entity_(entity) + enum CameraType { + Tpg, + Inline, + }; + + MaliC55CameraData(PipelineHandler *pipe) + : Camera::Private(pipe) { } - int init(); int loadIPA(); + CameraType type() const { return type_; } + /* Deflect these functionalities to either TPG or CameraSensor. */ - std::vector sizes(unsigned int mbusCode) const; - Size resolution() const; + virtual int init(MediaEntity *entity) = 0; + + virtual std::vector sizes(unsigned int mbusCode) const = 0; + virtual V4L2Subdevice *subdev() const = 0; + virtual CameraSensor *sensor() const = 0; + virtual V4L2Subdevice *csi2() const = 0; + virtual Size resolution() const = 0; int pixfmtToMbusCode(const PixelFormat &pixFmt) const; const PixelFormat &bestRawFormat() const; @@ -112,11 +123,6 @@ public: PixelFormat adjustRawFormat(const PixelFormat &pixFmt) const; Size adjustRawSizes(const PixelFormat &pixFmt, const Size &rawSize) const; - std::unique_ptr sensor_; - - MediaEntity *entity_; - std::unique_ptr csi_; - std::unique_ptr sd_; Stream frStream_; Stream dsStream_; @@ -126,58 +132,106 @@ public: std::unique_ptr delayedCtrls_; +protected: + CameraType type_; + private: - void initTPGData(); void setSensorControls(const ControlList &sensorControls); - std::string id_; - Size tpgResolution_; }; -int MaliC55CameraData::init() +class MaliC55TpgCameraData : public MaliC55CameraData { - int ret; +public: + MaliC55TpgCameraData(PipelineHandler *pipe); - sd_ = std::make_unique(entity_); - ret = sd_->open(); - if (ret) { - LOG(MaliC55, Error) << "Failed to open sensor subdevice"; - return ret; + int init(MediaEntity *entity) override; + + std::vector sizes(unsigned int mbusCode) const override; + + Size resolution() const override + { + return resolution_; } - /* If this camera is created from TPG, we return here. */ - if (entity_->name() == "mali-c55 tpg") { - initTPGData(); - return 0; + V4L2Subdevice *subdev() const override + { + return sd_.get(); } - /* - * Register a CameraSensor if we connect to a sensor and create - * an entity for the connected CSI-2 receiver. - */ - sensor_ = CameraSensorFactoryBase::create(entity_); - if (!sensor_) - return -ENODEV; + CameraSensor *sensor() const override + { + ASSERT(false); + return nullptr; + } - const MediaPad *sourcePad = entity_->getPadByIndex(0); - MediaEntity *csiEntity = sourcePad->links()[0]->sink()->entity(); + V4L2Subdevice *csi2() const override + { + ASSERT(false); + return nullptr; + } - csi_ = std::make_unique(csiEntity); - ret = csi_->open(); - if (ret) { - LOG(MaliC55, Error) << "Failed to open CSI-2 subdevice"; - return ret; +private: + Size resolution_; + std::unique_ptr sd_; +}; + +class MaliC55InlineCameraData : public MaliC55CameraData +{ +public: + MaliC55InlineCameraData(PipelineHandler *pipe); + + int init(MediaEntity *entity) override; + + std::vector sizes(unsigned int mbusCode) const override + { + return sensor_->sizes(mbusCode); } - return 0; + Size resolution() const override + { + return sensor_->resolution(); + } + + V4L2Subdevice *subdev() const override + { + return sensor_->device(); + } + + CameraSensor *sensor() const override + { + return sensor_.get(); + } + + V4L2Subdevice *csi2() const override + { + return csi2_.get(); + } + +private: + std::unique_ptr csi2_; + std::unique_ptr sensor_; +}; + +MaliC55TpgCameraData::MaliC55TpgCameraData(PipelineHandler *pipe) + : MaliC55CameraData(pipe) +{ + type_ = CameraType::Tpg; } -void MaliC55CameraData::initTPGData() +int MaliC55TpgCameraData::init(MediaEntity *tpg) { + sd_ = std::make_unique(tpg); + int ret = sd_->open(); + if (ret) { + LOG(MaliC55, Error) << "Failed to open TPG subdevice"; + return ret; + } + /* Replicate the CameraSensor implementation for TPG. */ V4L2Subdevice::Formats formats = sd_->formats(0); if (formats.empty()) - return; + return -EINVAL; std::vector tpgSizes; @@ -187,19 +241,13 @@ void MaliC55CameraData::initTPGData() [](const SizeRange &range) { return range.max; }); } - tpgResolution_ = tpgSizes.back(); -} + resolution_ = tpgSizes.back(); -void MaliC55CameraData::setSensorControls(const ControlList &sensorControls) -{ - delayedCtrls_->push(sensorControls); + return 0; } -std::vector MaliC55CameraData::sizes(unsigned int mbusCode) const +std::vector MaliC55TpgCameraData::sizes(unsigned int mbusCode) const { - if (sensor_) - return sensor_->sizes(mbusCode); - V4L2Subdevice::Formats formats = sd_->formats(0); if (formats.empty()) return {}; @@ -218,12 +266,35 @@ std::vector MaliC55CameraData::sizes(unsigned int mbusCode) const return sizes; } -Size MaliC55CameraData::resolution() const +MaliC55InlineCameraData::MaliC55InlineCameraData(PipelineHandler *pipe) + : MaliC55CameraData(pipe) { - if (sensor_) - return sensor_->resolution(); + type_ = CameraType::Inline; +} - return tpgResolution_; +int MaliC55InlineCameraData::init(MediaEntity *sensor) +{ + /* Register a CameraSensor create an entity for the CSI-2 receiver. */ + sensor_ = CameraSensorFactoryBase::create(sensor); + if (!sensor_) + return -EINVAL; + + const MediaPad *sourcePad = sensor->getPadByIndex(0); + MediaEntity *csiEntity = sourcePad->links()[0]->sink()->entity(); + + csi2_ = std::make_unique(csiEntity); + int ret = csi2_->open(); + if (ret) { + LOG(MaliC55, Error) << "Failed to open CSI-2 subdevice"; + return ret; + } + + return ret; +} + +void MaliC55CameraData::setSensorControls(const ControlList &sensorControls) +{ + delayedCtrls_->push(sensorControls); } /* @@ -242,7 +313,7 @@ int MaliC55CameraData::pixfmtToMbusCode(const PixelFormat &pixFmt) const if (!bayerFormat.isValid()) return -EINVAL; - V4L2Subdevice::Formats formats = sd_->formats(0); + V4L2Subdevice::Formats formats = subdev()->formats(0); unsigned int sensorMbusCode = 0; unsigned int bitDepth = 0; @@ -280,7 +351,7 @@ const PixelFormat &MaliC55CameraData::bestRawFormat() const { static const PixelFormat invalidPixFmt = {}; - for (const auto &fmt : sd_->formats(0)) { + for (const auto &fmt : subdev()->formats(0)) { BayerFormat sensorBayer = BayerFormat::fromMbusCode(fmt.first); if (!sensorBayer.isValid()) @@ -302,11 +373,11 @@ const PixelFormat &MaliC55CameraData::bestRawFormat() const void MaliC55CameraData::updateControls(const ControlInfoMap &ipaControls) { - if (!sensor_) + if (type_ == CameraType::Tpg) return; IPACameraSensorInfo sensorInfo; - int ret = sensor_->sensorInfo(&sensorInfo); + int ret = sensor()->sensorInfo(&sensorInfo); if (ret) { LOG(MaliC55, Error) << "Failed to retrieve sensor info"; return; @@ -379,7 +450,7 @@ int MaliC55CameraData::loadIPA() int ret; /* Do not initialize IPA for TPG. */ - if (!sensor_) + if (type_ == CameraType::Tpg) return 0; ipa_ = IPAManager::createIPA(pipe(), 1, 1); @@ -388,20 +459,20 @@ int MaliC55CameraData::loadIPA() ipa_->setSensorControls.connect(this, &MaliC55CameraData::setSensorControls); - std::string ipaTuningFile = ipa_->configurationFile(sensor_->model() + ".yaml", + std::string ipaTuningFile = ipa_->configurationFile(sensor()->model() + ".yaml", "uncalibrated.yaml"); /* We need to inform the IPA of the sensor configuration */ ipa::mali_c55::IPAConfigInfo ipaConfig{}; - ret = sensor_->sensorInfo(&ipaConfig.sensorInfo); + ret = sensor()->sensorInfo(&ipaConfig.sensorInfo); if (ret) return ret; - ipaConfig.sensorControls = sensor_->controls(); + ipaConfig.sensorControls = sensor()->controls(); ControlInfoMap ipaControls; - ret = ipa_->init({ ipaTuningFile, sensor_->model() }, ipaConfig, + ret = ipa_->init({ ipaTuningFile, sensor()->model() }, ipaConfig, &ipaControls); if (ret) { LOG(MaliC55, Error) << "Failed to initialise the Mali-C55 IPA"; @@ -444,13 +515,13 @@ CameraConfiguration::Status MaliC55CameraConfiguration::validate() * The TPG doesn't support flips, so we only need to calculate a * transform if we have a sensor. */ - if (data_->sensor_) { + if (data_->type() == MaliC55CameraData::CameraType::Tpg) { + combinedTransform_ = Transform::Rot0; + } else { Orientation requestedOrientation = orientation; - combinedTransform_ = data_->sensor_->computeTransform(&orientation); + combinedTransform_ = data_->sensor()->computeTransform(&orientation); if (orientation != requestedOrientation) status = Adjusted; - } else { - combinedTransform_ = Transform::Rot0; } /* Only 2 streams available. */ @@ -927,11 +998,17 @@ int PipelineHandlerMaliC55::configure(Camera *camera, /* Link the graph depending if we are operating the TPG or a sensor. */ MaliC55CameraData *data = cameraData(camera); - if (data->csi_) { - const MediaEntity *csiEntity = data->csi_->entity(); - ret = csiEntity->getPadByIndex(1)->links()[0]->setEnabled(true); - } else { - ret = data->entity_->getPadByIndex(0)->links()[0]->setEnabled(true); + switch (data->type()) { + case MaliC55CameraData::CameraType::Tpg: { + const MediaEntity *tpgEntity = data->subdev()->entity(); + ret = tpgEntity->getPadByIndex(0)->links()[0]->setEnabled(true); + break; + } + case MaliC55CameraData::CameraType::Inline: { + const MediaEntity *csi2Entity = data->csi2()->entity(); + ret = csi2Entity->getPadByIndex(1)->links()[0]->setEnabled(true); + break; + } } if (ret) return ret; @@ -939,26 +1016,30 @@ int PipelineHandlerMaliC55::configure(Camera *camera, MaliC55CameraConfiguration *maliConfig = static_cast(config); V4L2SubdeviceFormat subdevFormat = maliConfig->sensorFormat_; - ret = data->sd_->getFormat(0, &subdevFormat); - if (ret) - return ret; - if (data->sensor_) { - ret = data->sensor_->setFormat(&subdevFormat, - maliConfig->combinedTransform()); - if (ret) - return ret; + /* Apply format to the origin of the pipeline and propagate it. */ + switch (data->type()) { + case MaliC55CameraData::CameraType::Tpg: { + ret = data->subdev()->setFormat(0, &subdevFormat); + break; } - - if (data->csi_) { - ret = data->csi_->setFormat(0, &subdevFormat); + case MaliC55CameraData::CameraType::Inline: { + ret = data->sensor()->setFormat(&subdevFormat, + maliConfig->combinedTransform()); if (ret) return ret; - ret = data->csi_->getFormat(1, &subdevFormat); + ret = data->csi2()->setFormat(0, &subdevFormat); if (ret) return ret; + + ret = data->csi2()->getFormat(1, &subdevFormat); + + break; + } } + if (ret) + return ret; V4L2DeviceFormat statsFormat; ret = stats_->getFormat(&statsFormat); @@ -973,8 +1054,6 @@ int PipelineHandlerMaliC55::configure(Camera *camera, /* * Propagate the format to the ISP sink pad and configure the input * crop rectangle (no crop at the moment). - * - * \todo Configure the CSI-2 receiver. */ ret = isp_->setFormat(0, &subdevFormat); if (ret) @@ -1058,18 +1137,18 @@ int PipelineHandlerMaliC55::configure(Camera *camera, /* We need to inform the IPA of the sensor configuration */ ipa::mali_c55::IPAConfigInfo ipaConfig{}; - ret = data->sensor_->sensorInfo(&ipaConfig.sensorInfo); + ret = data->sensor()->sensorInfo(&ipaConfig.sensorInfo); if (ret) return ret; - ipaConfig.sensorControls = data->sensor_->controls(); + ipaConfig.sensorControls = data->sensor()->controls(); /* * And we also need to tell the IPA the bayerOrder of the data (as * affected by any flips that we've configured) */ const Transform &combinedTransform = maliConfig->combinedTransform(); - BayerFormat::Order bayerOrder = data->sensor_->bayerOrder(combinedTransform); + BayerFormat::Order bayerOrder = data->sensor()->bayerOrder(combinedTransform); ControlInfoMap ipaControls; ret = data->ipa_->configure(ipaConfig, utils::to_underlying(bayerOrder), @@ -1283,7 +1362,7 @@ void PipelineHandlerMaliC55::applyScalerCrop(Camera *camera, if (!scalerCrop) return; - if (!data->sensor_) { + if (data->type() == MaliC55CameraData::CameraType::Tpg) { LOG(MaliC55, Error) << "ScalerCrop not supported for TPG"; return; } @@ -1291,7 +1370,7 @@ void PipelineHandlerMaliC55::applyScalerCrop(Camera *camera, Rectangle nativeCrop = *scalerCrop; IPACameraSensorInfo sensorInfo; - int ret = data->sensor_->sensorInfo(&sensorInfo); + int ret = data->sensor()->sensorInfo(&sensorInfo); if (ret) { LOG(MaliC55, Error) << "Failed to retrieve sensor info"; return; @@ -1573,10 +1652,11 @@ bool PipelineHandlerMaliC55::registerTPGCamera(MediaLink *link) } std::unique_ptr data = - std::make_unique(this, link->source()->entity()); + std::make_unique(this); - if (data->init()) - return false; + int ret = data->init(link->source()->entity()); + if (ret) + return ret; return registerMaliCamera(std::move(data), name); } @@ -1600,21 +1680,24 @@ bool PipelineHandlerMaliC55::registerSensorCamera(MediaLink *ispLink) continue; std::unique_ptr data = - std::make_unique(this, sensor); - if (data->init()) - return false; + std::make_unique(this); + + int ret = data->init(sensor); + if (ret) + return ret; - data->properties_ = data->sensor_->properties(); + data->properties_ = data->sensor()->properties(); - const CameraSensorProperties::SensorDelays &delays = data->sensor_->sensorDelays(); + const CameraSensorProperties::SensorDelays &delays = + data->sensor()->sensorDelays(); std::unordered_map params = { { V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } }, { V4L2_CID_EXPOSURE, { delays.exposureDelay, false } }, }; - data->delayedCtrls_ = - std::make_unique(data->sensor_->device(), - params); + V4L2Subdevice *sensorSubdev = data->sensor()->device(); + data->delayedCtrls_ = std::make_unique(sensorSubdev, + params); isp_->frameStart.connect(data->delayedCtrls_.get(), &DelayedControls::applyControls); From patchwork Wed Dec 10 14:39:19 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25460 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 EB8DEC326B for ; Wed, 10 Dec 2025 14:39:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A78BC61491; Wed, 10 Dec 2025 15:39:37 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="dxFz8J9n"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1E2E361489 for ; Wed, 10 Dec 2025 15:39:33 +0100 (CET) Received: from [192.168.1.106] (mob-5-90-55-146.net.vodafone.it [5.90.55.146]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id ACE9310C4; Wed, 10 Dec 2025 15:39:31 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1765377571; bh=t25rGKH/UyqsD/vjAyQ7tCb32tcmFGJ1n8U13MRNpkE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=dxFz8J9ngkzaWHxa73Gyo5BhDaxBQwlNonkW35seVFo+5S1ehuUO0XXgg48QPerzr 5cvQm1u0cTLiTwBoWZw/q76VbahJqtb1zrKSf35YvRn0FPrcF9pdQINVSncWN1sdCa gvGQcHhXCE1QZedPcdcZMWBg26tbrCQmbG5Qxzzc= From: Jacopo Mondi Date: Wed, 10 Dec 2025 15:39:19 +0100 Subject: [PATCH v2 3/7] libcamera: mali-c55: Register memory input camera MIME-Version: 1.0 Message-Id: <20251210-mali-cru-v2-3-e26421de202b@ideasonboard.com> References: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> In-Reply-To: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> To: Daniel Scally , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=14098; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=I2zhK9XmPmyRoJdqHwcIVL1RAhvX2ns7rhneW2H7+3I=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpOYYixUPnbOrBTKZzBowmNMYhPP05PnqkSd9gt BScniYwQcyJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaTmGIgAKCRByNAaPFqFW PHQxD/0bK5yanMhXc/1+25HbB8tuwdH9daC34V5x1C8FuKAEV8G0WTdAWy30l8xpr7lSjTlROjB rTG7oCqzw5WqUZc7SHQAV8UPgUgFn5uZ+8ZGvEBIlPq8xS/i1I+5HOUK8fwBhsKb0OYtF5dvF2F C5foS1yJyeAJ/jPoJqdw1Y9xvbaKbMchgE5FrMzA71S489ds0YW9NdaRqSxnsQCdJ1eSIG9+4Xc 5463xmr1dOppYI+cy1b2rO30DDcwSVCVIUiOeYrKHzqszVKy5mxv1HGBDOdhuUPiRgldiQwzCoE qxl/02Yeqm4EZrCzxOTKrN6EE7/OA4R7u4g8KvgGEaulHZc/+P855+sJ6sU5Z4B+gFGfuXseMuG fJXWcTzkYXPRmdWFWWXZkqPL1KXhH/RUSxmP6U+UU3JBWUHG9ia6D7mCD8aPzTsPD2DriPwZw48 RWnX6EYoBvxveyqaqyQK7d1mWHkjHZS46/NvvZPtw8KjBTt+LIAGYRyKMuVH/G6e0FB6rUJxblT vav6y2kCXYMpMdNBR0knehEGI91jc/5MPK7RjF59Ix0ZV7JhNViexy7ffvNCPd85MIyIi3W0C5i kef5pdQopfYvPRafQiF1lieJ2CP40ZytY6M6+eB26wdzqYQkY1RW0H/BhozEsrHkU7qjJZ+phNp M0PHbXByDjDvreA== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B 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" From: Daniel Scally Add support to Mali C55 pipeline handler for memory-to-memory camera mode. The Mali-C55, as integrated in the Renesas RZ/V2H(P) SoC, is fed with images saved to memory by the CRU unit and read on behalf of the ISP by the IVC. Update the pipeline handler's match() function to search for the CRU media entity necessary to register a memory input camera. Create and initialize a CameraData instance for the memory camera use case if a valid CRU is found and initialize the IVC components used to feed the ISP with images read from memory. The MaliC55CameraData class is now derived by a templated base class which allows, by using the CRTP pattern, to template the argument of the init() function which would have otherwise required to implement placeholders in all derived classes. Signed-off-by: Daniel Scally Signed-off-by: Jacopo Mondi --- src/libcamera/pipeline/mali-c55/mali-c55.cpp | 245 ++++++++++++++++++++++----- 1 file changed, 205 insertions(+), 40 deletions(-) diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp index 552a258a6b849a2518fa6c83226cf9ab4e657717..c0c14338ddb3f1d0075df3b3d3d154bc1b5d678a 100644 --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp @@ -42,6 +42,8 @@ #include "libcamera/internal/v4l2_subdevice.h" #include "libcamera/internal/v4l2_videodevice.h" +#include "rzg2l-cru.h" + namespace { bool isFormatRaw(const libcamera::PixelFormat &pixFmt) @@ -95,6 +97,7 @@ public: enum CameraType { Tpg, Inline, + Memory, }; MaliC55CameraData(PipelineHandler *pipe) @@ -107,13 +110,12 @@ public: CameraType type() const { return type_; } /* Deflect these functionalities to either TPG or CameraSensor. */ - virtual int init(MediaEntity *entity) = 0; - virtual std::vector sizes(unsigned int mbusCode) const = 0; virtual V4L2Subdevice *subdev() const = 0; virtual CameraSensor *sensor() const = 0; virtual V4L2Subdevice *csi2() const = 0; virtual Size resolution() const = 0; + virtual RZG2LCRU *cru() const = 0; int pixfmtToMbusCode(const PixelFormat &pixFmt) const; const PixelFormat &bestRawFormat() const; @@ -140,12 +142,34 @@ private: std::string id_; }; -class MaliC55TpgCameraData : public MaliC55CameraData +template +class MaliC55TemplatedCameraData : public MaliC55CameraData +{ +public: + MaliC55TemplatedCameraData(PipelineHandler *pipe) + : MaliC55CameraData(pipe) + { + } + + /* + * The signature of the init() functions is different between the + * Tpg, Inline and Memory camera use cases. Use CRTP to deflect to the + * right implementation the templated argument and avoid polluting the + * interface of the base class. + */ + template + int init(MediaType *media) + { + return static_cast(this)->init(media); + } +}; + +class MaliC55TpgCameraData : public MaliC55TemplatedCameraData { public: MaliC55TpgCameraData(PipelineHandler *pipe); - int init(MediaEntity *entity) override; + int init(MediaEntity *entity); std::vector sizes(unsigned int mbusCode) const override; @@ -171,17 +195,23 @@ public: return nullptr; } + RZG2LCRU *cru() const override + { + ASSERT(false); + return nullptr; + } + private: Size resolution_; std::unique_ptr sd_; }; -class MaliC55InlineCameraData : public MaliC55CameraData +class MaliC55InlineCameraData : public MaliC55TemplatedCameraData { public: MaliC55InlineCameraData(PipelineHandler *pipe); - int init(MediaEntity *entity) override; + int init(MediaEntity *entity); std::vector sizes(unsigned int mbusCode) const override { @@ -208,13 +238,62 @@ public: return csi2_.get(); } + RZG2LCRU *cru() const override + { + ASSERT(false); + return nullptr; + } + private: std::unique_ptr csi2_; std::unique_ptr sensor_; }; +class MaliC55MemoryCameraData + : public MaliC55TemplatedCameraData +{ +public: + MaliC55MemoryCameraData(PipelineHandler *pipe); + + int init(MediaDevice *cruMedia); + + std::vector + sizes([[maybe_unused]] unsigned int mbusCode) const override + { + return cru_->sizes(); + } + + Size resolution() const override + { + return cru_->resolution(); + } + + V4L2Subdevice *subdev() const override + { + return cru_->sensor()->device(); + } + + CameraSensor *sensor() const override + { + return cru_->sensor(); + } + + V4L2Subdevice *csi2() const override + { + return cru_->csi2(); + } + + RZG2LCRU *cru() const override + { + return cru_.get(); + } + +private: + std::unique_ptr cru_; +}; + MaliC55TpgCameraData::MaliC55TpgCameraData(PipelineHandler *pipe) - : MaliC55CameraData(pipe) + : MaliC55TemplatedCameraData(pipe) { type_ = CameraType::Tpg; } @@ -267,7 +346,7 @@ std::vector MaliC55TpgCameraData::sizes(unsigned int mbusCode) const } MaliC55InlineCameraData::MaliC55InlineCameraData(PipelineHandler *pipe) - : MaliC55CameraData(pipe) + : MaliC55TemplatedCameraData(pipe) { type_ = CameraType::Inline; } @@ -292,6 +371,18 @@ int MaliC55InlineCameraData::init(MediaEntity *sensor) return ret; } +MaliC55MemoryCameraData::MaliC55MemoryCameraData(PipelineHandler *pipe) + : MaliC55TemplatedCameraData(pipe) +{ + type_ = CameraType::Memory; +} + +int MaliC55MemoryCameraData::init(MediaDevice *cruMedia) +{ + cru_ = std::make_unique(); + return cru_->init(cruMedia); +} + void MaliC55CameraData::setSensorControls(const ControlList &sensorControls) { delayedCtrls_->push(sensorControls); @@ -752,11 +843,15 @@ private: const std::string &name); bool registerTPGCamera(MediaLink *link); bool registerSensorCamera(MediaLink *link); + bool registerMemoryInputCamera(); std::shared_ptr media_; + std::shared_ptr cruMedia_; std::unique_ptr isp_; std::unique_ptr stats_; std::unique_ptr params_; + std::unique_ptr ivc_; + std::unique_ptr input_; std::vector> statsBuffers_; std::queue availableStatsBuffers_; @@ -1009,6 +1104,8 @@ int PipelineHandlerMaliC55::configure(Camera *camera, ret = csi2Entity->getPadByIndex(1)->links()[0]->setEnabled(true); break; } + case MaliC55CameraData::Memory: + break; } if (ret) return ret; @@ -1035,6 +1132,8 @@ int PipelineHandlerMaliC55::configure(Camera *camera, ret = data->csi2()->getFormat(1, &subdevFormat); + break; + case MaliC55CameraData::Memory: break; } } @@ -1110,6 +1209,7 @@ int PipelineHandlerMaliC55::configure(Camera *camera, pipe->stream = stream; } + /* TPG doesn't support the IPA, so stop here. */ if (!data->ipa_) return 0; @@ -1651,13 +1751,14 @@ bool PipelineHandlerMaliC55::registerTPGCamera(MediaLink *link) return true; } - std::unique_ptr data = - std::make_unique(this); - - int ret = data->init(link->source()->entity()); - if (ret) + MaliC55TpgCameraData *tpgData = new MaliC55TpgCameraData(this); + int ret = tpgData->init(link->source()->entity()); + if (ret) { + delete tpgData; return ret; + } + std::unique_ptr data(tpgData); return registerMaliCamera(std::move(data), name); } @@ -1679,30 +1780,33 @@ bool PipelineHandlerMaliC55::registerSensorCamera(MediaLink *ispLink) if (function != MEDIA_ENT_F_CAM_SENSOR) continue; - std::unique_ptr data = - std::make_unique(this); - - int ret = data->init(sensor); - if (ret) + MaliC55InlineCameraData *inlineData = + new MaliC55InlineCameraData(this); + int ret = inlineData->init(sensor); + if (ret) { + delete inlineData; return ret; + } - data->properties_ = data->sensor()->properties(); + inlineData->properties_ = inlineData->sensor()->properties(); const CameraSensorProperties::SensorDelays &delays = - data->sensor()->sensorDelays(); + inlineData->sensor()->sensorDelays(); std::unordered_map params = { { V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } }, { V4L2_CID_EXPOSURE, { delays.exposureDelay, false } }, }; - V4L2Subdevice *sensorSubdev = data->sensor()->device(); - data->delayedCtrls_ = std::make_unique(sensorSubdev, - params); - isp_->frameStart.connect(data->delayedCtrls_.get(), + V4L2Subdevice *sensorSubdev = inlineData->sensor()->device(); + inlineData->delayedCtrls_ = + std::make_unique(sensorSubdev, + params); + isp_->frameStart.connect(inlineData->delayedCtrls_.get(), &DelayedControls::applyControls); /* \todo Init properties. */ + std::unique_ptr data(inlineData); if (!registerMaliCamera(std::move(data), sensor->name())) return false; } @@ -1710,6 +1814,39 @@ bool PipelineHandlerMaliC55::registerSensorCamera(MediaLink *ispLink) return true; } +bool PipelineHandlerMaliC55::registerMemoryInputCamera() +{ + MaliC55MemoryCameraData *memoryData = new MaliC55MemoryCameraData(this); + + int ret = memoryData->init(cruMedia_.get()); + if (ret) { + delete memoryData; + return false; + } + + CameraSensor *sensor = memoryData->sensor(); + memoryData->properties_ = sensor->properties(); + + const CameraSensorProperties::SensorDelays &delays = sensor->sensorDelays(); + std::unordered_map params = { + { V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } }, + { V4L2_CID_EXPOSURE, { delays.exposureDelay, false } }, + }; + + memoryData->delayedCtrls_ = + std::make_unique(sensor->device(), params); + isp_->frameStart.connect(memoryData->delayedCtrls_.get(), + &DelayedControls::applyControls); + + input_->bufferReady.connect(memoryData->cru(), &RZG2LCRU::cruReturnBuffer); + + std::unique_ptr data(memoryData); + if (!registerMaliCamera(std::move(data), sensor->device()->entity()->name())) + return false; + + return true; +} + bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator) { const MediaPad *ispSink; @@ -1719,14 +1856,14 @@ bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator) * The TPG and the downscale pipe are both optional blocks and may not * be fitted. */ - DeviceMatch dm("mali-c55"); - dm.add("mali-c55 isp"); - dm.add("mali-c55 resizer fr"); - dm.add("mali-c55 fr"); - dm.add("mali-c55 3a stats"); - dm.add("mali-c55 3a params"); - - media_ = acquireMediaDevice(enumerator, dm); + DeviceMatch c55_dm("mali-c55"); + c55_dm.add("mali-c55 isp"); + c55_dm.add("mali-c55 resizer fr"); + c55_dm.add("mali-c55 fr"); + c55_dm.add("mali-c55 3a stats"); + c55_dm.add("mali-c55 3a params"); + + media_ = acquireMediaDevice(enumerator, c55_dm); if (!media_) return false; @@ -1786,6 +1923,25 @@ bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator) stats_->bufferReady.connect(this, &PipelineHandlerMaliC55::statsBufferReady); params_->bufferReady.connect(this, &PipelineHandlerMaliC55::paramsBufferReady); + /* + * We also need to search for the rzg2l-cru CSI-2 receiver. If we find + * that then we need to work in memory input mode instead of the inline + * mode. The absence of this match is not necessarily a failure at this + * point...it depends on the media links that we investigate momentarily. + * + * This is a bit hacky, because there could be multiple of these media + * devices and we're just taking the first. We need modular pipelines to + * properly solve the issue. + */ + static const std::regex cruCsi2Regex("csi-[0-9a-f]{8}.csi2"); + static const std::regex cruIpRegex("cru-ip-[0-9a-f]{8}.cru[0-9]"); + + DeviceMatch cruDm("rzg2l_cru"); + cruDm.add(cruCsi2Regex); + cruDm.add(cruIpRegex); + cruDm.add("CRU output"); + cruMedia_ = acquireMediaDevice(enumerator, cruDm); + ispSink = isp_->entity()->getPadByIndex(0); if (!ispSink || ispSink->links().empty()) { LOG(MaliC55, Error) << "ISP sink pad error"; @@ -1799,13 +1955,6 @@ bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator) * MEDIA_ENT_F_CAM_SENSOR - The test pattern generator * MEDIA_ENT_F_VID_IF_BRIDGE - A CSI-2 receiver * MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER - An input device - * - * The last one will be unsupported for now. The TPG is relatively easy, - * we just register a Camera for it. If we have a CSI-2 receiver we need - * to check its sink pad and register Cameras for anything connected to - * it (probably...there are some complex situations in which that might - * not be true but let's pretend they don't exist until we come across - * them) */ bool registered; for (MediaLink *link : ispSink->links()) { @@ -1825,7 +1974,23 @@ bool PipelineHandlerMaliC55::match(DeviceEnumerator *enumerator) break; case MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER: - LOG(MaliC55, Warning) << "Memory input not yet supported"; + if (!cruMedia_) + return false; + + ivc_ = V4L2Subdevice::fromEntityName(media_.get(), + "rzv2h ivc block"); + if (ivc_->open() < 0) + return false; + + input_ = V4L2VideoDevice::fromEntityName(media_.get(), + "rzv2h-ivc"); + if (input_->open() < 0) + return false; + + registered = registerMemoryInputCamera(); + if (!registered) + return registered; + break; default: LOG(MaliC55, Error) << "Unsupported entity function"; From patchwork Wed Dec 10 14:39:20 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25461 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 2CFEABD1F1 for ; Wed, 10 Dec 2025 14:39:40 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4B571614A6; Wed, 10 Dec 2025 15:39:38 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="KTbiaYwI"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 75E4E61489 for ; Wed, 10 Dec 2025 15:39:33 +0100 (CET) Received: from [192.168.1.106] (mob-5-90-55-146.net.vodafone.it [5.90.55.146]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 162656DC; Wed, 10 Dec 2025 15:39:32 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1765377572; bh=lbZcUA7WzPjLCGZZ9wA2Hk/FxcQMuQFHgMl10GVovrI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=KTbiaYwIIe07bxqR8d8rp6ORl912t6RLuJvs8qk/goliI0Mz4xStNRSuK6nqRcQSZ UPHM6CUKO8go/jH6PiAz9ZRmGVimghEqTp4RpNPCY2eE0E6J2Wuell903f5ux7nsb7 N6KhNsQZtKu9ek/3irM9o5lsue0DLMaVm/czcf34= From: Jacopo Mondi Date: Wed, 10 Dec 2025 15:39:20 +0100 Subject: [PATCH v2 4/7] libcamera: mali-c55: Configure camera in memory-to-memory MIME-Version: 1.0 Message-Id: <20251210-mali-cru-v2-4-e26421de202b@ideasonboard.com> References: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> In-Reply-To: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> To: Daniel Scally , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=1784; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=lbZcUA7WzPjLCGZZ9wA2Hk/FxcQMuQFHgMl10GVovrI=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpOYYiPRo69EWop+IY9nrHGSJcy9jOlGZUI9kfN c77b4bElfaJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaTmGIgAKCRByNAaPFqFW PIOjEADCikhHIVINMoubyAnpylo9zD2hWZ1hGIQ6riw4+tu52y+BSvRaqurmtbeAdwu3KNwHMBN Do2b9eggp5AZhYhUTr33OKftHzCc0ZGKoAfsuSttyV28cwNMRaV9jgHdpLg1475nSnWF9bBiq/v rPj2bRNYqo/PhP7+2iHRnT8zewhRIX2BRPzZ5OW9qYcG4GF1cu70oFqRHwE/tsRTC9v+VOPWFMe jVc5Ekvp5kuTNIIb0YAfVeROfnarTu5piPKJBdZdx29cNPnaUAn32ln08O6CpHr70WNYYWH+eYA ZlI2MzRBCZRi1TkwdXbty3sDDXYBSsB0vCkGbejerA6jH66EbB5YWU04jW6tQwhlmExxPdNyv7+ zNRvuqja/lOqS8BXVNZNsvHe+WwdnGvm6/8KbM7OzGal7wcDU7LUq463EmYhHQ/tdmzCrk4L9Dv LRAhQKyrORzKYf6ZQuyNN/2RQJ72w+im5wlaEI5yrM9Ylz51hhRlVqIZDDPoX8m6nmrcc+yMZJv nwd1pwR5RBGp0We72pZktynq6LfHJXb2vOI+v2vI545CsooKtUopOrjfrdDMUugcixduZ+Valqs y21D4tWUUV+y+XUFdJjX37gKUxRZEvfcVOcBOt6uq+dgVXV0PgPnxK7Etj9vmVak4lB36xFAGjD LdqpDIoi/5vyt+A== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B 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" Add support for memory-to-memory Camera to the PipelineHandlerMaliC55::configure() function. Start by enabling the IVC links, then configure the CRU and propagate the format to the IVC. Signed-off-by: Jacopo Mondi Reviewed-by: Daniel Scally --- src/libcamera/pipeline/mali-c55/mali-c55.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp index c0c14338ddb3f1d0075df3b3d3d154bc1b5d678a..8dcc51e27020c754004cd98cbdf73e771275b059 100644 --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp @@ -1104,9 +1104,12 @@ int PipelineHandlerMaliC55::configure(Camera *camera, ret = csi2Entity->getPadByIndex(1)->links()[0]->setEnabled(true); break; } - case MaliC55CameraData::Memory: + case MaliC55CameraData::Memory: { + const MediaEntity *ivcEntity = ivc_->entity(); + ret = ivcEntity->getPadByIndex(1)->links()[0]->setEnabled(true); break; } + } if (ret) return ret; @@ -1133,7 +1136,27 @@ int PipelineHandlerMaliC55::configure(Camera *camera, ret = data->csi2()->getFormat(1, &subdevFormat); break; - case MaliC55CameraData::Memory: + } + case MaliC55CameraData::Memory: { + V4L2DeviceFormat inputFormat; + + ret = data->cru()->configure(&subdevFormat, &inputFormat); + if (ret) + return ret; + + /* Propagate the CRU format to the IVC input. */ + ret = ivc_->setFormat(0, &subdevFormat); + if (ret) + return ret; + + ret = ivc_->getFormat(1, &subdevFormat); + if (ret) + return ret; + + ret = input_->setFormat(&inputFormat); + if (ret) + return ret; + break; } } From patchwork Wed Dec 10 14:39:21 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25462 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 EA2ABC326B for ; Wed, 10 Dec 2025 14:39:40 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 63CA661495; Wed, 10 Dec 2025 15:39:39 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="FUZFw9Pc"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E5E8961499 for ; Wed, 10 Dec 2025 15:39:33 +0100 (CET) Received: from [192.168.1.106] (mob-5-90-55-146.net.vodafone.it [5.90.55.146]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7F0D610C4; Wed, 10 Dec 2025 15:39:32 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1765377572; bh=iqmzamUpUgXPzSUkiK1h0Gp1XC6pXJetUbGunDuyNzU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=FUZFw9Pcw6oqeQ4FOIhGgSK5Fr/UMsrdty2OsRxVtrynx0rb1xzIPImg3XR7YNZK6 zx37tWiIfAKOQkvXZ9SlYMXqC1lX/FrDnnyp/pVIUIT81H92U5RQJLn95vGsGRwCI9 8BH3Nc6bF7TsVEbN8Sv5mOONZOhmWNkaNSbjSsrg= From: Jacopo Mondi Date: Wed, 10 Dec 2025 15:39:21 +0100 Subject: [PATCH v2 5/7] libcamera: mali-c55: Implement capture for memory-to-memory MIME-Version: 1.0 Message-Id: <20251210-mali-cru-v2-5-e26421de202b@ideasonboard.com> References: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> In-Reply-To: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> To: Daniel Scally , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=9438; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=IWn36FKOAlst4y5gWEgsituAx7g1CDa+O1cF2zmWej4=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpOYYjx+J/n2otU2sebkVQmtV0KIprL+8Lxa92I CmXDvQqSpSJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaTmGIwAKCRByNAaPFqFW PAjkD/0Q9cBCmBijnzRUqDNmvMoAADYBfdXEFzjRzP4iJb7tgwBbWOrY5CeNVgko9hHHf6Z7bkP C0lQcscVOzGmZqhqhvsXhT2xLCNT2i7TL5YizIDV+IyaMOen6G6AnIBsxFrRocyrcEO6U/PTkg6 n0f9JquU37vgqTBCLnavwm9HyGXIoZZJs3kdqsP5VOLgwe5GNiWhWWalJ/CN54LQRHq0nekQFyt lloBB8iro8RRPVIcMHuWIwIUDv85wZ9x1BZGTmILJ6t0ZIeIpYI0svzd2soYyHTkwysa2nXhmGQ 7mht6zlhpHN6wMqSIa2n0Yk46veKAsyAMBYYlfglGC4r1hdP2OF8aO+aZoEB8m2Jcq5oMtH2sbW 6rAE8AtoYFxBu7Dtafb63LnJ/hseREHaIOJvy6KOdvneVzR9OpSu9fA4a9/GhcsZSTa7ZH616xC YbWhMo7e0qBFIsZdKaeJ3M1HKJsQ/oKGEvyAk6KAFq7y4jmoxZClhywp5GZ1L36GlrszPCpO3MG SUB6qwXQ7Z70q4RVX1OMRxxSXrkg5M5UEONir+amfylEnFmIVxz+M4ArqelBvEd/74HTjnLfSsd 2AkXRyk5aLZ5Bl1H9LOeHxSTsiJhEWut2KQ+U/g9eRQ/N/U/MUZMiRu9uEBn5e4O4Wr31OqF5Lt wZZP5/MjPjaiBhQ== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B 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" From: Daniel Scally Plumb in the MaliC55 pipeline handler support for capturing frames from memory using the CRU. Introduce a data flow which uses the CRU to feed the ISP through the IVC. In detail: - push incoming request to a pending queue until a buffer from the CRU is available - delay the call to ipa_->fillParams() to the CRU buffer ready even - once the IPA has computed parameters feed the ISP through the IVC with buffers from the CRU, params and statistics. Signed-off-by: Daniel Scally Signed-off-by: Jacopo Mondi --- src/libcamera/pipeline/mali-c55/mali-c55.cpp | 166 ++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 5 deletions(-) diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp index 8dcc51e27020c754004cd98cbdf73e771275b059..83cb10af5d3c0588b25e4a3190f561f09627d239 100644 --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -86,6 +87,7 @@ struct MaliC55FrameInfo { FrameBuffer *paramBuffer; FrameBuffer *statBuffer; + FrameBuffer *rawBuffer; bool paramsDone; bool statsDone; @@ -775,11 +777,14 @@ public: int start(Camera *camera, const ControlList *controls) override; void stopDevice(Camera *camera) override; + int queuePendingRequests(MaliC55CameraData *data); + void cancelPendingRequests(); int queueRequestDevice(Camera *camera, Request *request) override; void imageBufferReady(FrameBuffer *buffer); void paramsBufferReady(FrameBuffer *buffer); void statsBufferReady(FrameBuffer *buffer); + void cruBufferReady(FrameBuffer *buffer); void paramsComputed(unsigned int requestId, uint32_t bytesused); void statsProcessed(unsigned int requestId, const ControlList &metadata); @@ -861,6 +866,11 @@ private: std::map frameInfoMap_; + /* Requests for which no buffer has been queued to the CRU device yet. */ + std::queue pendingRequests_; + /* Requests queued to the CRU device but not yet processed by the ISP. */ + std::queue processingRequests_; + std::array pipes_; bool dsFitted_; @@ -1320,6 +1330,11 @@ void PipelineHandlerMaliC55::freeBuffers(Camera *camera) if (params_->releaseBuffers()) LOG(MaliC55, Error) << "Failed to release params buffers"; + if (data->type() == MaliC55CameraData::CameraType::Memory) { + if (input_->releaseBuffers()) + LOG(MaliC55, Error) << "Failed to release input buffers"; + } + return; } @@ -1349,6 +1364,12 @@ int PipelineHandlerMaliC55::allocateBuffers(Camera *camera) } }; + if (input_) { + ret = input_->importBuffers(RZG2LCRU::kBufferCount); + if (ret < 0) + return ret; + } + ret = stats_->allocateBuffers(bufferCount, &statsBuffers_); if (ret < 0) return ret; @@ -1380,6 +1401,24 @@ int PipelineHandlerMaliC55::start(Camera *camera, [[maybe_unused]] const Control if (ret) return ret; + if (data->type() == MaliC55CameraData::CameraType::Memory) { + ret = data->cru()->start(); + if (ret) { + LOG(MaliC55, Error) + << "Failed to start CRU " << camera->id(); + freeBuffers(camera); + return ret; + } + + ret = input_->streamOn(); + if (ret) { + LOG(MaliC55, Error) + << "Failed to start IVC" << camera->id(); + freeBuffers(camera); + return ret; + } + } + if (data->ipa_) { ret = data->ipa_->start(); if (ret) { @@ -1469,6 +1508,12 @@ void PipelineHandlerMaliC55::stopDevice(Camera *camera) pipe.cap->releaseBuffers(); } + if (data->type() == MaliC55CameraData::CameraType::Memory) { + cancelPendingRequests(); + input_->streamOff(); + data->cru()->stop(); + } + stats_->streamOff(); params_->streamOff(); if (data->ipa_) @@ -1572,10 +1617,88 @@ void PipelineHandlerMaliC55::applyScalerCrop(Camera *camera, } } +void PipelineHandlerMaliC55::cancelPendingRequests() +{ + processingRequests_ = {}; + + while (!pendingRequests_.empty()) { + Request *request = pendingRequests_.front(); + + completeRequest(request); + pendingRequests_.pop(); + } +} + +int PipelineHandlerMaliC55::queuePendingRequests(MaliC55CameraData *data) +{ + ASSERT(data->type() == MaliC55CameraData::CameraType::Memory); + + while (!pendingRequests_.empty()) { + Request *request = pendingRequests_.front(); + + if (availableStatsBuffers_.empty()) { + LOG(MaliC55, Error) << "Stats buffer underrun"; + return -ENOENT; + } + + if (availableParamsBuffers_.empty()) { + LOG(MaliC55, Error) << "Params buffer underrun"; + return -ENOENT; + } + + MaliC55FrameInfo frameInfo; + frameInfo.request = request; + + frameInfo.rawBuffer = data->cru()->queueBuffer(request); + if (!frameInfo.rawBuffer) + return -ENOENT; + + frameInfo.statBuffer = availableStatsBuffers_.front(); + availableStatsBuffers_.pop(); + frameInfo.paramBuffer = availableParamsBuffers_.front(); + availableParamsBuffers_.pop(); + + frameInfo.paramsDone = false; + frameInfo.statsDone = false; + + frameInfoMap_[request->sequence()] = frameInfo; + + for (auto &[stream, buffer] : request->buffers()) { + MaliC55Pipe *pipe = pipeFromStream(data, stream); + + pipe->cap->queueBuffer(buffer); + } + + data->ipa_->queueRequest(request->sequence(), request->controls()); + + pendingRequests_.pop(); + processingRequests_.push(request); + } + + return 0; +} + int PipelineHandlerMaliC55::queueRequestDevice(Camera *camera, Request *request) { MaliC55CameraData *data = cameraData(camera); + /* + * If we're in memory input mode, we need to pop the requests onto the + * pending list until a CRU buffer is ready...otherwise we can just do + * everything immediately. + */ + if (data->type() == MaliC55CameraData::CameraType::Memory) { + pendingRequests_.push(request); + + int ret = queuePendingRequests(data); + if (ret) { + pendingRequests_.pop(); + return ret; + } + + return 0; + } + /* Do not run the IPA if the TPG is in use. */ if (!data->ipa_) { MaliC55FrameInfo frameInfo; @@ -1640,7 +1763,8 @@ MaliC55FrameInfo *PipelineHandlerMaliC55::findFrameInfo(FrameBuffer *buffer) { for (auto &[sequence, info] : frameInfoMap_) { if (info.paramBuffer == buffer || - info.statBuffer == buffer) + info.statBuffer == buffer || + info.rawBuffer == buffer) return &info; } @@ -1702,6 +1826,26 @@ void PipelineHandlerMaliC55::statsBufferReady(FrameBuffer *buffer) sensorControls); } +void PipelineHandlerMaliC55::cruBufferReady(FrameBuffer *buffer) +{ + MaliC55FrameInfo *info = findFrameInfo(buffer); + Request *request = info->request; + ASSERT(info); + + if (buffer->metadata().status == FrameMetadata::FrameCancelled) { + frameInfoMap_.erase(request->sequence()); + completeRequest(request); + return; + } + + request->metadata().set(controls::SensorTimestamp, + buffer->metadata().timestamp); + + /* Ought we do something with the sensor's controls here...? */ + MaliC55CameraData *data = cameraData(request->_d()->camera()); + data->ipa_->fillParams(request->sequence(), info->paramBuffer->cookie()); +} + void PipelineHandlerMaliC55::paramsComputed(unsigned int requestId, uint32_t bytesused) { MaliC55FrameInfo &frameInfo = frameInfoMap_[requestId]; @@ -1710,18 +1854,27 @@ void PipelineHandlerMaliC55::paramsComputed(unsigned int requestId, uint32_t byt /* * Queue buffers for stats and params, then queue buffers to the capture - * video devices. + * video devices if we're running in Inline mode or with the TPG. + * + * If we're running in M2M buffers have been queued to the capture + * devices at queuePendingRequests() time and here we only have to queue + * buffers to the IVC input to start a transfer. */ frameInfo.paramBuffer->_d()->metadata().planes()[0].bytesused = bytesused; params_->queueBuffer(frameInfo.paramBuffer); stats_->queueBuffer(frameInfo.statBuffer); - for (auto &[stream, buffer] : request->buffers()) { - MaliC55Pipe *pipe = pipeFromStream(data, stream); + if (data->type() != MaliC55CameraData::CameraType::Memory) { + for (auto &[stream, buffer] : request->buffers()) { + MaliC55Pipe *pipe = pipeFromStream(data, stream); - pipe->cap->queueBuffer(buffer); + pipe->cap->queueBuffer(buffer); + } } + + if (data->type() == MaliC55CameraData::CameraType::Memory) + input_->queueBuffer(frameInfo.rawBuffer); } void PipelineHandlerMaliC55::statsProcessed(unsigned int requestId, @@ -1863,6 +2016,9 @@ bool PipelineHandlerMaliC55::registerMemoryInputCamera() input_->bufferReady.connect(memoryData->cru(), &RZG2LCRU::cruReturnBuffer); + V4L2VideoDevice *cruOutput = memoryData->cru()->output(); + cruOutput->bufferReady.connect(this, &PipelineHandlerMaliC55::cruBufferReady); + std::unique_ptr data(memoryData); if (!registerMaliCamera(std::move(data), sensor->device()->entity()->name())) return false; From patchwork Wed Dec 10 14:39:22 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25463 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 C456FC326C for ; Wed, 10 Dec 2025 14:39:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 208BD61494; Wed, 10 Dec 2025 15:39:40 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="dyz1AhzO"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5F97D6149E for ; Wed, 10 Dec 2025 15:39:34 +0100 (CET) Received: from [192.168.1.106] (mob-5-90-55-146.net.vodafone.it [5.90.55.146]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E0B3A6DC; Wed, 10 Dec 2025 15:39:32 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1765377573; bh=WoGI79+nMuTORLewcim5V0njAoU+C2eBVttHuEH4EG0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=dyz1AhzOugqiHBeqbPh1HF04NzEGNZn8KZGD2K0qV0Y+945TerHrVpBcnuXeRbzKB xAp7bRCNyx1LoPZtKxubsOT83CvmEIdQa1N0hydLmjHOF3/QVHsmDwXlVsqjAH+CcQ +Z/8RkA1YqGkHjy4qp+kUgnlcPIO47uHJmO5hoNg= From: Jacopo Mondi Date: Wed, 10 Dec 2025 15:39:22 +0100 Subject: [PATCH v2 6/7] libcamera: mali-c55: Fix sensor size computation MIME-Version: 1.0 Message-Id: <20251210-mali-cru-v2-6-e26421de202b@ideasonboard.com> References: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> In-Reply-To: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> To: Daniel Scally , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=1118; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=WoGI79+nMuTORLewcim5V0njAoU+C2eBVttHuEH4EG0=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpOYYjhOg7MYF+hyMeDSFRST0bp3YOrVqlSB6+O gXZshVX+7+JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaTmGIwAKCRByNAaPFqFW PBqBD/95nAnDni1LmH8tfmGjLdGL3qb/phph27MLuZNazeC0BBvjW60hW0pyoEgE6tPDdLBzHNi P1jhqLOpQ9DGw9YIdrlnQi2Fxq8a0yhoErh/JKc4HX2IBwlWSgp/qF/ULPcGS43mlTFfJLtCy8f shBqMfIlV1YTPrQZ+8GQsyORoApBFvXnQH2Ji70YyhZskTb4k6Js0a5Cs7rER5jFafvY1373IMV NQg7mI8s1EleTeaxsT92EKhswtxPSNiQPu+ViV5uewb+vuyFjjLh+SdHy680SfUvHGfs3HyHlDY nfQda9P0i2rMqxGOdJS7P25NfUMY0ySbX0wmQaT+KeYsCDSt8mRv+degw3Ckb3YLWgE4+CUWbh6 53WnlSfj+cNDESr46jL/6tSHNKkQjqNL68f4oBdPKr0/Uu8ZkSIn4VKONoDuSs1n4xcpeoNGHUy ZstyQCuGKWIhHysS+/kCfUyUSeX1ncdsY108zd68acR362UxchHFCA5fdx8emg+6NhGQEv1B8lv iipDtc6anLZ8D0GrBVZ+WtgMmCW+6q63md0+C4/nON07oQD/73CJKmnRELx7bWPdgiOF8863WSd DW+wdKBFOePOqgPNgLCYNOgT2fKeZYjlGmY1utXeZJdfhFUdLB4hH4id+vWks0q4KkYToDCu/nM 2Hv/ZoLLrQ4FLoQ== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B 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 sensor size computation routine doesn't work well. Even if a more appriate size for a sensor is available, the largest one is always selected. Fix the size computation procedure which has an inverted assignment. Signed-off-by: Jacopo Mondi Reviewed-by: Daniel Scally Tested-by: Daniel Scally --- src/libcamera/pipeline/mali-c55/mali-c55.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp index 83cb10af5d3c0588b25e4a3190f561f09627d239..24369a341db567bc431a4bd2aecfd6865c676347 100644 --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp @@ -749,7 +749,7 @@ CameraConfiguration::Status MaliC55CameraConfiguration::validate() std::abs(static_cast(minSensorSize.height) - static_cast(size.height)); if (dist < distance) { - dist = distance; + distance = dist; bestSize = size; } } From patchwork Wed Dec 10 14:39:23 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25464 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 8D81EBD1F1 for ; Wed, 10 Dec 2025 14:39:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 94589614AD; Wed, 10 Dec 2025 15:39:41 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="bZHPTQs3"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A4807614A0 for ; Wed, 10 Dec 2025 15:39:34 +0100 (CET) Received: from [192.168.1.106] (mob-5-90-55-146.net.vodafone.it [5.90.55.146]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 43F2C10C4; Wed, 10 Dec 2025 15:39:33 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1765377573; bh=un+a0SQRfH4Bnhhwarp8oR/HjbKYWld2e4cEim/wHJQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=bZHPTQs3OZDX3SXzuLDdHVven6mHsDSmymhFnKvznpJcugKXlWjls9V8ohzksxRSi v8hCyb8/2TahdANCdp8MffNo/FxfP3hWbnKlqL+NJ1sQBL1+MCGnplxo8npy0lR6AC IAdlfBMNOgPYhHz8348xJoo9W1UsiaJkIN+d3vMI= From: Jacopo Mondi Date: Wed, 10 Dec 2025 15:39:23 +0100 Subject: [PATCH DNI v2 7/7] Please the CI loop MIME-Version: 1.0 Message-Id: <20251210-mali-cru-v2-7-e26421de202b@ideasonboard.com> References: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> In-Reply-To: <20251210-mali-cru-v2-0-e26421de202b@ideasonboard.com> To: Daniel Scally , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=1104; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=un+a0SQRfH4Bnhhwarp8oR/HjbKYWld2e4cEim/wHJQ=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpOYYjOoTX0mR+Nf+HVL7YmbWe4jlKBkIWe7nxi taEwPri4g2JAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaTmGIwAKCRByNAaPFqFW PMFYEACTYgi51PgOQflClcA/oQ/dKaWXNv4xzPKvQeIVwOmn8lCdlN520OXxg12fBoKRzrG4jo2 RopYqCKguFeXuzdWtZ0HLy1BgHT5/Qbut1cmwWSpt4fSiAyShBIaVpr+b/z/bhr3un6snZzezY+ vEjiMj4UITQU7D4PMV/ontoSqMGzXiHEPh9G4WKnh6jOqfO8bPbRA1bI3EXa4T5V2tdkmHdi79c SEQQdPuDAviEQ35Ean0OdcmwybDpeTEQMZNa9Y0DIVlDsgCz3UACGGm2n5GtL5JFUgoqE6yXClO amFRDbAYGZl8ZSAknQF9+z+071FLTiTSLi2kP1mpfU/3E6S2RU8q3EePWO5OXFYOTFVyYxORAJY rYK/3mGqimh5vzMOsne+o+oExQzcMKdUlGxdlwN+VE2qMv/Hxp+mCn+OXaCAxfSdzkn5e+rVZgh OX9GbmEpV4EKuCe6/Q5vii6ol8aLklvAzMbkJzJGYTAruFpbCNf9WgYfYXEAUwnjw6+9GYqHlse uvD2ZvFcG+ziubznErc6oPNVA7qp/hhFJFmYXI53fFGOL1qDS6YUpfW1MCmXoDyaB6zgQ7CAoYt UC84zkF50pK5PU2k9MM402G1tILygn63v16ZJIJCIa4lSoIMi3O9cLy0Ihm28dFW0lpDU5Xjh6a RLyvW8j4Xc/877A== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B 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" A non-sense commit just to make sure CI is run properly, as we can't specify dependencies on other series. Of course, not for inclusion. Signed-off-by: Jacopo Mondi --- src/libcamera/pipeline/mali-c55/rzg2l-cru.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcamera/pipeline/mali-c55/rzg2l-cru.cpp b/src/libcamera/pipeline/mali-c55/rzg2l-cru.cpp index 44bbf102cd25df301572393fe344e8074fa284b8..83d2c2cda2a180ba9b0962e669390ccde47679f3 100644 --- a/src/libcamera/pipeline/mali-c55/rzg2l-cru.cpp +++ b/src/libcamera/pipeline/mali-c55/rzg2l-cru.cpp @@ -27,9 +27,9 @@ namespace libcamera { static const std::map bitDepthToFmt{ - { 10, V4L2PixelFormat(V4L2_PIX_FMT_RAW_CRU10) }, - { 12, V4L2PixelFormat(V4L2_PIX_FMT_RAW_CRU12) }, - { 14, V4L2PixelFormat(V4L2_PIX_FMT_RAW_CRU14) }, + { 10, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10) }, + { 12, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12) }, + { 14, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR14) }, }; LOG_DEFINE_CATEGORY(RZG2LCRU)