From patchwork Fri Feb 13 16:57:48 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 26151 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 D3A91C32F4 for ; Fri, 13 Feb 2026 16:58:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4A6C56220C; Fri, 13 Feb 2026 17:58:17 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="nWKKSt0s"; 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 2B953621EB for ; Fri, 13 Feb 2026 17:58:06 +0100 (CET) Received: from ping.linuxembedded.co.uk (cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 558CB26C4; Fri, 13 Feb 2026 17:57:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1771001837; bh=9l1KDP7Esu5b5XWJMph6Z3R7VlhMCGR2V6tiw0wVo4w=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=nWKKSt0sOACALQpA2GCt86QQ1rROqVGojp8pFUDR13xC5k3TAq4Kca4cG+716ZM2R yUkLvVU1Yp4BmLXsmvAJcdlZ/vu09NXWy+WfK7XGIqBMXQbxNc+w/p1GHR3F9w0eYz tdew1+ZSiI1ixmxn/2+FELuVSWrO+YeqMQ96NeVQ= From: Kieran Bingham Date: Fri, 13 Feb 2026 16:57:48 +0000 Subject: [PATCH v7 09/15] ipa: rkisp1: cproc: Provide a Hue control MIME-Version: 1.0 Message-Id: <20260213-kbingham-quantizers-v7-9-1626b9aaabf1@ideasonboard.com> References: <20260213-kbingham-quantizers-v7-0-1626b9aaabf1@ideasonboard.com> In-Reply-To: <20260213-kbingham-quantizers-v7-0-1626b9aaabf1@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Kieran Bingham , Isaac Scott , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771001883; l=5043; i=kieran.bingham@ideasonboard.com; s=20260207; h=from:subject:message-id; bh=9l1KDP7Esu5b5XWJMph6Z3R7VlhMCGR2V6tiw0wVo4w=; b=HlCQVELubasZCDke/sijo8XfCIK10F+h5lfq9eU/C0SfxThuXYSyw8+3v3/9nSj4WgcFLa5gX 8XjJIbWCBSkBIhSB/vlqorZnpE6LxX6XgCuIn3YR230HDPPVMStG482 X-Developer-Key: i=kieran.bingham@ideasonboard.com; a=ed25519; pk=FVXKN7YuwHc6UtbRUeTMAmranfsQomA+vnilfglWdaY= 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 RKISP1 supports a configurable Hue as part of the colour processing unit (cproc). Implement the new control converting to the hardware scale accordingly and report the applied control in the completed request metadata. This is implemented as a phase shift of the chrominance values between -90 and +87.188 degrees according to the datasheet however the type itself would imply that this is a range between -90 and 89.2969. Moreover, the hardware applies the inverse phase shift to the operation expected and documented by libcamera, so we apply a negative scale when converting the libcamera control to the Q<1,7> type, resulting in a range of -89.2969 to +90.0 degrees control. Reviewed-by: Isaac Scott Tested-by: Barnabás Pőcze Signed-off-by: Kieran Bingham --- v5: - Use Q<1, 7> directly and handle scaling in cproc.cpp - Use full string of quantized in debug log - Invert/Negate the hardware hue direction --- src/ipa/rkisp1/algorithms/cproc.cpp | 28 ++++++++++++++++++++++++++++ src/ipa/rkisp1/ipa_context.h | 3 +++ 2 files changed, 31 insertions(+) diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp index e9e2b5444bc9..7484e4780094 100644 --- a/src/ipa/rkisp1/algorithms/cproc.cpp +++ b/src/ipa/rkisp1/algorithms/cproc.cpp @@ -37,8 +37,15 @@ namespace { constexpr float kDefaultBrightness = 0.0f; constexpr float kDefaultContrast = 1.0f; +constexpr float kDefaultHue = 0.0f; constexpr float kDefaultSaturation = 1.0f; +/* + * The Hue scale is negated as the hardware performs the opposite phase shift + * to what is expected and defined from the libcamera Hue control value. + */ +constexpr float kHueScale = -90.0f; + } /* namespace */ /** @@ -53,6 +60,11 @@ int ColorProcessing::init(IPAContext &context, cmap[&controls::Contrast] = ControlInfo(0.0f, 1.993f, kDefaultContrast); cmap[&controls::Saturation] = ControlInfo(0.0f, 1.993f, kDefaultSaturation); + /* Hue adjustment is negated by kHueScale, min/max are swapped */ + cmap[&controls::Hue] = ControlInfo(HueQ::TraitsType::max * kHueScale, + HueQ::TraitsType::min * kHueScale, + kDefaultHue); + return 0; } @@ -66,6 +78,7 @@ int ColorProcessing::configure(IPAContext &context, cproc.brightness = BrightnessQ(kDefaultBrightness); cproc.contrast = ContrastQ(kDefaultContrast); + cproc.hue = HueQ(kDefaultHue); cproc.saturation = SaturationQ(kDefaultSaturation); return 0; @@ -107,6 +120,18 @@ void ColorProcessing::queueRequest(IPAContext &context, LOG(RkISP1CProc, Debug) << "Set contrast to " << value; } + const auto &hue = controls.get(controls::Hue); + if (hue) { + /* Scale the Hue from ]-90, +90] */ + HueQ value = *hue / kHueScale; + if (cproc.hue != value) { + cproc.hue = value; + update = true; + } + + LOG(RkISP1CProc, Debug) << "Set hue to " << value; + } + const auto saturation = controls.get(controls::Saturation); if (saturation) { SaturationQ value = *saturation; @@ -120,6 +145,7 @@ void ColorProcessing::queueRequest(IPAContext &context, frameContext.cproc.brightness = cproc.brightness; frameContext.cproc.contrast = cproc.contrast; + frameContext.cproc.hue = cproc.hue; frameContext.cproc.saturation = cproc.saturation; frameContext.cproc.update = update; } @@ -140,6 +166,7 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context, config.setEnabled(true); config->brightness = frameContext.cproc.brightness.quantized(); config->contrast = frameContext.cproc.contrast.quantized(); + config->hue = frameContext.cproc.hue.quantized(); config->sat = frameContext.cproc.saturation.quantized(); } @@ -154,6 +181,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context, { metadata.set(controls::Brightness, frameContext.cproc.brightness.value()); metadata.set(controls::Contrast, frameContext.cproc.contrast.value()); + metadata.set(controls::Hue, frameContext.cproc.hue.value() * kHueScale); metadata.set(controls::Saturation, frameContext.cproc.saturation.value()); } diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index cca5e281c732..e61391bb1510 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -36,6 +36,7 @@ namespace ipa::rkisp1 { /* Fixed point types used by CPROC */ using BrightnessQ = Q<1, 7>; using ContrastQ = UQ<1, 7>; +using HueQ = Q<1, 7>; using SaturationQ = UQ<1, 7>; struct IPAHwSettings { @@ -119,6 +120,7 @@ struct IPAActiveState { struct { BrightnessQ brightness; ContrastQ contrast; + HueQ hue; SaturationQ saturation; } cproc; @@ -181,6 +183,7 @@ struct IPAFrameContext : public FrameContext { struct { BrightnessQ brightness; ContrastQ contrast; + HueQ hue; SaturationQ saturation; bool update;