From patchwork Fri Oct 23 10:58:33 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jai Luthra X-Patchwork-Id: 28595 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 D389FC324C for ; Fri, 23 Oct 2026 11:00:57 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8A06968FED; Fri, 23 Oct 2026 13:00:57 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="P2u47+y7"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3FBEC68FD3 for ; Fri, 23 Oct 2026 13:00:56 +0200 (CEST) Received: from mail.ideasonboard.com (unknown [IPv6:2401:4900:1c30:59f6:be2b:2877:6bc4:392]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1CD8A4F03; Fri, 23 Oct 2026 12:58:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1792753127; bh=BEO1XETINLB0lHCqGXF56NmXiRrc1/OzDmHvBhf3d+o=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=P2u47+y7S7tGsQyb2KsaWDSXPNWX5sC2Rbch5ECClazHaP/4nP7YvDoIIct3Cp+aP PX7gjLbqzCUh2Uq9LJENB5PFkMUYExu3Yd3pe5ATXvxiInPZMxMlLer6LQNEEN2kGL ZXrERIy9rhuoLhgsJeX6+BcVuHrU7bIJ/lxwvZj4= From: Jai Luthra Date: Fri, 23 Oct 2026 16:28:33 +0530 Subject: [PATCH WIP RFC 25/26] pipeline: mali-c55: Use generic RAW formats MIME-Version: 1.0 Message-Id: <20261023-b4-raw-model-v1-25-eadb9948901b@ideasonboard.com> References: <20261023-b4-raw-model-v1-0-eadb9948901b@ideasonboard.com> In-Reply-To: <20261023-b4-raw-model-v1-0-eadb9948901b@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Laurent Pinchart , Kieran Bingham , Sakari Ailus , Naushir Patuck , Jai Luthra X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=11619; i=jai.luthra@ideasonboard.com; h=from:subject:message-id; bh=BEO1XETINLB0lHCqGXF56NmXiRrc1/OzDmHvBhf3d+o=; b=owEBbQKS/ZANAwAKAUPekfkkmnFFAcsmYgBq2z3amrBoM54D30WaT7aRQ1V5tHOH9wdRiWpaC Y1C24QyFmmJAjMEAAEKAB0WIQRN4NgY5dV16NRar8VD3pH5JJpxRQUCats92gAKCRBD3pH5JJpx RaFlEADDyT6cQKe0zBbG64BE2k1obgl7XPBaDUk8IzPsaxFGsRjSkG1PE8wkj4hH6Saemiq+0Jl 782Tsi751l8skP4JVnTKzSTMZeo82qd6+wu7TP6Ts727BaMkksMaQ9SGsIdjd8ZsLkVcgGE8n6s CHoLo1gxKB85IiTWxpHeNSuD2xH1PI8b1DgLJWny3CMe7dSk92bayJQtMoPfbSpjHOpfPJ3nRH5 dsroDVoKvmG8n1u4g4TFgypD2NeFTO4TZwKi3o+sEoyKIArJOxImf7vaXwHvvXJEhLNqiXMWh4f JqbuF7kmvf06mBlmcsjbOgEk278qE519YG9pOfrbwE8VpzKFXLEwaikQp15ljFqiuGkYkeqAsn1 NpT+sam8jEJuGrIYld8Va0BbWecZAgko3KaZQcVO15NtAx1o7Ae4GX7Pdcd3kAdKCE/tlv/oFnS QwTXiHnmWk9LYgvc8D1K9E7dH5XlA669fZkEsD5iNyupd1LrRkIvMN1DLqg9aXROb/YKaKXinxl skoiHcR+Vr+k8U9wLwaTCkojkEP8sOJtG2yft38XGU4wnyeZFjfYvnTEK0YY/Q2Gm9wJDwqvnwt pgQLhqRhFjQuJArouH06RC9F8Zu9wsbort2rDMRNvfwwkXqJuXrywe22jwiEkFfoiAJUsQAkmkU OpGfQtLTLrAgp+w== X-Developer-Key: i=jai.luthra@ideasonboard.com; a=openpgp; fpr=4DE0D818E5D575E8D45AAFC543DE91F9249A7145 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" Split the CFA from the pixel format. Ensure we populate the CFA order for the source (sensor, memory, TPG) in generateConfiguration() and update it during validate() depending upon the transformations. With generic formats the logic for selecting output pixel formats for raw streams is quite simplified, as we only work with 16-bit, which is formats::RAW16. So we no longer need adjustRawFormat() helper, and can replace it with a simple check to ensure the sensor advertises raw formats. We still need to figure out the source mbus code that has the largest possible bit-depth, so we repurpose pixfmtToMbusCode() to look for one, given a CFA order. Note that the Mali-C55 kernel drivers are not yet updated to work with the new generic media bus codes, so we skip those. Signed-off-by: Jai Luthra --- src/libcamera/pipeline/mali-c55/mali-c55.cpp | 162 ++++++++++++--------------- 1 file changed, 70 insertions(+), 92 deletions(-) diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp index af518d0b2c28..421f3d925a3f 100644 --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp @@ -76,10 +76,7 @@ const std::map maliC55FmtToCode = { { formats::NV21, MEDIA_BUS_FMT_YUV10_1X30 }, /* RAW formats, FR pipe only. */ - { formats::SGBRG16, MEDIA_BUS_FMT_SGBRG16_1X16 }, - { formats::SRGGB16, MEDIA_BUS_FMT_SRGGB16_1X16 }, - { formats::SBGGR16, MEDIA_BUS_FMT_SBGGR16_1X16 }, - { formats::SGRBG16, MEDIA_BUS_FMT_SGRBG16_1X16 }, + { formats::RAW16, MEDIA_BUS_FMT_RAW_16 }, }; constexpr Size kMaliC55MinInputSize = { 640, 480 }; @@ -191,13 +188,33 @@ public: }, input_); } - int pixfmtToMbusCode(const PixelFormat &pixFmt) const; - const PixelFormat &bestRawFormat() const; + ColorFilterArray bayerOrder(Transform t) const + { + return std::visit(utils::overloaded{ + [&](const Tpg &tpg) -> ColorFilterArray { + V4L2Subdevice::Formats formats = tpg.sd_->formats(0); + for (const auto &[code, sizes] : formats) { + auto cfa = ColorFilterArray::fromMbusCode(code); + if (cfa) + return cfa.value(); + } + return ColorFilterArray::MONO; + }, + [&](const Inline &in) -> ColorFilterArray { + return in.sensor_->bayerOrder(t); + }, + [&](const Memory &mem) -> ColorFilterArray { + return mem.cru_->sensor()->bayerOrder(t); + }, + }, input_); + } + + int bestMbusCode(ColorFilterArray cfa) const; + bool isSensorRaw() const; void updateControls(const ControlInfoMap &ipaControls); - PixelFormat adjustRawFormat(const PixelFormat &pixFmt) const; - Size adjustRawSizes(const PixelFormat &pixFmt, const Size &rawSize) const; + Size adjustRawSizes(const Size &rawSize, unsigned int rawCode) const; Stream frStream_; Stream dsStream_; @@ -305,21 +322,13 @@ void MaliC55CameraData::setSensorControls(const ControlList &sensorControls) } /* - * The Mali C55 ISP can only produce 16-bit RAW output in bypass modes, but the - * sensors connected to it might produce 8/10/12/16 bits. We simply search the - * sensor's supported formats for the one with a matching bayer order and the - * greatest bitdepth. + * The Mali C55 ISP can only accept as input RAW20 and can only produce as + * output RAW16. The sensors connected to it might produce 8/10/12/16 bits. + * We simply search the sensor's supported formats for the one with greatest + * bitdepth and matches the bayer color filter array order. */ -int MaliC55CameraData::pixfmtToMbusCode(const PixelFormat &pixFmt) const +int MaliC55CameraData::bestMbusCode(ColorFilterArray cfa) const { - auto it = maliC55FmtToCode.find(pixFmt); - if (it == maliC55FmtToCode.end()) - return -EINVAL; - - BayerFormat bayerFormat = BayerFormat::fromMbusCode(it->second); - if (!bayerFormat.isValid()) - return -EINVAL; - V4L2Subdevice::Formats formats = subdev()->formats(0); unsigned int sensorMbusCode = 0; unsigned int bitDepth = 0; @@ -329,7 +338,12 @@ int MaliC55CameraData::pixfmtToMbusCode(const PixelFormat &pixFmt) const if (!sdBayerFormat.isValid()) continue; - if (sdBayerFormat.cfa.order != bayerFormat.cfa.order) + /* The pipeline currently does not support generic codes */ + if (MediaBusFormatInfo::info(code).genericRaw) + continue; + + auto c = ColorFilterArray::fromMbusCode(code); + if (!c || c != cfa) continue; if (sdBayerFormat.bitDepth > bitDepth) { @@ -344,38 +358,20 @@ int MaliC55CameraData::pixfmtToMbusCode(const PixelFormat &pixFmt) const return sensorMbusCode; } -/* - * Find a RAW PixelFormat supported by both the ISP and the sensor. - * - * The situation is mildly complicated by the fact that we expect the sensor to - * output something like RAW8/10/12/16, but the ISP can only accept as input - * RAW20 and can only produce as output RAW16. The one constant in that is the - * bayer order of the data, so we'll simply check that the sensor produces a - * format with a bayer order that matches that of one of the formats we support, - * and select that. - */ -const PixelFormat &MaliC55CameraData::bestRawFormat() const +bool MaliC55CameraData::isSensorRaw() const { - static const PixelFormat invalidPixFmt = {}; + if (!std::holds_alternative(input_)) + return this->sensor()->isRaw(); for (const auto &fmt : subdev()->formats(0)) { BayerFormat sensorBayer = BayerFormat::fromMbusCode(fmt.first); - if (!sensorBayer.isValid()) - continue; - - for (const auto &[pixFmt, rawCode] : maliC55FmtToCode) { - if (!isFormatRaw(pixFmt)) - continue; - - BayerFormat bayer = BayerFormat::fromMbusCode(rawCode); - if (bayer.cfa.order == sensorBayer.cfa.order) - return pixFmt; - } + if (sensorBayer.isValid()) + return true; } - LOG(MaliC55, Error) << "Sensor doesn't provide a compatible format"; - return invalidPixFmt; + LOG(MaliC55, Error) << "Sensor doesn't provide a raw bayer format"; + return false; } void MaliC55CameraData::updateControls(const ControlInfoMap &ipaControls) @@ -402,34 +398,13 @@ void MaliC55CameraData::updateControls(const ControlInfoMap &ipaControls) controlInfo_ = ControlInfoMap(std::move(controls), controls::controls); } -/* - * Make sure the provided raw pixel format is supported and adjust it to - * one of the supported ones if it's not. - */ -PixelFormat MaliC55CameraData::adjustRawFormat(const PixelFormat &rawFmt) const -{ - /* Make sure the RAW mbus code is supported by the image source. */ - int rawCode = pixfmtToMbusCode(rawFmt); - if (rawCode < 0) - return bestRawFormat(); - - const auto rawSizes = sizes(rawCode); - if (rawSizes.empty()) - return bestRawFormat(); - - return rawFmt; -} - -Size MaliC55CameraData::adjustRawSizes(const PixelFormat &rawFmt, const Size &size) const +Size MaliC55CameraData::adjustRawSizes(const Size &size, + unsigned int rawCode) const { /* Expand the RAW size to the minimum ISP input size. */ Size rawSize = size.expandedTo(kMaliC55MinInputSize); /* Check if the size is natively supported. */ - int rawCode = pixfmtToMbusCode(rawFmt); - if (rawCode < 0) - return {}; - const auto rawSizes = sizes(rawCode); auto sizeIt = std::find(rawSizes.begin(), rawSizes.end(), rawSize); if (sizeIt != rawSizes.end()) @@ -552,21 +527,28 @@ CameraConfiguration::Status MaliC55CameraConfiguration::validate() rawConfig = &config; } + /* Populate a valid sensor mbus code */ + ColorFilterArray cfa = data_->bayerOrder(combinedTransform_); + int code = data_->bestMbusCode(cfa); + if (code < 0) + return Invalid; + + sensorFormat_.code = code; + /* * The C55 can not upscale. Limit the configuration to the ISP * capabilities and the sensor resolution. */ Size maxSize = kMaliC55MaxSize.boundedTo(data_->resolution()); if (rawConfig) { - /* - * \todo Take into account the Bayer components ordering once - * we support rotations. - */ - PixelFormat rawFormat = - data_->adjustRawFormat(rawConfig->pixelFormat); + if (!rawConfig->bayerOrder || + rawConfig->bayerOrder.value() != cfa) { + rawConfig->bayerOrder = cfa; + status = Adjusted; + } - if (!rawFormat.isValid()) - return Invalid; + /* C55 can only produce 16-bit raw output */ + PixelFormat rawFormat = formats::RAW16; if (rawFormat != rawConfig->pixelFormat) { LOG(MaliC55, Debug) @@ -576,7 +558,7 @@ CameraConfiguration::Status MaliC55CameraConfiguration::validate() } Size rawSize = - data_->adjustRawSizes(rawFormat, rawConfig->size); + data_->adjustRawSizes(rawConfig->size, sensorFormat_.code); if (rawSize != rawConfig->size) { LOG(MaliC55, Debug) << "RAW sizes adjusted to " << rawSize; @@ -637,19 +619,15 @@ CameraConfiguration::Status MaliC55CameraConfiguration::validate() /* If there's a RAW config, sensor configuration follows it. */ if (rawConfig) { - sensorFormat_.code = data_->pixfmtToMbusCode(rawConfig->pixelFormat); sensorFormat_.size = rawConfig->size.expandedTo(minSensorSize); return status; } /* If there's no RAW config, compute the sensor configuration here. */ - PixelFormat rawFormat = data_->bestRawFormat(); - if (!rawFormat.isValid()) + if (!data_->isSensorRaw()) return Invalid; - sensorFormat_.code = data_->pixfmtToMbusCode(rawFormat); - uint16_t distance = std::numeric_limits::max(); const auto sizes = data_->sizes(sensorFormat_.code); Size bestSize; @@ -799,6 +777,7 @@ PipelineHandlerMaliC55::generateConfiguration(Camera *camera, std::unique_ptr config = std::make_unique(data); bool frPipeAvailable = true; + ColorFilterArray cameraCfa = data->bayerOrder(Transform::Identity); if (roles.empty()) return config; @@ -838,12 +817,8 @@ PipelineHandlerMaliC55::generateConfiguration(Camera *camera, break; case StreamRole::Raw: - pixelFormat = data->bestRawFormat(); - if (!pixelFormat.isValid()) { - LOG(MaliC55, Error) - << "Camera does not support RAW formats"; + if (!data->isSensorRaw()) return nullptr; - } size = data->resolution(); break; @@ -865,7 +840,7 @@ PipelineHandlerMaliC55::generateConfiguration(Camera *camera, if (isRaw) { /* Make sure the mbus code is supported. */ - int rawCode = data->pixfmtToMbusCode(pixFmt); + int rawCode = data->bestMbusCode(cameraCfa); if (rawCode < 0) continue; @@ -896,6 +871,9 @@ PipelineHandlerMaliC55::generateConfiguration(Camera *camera, cfg.bufferCount = 4; cfg.size = size; + if (role == StreamRole::Raw) + cfg.bayerOrder = cameraCfa; + config->addConfiguration(cfg); } @@ -1180,10 +1158,10 @@ int PipelineHandlerMaliC55::configure(Camera *camera, * affected by any flips that we've configured) */ const Transform &combinedTransform = maliConfig->combinedTransform(); - ColorFilterArray::Order cfaOrder = data->sensor()->bayerOrder(combinedTransform); + ColorFilterArray cfa = data->bayerOrder(combinedTransform); ControlInfoMap ipaControls; - ret = data->ipa_->configure(ipaConfig, utils::to_underlying(cfaOrder), + ret = data->ipa_->configure(ipaConfig, utils::to_underlying(cfa.order), &ipaControls); if (ret) { LOG(MaliC55, Error) << "Failed to configure IPA";