From patchwork Fri Dec 5 14:52:08 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 25373 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 B20B8BD80A for ; Fri, 5 Dec 2025 14:52:39 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BD467613DA; Fri, 5 Dec 2025 15:52:34 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="k/m5f48R"; 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 3D9EB611BA for ; Fri, 5 Dec 2025 15:52:31 +0100 (CET) Received: from [192.168.1.4] (net-93-65-100-155.cust.vodafonedsl.it [93.65.100.155]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 9CE9AE7C; Fri, 5 Dec 2025 15:50:14 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1764946214; bh=/1F1iRZXbLowSjqdNb+I6FehS3zf86nbjM1hxdB7mRM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=k/m5f48RIAGaCFAihc3D4SbCRwcyCSXaKr9mJTwYzz0J+nv2tt7fgE5GAPVw/mLfD wxJwyxh80wPIOa0nmIn5GotygEeb2Rm1NOT2PpwmKG81NcK2DtcZ84VuJX9iRk7fr2 9yNOwjyuvPPBTrwrjmfRq0dv+VKGq6ANdPt8o50c= From: Jacopo Mondi Date: Fri, 05 Dec 2025 15:52:08 +0100 Subject: [PATCH 2/7] libcamera: mali-c55: Split TPG and Inline camera handling MIME-Version: 1.0 Message-Id: <20251205-mali-cru-v1-2-d81bb5ffe73a@ideasonboard.com> References: <20251205-mali-cru-v1-0-d81bb5ffe73a@ideasonboard.com> In-Reply-To: <20251205-mali-cru-v1-0-d81bb5ffe73a@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=15652; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=/1F1iRZXbLowSjqdNb+I6FehS3zf86nbjM1hxdB7mRM=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBpMvGsvazwL5+i78/vL5P+m8gDxyGW+XQcELwRQ Eu6yyIgDzuJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaTLxrAAKCRByNAaPFqFW POwtD/9wVRlRMrBS4vBukOWDvgRVVgB/kdL+b+7pmkYDWsYVsN4naSV7aXoXk8FUN/ML+Z3FDME 00iG7FEcPyGruH5Jc7CqiulVG3I7FIbTN99DAzDsJeM/KeI7YjzthH4QjgeEheImLn0XyC4IcYJ jlmdMfvfy/vmN/wYaD9+jJUK4iZWlj01CQnENyEB0YTflLEuQb47rVZye/WfAq96fPC2jq7APfq zysvm+qSdG+MpDynAX3YYAF/7TnJd5sD+mr7rxAliDzn6XPE9KmoUIsTlxmChPn6aJ3XRHKFJqj bnXdqcyUdCCPPBjvfCwe5gbEiuKvWEvSe/cKC4JYI+vDfWrQHOVTRUkLnCmXCQW+CeMTiLYFMiV hLEAKGrYFMeIB8dHvRrANBrBMEgRlBzzJhOrVVQnxgYVLOJKA8/jG4GZzkssThyW2mUYAa8DN+o 6bDfWGLdqn7YhdLiiPjd3l7nSyAXBo/w9weAmWWJgWl58tEIJhX8HG2TtKvMNT9T1F97seaO8rK JbbgMwBIyYxrcBDk8bhbcbsguZ+aAY7GPrvMdqBJDbgTN091v2c+f68Jwgb6SY6gNOn/C22LEyo lT0g+h2SRMKQPgGyxZHfhXYKZ63f7WJ4I6VKImctbetiL5DOZ80Gnj3XQTaaxGhAZM5UZYY0ROg iGGSjS4HhoQSXxw== 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 an enumeration for the camera operation modes, separating data into anonymous structures and handling the configuration and linking of the media graphs separately. Signed-off-by: Jacopo Mondi --- src/libcamera/pipeline/mali-c55/mali-c55.cpp | 236 +++++++++++++++------------ 1 file changed, 135 insertions(+), 101 deletions(-) diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp index cf0cb15f8bb39143eea38aa8acb8d2b1268f5530..801067ce00fe4e0fd6b81db699fcaed2ebb840b6 100644 --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp @@ -92,17 +92,22 @@ 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(); /* Deflect these functionalities to either TPG or CameraSensor. */ std::vector sizes(unsigned int mbusCode) const; Size resolution() const; + V4L2Subdevice *subdev() const; int pixfmtToMbusCode(const PixelFormat &pixFmt) const; const PixelFormat &bestRawFormat() const; @@ -112,11 +117,9 @@ public: PixelFormat adjustRawFormat(const PixelFormat &pixFmt) const; Size adjustRawSizes(const PixelFormat &pixFmt, const Size &rawSize) const; - std::unique_ptr sensor_; + int initTPG(MediaEntity *tpg); + int initInlineCamera(MediaEntity *sensor); - MediaEntity *entity_; - std::unique_ptr csi_; - std::unique_ptr sd_; Stream frStream_; Stream dsStream_; @@ -126,68 +129,71 @@ public: std::unique_ptr delayedCtrls_; + struct { + Size resolution_; + std::unique_ptr sd_; + } tpgInput; + + struct { + std::unique_ptr csi2_; + std::unique_ptr sensor_; + } inlineInput; + + CameraType input_; + private: - void initTPGData(); void setSensorControls(const ControlList &sensorControls); - std::string id_; - Size tpgResolution_; }; -int MaliC55CameraData::init() +int MaliC55CameraData::initTPG(MediaEntity *tpg) { - int ret; - - sd_ = std::make_unique(entity_); - ret = sd_->open(); + tpgInput.sd_ = std::make_unique(tpg); + int ret = tpgInput.sd_->open(); if (ret) { - LOG(MaliC55, Error) << "Failed to open sensor subdevice"; + LOG(MaliC55, Error) << "Failed to open TPG subdevice"; return ret; } - /* If this camera is created from TPG, we return here. */ - if (entity_->name() == "mali-c55 tpg") { - initTPGData(); - return 0; - } - - /* - * 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; + /* Replicate the CameraSensor implementation for TPG. */ + V4L2Subdevice::Formats formats = tpgInput.sd_->formats(0); + if (formats.empty()) + return -EINVAL; - const MediaPad *sourcePad = entity_->getPadByIndex(0); - MediaEntity *csiEntity = sourcePad->links()[0]->sink()->entity(); + std::vector tpgSizes; - csi_ = std::make_unique(csiEntity); - ret = csi_->open(); - if (ret) { - LOG(MaliC55, Error) << "Failed to open CSI-2 subdevice"; - return ret; + for (const auto &format : formats) { + const std::vector &ranges = format.second; + std::transform(ranges.begin(), ranges.end(), std::back_inserter(tpgSizes), + [](const SizeRange &range) { return range.max; }); } + tpgInput.resolution_ = tpgSizes.back(); + input_ = TPG; + return 0; } -void MaliC55CameraData::initTPGData() +int MaliC55CameraData::initInlineCamera(MediaEntity *sensor) { - /* Replicate the CameraSensor implementation for TPG. */ - V4L2Subdevice::Formats formats = sd_->formats(0); - if (formats.empty()) - return; + /* Register a CameraSensor create an entity for the CSI-2 receiver. */ + inlineInput.sensor_ = CameraSensorFactoryBase::create(sensor); + if (!inlineInput.sensor_) + return -ENODEV; - std::vector tpgSizes; + const MediaPad *sourcePad = sensor->getPadByIndex(0); + MediaEntity *csiEntity = sourcePad->links()[0]->sink()->entity(); - for (const auto &format : formats) { - const std::vector &ranges = format.second; - std::transform(ranges.begin(), ranges.end(), std::back_inserter(tpgSizes), - [](const SizeRange &range) { return range.max; }); + inlineInput.csi2_ = std::make_unique(csiEntity); + int ret = inlineInput.csi2_->open(); + if (ret) { + LOG(MaliC55, Error) << "Failed to open CSI-2 subdevice"; + return ret; } - tpgResolution_ = tpgSizes.back(); + input_ = Inline; + + return 0; } void MaliC55CameraData::setSensorControls(const ControlList &sensorControls) @@ -197,10 +203,10 @@ void MaliC55CameraData::setSensorControls(const ControlList &sensorControls) std::vector MaliC55CameraData::sizes(unsigned int mbusCode) const { - if (sensor_) - return sensor_->sizes(mbusCode); + if (input_ == Inline) + return inlineInput.sensor_->sizes(mbusCode); - V4L2Subdevice::Formats formats = sd_->formats(0); + V4L2Subdevice::Formats formats = tpgInput.sd_->formats(0); if (formats.empty()) return {}; @@ -220,10 +226,28 @@ std::vector MaliC55CameraData::sizes(unsigned int mbusCode) const Size MaliC55CameraData::resolution() const { - if (sensor_) - return sensor_->resolution(); + switch (input_) { + case TPG: + return tpgInput.resolution_; + case Inline: + return inlineInput.sensor_->resolution(); + } + + assert(false); + return {}; +} + +V4L2Subdevice *MaliC55CameraData::subdev() const +{ + switch (input_) { + case TPG: + return tpgInput.sd_.get(); + case Inline: + return inlineInput.sensor_->device(); + } - return tpgResolution_; + assert(false); + return nullptr; } /* @@ -242,7 +266,8 @@ int MaliC55CameraData::pixfmtToMbusCode(const PixelFormat &pixFmt) const if (!bayerFormat.isValid()) return -EINVAL; - V4L2Subdevice::Formats formats = sd_->formats(0); + V4L2Subdevice *sd = subdev(); + V4L2Subdevice::Formats formats = sd->formats(0); unsigned int sensorMbusCode = 0; unsigned int bitDepth = 0; @@ -280,7 +305,8 @@ const PixelFormat &MaliC55CameraData::bestRawFormat() const { static const PixelFormat invalidPixFmt = {}; - for (const auto &fmt : sd_->formats(0)) { + V4L2Subdevice *sd = subdev(); + for (const auto &fmt : sd->formats(0)) { BayerFormat sensorBayer = BayerFormat::fromMbusCode(fmt.first); if (!sensorBayer.isValid()) @@ -302,11 +328,11 @@ const PixelFormat &MaliC55CameraData::bestRawFormat() const void MaliC55CameraData::updateControls(const ControlInfoMap &ipaControls) { - if (!sensor_) + if (input_ == TPG) return; IPACameraSensorInfo sensorInfo; - int ret = sensor_->sensorInfo(&sensorInfo); + int ret = inlineInput.sensor_->sensorInfo(&sensorInfo); if (ret) { LOG(MaliC55, Error) << "Failed to retrieve sensor info"; return; @@ -379,7 +405,7 @@ int MaliC55CameraData::loadIPA() int ret; /* Do not initialize IPA for TPG. */ - if (!sensor_) + if (input_ == TPG) return 0; ipa_ = IPAManager::createIPA(pipe(), 1, 1); @@ -388,20 +414,21 @@ int MaliC55CameraData::loadIPA() ipa_->setSensorControls.connect(this, &MaliC55CameraData::setSensorControls); - std::string ipaTuningFile = ipa_->configurationFile(sensor_->model() + ".yaml", + CameraSensor *sensor = inlineInput.sensor_.get(); + 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 +471,14 @@ 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_->input_ == MaliC55CameraData::TPG) { + combinedTransform_ = Transform::Rot0; + } else { Orientation requestedOrientation = orientation; - combinedTransform_ = data_->sensor_->computeTransform(&orientation); + combinedTransform_ = + data_->inlineInput.sensor_->computeTransform(&orientation); if (orientation != requestedOrientation) status = Adjusted; - } else { - combinedTransform_ = Transform::Rot0; } /* Only 2 streams available. */ @@ -927,11 +955,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->input_) { + case MaliC55CameraData::TPG: { + const MediaEntity *tpgEntity = data->tpgInput.sd_->entity(); + ret = tpgEntity->getPadByIndex(0)->links()[0]->setEnabled(true); + break; + } + case MaliC55CameraData::Inline: { + const MediaEntity *csi2Entity = data->inlineInput.csi2_->entity(); + ret = csi2Entity->getPadByIndex(1)->links()[0]->setEnabled(true); + break; + } } if (ret) return ret; @@ -939,26 +973,28 @@ 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()); + /* Apply format to the origin of the pipeline and propagate it. */ + switch (data->input_) { + case MaliC55CameraData::TPG: + ret = data->tpgInput.sd_->setFormat(0, &subdevFormat); + break; + case MaliC55CameraData::Inline: + ret = data->inlineInput.sensor_->setFormat(&subdevFormat, + maliConfig->combinedTransform()); if (ret) return ret; - } - if (data->csi_) { - ret = data->csi_->setFormat(0, &subdevFormat); + ret = data->inlineInput.csi2_->setFormat(0, &subdevFormat); if (ret) return ret; - ret = data->csi_->getFormat(1, &subdevFormat); - if (ret) - return ret; + ret = data->inlineInput.csi2_->getFormat(1, &subdevFormat); + + break; } + if (ret) + return ret; V4L2DeviceFormat statsFormat; ret = stats_->getFormat(&statsFormat); @@ -973,8 +1009,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 +1092,19 @@ 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->inlineInput.sensor_->sensorInfo(&ipaConfig.sensorInfo); if (ret) return ret; - ipaConfig.sensorControls = data->sensor_->controls(); + ipaConfig.sensorControls = data->inlineInput.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->inlineInput.sensor_->bayerOrder(combinedTransform); ControlInfoMap ipaControls; ret = data->ipa_->configure(ipaConfig, utils::to_underlying(bayerOrder), @@ -1283,7 +1318,7 @@ void PipelineHandlerMaliC55::applyScalerCrop(Camera *camera, if (!scalerCrop) return; - if (!data->sensor_) { + if (data->input_ == MaliC55CameraData::TPG) { LOG(MaliC55, Error) << "ScalerCrop not supported for TPG"; return; } @@ -1291,7 +1326,7 @@ void PipelineHandlerMaliC55::applyScalerCrop(Camera *camera, Rectangle nativeCrop = *scalerCrop; IPACameraSensorInfo sensorInfo; - int ret = data->sensor_->sensorInfo(&sensorInfo); + int ret = data->inlineInput.sensor_->sensorInfo(&sensorInfo); if (ret) { LOG(MaliC55, Error) << "Failed to retrieve sensor info"; return; @@ -1572,10 +1607,8 @@ bool PipelineHandlerMaliC55::registerTPGCamera(MediaLink *link) return true; } - std::unique_ptr data = - std::make_unique(this, link->source()->entity()); - - if (data->init()) + std::unique_ptr data = std::make_unique(this); + if (data->initTPG(link->source()->entity())) return false; return registerMaliCamera(std::move(data), name); @@ -1600,21 +1633,22 @@ bool PipelineHandlerMaliC55::registerSensorCamera(MediaLink *ispLink) continue; std::unique_ptr data = - std::make_unique(this, sensor); - if (data->init()) + std::make_unique(this); + if (data->initInlineCamera(sensor)) return false; - data->properties_ = data->sensor_->properties(); + data->properties_ = data->inlineInput.sensor_->properties(); - const CameraSensorProperties::SensorDelays &delays = data->sensor_->sensorDelays(); + const CameraSensorProperties::SensorDelays &delays = + data->inlineInput.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->inlineInput.sensor_->device(); + data->delayedCtrls_ = std::make_unique(sensorSubdev, + params); isp_->frameStart.connect(data->delayedCtrls_.get(), &DelayedControls::applyControls);