From patchwork Tue Jul 21 14:59:51 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 27427 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 D6ADDBE080 for ; Tue, 21 Jul 2026 15:00:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2EEB967E6E; Tue, 21 Jul 2026 17:00:07 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="nrIlssm9"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="DakKHFYh"; dkim-atps=neutral Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CE6596601C for ; Tue, 21 Jul 2026 17:00:05 +0200 (CEST) Received: from pps.filterd (m0279871.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66LDg3W91958685 for ; Tue, 21 Jul 2026 15:00:04 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=26USYEaNweJ ZtnHULZ3L3rzdqfTLZs2n2K/0B+cc0ok=; b=nrIlssm9LbQLbfsMWSE6tR+FGIH xTjZd4aUezo7pY/ZvgkPwEuQ0KqgGb2o9/o66YflaBI2NwArmFUVfyZRasfDQcu3 wJWZC6iIWPPr6RPJifaIjthWoUprdw2MQFuG8vLxGg9T/zHCdsdpA3uayM4mG3Qh qVTuR66FtjLHtSnjpSjBhN3hc9ACUy243ZOAR5qRYxuQ+2KjlzjUzPuorcyOiI0k 67ZX0aciIu2sGzMJvcX+CFjgv2MzqybILEjoDf9GpdmpDuXVwie0sLgT6ypMFKWt OwM8TJq1WqGjFvaLyb6kFFzRJQNM5uhWaMsVXBYGLDR1aBgGKmjZAEflszQ== Received: from mail-qt1-f200.google.com (mail-qt1-f200.google.com [209.85.160.200]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fj1bvu2q4-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Tue, 21 Jul 2026 15:00:04 +0000 (GMT) Received: by mail-qt1-f200.google.com with SMTP id d75a77b69052e-51c12e43b98so222069171cf.1 for ; Tue, 21 Jul 2026 08:00:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1784646003; x=1785250803; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=26USYEaNweJZtnHULZ3L3rzdqfTLZs2n2K/0B+cc0ok=; b=DakKHFYhhsMj1vODeCkMvisP89ByZSKAqUqHxbwUzWmJ+cl/ShB6MwRNUAHZHG2Ft+ 3mn4AEJarWsteQzO6sAvZzlKBN1IsRcBG8h2fvGFCa0k3B1v2xLyRcYkFoR80mVO0mBa HBQaGQp14ZJ5Sf+OZ12RCLR8A0gvRnziJYnc1ZeHcsAHPisfjbNtdaIR+LkiOU8STAJm d+zSFLKUTAhaqqg2LqoUj5q9MGMkvSA454uNZ5CBy6z8uoidzEZaO0LFcO/B/PaX8Yrr P9yHsUqtr9MIJ3+yLcTPZvU5404VNiCF2B0TqRZN5xrqBJoW8jmNbc15/dLcsP/tf83G ezbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784646003; x=1785250803; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=26USYEaNweJZtnHULZ3L3rzdqfTLZs2n2K/0B+cc0ok=; b=Xzm5wPmczmFd64qKZn07zzsloRz6LUNdP+a6sNjdWF+kMoUWcZqyosYYGElGYiw0Qp UNX0mJhqwnAKb0GxxMvdYk30/MiGY+HcR7CGQ4PE72FA7FMUGOpwJW/s2PTrwsF5t642 1lI+TTwCY//59V8Q2p1WukNHAORQnqlA2Hg/CtgV4fNBkUBe3LLyEQkHH3maSaOC6ost jDbXAzE27WCg9ykhS+Hf7IWO4EmgnBweYxcdNQNOPPC8irCoTCJfbW+jRPjslJQHJXy7 kpEg72uEW/Up6ziZdbEEYFC2mnk2S/XT2wumDaSyLfA7MmH0yoDtBv1bvmw2/9omGD1J ZhJw== X-Gm-Message-State: AOJu0Yw1wUOktwA458J4mv/HpuYntpb/uzVzMmo6C38ZvrQoedNZx8/l jBFV6BBjz5AHT7M8A1HciO+mxFv4OquIubjW9Hhez9ywZft8hFTYjvgJGlivMeiPSpsJnMXr5Xe 5Z3QN/C+wpN2+EGu68I8ybA5s7myW54i6A0lTGiR9JWTL1WJuE8B244K837b2oLLOwxe1ZyrXcL VNe13A8OKi X-Gm-Gg: AfdE7cm9/3azcCj174gR4OY8yN5HMqC4mJW9aFUwkKMVQ2u7Ub6k+POeff6WVOQR4/8 0g0CrWK1w31yWPWulThmWqVcAjIqXyX+v7qmxAztTalItdme9AC5w23UV6bA/MiVFk7qLVDeqU+ JN7JuLx5G+9JakQ6nz+3yZ/l0LcRVkxTfyS7K1fkWnb45bdF1hDNrlhGtY5NsR7MDBZpvRuI2jF 4DOwa21HVtgZ3k+BY3Hjt0wdx7qCmVJtXLWgx3Ict3dE0BVNmhoF089K9SWlH0+g51rN+aN7WUu dzjB6mmZz7HM2TcjLc6mI4mQt5wYp5EekGTdiot0O3slz8P7Xbh9LbjJw6jYnFl70OZRjeq/XGV o/um893zGMbexop+BJNIO9Xe8+pv3BCSfDOr1RRo0dqK7a4U+yMcpAV5C59URWXFq52aRhpB+72 RRDEN8iO12xtH595s= X-Received: by 2002:a05:622a:c85:b0:519:e020:6087 with SMTP id d75a77b69052e-5213c013438mr183324131cf.11.1784646002245; Tue, 21 Jul 2026 08:00:02 -0700 (PDT) X-Received: by 2002:a05:622a:c85:b0:519:e020:6087 with SMTP id d75a77b69052e-5213c013438mr183323661cf.11.1784646001609; Tue, 21 Jul 2026 08:00:01 -0700 (PDT) Received: from shalem (2001-1c00-0c32-7800-5bfa-a036-83f0-f9ec.cable.dynamic.v6.ziggo.nl. [2001:1c00:c32:7800:5bfa:a036:83f0:f9ec]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c1712a5f837sm615968066b.32.2026.07.21.08.00.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Jul 2026 08:00:00 -0700 (PDT) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Loic Poulain , Hans de Goede Subject: [PATCH v2 1/7] libcamera: V4L2Subdevice: Add whence argument to [get|set]Selection() Date: Tue, 21 Jul 2026 16:59:51 +0200 Message-ID: <20260721145958.68489-2-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> References: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: JEY2GDOIFrFtpwc0rh_lJItj3JT_adKL X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfX4C55jp9GtyEK s/aJ6+bC3TiXLBin2FN85vkQu/wjhUnRV5XHI6hdv7oOarFqo/Q4jS4Oanxyhx2G3m6pnSyQ7cq ep+wY4Z8jT06fQJ6nbQPNGmiMFj7BG4= X-Proofpoint-GUID: JEY2GDOIFrFtpwc0rh_lJItj3JT_adKL X-Authority-Analysis: v=2.4 cv=Sb7HsPRu c=1 sm=1 tr=0 ts=6a5f8974 cx=c_pps a=JbAStetqSzwMeJznSMzCyw==:117 a=xqWC_Br6kY4A:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=3WHJM1ZQz_JShphwDgj5:22 a=EUspDBNiAAAA:8 a=BV3TIWPM9q02PJwuZ20A:9 a=uxP6HrT_eTzRwkO_Te1X:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfX28oV5yqvvMIK z2q+FGkwk3OAUZEdtWB/n3cCFOndNLOT4pjdIR7Tdiq6wQtWkhct0Q6WvvCEqwWU7xCRlFCoGID 5iNjBQBvlqWkb1woX3geLG14SX0OCaxEG4WKq79y/IwGDXzJd0hLqtSwten30udjxhSkwJ9TPx1 FGmQG5L4L92TWkd7j9AsZwCH+Vpg4wqdg9NWNsGrA8VnAbVt7RzQbcL/lUbSFDxXDyhRV4Q71/i QSDcpP0UlchlWVp0xLGqPQ0/YyFdxGX5w9yB8hXWI1iDRlCoM1k589WUual88ZY9dP3mIF0n8o7 W09Em1GBrps0F9tqcN8GinU9AM5DNw8ABM1kHKr2lVqXz3SvZI8mVwP7D1lDMNl2HNuMWMTgazt t8EAgxWFpMnsg5AUC7DMki19hw3jeX69XqdcgXyNfrkeOz83UyeTo6f2X4XyM+DqPiPxCkKFyi4 ATfAQcg/JQMY0g1ODAA== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1143, Hydra:6.1.134, FMLib:17.12.100.49 definitions=2026-07-21_03,2026-07-20_03,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 malwarescore=0 spamscore=0 suspectscore=0 clxscore=1015 lowpriorityscore=0 impostorscore=0 adultscore=0 phishscore=0 priorityscore=1501 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607210158 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 a whence argument to V4L2Subdevice::[get|set]Selection() allowing callers to specify that the operation should be applied to the try state, rather then always applying it to the active state. Signed-off-by: Hans de Goede --- include/libcamera/internal/v4l2_subdevice.h | 14 ++++++++------ src/libcamera/v4l2_subdevice.cpp | 20 ++++++++++++++------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/include/libcamera/internal/v4l2_subdevice.h b/include/libcamera/internal/v4l2_subdevice.h index c37a82afa..36848915f 100644 --- a/include/libcamera/internal/v4l2_subdevice.h +++ b/include/libcamera/internal/v4l2_subdevice.h @@ -123,16 +123,18 @@ public: const MediaEntity *entity() const { return entity_; } int getSelection(const Stream &stream, unsigned int target, - Rectangle *rect); - int getSelection(unsigned int pad, unsigned int target, Rectangle *rect) + Rectangle *rect, Whence whence = ActiveFormat); + int getSelection(unsigned int pad, unsigned int target, Rectangle *rect, + Whence whence = ActiveFormat) { - return getSelection({ pad, 0 }, target, rect); + return getSelection({ pad, 0 }, target, rect, whence); } int setSelection(const Stream &stream, unsigned int target, - Rectangle *rect); - int setSelection(unsigned int pad, unsigned int target, Rectangle *rect) + Rectangle *rect, Whence whence = ActiveFormat); + int setSelection(unsigned int pad, unsigned int target, Rectangle *rect, + Whence whence = ActiveFormat) { - return setSelection({ pad, 0 }, target, rect); + return setSelection({ pad, 0 }, target, rect, whence); } Formats formats(const Stream &stream); diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp index a69de154e..8f9d4dbd9 100644 --- a/src/libcamera/v4l2_subdevice.cpp +++ b/src/libcamera/v4l2_subdevice.cpp @@ -1171,17 +1171,19 @@ int V4L2Subdevice::open() * \param[in] stream The stream the rectangle is retrieved from * \param[in] target The selection target defined by the V4L2_SEL_TGT_* flags * \param[out] rect The retrieved selection rectangle + * \param[in] whence The format to get, \ref V4L2Subdevice::ActiveFormat + * "ActiveFormat" or \ref V4L2Subdevice::TryFormat "TryFormat" * * \todo Define a V4L2SelectionTarget enum for the selection target * * \return 0 on success or a negative error code otherwise */ int V4L2Subdevice::getSelection(const Stream &stream, unsigned int target, - Rectangle *rect) + Rectangle *rect, Whence whence) { struct v4l2_subdev_selection sel = {}; - sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sel.which = whence; sel.pad = stream.pad; sel.stream = stream.stream; sel.target = target; @@ -1205,11 +1207,13 @@ int V4L2Subdevice::getSelection(const Stream &stream, unsigned int target, /** * \fn V4L2Subdevice::getSelection(unsigned int pad, unsigned int target, - * Rectangle *rect) + * Rectangle *rect, Whence whence = ActiveFormat) * \brief Get selection rectangle \a rect for \a target * \param[in] pad The 0-indexed pad number the rectangle is retrieved from * \param[in] target The selection target defined by the V4L2_SEL_TGT_* flags * \param[out] rect The retrieved selection rectangle + * \param[in] whence The format to get, \ref V4L2Subdevice::ActiveFormat + * "ActiveFormat" or \ref V4L2Subdevice::TryFormat "TryFormat" * * \return 0 on success or a negative error code otherwise */ @@ -1219,17 +1223,19 @@ int V4L2Subdevice::getSelection(const Stream &stream, unsigned int target, * \param[in] stream The stream the rectangle is to be applied to * \param[in] target The selection target defined by the V4L2_SEL_TGT_* flags * \param[inout] rect The selection rectangle to be applied + * \param[in] whence The format to get, \ref V4L2Subdevice::ActiveFormat + * "ActiveFormat" or \ref V4L2Subdevice::TryFormat "TryFormat" * * \todo Define a V4L2SelectionTarget enum for the selection target * * \return 0 on success or a negative error code otherwise */ int V4L2Subdevice::setSelection(const Stream &stream, unsigned int target, - Rectangle *rect) + Rectangle *rect, Whence whence) { struct v4l2_subdev_selection sel = {}; - sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sel.which = whence; sel.pad = stream.pad; sel.stream = stream.stream; sel.target = target; @@ -1258,11 +1264,13 @@ int V4L2Subdevice::setSelection(const Stream &stream, unsigned int target, /** * \fn V4L2Subdevice::setSelection(unsigned int pad, unsigned int target, - * Rectangle *rect) + * Rectangle *rect, Whence whence = ActiveFormat) * \brief Set selection rectangle \a rect for \a target * \param[in] pad The 0-indexed pad number the rectangle is to be applied to * \param[in] target The selection target defined by the V4L2_SEL_TGT_* flags * \param[inout] rect The selection rectangle to be applied + * \param[in] whence The format to get, \ref V4L2Subdevice::ActiveFormat + * "ActiveFormat" or \ref V4L2Subdevice::TryFormat "TryFormat" * * \todo Define a V4L2SelectionTarget enum for the selection target * From patchwork Tue Jul 21 14:59:52 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 27433 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 A9315BDE4C for ; Tue, 21 Jul 2026 15:00:55 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 55D6C67E63; Tue, 21 Jul 2026 17:00:55 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="gq7l7GNi"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="DINaFbK1"; dkim-atps=neutral Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5E04167E67 for ; Tue, 21 Jul 2026 17:00:53 +0200 (CEST) Received: from pps.filterd (m0279868.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66LDg8hu1842122 for ; Tue, 21 Jul 2026 15:00:52 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=+3RCnHl3qpA ursGcygSIZTEW6IU52Xet4/q1mpbNFAA=; b=gq7l7GNi/vHxkVkDkLjGyLqv/mX 4Fu0iAdLfnq+NwcAwoeG+nMhexLAflo4Sc8GB5oTA54k+g6gr9v9YeYKcbb6KM4w pQRLIckQhfqY1rfUwQ08Tc/CSpjC7salj9RvkdUrkyiEkCqqkLrhCyB53Z6DITID 15SMA4LyBFRgarL9YR60DOWGeA7Z+ljk7h0r4rkAShyIk6aaShp+6FgJdTrIyafQ QFuz8RXnucPQVwkScIYgXnpuQtp41YOmlfoA49cUM91cpKEa3k8xEJn8Pdm02Thh NquKhhcO11MYKTEVG/bmuWhvKLGkjTtX023CgOR9xtAPgkP2eyd4aS3Xm3w== Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fj1se2vn4-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Tue, 21 Jul 2026 15:00:51 +0000 (GMT) Received: by mail-qt1-f197.google.com with SMTP id d75a77b69052e-5276aa86d00so4061101cf.2 for ; Tue, 21 Jul 2026 08:00:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1784646051; x=1785250851; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=+3RCnHl3qpAursGcygSIZTEW6IU52Xet4/q1mpbNFAA=; b=DINaFbK1rpZe9x26LpB9+/tB/9HdxnTZ7PRwb/gc2xj0la5qKlrtk18TxnlERZXt0g Ge+lnMdjBmH2+l1fM69iCWuBMnz48OrvzLqqgc7t/zUqmHd5Fuh7NlYjdza/95+t6Xju AUEQ09GITU8n2YS9bkBjJiCVsRecUTPjTMocoho8vI3kvwnUCjMUzO1kmONo8aLPL8W5 yvUOTi1Z3LPYJxpfUWWvKEYi47wDTgPH8RO/kSdDgQgZ3LAtEM4pYqKuZrgXOU6vFKLN Xsm0fsLTh6MpxdeAK8s9vrpfa596RyQ7ZZQgZzHYHI4cQ9vk5iZp66SdyNy3xBHsrdAw QOIA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784646051; x=1785250851; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=+3RCnHl3qpAursGcygSIZTEW6IU52Xet4/q1mpbNFAA=; b=jDWV2MJxUHO5CNmtyva9NmGnOddOu0oY9B/P2l5sO/ipDmM0MIZTt0w1GzSnrSZhS9 gkXg1GXicWKCwkcmySAz6SHWrQiRDX7NTNDyHyG1tsCsqh0jfPQxUYt/CkIZvairKsLH hL6U09gWx1WTuKeV/SGnsRsmSZunY4lKwN4NtJT0zYFiGSrOlDUYlqpnIMWn/EjsjlCF mg4iVCXWbQOb+tsZAkhmf+WMipxosJ/L3HpxtaXUB6/GiZUBoikdYO8F3FwhzHMFNQDo gR4jEmpywvcg9q8YBDAX8fVmk3G6pzQNuBqUYUb5Owyr38SFQZBlw0EM/Gl2ERkIEapg B1Sw== X-Gm-Message-State: AOJu0YzOmQF19sNE3lwopoD6IjDUgklj00LCZvn7fXuip1cJFrb6JZCK aJN2FUFYPlVcSPTeCoHG6WcEODH1RGpbBam+C0pv2s5gXOOI0aHnSEGUN8N8FgKJAx6XNdfxe3b Kyfhm/KTqNmzJY4T3l/1RO6KmMEfpMkSfMb1r0wLlauj1tuIP7RWOZ2vYzBrog/wfFa1hswPET+ TcN/J7TTNN X-Gm-Gg: AfdE7clnN3l0EgjPjYEY9kD/1cIatFQyYVSNzYDV7W5gztQBmx8Aq6hFHZhzUb6VwpE 6bpiMus4HC09S92nJjyMbt+SoR7ChMtmy4aSP0H/WUpgYQG7R8r8oUevZDQOoNabvZk1ChlbX6m 0NddSquErzG3RxiGJzWMggR5W+T4ZuuFKQb6AmRT1yDxczn7vsiCqnWLYC8Q6cViPXTB45Ac8XC dZOwML/tJHlU8NSrgYg6cpVOBMPRsPeFtMFEyG4B5FOFDA/spUs44vhUU6HjqYUb+96xnXUp++W yt+m1hurJQb0ohj7aToEdd9upDQNBNXljPm5klOKyApu0U8pUU1nhn7V7eOKcoEHC/lx0EA2Qf9 22nf8EXxaogellHGSYSwstyQM+Ssv1OR9HbEmDnnq/ioNAJraMtKyi/0R/nddcFJfnfew/yA8Bh X6+Wzolo47hNOSFnM= X-Received: by 2002:ac8:5809:0:b0:51c:709:b81c with SMTP id d75a77b69052e-5213b1494c9mr180953511cf.32.1784646011114; Tue, 21 Jul 2026 08:00:11 -0700 (PDT) X-Received: by 2002:ac8:5809:0:b0:51c:709:b81c with SMTP id d75a77b69052e-5213b1494c9mr180943681cf.32.1784646002810; Tue, 21 Jul 2026 08:00:02 -0700 (PDT) Received: from shalem (2001-1c00-0c32-7800-5bfa-a036-83f0-f9ec.cable.dynamic.v6.ziggo.nl. [2001:1c00:c32:7800:5bfa:a036:83f0:f9ec]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c1712a5f837sm615968066b.32.2026.07.21.08.00.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Jul 2026 08:00:02 -0700 (PDT) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Loic Poulain , Hans de Goede Subject: [PATCH v2 2/7] software_isp: Rename simple IPA setIspParams signal to paramsComputed Date: Tue, 21 Jul 2026 16:59:52 +0200 Message-ID: <20260721145958.68489-3-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> References: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: 93kTLbL8m6XAGgDP2kxfeS8uhjPH4WM5 X-Authority-Analysis: v=2.4 cv=TIZ1jVla c=1 sm=1 tr=0 ts=6a5f89a3 cx=c_pps a=EVbN6Ke/fEF3bsl7X48z0g==:117 a=xqWC_Br6kY4A:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=ZpdpYltYx_vBUK5n70dp:22 a=EUspDBNiAAAA:8 a=X9iWcBBwFAc5_HUTaPUA:9 a=a_PwQJl-kcHnX1M80qC6:22 X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfXwiTmHttPCHYp 4xKK8CU84VPkRCbmPHvuMoNa9eRX06yxjOMxKLl+D4otauRrbS9ZA3TMANjs4Dhpm6dIa7CrgfC pRq1WwgCP4IYNfeyp2N136CoK+BaoNg= X-Proofpoint-GUID: 93kTLbL8m6XAGgDP2kxfeS8uhjPH4WM5 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfX1M+j7yefVhNl XHcHD+VFwCICO+I8qcQFioODBfmVuTZVYZwUjG7sOVkwpBpBcZtCL0tYoltyUXnqRW2qtiVob2X YEH+EcJlD6ZUaUB+PIvyXeNEb78xpd4U1xzy3VIawkzWI34zjQhwxF8xwPVuPafBNv2eL2HDSR/ wbAtJWwgh9ZlaxOlRSHmBOY9JSbzwK9gOBFNSuv4IQVg5uWmc8Lu/wGLKR9wx16FAqI8/XCvGLH 3S4948wtnGg3LvEY1nvEGL/qnetecYpjgg0J5Yvg3UoPgozc90PG2XEsZ8alU+Vq1kaK0S4emWY m7KwSk+3LhjkCWrj3na1N/lNUBSsviph/TFh7Udu/NGLtji5XVjL2hO0q9DIyoy2l6kthiviz8B Ubx4sIEXUDZOQcSTOWrYiO0izXuxzIaJXhyUZM4LVE9ht7S+qrSwkOo6v8YIZLks+9ngKngWsq+ OSVRtKBpjkt8TOzwbfw== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1143, Hydra:6.1.134, FMLib:17.12.100.49 definitions=2026-07-21_03,2026-07-20_03,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 lowpriorityscore=0 impostorscore=0 priorityscore=1501 spamscore=0 clxscore=1015 malwarescore=0 bulkscore=0 suspectscore=0 adultscore=0 phishscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607210158 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" Rename the simple IPA setIspParams signal to paramsComputed and pass the frame-number as argument to make the simple IPA behavior consistent with other IPA implementations. Signed-off-by: Hans de Goede --- include/libcamera/internal/software_isp/software_isp.h | 2 +- include/libcamera/ipa/soft.mojom | 2 +- src/ipa/simple/soft_simple.cpp | 2 +- src/libcamera/software_isp/software_isp.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h index 4f72dce9b..58353301f 100644 --- a/include/libcamera/internal/software_isp/software_isp.h +++ b/include/libcamera/internal/software_isp/software_isp.h @@ -89,7 +89,7 @@ public: Signal setSensorControls; private: - void saveIspParams(); + void saveIspParams(uint32_t frame); void setSensorCtrls(const ControlList &sensorControls); void statsReady(uint32_t frame, uint32_t bufferId); void inputReady(FrameBuffer *input); diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom index 77328c5fd..55a00b598 100644 --- a/include/libcamera/ipa/soft.mojom +++ b/include/libcamera/ipa/soft.mojom @@ -33,6 +33,6 @@ interface IPASoftInterface { interface IPASoftEventInterface { setSensorControls(libcamera.ControlList sensorControls); - setIspParams(); + paramsComputed(uint32 frame); metadataReady(uint32 frame, libcamera.ControlList metadata); }; diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp index d4ab91e30..3ee7b0d65 100644 --- a/src/ipa/simple/soft_simple.cpp +++ b/src/ipa/simple/soft_simple.cpp @@ -292,7 +292,7 @@ void IPASoftSimple::computeParams(const uint32_t frame) algo->prepare(context_, frame, frameContext, params_); params_->combinedMatrix = context_.activeState.combinedMatrix; - setIspParams.emit(); + paramsComputed.emit(frame); } void IPASoftSimple::processStats(const uint32_t frame, diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp index 137341fe2..7efd5cf4c 100644 --- a/src/libcamera/software_isp/software_isp.cpp +++ b/src/libcamera/software_isp/software_isp.cpp @@ -170,7 +170,7 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, return; } - ipa_->setIspParams.connect(this, &SoftwareIsp::saveIspParams); + ipa_->paramsComputed.connect(this, &SoftwareIsp::saveIspParams); ipa_->metadataReady.connect(this, [this](uint32_t frame, const ControlList &metadata) { metadataReady.emit(frame, metadata); @@ -431,7 +431,7 @@ void SoftwareIsp::process(uint32_t frame, FrameBuffer *input, FrameBuffer *outpu ConnectionTypeQueued, frame, input, output, debayerParams_); } -void SoftwareIsp::saveIspParams() +void SoftwareIsp::saveIspParams([[maybe_unused]] uint32_t frame) { debayerParams_ = *sharedParams_; } From patchwork Tue Jul 21 14:59:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 27428 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 78C71BDE4C for ; Tue, 21 Jul 2026 15:00:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CC64567E7D; Tue, 21 Jul 2026 17:00:10 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="T1GBItW3"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="ONohk9CK"; dkim-atps=neutral Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DDFFD67E79 for ; Tue, 21 Jul 2026 17:00:07 +0200 (CEST) Received: from pps.filterd (m0279862.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66LDfvI61687427 for ; Tue, 21 Jul 2026 15:00:05 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=8MmRQKljRR3 2dXHcem2mQCWfAENUzFEK6jiQMlb0uCg=; b=T1GBItW3K/q35wY2t0CkvUkotlw e5UEMPczRKUMNDwJKzj9qho2nDht3f4vGf1aSnpEdYuIbQFUte+HvEBeAgFLzJ74 jyWyGaA3/hV7+W3WiPC+ClvU0n8TkKTcDpofSCWdD5tMwt8L8l8tNABSbCHr0Yg9 RK8rsh6oR7MmVMmOY3X/QidiLT2cEU7teC2jqcDov5PSboN/G+M9f+DfwUvIOLmb 8kNi2GDr8jRNm1FoC6uH6G+LGHgoYO0wimQwDvntPbN/i5UD03s6FEIkvOpX/glF oT7V+67pzjkN2+ujxDdysNzSUQ4hxhtJMgpD/+QJSD911F+VcekKp+9gerw== Received: from mail-qt1-f198.google.com (mail-qt1-f198.google.com [209.85.160.198]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fj9c40hbj-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Tue, 21 Jul 2026 15:00:05 +0000 (GMT) Received: by mail-qt1-f198.google.com with SMTP id d75a77b69052e-51c19493ac2so11024051cf.1 for ; Tue, 21 Jul 2026 08:00:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1784646005; x=1785250805; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=8MmRQKljRR32dXHcem2mQCWfAENUzFEK6jiQMlb0uCg=; b=ONohk9CK1rdg59wuHLSgWpqpLwTuQgPQP/PK6gcivdZxYaQd+6hKfDuJ0qSuZywm5m pgzhDnHfB5ddarASbQZQxmTz0z0bAYpqhWJgG4Nv05LWWI8wdaNG2UIBM3MGM+OFW1ag tQ0kg2BrzVzPoelINQsfldsHeaSYNuTwqc7HI8Xr1FWNhrplQVKNMGie9xQ8G1PI5Lw0 iIysndKq3Dskitp7IsOTU3V3vvZJOnAb7/pyySvHGEtgfIRnHH5orpH30y8sTN6Jjq1v qCLeZ81IZmnXkZUAZt7WPAgmG2weXFgBGhpbCJhdt4gepR7LIfsHgadMPBfWdqzGzZ9n +EZg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784646005; x=1785250805; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=8MmRQKljRR32dXHcem2mQCWfAENUzFEK6jiQMlb0uCg=; b=K4mveVjjJZVtFZ6kM3MIqLqWtCvB+2cHHfbgve2w92yev8dp3xEFirCZrzhiJT/BsZ Rje8dT9MsrgLvwVmtLnp5SIWKyCMHdVL+17I9vPt55NvvuQNRxUD9+YcfIfXte7Tsx+H Zblv/wZyfhg7mUbnrl8U00CJYYkecNCnrIYpZ4n9E7FvSrpzNqy5pH3q9yon4n/euGvZ gjJbnCIjzctlLdXaE+mJ+BNkL+LoNC565QWAfyUNPAm1hBuD9kbH13kDe+YNCvODD+Zf NUI1pAztGCKWA4QfpfvYa4bx4UXEw+Zno1vWZ4f1plDCtPDgeLoziEkM3YrImgITnI8V 72zA== X-Gm-Message-State: AOJu0Yw456yialNCivQDbyRT/8kiU2kOd8uxw4QRLUN2uUC4oV0rs5qB 6bpDBzN2MK+MwnA3Av8CPjtcnvcp7i06XeMk4OvU5ZB+eZSQr5T9PN01Wmh+7MAU1OCfs+d1Otc XQ8p+s8tT/cU8Sqw0i8sDa/zG4O7mGkAxCnSoq0MieY6KAIE3JMG9iOlm5EEnCQ9qDkTLIkBj5d EvQPbsvH7m X-Gm-Gg: AfdE7cljAXa1KrxPquttBmOz9jCjkiVMiwYBzX4kjr0KPc46/yfBAOxEr8CoGXpDhOM 7dOtuAUztHEI51hzRtMM5tUGcSXB1dA32+XoWqaq2MeRcUrTdZ08hBlaND+w8y6Sdt/iK07eAuP 0j+eB6NtCRx/AG4Tk0MvSZ8uS5A90nRAc8chY7qcQX5vHffnJoS4BVselLqlq//drr9vroaaQNC uUfl0LRJa4bDMrqPRaLy+AH21DB0NNscKVcmFgv+IODSC9erbxT6Ks6Z1cGVWPKa6UTGL8SNYpR CWFEV6gcPT9OihU2KQ4BHOldswlqRjHoTvjjSae6B9m8dK4nVDnyZP3XECTM6+aSNcgvZUvg72B MiF0nmmI1Gy7SPUtm7e/cHAYcAv36TygJrrU6dieZoPsD+LPaeN9wm/Gq3e5/T4pYbF6as1l7zP 38GjpZQpRgBToG65A= X-Received: by 2002:a05:622a:4188:b0:51b:fb4f:afdc with SMTP id d75a77b69052e-5213c0081ccmr175885331cf.11.1784646004613; Tue, 21 Jul 2026 08:00:04 -0700 (PDT) X-Received: by 2002:a05:622a:4188:b0:51b:fb4f:afdc with SMTP id d75a77b69052e-5213c0081ccmr175884701cf.11.1784646003992; Tue, 21 Jul 2026 08:00:03 -0700 (PDT) Received: from shalem (2001-1c00-0c32-7800-5bfa-a036-83f0-f9ec.cable.dynamic.v6.ziggo.nl. [2001:1c00:c32:7800:5bfa:a036:83f0:f9ec]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c1712a5f837sm615968066b.32.2026.07.21.08.00.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Jul 2026 08:00:03 -0700 (PDT) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Loic Poulain , Hans de Goede Subject: [PATCH v2 3/7] camss: Add cammss utility functions Date: Tue, 21 Jul 2026 16:59:53 +0200 Message-ID: <20260721145958.68489-4-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> References: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Authority-Analysis: v=2.4 cv=I4JVgtgg c=1 sm=1 tr=0 ts=6a5f8975 cx=c_pps a=mPf7EqFMSY9/WdsSgAYMbA==:117 a=xqWC_Br6kY4A:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=_K5XuSEh1TEqbUxoQ0s3:22 a=EUspDBNiAAAA:8 a=Z-vQv52y2RNpiqyDFZQA:9 a=dawVfQjAaf238kedN5IG:22 X-Proofpoint-ORIG-GUID: AeH8dsJxIMqhCvLhzMSX_FVCAMmZPxwk X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfX5m7jyfByMSCc 2yXfEdPiw+DpEiGUeu83Vw7BbGUCM4pP4kQZ2RYqjjiZNYHU2s4kEujDfEKuBS53tF6qXnAPIy7 mRTH0jTXcTtTFuotXJ+QqWLvmoHLp2Jic4yfmdrkZK0ZstCBxrXPsgWblLa0XWGPqJ++/Br4/u2 Kv1iVEhVZGMfyv+o3EVGuUcBqBenKpxzhkfNq7STaMt9E2IKg1nMh4TnriurRAjsJrOkEe4/nce HiDjKb9XZTk2Ejhq/I5WE1aFLVUynw1ql4HYVZoNkuE9LafHEN5258XhdvN6lmNVaS04Bn4HAQK Da3OBTU1Ic1VO5/ChMx/ihRRz2VXglg/lZOdvMSFpIFCM9An9iYEXM0smH8KZ/zFSsy3Q2jslxP bKq1+JNbyNiE4qyBCZg8iL5uisYH+Kq7t65UUr1qkQqRcSAeT0indAQSWAsesb7bF4jwYq5Cl9e CqaBLnkY5hW5Cqel+dA== X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfX/E57ePJ11ILH WsraElOwBIM/7r+4ac1wwKqwlf+7kST21bwPL35X0HPR6SB5umCG3MyPxHQIDZgkJM0KpHk5hFC aADPvjLgUIM0anJx6MRFtrzdZYUxbAA= X-Proofpoint-GUID: AeH8dsJxIMqhCvLhzMSX_FVCAMmZPxwk X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1143, Hydra:6.1.134, FMLib:17.12.100.49 definitions=2026-07-21_03,2026-07-20_03,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 spamscore=0 lowpriorityscore=0 malwarescore=0 adultscore=0 priorityscore=1501 phishscore=0 bulkscore=0 suspectscore=0 clxscore=1015 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607210158 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 camss_util.[h|cpp] files with some small helper functions shared between Camss* classes. Signed-off-by: Hans de Goede --- src/libcamera/pipeline/camss/camss_util.cpp | 39 +++++++++++++++++++++ src/libcamera/pipeline/camss/camss_util.h | 19 ++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/libcamera/pipeline/camss/camss_util.cpp create mode 100644 src/libcamera/pipeline/camss/camss_util.h diff --git a/src/libcamera/pipeline/camss/camss_util.cpp b/src/libcamera/pipeline/camss/camss_util.cpp new file mode 100644 index 000000000..96b1bbc72 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_util.cpp @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS utility functions + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include "camss_util.h" + +#include + +#include + +#include "libcamera/internal/v4l2_videodevice.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(Camss) + +bool camssV4L2DeviceFormatMatchesStreamConfig(const V4L2DeviceFormat &fmt, + const StreamConfiguration &cfg, + const char *msgPrefix) +{ + if (cfg.pixelFormat != fmt.fourcc.toPixelFormat(false) || cfg.size != fmt.size || + cfg.stride != fmt.planes[0].bpl) { + LOG(Camss, Error) + << msgPrefix + << " StreamConfiguration vs V4L2DeviceFormat mismatch" + << " pixelFormat " << cfg.pixelFormat << ", " << fmt.fourcc.toPixelFormat(false) + << " size " << cfg.size << ", " << fmt.size + << " stride " << cfg.stride << ", " << fmt.planes[0].bpl + << " frameSize " << cfg.frameSize << ", " << fmt.planes[0].size; + return false; + } + + return true; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/camss_util.h b/src/libcamera/pipeline/camss/camss_util.h new file mode 100644 index 000000000..111c305aa --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_util.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS utility functions + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#pragma once + +namespace libcamera { + +struct StreamConfiguration; +class V4L2DeviceFormat; + +bool camssV4L2DeviceFormatMatchesStreamConfig(const V4L2DeviceFormat &fmt, + const StreamConfiguration &cfg, + const char *msgPrefix); + +} /* namespace libcamera */ From patchwork Tue Jul 21 14:59:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 27432 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 09CD9C3264 for ; Tue, 21 Jul 2026 15:00:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9907967E89; Tue, 21 Jul 2026 17:00:22 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="b0pw65Ea"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="VXM3DPcO"; dkim-atps=neutral Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id AEFAD67E6C for ; Tue, 21 Jul 2026 17:00:21 +0200 (CEST) Received: from pps.filterd (m0279871.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66LDg7vJ1958776 for ; Tue, 21 Jul 2026 15:00:20 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=tYoNYY85wM7 XfMX6FMGkdykz6deHf6iayBtKtnZG0hg=; b=b0pw65Ean9Oh0mIAeGwr2eG4XBh /N9WHURJvrgGl5qhyfjw018mXqwioIyzfbtNEvXWxVKFj02kZjyRQ4KAXgiaudgY t7BcQNBUu479I7FRHDXvrGKCqVfgbRYFPP9wUgfCxdiX6+zLYLcmAN6WMrmACU0Y giFjCvyiA5vtR85kc6P5aPkfaLeIoOr+n+azOrnmjVAsfkXvjNUb/eJq3q1nDvC+ Yoh3bI4Mge7jtmZyJpyOVaglGSN7s75cKjtpb5eXMN1uexxPM5wa6X0eH0/CwjWB FO2x2uFEG+bJ5WPf4+bQCuelsOcOGpimhc4vaUVCrSUlPfigFomL77yOAdA== Received: from mail-qt1-f200.google.com (mail-qt1-f200.google.com [209.85.160.200]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fj1bvu2sf-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Tue, 21 Jul 2026 15:00:20 +0000 (GMT) Received: by mail-qt1-f200.google.com with SMTP id d75a77b69052e-51c20762d21so111091111cf.0 for ; Tue, 21 Jul 2026 08:00:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1784646020; x=1785250820; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=tYoNYY85wM7XfMX6FMGkdykz6deHf6iayBtKtnZG0hg=; b=VXM3DPcOVubXcDgWN0RLNiSyx+DmHywwKBnFNwIuKRk2VZ6dC0XU/0dywX3+APIaz+ 3JhajMCb0Fg+CbdXyh9sQzs7eMOrmMzyA/e7BmgVd2U6XSv7SOZwVKIuX/1y1AuVnc4O RCC5UTgWcXb/vUbrbs9Z5WJrvM4iXEStAMlpOKmeoYbRBhrAWcqUrIPWEFDO6OfA6Hj+ ZgFNj7O80dl8hz48Wkns7ztRYl5sfIm3q8N0alRmayJRFU2mXC0EifU/ePiwfboX2jvT JCqwrmFx+CnMmVIs5rdo4UR/RdllgIQXlj0RaONjneiBniUGNIGezBG0C5xdTIS2mfx7 s7MQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784646020; x=1785250820; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=tYoNYY85wM7XfMX6FMGkdykz6deHf6iayBtKtnZG0hg=; b=Txvu1oZIwwtadcF154MGyYVNaNg8DHU26MxKk2ec9tlaLh4wUaBetQ4lRsr9tWES8W zR4SB+O2dk2oKR/qRoGynvNjnZX1TxsXDlgaahaGaK2TUq1AvaqSCa/nGpqzwBFEGHyx WI9LzoFTFqCTjlhjITt0Sqt6c+zjNrpulQNdwUoRpNlLsu5nlLMAqZerI/IeRimHrChm yQxymz1kS9Z3oOMFZUTdBbC27zYa+yv1rzrQ/EO/4i9/aQsNO/Wu9aqtfBB5u5UfgLY5 DQqUGDdsUjrDIa0gWkVhPR5p1uhcqsOvwKOft/oosjACzSC0pdEtQC/MO68nnMlm6caP 5Tmg== X-Gm-Message-State: AOJu0Yw97ecas012sVPWoI+dbCbnxNKPfnLch4TrOK9WvpMZRfBTavxg O/bj+RWSEriz8CZJV0t4H3rTmFClSRPy2ItGiJLSl4ZDiezAhrjn3zAGNDTQyd11lRByHT36wfs NosgY2r+w9OgGQYHYV7TAvLp6b7etseZeocYS6P6jGSQpz7ZKApqg34rpcHtbpq1CUsXgC2RF5R lUJ8gjU2p5 X-Gm-Gg: AfdE7clFSiohNnmZ39GiLtt6wc92703I47FkIuOBws3ZDPufZaHeIqoSXvotPk3L2fi QWNYTUzgMTh7R1uEZoBBF7omGqwbCPzcWywwYroC1pvW+U336bvCn/4yGhIxcoCuwspH1UMDX8V /OlDnw2v1l0Tf4V+hOcTHLAlSp9zYuY8rQ9MKaX7sJZcRd49DJjKF0FVSJeoyp89Rf/Qt/0huvk LqsIdnjaSmie6v9x+8qh29i5MdYtG3sb7UfBGLKhZGj7Xcdrh1D4x2pQnAWAUrqOkRCDoW8ds3w Lr5dL9dNZQYx58b/nXYo8mnND55F3Ybr7lV2UajplgGpBOy+aznlstg1WwVi3ORcM2r/B2X2/0R 6oqCz0mCpAIy0iSMKyZVpNnPDxPICTF1mj4dvZZ6WJXupCq+HIZgZOTalW6DtNejrqgsajdTCiC t6KmNDC8A+Zpk93w0= X-Received: by 2002:ac8:5dc7:0:b0:517:8f2e:5d97 with SMTP id d75a77b69052e-5213c7e6cafmr174471531cf.22.1784646008149; Tue, 21 Jul 2026 08:00:08 -0700 (PDT) X-Received: by 2002:ac8:5dc7:0:b0:517:8f2e:5d97 with SMTP id d75a77b69052e-5213c7e6cafmr174468611cf.22.1784646005579; Tue, 21 Jul 2026 08:00:05 -0700 (PDT) Received: from shalem (2001-1c00-0c32-7800-5bfa-a036-83f0-f9ec.cable.dynamic.v6.ziggo.nl. [2001:1c00:c32:7800:5bfa:a036:83f0:f9ec]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c1712a5f837sm615968066b.32.2026.07.21.08.00.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Jul 2026 08:00:04 -0700 (PDT) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Loic Poulain , Hans de Goede Subject: [PATCH v2 4/7] camss: Add camss pipeline handler Date: Tue, 21 Jul 2026 16:59:54 +0200 Message-ID: <20260721145958.68489-5-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> References: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: iixTOtrdxFIrQASx4dDH5pDnvZ5rj65v X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfX2vcgLHOrRtrr ouApr46sDxI+7khPDrbHpNDmVEIIGyXcvye5ZSbbhuOEjcuqzkCnmybevJgqijjP2eaVOH/L7Ln +Rc+noaXoZc91HqQ/vOnG26BgjXxoJA= X-Proofpoint-GUID: iixTOtrdxFIrQASx4dDH5pDnvZ5rj65v X-Authority-Analysis: v=2.4 cv=Sb7HsPRu c=1 sm=1 tr=0 ts=6a5f8984 cx=c_pps a=JbAStetqSzwMeJznSMzCyw==:117 a=xqWC_Br6kY4A:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=3WHJM1ZQz_JShphwDgj5:22 a=EUspDBNiAAAA:8 a=ErEb1ksjjALba-Z9VpUA:9 a=HcG5QfLkgl6EuUUG:21 a=uxP6HrT_eTzRwkO_Te1X:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfX5qTMNIAGExnA 53KAf+RFepSvsM1kLl7W/mNFd+gLX33dXdh9G07cHoZgqG4cjSooLL4PBNxO4GSNZrUgWPBwS3J uaKWiAFbkOkiGiKnbRSYMvRQca7MPir8z4p1BSlD0Iz5N0YYqMj0ZbCdOpeUYCnZ5kMZR0QAIxl QWUhJwo4Yy5nHWoEfdl3h7wAQCSi8b10zxcNnSdhFO/M7AAALXnwWzfyfiQNrEHGJ35SqOGQvIA rJQmdQAHEwPdQECFPjsLh3BUhspDvTwgFNWVWZg9nqlkvH2o0Mork41dVaCgOd6huHySH8pXq20 D+q2N20jWGWGmuaD/BaN38i6uxL8s6B8+YPZvNOrx4Egbb7ygUI6dDYk6/FYt27pjq+AohZT/5A lLPgtHu0KnAhIPsQW0lgUKb/6oEKN+HLmnejtZGPAA20nGTBvcxYKy1f/sPnO/NJFc/aPT8dOIx mIG2Nwz+4qRNJEqs/Tw== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1143, Hydra:6.1.134, FMLib:17.12.100.49 definitions=2026-07-21_03,2026-07-20_03,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 malwarescore=0 spamscore=0 suspectscore=0 clxscore=1015 lowpriorityscore=0 impostorscore=0 adultscore=0 phishscore=0 priorityscore=1501 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607210158 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 a CAMSS pipeline handler. This initial version basically replaces the simple pipeline handler camss support and still depends on the software ISP. This uses a CamssIsp virtual base class which can be used to later implement hardware ISP support without requiring invasive changes to the initial pipeline handler introduced here. Support for the Offline Processing Engine HW ISP found in the Qualcomm Agetti SoC is introduced in a later patch in this series. Since the OPE is an offline ISP, the CAMSS pipeline handler is loosely based on the existing IPU3 pipeline handler as that also is for an offline ISP. The CamssFrameInfo class is an almost 1:1 copy of the IPU3 code and is a candidate for later being factored out into a generic helper class which could be shared between the IPU3, CAMSS and simple pipeline handlers. Signed-off-by: Hans de Goede --- meson.build | 1 + meson_options.txt | 1 + src/ipa/meson.build | 1 + src/libcamera/pipeline/camss/camss.cpp | 689 ++++++++++++++++++ src/libcamera/pipeline/camss/camss_csi.cpp | 570 +++++++++++++++ src/libcamera/pipeline/camss/camss_csi.h | 131 ++++ src/libcamera/pipeline/camss/camss_frames.cpp | 155 ++++ src/libcamera/pipeline/camss/camss_frames.h | 71 ++ src/libcamera/pipeline/camss/camss_isp.cpp | 26 + src/libcamera/pipeline/camss/camss_isp.h | 62 ++ .../pipeline/camss/camss_isp_soft.cpp | 226 ++++++ src/libcamera/pipeline/camss/camss_isp_soft.h | 56 ++ src/libcamera/pipeline/camss/meson.build | 10 + src/libcamera/pipeline/simple/simple.cpp | 1 - 14 files changed, 1999 insertions(+), 1 deletion(-) create mode 100644 src/libcamera/pipeline/camss/camss.cpp create mode 100644 src/libcamera/pipeline/camss/camss_csi.cpp create mode 100644 src/libcamera/pipeline/camss/camss_csi.h create mode 100644 src/libcamera/pipeline/camss/camss_frames.cpp create mode 100644 src/libcamera/pipeline/camss/camss_frames.h create mode 100644 src/libcamera/pipeline/camss/camss_isp.cpp create mode 100644 src/libcamera/pipeline/camss/camss_isp.h create mode 100644 src/libcamera/pipeline/camss/camss_isp_soft.cpp create mode 100644 src/libcamera/pipeline/camss/camss_isp_soft.h create mode 100644 src/libcamera/pipeline/camss/meson.build diff --git a/meson.build b/meson.build index d8e78ed6c..c9eb76d30 100644 --- a/meson.build +++ b/meson.build @@ -215,6 +215,7 @@ wanted_pipelines = get_option('pipelines') arch_arm = ['arm', 'aarch64'] arch_x86 = ['x86', 'x86_64'] pipelines_support = { + 'camss': arch_arm, 'imx8-isi': arch_arm, 'ipu3': arch_x86, 'mali-c55': arch_arm, diff --git a/meson_options.txt b/meson_options.txt index 20baacc4f..0d793a356 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -78,6 +78,7 @@ option('pipelines', choices : [ 'all', 'auto', + 'camss', 'imx8-isi', 'ipu3', 'mali-c55', diff --git a/src/ipa/meson.build b/src/ipa/meson.build index c583c7efd..a1f1a5200 100644 --- a/src/ipa/meson.build +++ b/src/ipa/meson.build @@ -25,6 +25,7 @@ subdir('libipa') ipa_sign = files('ipa-sign.sh') supported_ipas = { + 'camss': 'simple', 'ipu3': 'ipu3', 'mali-c55': 'mali-c55', 'rkisp1': 'rkisp1', diff --git a/src/libcamera/pipeline/camss/camss.cpp b/src/libcamera/pipeline/camss/camss.cpp new file mode 100644 index 000000000..371d4f8a2 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss.cpp @@ -0,0 +1,689 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Pipeline handler for Qualcomm CAMSS + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * Partially based on other pipeline-handlers which are: + * Copyright (C) 2020, Laurent Pinchart + * Copyright (C) 2019, Martijn Braam + * Copyright (C) 2019, Google Inc. + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "libcamera/internal/camera.h" +#include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/camera_sensor_properties.h" +#include "libcamera/internal/delayed_controls.h" +#include "libcamera/internal/device_enumerator.h" +#include "libcamera/internal/framebuffer.h" +#include "libcamera/internal/ipa_manager.h" +#include "libcamera/internal/media_device.h" +#include "libcamera/internal/pipeline_handler.h" +#include "libcamera/internal/request.h" + +#include "camss_csi.h" +#include "camss_frames.h" +#include "camss_isp.h" +#include "camss_isp_soft.h" + +namespace libcamera { + +LOG_DEFINE_CATEGORY(Camss) + +class CamssCameraData : public Camera::Private +{ +public: + CamssCameraData(PipelineHandler *pipe); + + void csiBufferReady(FrameBuffer *buffer); + void ispOutputBufferReady(FrameBuffer *buffer); + void ispParameterBufferReady(FrameBuffer *buffer); + void frameStart(uint32_t sequence); + void statsReady(uint32_t frame, uint32_t statsBufferId); + void metadataReady(unsigned int id, const ControlList &metadata); + void setSensorControls(const ControlList &sensorControls); + + void queuePendingRequests(); + void cancelPendingRequests(); + + std::unique_ptr csi_; + std::unique_ptr isp_; + std::unique_ptr delayedCtrls_; + CamssFrames frameInfos_; + + /* Requests for which no buffer has been queued to the CSI receiver yet. */ + std::queue pendingRequests_; +}; + +class CamssCameraConfiguration : public CameraConfiguration +{ +public: + static constexpr unsigned int kBufferCount = 4; + static constexpr unsigned int kMaxStreams = 2; + + CamssCameraConfiguration(CamssCameraData *data); + Status validate() override; + + /* Cache the combinedTransform_ that will be applied to the sensor */ + Transform combinedTransform_; + StreamConfiguration csiConfig_; + StreamConfiguration ispConfig_; + +private: + /* + * The CamssCameraData instance is guaranteed to be valid as long as the + * corresponding Camera instance is valid. In order to borrow a + * reference to the camera data, store a new reference to the camera. + */ + const CamssCameraData *data_; +}; + +class PipelineHandlerCamss : public PipelineHandler +{ +public: + PipelineHandlerCamss(CameraManager *manager); + + std::unique_ptr generateConfiguration(Camera *camera, + Span roles) override; + int configure(Camera *camera, CameraConfiguration *config) override; + + int exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) override; + + int start(Camera *camera, const ControlList *controls) override; + void stopDevice(Camera *camera) override; + + int queueRequestDevice(Camera *camera, Request *request) override; + + bool match(DeviceEnumerator *enumerator) override; + +private: + CamssCameraData *cameraData(Camera *camera) + { + return static_cast(camera->_d()); + } + + int allocateBuffers(Camera *camera); + void freeBuffers(Camera *camera); + + CamssCsi csi_; +}; + +CamssCameraConfiguration::CamssCameraConfiguration(CamssCameraData *data) + : CameraConfiguration() +{ + data_ = data; +} + +CameraConfiguration::Status CamssCameraConfiguration::validate() +{ + Status status = Valid; + + if (config_.empty()) + return Invalid; + + /* + * Validate the requested transform against the sensor capabilities and + * rotation and store the final combined transform that configure() will + * need to apply to the sensor to save us working it out again. + */ + Orientation requestedOrientation = orientation; + combinedTransform_ = data_->csi_->sensor()->computeTransform(&orientation); + if (orientation != requestedOrientation) + status = Adjusted; + + /* Max. 1 RAW + 1 processed stream is supported (for now). */ + StreamConfiguration rawConfig; + StreamConfiguration processedConfig; + unsigned int rawCount = 0; + unsigned int processedCount = 0; + + for (const StreamConfiguration &cfg : config_) { + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); + + if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW) { + if (rawCount) { + LOG(Camss, Debug) << "Multiple raw streams not supported"; + return Invalid; + } + rawConfig = cfg; + rawCount++; + } else { + if (processedCount) { + LOG(Camss, Debug) << "Multiple processed streams not supported"; + return Invalid; + } + processedConfig = cfg; + processedCount++; + } + } + + if (!processedCount) { + /* + * \todo allow this, add dummyISP ISP class which only + * calls CPU stats on ready raw output buffers + runs the result + * through the softIPA to get sensor-control + metadata-info + */ + LOG(Camss, Debug) + << "Camera configuration cannot support raw-only streams"; + return Invalid; + } + + if (!rawCount) { + rawConfig.size = processedConfig.size; + rawConfig.bufferCount = processedConfig.bufferCount; + } + + csiConfig_ = data_->csi_->validate(rawConfig); + if (!csiConfig_.pixelFormat.isValid()) + return Invalid; + + LOG(Camss, Debug) << "CSI configuration: " << csiConfig_.toString() + << " stride " << csiConfig_.stride + << " frameSize " << csiConfig_.frameSize; + + ispConfig_ = data_->isp_->validate(csiConfig_, processedConfig); + if (!ispConfig_.pixelFormat.isValid()) + return Invalid; + + LOG(Camss, Debug) << "ISP configuration: " << ispConfig_.toString() + << " stride " << ispConfig_.stride + << " frameSize " << ispConfig_.frameSize; + + for (unsigned int i = 0; i < config_.size(); ++i) { + const PixelFormatInfo &info = PixelFormatInfo::info(config_[i].pixelFormat); + const StreamConfiguration *hwCfg, originalCfg = config_[i]; + Stream *stream; + + LOG(Camss, Debug) << "Validating stream: " << config_[i].toString(); + + if (info.colourEncoding == PixelFormatInfo::ColourEncodingRAW) { + hwCfg = &csiConfig_; + stream = &data_->csi_->rawStream_; + } else { + hwCfg = &ispConfig_; + stream = &data_->isp_->outStream_; + } + + StreamConfiguration &cfg = config_[i]; + cfg.size = hwCfg->size; + cfg.pixelFormat = hwCfg->pixelFormat; + cfg.stride = hwCfg->stride; + cfg.frameSize = hwCfg->frameSize; + cfg.bufferCount = hwCfg->bufferCount; + cfg.setStream(stream); + + if (cfg.pixelFormat != originalCfg.pixelFormat || + cfg.size != originalCfg.size) { + LOG(Camss, Debug) + << "Stream " << i << " configuration adjusted to " + << cfg.toString(); + status = Adjusted; + } + + if (originalCfg.bufferCount && cfg.bufferCount != originalCfg.bufferCount) { + LOG(Camss, Debug) + << "Adjusting bufferCount from " << originalCfg.bufferCount + << " to " << cfg.bufferCount; + status = Adjusted; + } + + /* + * \todo copy-pasted from src/libcamera/pipeline/simple/simple.cpp turn + * this into a generic helper? + * Best effort to fix the color space. If the color space is not set, + * set it according to the pixel format, which may not be correct (pixel + * formats and color spaces are different things, although somewhat + * related) but we don't have a better option at the moment. Then in any + * case, perform the standard pixel format based color space adjustment. + */ + if (!cfg.colorSpace) { + const PixelFormatInfo &pfi = PixelFormatInfo::info(cfg.pixelFormat); + switch (pfi.colourEncoding) { + case PixelFormatInfo::ColourEncodingRGB: + cfg.colorSpace = ColorSpace::Srgb; + break; + case PixelFormatInfo::ColourEncodingYUV: + cfg.colorSpace = ColorSpace::Sycc; + break; + default: + cfg.colorSpace = ColorSpace::Raw; + } + /* + * Adjust the assigned color space to make sure everything is OK. + * Since this is assigning an unspecified color space rather than + * adjusting a requested one, changes here shouldn't set the status + * to Adjusted. + */ + cfg.colorSpace->adjust(cfg.pixelFormat); + LOG(Camss, Debug) + << "Unspecified color space set to " + << cfg.colorSpace.value().toString(); + } else { + if (cfg.colorSpace->adjust(cfg.pixelFormat)) { + LOG(Camss, Debug) + << "Color space adjusted to " + << cfg.colorSpace.value().toString(); + status = Adjusted; + } + } + } + + return status; +} + +PipelineHandlerCamss::PipelineHandlerCamss(CameraManager *manager) + : PipelineHandler(manager), csi_() +{ +} + +std::unique_ptr +PipelineHandlerCamss::generateConfiguration(Camera *camera, Span roles) +{ + CamssCameraData *data = cameraData(camera); + std::unique_ptr config = + std::make_unique(data); + StreamConfiguration cfg, csiConfig; + + if (roles.empty()) + return config; + + csiConfig = data->csi_->generateConfiguration(); + if (!csiConfig.pixelFormat.isValid()) + return nullptr; + + LOG(Camss, Debug) << "Generated CSI cfg " << csiConfig; + + bool processedRequested = false; + bool rawRequested = false; + for (const auto &role : roles) { + if (role == StreamRole::Raw) + rawRequested = true; + else + processedRequested = true; + } + + if (rawRequested) + config->addConfiguration(csiConfig); + + if (processedRequested) { + cfg = data->isp_->generateConfiguration(csiConfig); + if (!cfg.pixelFormat.isValid()) + return nullptr; + + LOG(Camss, Debug) << "Generated ISP cfg " << cfg; + config->addConfiguration(cfg); + } + + if (config->validate() == CameraConfiguration::Invalid) + return nullptr; + + return config; +} + +int PipelineHandlerCamss::configure(Camera *camera, CameraConfiguration *c) +{ + CamssCameraConfiguration *config = + static_cast(c); + CamssCameraData *data = cameraData(camera); + int ret; + + ret = data->csi_->configure(config->csiConfig_, config->combinedTransform_); + if (ret) + return ret; + + return data->isp_->configure(config->csiConfig_, config->ispConfig_); +} + +int PipelineHandlerCamss::exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) +{ + CamssCameraData *data = cameraData(camera); + unsigned int count = stream->configuration().bufferCount; + + if (stream == &data->csi_->rawStream_) + return data->csi_->exportBuffers(count, buffers); + else if (stream == &data->isp_->outStream_) + return data->isp_->exportOutputBuffers(stream, count, buffers); + + return -EINVAL; +} + +int PipelineHandlerCamss::allocateBuffers(Camera *camera) +{ + CamssCameraData *data = cameraData(camera); + unsigned int bufferCount; + + bufferCount = std::max({ + data->csi_->rawStream_.configuration().bufferCount, + data->isp_->outStream_.configuration().bufferCount, + }); + + return data->isp_->allocateBuffers(bufferCount); +} + +void PipelineHandlerCamss::freeBuffers(Camera *camera) +{ + cameraData(camera)->isp_->freeBuffers(); +} + +int PipelineHandlerCamss::start(Camera *camera, [[maybe_unused]] const ControlList *controls) +{ + CamssCameraData *data = cameraData(camera); + int ret; + + /* Allocate buffers for internal pipeline usage. */ + ret = allocateBuffers(camera); + if (ret) + return ret; + + data->delayedCtrls_->reset(); + + ret = data->csi_->start(); + if (ret) + goto freebuffers; + + ret = data->isp_->start(); + if (ret) + goto stop; + + return 0; + +stop: + data->csi_->stop(); +freebuffers: + freeBuffers(camera); + + LOG(Camss, Error) << "Failed to start camera " << camera->id(); + return ret; +} + +void PipelineHandlerCamss::stopDevice(Camera *camera) +{ + CamssCameraData *data = cameraData(camera); + + data->cancelPendingRequests(); + + data->isp_->stop(); + data->csi_->stop(); + + freeBuffers(camera); +} + +CamssCameraData::CamssCameraData(PipelineHandler *pipe) + : Camera::Private(pipe) +{ + frameInfos_.bufferAvailable.connect(this, &CamssCameraData::queuePendingRequests); +} + +void CamssCameraData::cancelPendingRequests() +{ + while (!pendingRequests_.empty()) { + Request *request = pendingRequests_.front(); + + for (const auto &[stream, buffer] : request->buffers()) { + buffer->_d()->cancel(); + pipe()->completeBuffer(request, buffer); + } + + pipe()->completeRequest(request); + pendingRequests_.pop(); + } +} + +void CamssCameraData::queuePendingRequests() +{ + while (!pendingRequests_.empty()) { + Request *request = pendingRequests_.front(); + + CamssFrames::Info *info = frameInfos_.create(request); + if (!info) + break; + + /* + * Queue a buffer on the CSI, using the raw stream buffer + * provided in the request, if any, or a CIO2 internal buffer + * otherwise. + */ + FrameBuffer *reqRawBuffer = request->findBuffer(&csi_->rawStream_); + FrameBuffer *rawBuffer = csi_->queueBuffer(request, reqRawBuffer); + /* + * \todo If queueBuffer fails in queuing a buffer to the device, + * report the request as error by cancelling the request and + * calling PipelineHandler::completeRequest(). + */ + if (!rawBuffer) { + frameInfos_.remove(info); + break; + } + + info->rawBuffer = rawBuffer; + + pendingRequests_.pop(); + } +} + +int PipelineHandlerCamss::queueRequestDevice(Camera *camera, Request *request) +{ + CamssCameraData *data = cameraData(camera); + + data->pendingRequests_.push(request); + data->queuePendingRequests(); + + return 0; +} + +bool PipelineHandlerCamss::match(DeviceEnumerator *enumerator) +{ + CamssCsi::Cameras csiCams; + + csiCams = csi_.match(this, enumerator); + if (csiCams.empty()) + return false; + + unsigned int numCameras = 0; + for (unsigned int i = 0; i < csiCams.size(); i++) { + std::unique_ptr data = + std::make_unique(this); + data->csi_ = std::move(csiCams[i]); + + data->csi_->frameStart().connect(data.get(), + &CamssCameraData::frameStart); + data->csi_->bufferReady().connect(data.get(), + &CamssCameraData::csiBufferReady); + data->csi_->bufferAvailable.connect(data.get(), + &CamssCameraData::queuePendingRequests); + + CameraSensor *sensor = data->csi_->sensor(); + + /* Initialize the camera properties. */ + data->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 } }, + }; + + data->delayedCtrls_ = + std::make_unique(sensor->device(), params); + + data->isp_ = std::make_unique(this, sensor, + &data->frameInfos_, + &data->controlInfo_); + if (!data->isp_->isValid()) { + LOG(Camss, Error) << "Failed to create software ISP"; + continue; + } + + data->isp_->inputBufferReady.connect(data->csi_.get(), + &CamssCsiCamera::tryReturnBuffer); + data->isp_->outputBufferReady.connect(data.get(), + &CamssCameraData::ispOutputBufferReady); + data->isp_->paramBufferReady.connect(data.get(), + &CamssCameraData::ispParameterBufferReady); + data->isp_->statsReady.connect(data.get(), &CamssCameraData::statsReady); + data->isp_->metadataReady.connect(data.get(), &CamssCameraData::metadataReady); + data->isp_->setSensorControls.connect(data.get(), &CamssCameraData::setSensorControls); + + data->csi_->setIsp(data->isp_.get()); + + /* Create and register the Camera instance. */ + std::set streams = { + &data->isp_->outStream_, + &data->csi_->rawStream_, + }; + std::shared_ptr camera = + Camera::create(std::move(data), sensor->id(), streams); + + registerCamera(std::move(camera)); + numCameras++; + } + + return numCameras != 0; +} + +void CamssCameraData::setSensorControls(const ControlList &sensorControls) +{ + delayedCtrls_->push(sensorControls); + + /* + * Directly apply controls now if there is no frameStart signal. + * + * \todo Applying controls directly not only increases the risk of + * applying them to the wrong frame (or across a frame boundary), + * but it also bypasses delayedCtrls_, creating AGC regulation issues. + * Both problems should be fixed. + */ + if (!csi_->supportsFrameStart()) { + ControlList ctrls(sensorControls); + csi_->sensor()->setControls(&ctrls); + } +} + +void CamssCameraData::metadataReady(unsigned int id, const ControlList &metadata) +{ + CamssFrames::Info *info = frameInfos_.find(id); + if (!info) + return; + + /* tryComplete(info) will free info on success! */ + Request *request = info->request; + request->_d()->metadata().merge(metadata); + + info->metadataProcessed = true; + if (frameInfos_.tryComplete(info)) + pipe()->completeRequest(request); +} + +/* ----------------------------------------------------------------------------- + * Buffer Ready slots + */ + +/** + * \brief Handle buffers completion at the ISP output + * \param[in] buffer The completed buffer + * + * Buffers completed from the ISP output are directed to the application. + */ +void CamssCameraData::ispOutputBufferReady(FrameBuffer *buffer) +{ + CamssFrames::Info *info = frameInfos_.find(buffer); + if (!info) + return; + + /* tryComplete(info) will free info on success! */ + Request *request = info->request; + + pipe()->completeBuffer(request, buffer); + if (frameInfos_.tryComplete(info)) + pipe()->completeRequest(request); +} + +void CamssCameraData::ispParameterBufferReady(FrameBuffer *buffer) +{ + CamssFrames::Info *info = frameInfos_.find(buffer); + if (!info) + return; + + /* tryComplete(info) will free info on success! */ + Request *request = info->request; + + /* Parameter buffer is internal, no pipe()->completeBuffer() */ + if (frameInfos_.tryComplete(info)) + pipe()->completeRequest(request); +} + +/** + * \brief Handle buffers completion at the CSI-receiver output + * \param[in] buffer The completed buffer + * + * Buffers completed from the CSI-receiver are immediately queued to the ISP + * for further processing. + */ +void CamssCameraData::csiBufferReady(FrameBuffer *buffer) +{ + CamssFrames::Info *info = frameInfos_.find(buffer); + if (!info) + return; + + Request *request = info->request; + + /* On error / cancellation force a complete of the whole request. */ + if (buffer->metadata().status != FrameMetadata::FrameSuccess) { + for (const auto &[stream, b] : request->buffers()) { + b->_d()->cancel(); + pipe()->completeBuffer(request, b); + } + + frameInfos_.remove(info); + pipe()->cancelRequest(request); + return; + } + + /* + * Record the sensor's timestamp in the request metadata. + * + * \todo The sensor timestamp should be better estimated by connecting + * to the V4L2Device::frameStart signal. + */ + request->_d()->metadata().set(controls::SensorTimestamp, + buffer->metadata().timestamp); + + if (request->findBuffer(&csi_->rawStream_)) + pipe()->completeBuffer(request, buffer); + + isp_->queueBuffers(request, buffer); +} + +/* + * \brief Handle the start of frame exposure signal + * \param[in] sequence The sequence number of frame + */ +void CamssCameraData::frameStart(uint32_t sequence) +{ + delayedCtrls_->applyControls(sequence); +} + +void CamssCameraData::statsReady(uint32_t frame, uint32_t statsBufferId) +{ + isp_->processStats(frame, statsBufferId, delayedCtrls_->get(frame)); +} + +REGISTER_PIPELINE_HANDLER(PipelineHandlerCamss, "camss") + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/camss_csi.cpp b/src/libcamera/pipeline/camss/camss_csi.cpp new file mode 100644 index 000000000..481aaa203 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_csi.cpp @@ -0,0 +1,570 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS CSI phy/decoder and VFE handling + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * Partially based on other pipeline-handlers which are: + * Copyright (C) 2020, Laurent Pinchart + * Copyright (C) 2019, Martijn Braam + * Copyright (C) 2019, Google Inc. + */ + +#include "camss_csi.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/v4l2_subdevice.h" + +#include "camss_isp.h" +#include "camss_util.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(Camss) + +CamssCsiCamera::CamssCsiCamera() +{ +} + +/** + * \brief Get output V4L2PixelFormat for media bus code + * + * Get output video node V4L2PixelFormat for the given media bus code. + * \param[in] code The media bus code + * + * \return V4L2PixelFormat + */ +V4L2PixelFormat CamssCsiCamera::mbusCodeToV4L2PixelFormat(unsigned int code) const +{ + V4L2VideoDevice::Formats formats = output_->formats(code); + + if (formats.empty()) { + LOG(Camss, Error) + << "No formats for media bus code " << code; + return V4L2PixelFormat(); + } + + /* + * camss supports only 1 V4L2 output format per media bus code and not + * multiple (e.g. not mipi-packed + sparse for raw-bayer). + */ + return formats.begin()->first; +} + +/** + * \brief Get output PixelFormat for media bus code + * + * Get output video node PixelFormat for the given media bus code. + * \param[in] code The media bus code + * + * \return PixelFormat + */ +PixelFormat CamssCsiCamera::mbusCodeToPixelFormat(unsigned int code) const +{ + V4L2PixelFormat v4l2Format = mbusCodeToV4L2PixelFormat(code); + if (!v4l2Format.isValid()) + return PixelFormat(); + + return v4l2Format.toPixelFormat(); +} + +/** + * \brief Get media bus code for desired output PixelFormat + * + * Get the media bus code for a desired output video node PixelFormat. + * \param[in] format The PixelFormat + * + * \return Media bus code or 0 if no matching code is found + */ +unsigned int CamssCsiCamera::PixelFormatToMbusCode(const PixelFormat &format) const +{ + for (unsigned int code : sensor_->mbusCodes()) { + PixelFormat pixelFormat = mbusCodeToPixelFormat(code); + if (pixelFormat == format) + return code; + } + + return 0; +} + +/** + * \brief Retrieve the best sensor format for a desired output size and format + * \param[in] size The desired size + * \param[in] format The desired PixelFormat + * \param[in] useIspMargins Take ISP debayering margin into account? + * + * \a size indicates the desired size at the output of the sensor. This method + * selects the best media bus code and size supported by the sensor according + * to the following criteria. + * + * - The desired \a size shall fit in the sensor output size to avoid the need + * to up-scale. + * - The aspect ratio of sensor output size shall be as close as possible to + * the sensor's native resolution field of view. + * - The sensor output size shall be as small as possible to lower the required + * bandwidth. + * + * When \a format is empty and multiple media bus codes can produce the same + * size, the media bus code with the highest bits-per-pixel is selected. + * + * The returned sensor output format is guaranteed to be acceptable by the + * setFormat() method without any modification. + * + * \return The best sensor output format matching the desired size and format + * on success, or an empty format otherwise. + */ +V4L2SubdeviceFormat CamssCsiCamera::getSensorFormat(Size size, + const PixelFormat &format, + bool useIspMargins) const +{ + unsigned int desiredArea = size.width * size.height; + unsigned int bestArea = std::numeric_limits::max(); + const Size &resolution = sensor_->resolution(); + std::vector mbusCodes; + float desiredRatio = static_cast(resolution.width) / + resolution.height; + float bestRatio = std::numeric_limits::max(); + unsigned int desiredCode = 0; + uint32_t bestCode = 0; + uint8_t bestDepth = 0; + Size bestSize; + + /* If no desired size use the sensor resolution. */ + if (size.isNull()) + size = resolution; + + if (format.isValid()) + desiredCode = PixelFormatToMbusCode(format); + + if (desiredCode) + mbusCodes.push_back(desiredCode); + else + mbusCodes = sensor_->mbusCodes(); + + for (unsigned int code : mbusCodes) { + PixelFormat pixelFormat = mbusCodeToPixelFormat(code); + BayerFormat bayerFormat = BayerFormat::fromPixelFormat(pixelFormat); + Size margin; + + /* Only Bayer formats are supported for now */ + if (!bayerFormat.isValid()) + continue; + + const auto sizes = sensor_->sizes(code); + if (!sizes.size()) + continue; + + if (useIspMargins) + margin = isp_->getMargins(pixelFormat); + + for (const Size &sz : sizes) { + if (sz.width < (size.width + margin.width) || + sz.height < (size.height + margin.height)) + continue; + + float ratio = static_cast(sz.width) / sz.height; + /* + * Ratios can differ by small mantissa difference which + * can affect the selection of the sensor output size + * wildly. We are interested in selection of the closest + * size with respect to the desired output size, hence + * comparing it with a single precision digit is enough. + */ + ratio = static_cast(ratio * 10) / 10.0; + float ratioDiff = std::abs(ratio - desiredRatio); + unsigned int area = (sz.width - margin.width) * + (sz.height - margin.height); + unsigned int areaDiff = area - desiredArea; + + if (ratioDiff > bestRatio) + continue; + + if ((ratioDiff < bestRatio || areaDiff < bestArea) || + (ratioDiff == bestRatio && areaDiff == bestArea && + bayerFormat.bitDepth > bestDepth)) { + bestRatio = ratioDiff; + bestArea = areaDiff; + bestSize = sz; + bestCode = code; + bestDepth = bayerFormat.bitDepth; + } + } + } + + if (bestSize.isNull()) { + LOG(Camss, Warning) << "No supported format or size found"; + return {}; + } + + V4L2SubdeviceFormat sensorFormat{}; + sensorFormat.code = bestCode; + sensorFormat.size = bestSize; + + return sensorFormat; +} + +int CamssCsiCamera::tryFormat(const V4L2SubdeviceFormat &sensorFormat, + StreamConfiguration &cfg) const +{ + /* Try format to get stride and framesize */ + V4L2DeviceFormat format; + format.fourcc = mbusCodeToV4L2PixelFormat(sensorFormat.code); + format.size = sensorFormat.size; + format.planesCount = 1; + + int ret = output_->tryFormat(&format); + if (ret < 0) + return ret; + + if (format.planesCount != 1) + return -EINVAL; + + cfg.size = format.size; + cfg.pixelFormat = format.fourcc.toPixelFormat(); + cfg.stride = format.planes[0].bpl; + cfg.frameSize = format.planes[0].size; + + return 0; +} + +StreamConfiguration CamssCsiCamera::generateConfiguration(void) const +{ + std::map> formats; + + for (unsigned int code : sensor_->mbusCodes()) { + PixelFormat pixelFormat = mbusCodeToPixelFormat(code); + if (!pixelFormat) + continue; + + std::vector sizes; + for (const Size &sz : sensor_->sizes(code)) + sizes.emplace_back(sz); + + formats[pixelFormat] = sizes; + } + + StreamConfiguration cfg{ StreamFormats{ formats } }; + + V4L2SubdeviceFormat sensorFormat = getSensorFormat(); + + if (tryFormat(sensorFormat, cfg)) + return {}; + + cfg.bufferCount = kBufferCount; + + return cfg; +} + +StreamConfiguration CamssCsiCamera::validate(const StreamConfiguration &req) const +{ + StreamConfiguration cfg; + + /* Query the sensor static information for closest match. */ + V4L2SubdeviceFormat sensorFormat = getSensorFormat(req.size, req.pixelFormat, true); + + if (tryFormat(sensorFormat, cfg)) + return {}; + + cfg.bufferCount = std::max(kBufferCount, req.bufferCount); + + return cfg; +} + +/** + * \brief Configure the CamssCsi unit + * \param[in] cfg Requested CamssCsi stream config from an earlier validate() call + * \param[in] transform The transformation to be applied on the image sensor + * \return 0 on success or a negative error code otherwise + */ +int CamssCsiCamera::configure(const StreamConfiguration &cfg, const Transform &transform) +{ + V4L2SubdeviceFormat sensorFormat; + V4L2DeviceFormat outputFormat; + int ret; + + sensorFormat = getSensorFormat(cfg.size, cfg.pixelFormat, false); + /* This updates sensorFormat with the actual established format */ + ret = sensor_->setFormat(&sensorFormat, transform); + if (ret) + return ret; + + for (auto &link : links_) { + if (!(link.link->flags() & MEDIA_LNK_FL_ENABLED)) { + ret = link.link->setEnabled(true); + if (ret) + return ret; + } + + MediaPad *sink = link.link->sink(); + ret = link.sinkSubdev->setFormat(sink->index(), &sensorFormat, + V4L2Subdevice::ActiveFormat); + if (ret) + return ret; + } + + outputFormat.fourcc = mbusCodeToV4L2PixelFormat(sensorFormat.code); + outputFormat.size = sensorFormat.size; + outputFormat.planesCount = 1; + + /* This updates outputFormat with the actual established format */ + ret = output_->setFormat(&outputFormat); + if (ret) + return ret; + + if (!camssV4L2DeviceFormatMatchesStreamConfig(outputFormat, cfg, "CSI configure")) + return -EIO; /* Must match the tryFormat() from validate() */ + + bufferCount_ = cfg.bufferCount; + + return 0; +} + +int CamssCsiCamera::exportBuffers(unsigned int count, + std::vector> *buffers) +{ + return output_->exportBuffers(count, buffers); +} + +int CamssCsiCamera::start() +{ + int ret = output_->exportBuffers(bufferCount_, &buffers_); + if (ret < 0) + return ret; + + ret = output_->importBuffers(bufferCount_); + if (ret) + LOG(Camss, Error) << "Failed to import CamssCsi buffers"; + + for (std::unique_ptr &buffer : buffers_) + availableBuffers_.push(buffer.get()); + + ret = output_->streamOn(); + if (ret) { + freeBuffers(); + return ret; + } + + return 0; +} + +void CamssCsiCamera::stop() +{ + if (output_->streamOff()) + LOG(Camss, Error) << "CamssCsi stream off failed"; + + freeBuffers(); +} + +FrameBuffer *CamssCsiCamera::queueBuffer(Request *request, FrameBuffer *rawBuffer) +{ + FrameBuffer *buffer = rawBuffer; + + /* If no buffer is provided in the request, use an internal one. */ + if (!buffer) { + if (availableBuffers_.empty()) { + LOG(Camss, Debug) << "CamssCsi buffer underrun"; + return nullptr; + } + + buffer = availableBuffers_.front(); + availableBuffers_.pop(); + buffer->_d()->setRequest(request); + } + + int ret = output_->queueBuffer(buffer); + if (ret) + return nullptr; + + return buffer; +} + +void CamssCsiCamera::tryReturnBuffer(FrameBuffer *buffer) +{ + /* + * \todo Once more pipelines deal with buffers that may be allocated + * internally or externally this pattern might become a common need. At + * that point this check should be moved to something clever in + * FrameBuffer. + */ + for (const std::unique_ptr &buf : buffers_) { + if (buf.get() == buffer) { + availableBuffers_.push(buffer); + break; + } + } + + bufferAvailable.emit(); +} + +void CamssCsiCamera::freeBuffers() +{ + availableBuffers_ = {}; + buffers_.clear(); + + if (output_->releaseBuffers()) + LOG(Camss, Error) << "Failed to release CamssCsi buffers"; +} + +CamssCsi::CamssCsi() +{ +} + +void CamssCsi::getEntities(std::vector &ents, const char *fmt, unsigned int max) +{ + for (unsigned int i = 0; i < max; i++) { + char name[16]; + snprintf(name, sizeof(name), fmt, i); + MediaEntity *ent = camssMediaDev_->getEntityByName(name); + if (ent) + ents.push_back(ent); + } +} + +CamssCsi::Cameras CamssCsi::match(PipelineHandler *pipe, DeviceEnumerator *enumerator) +{ + DeviceMatch camssDm("qcom-camss"); + Cameras cameras; + + /* + * On SoCs where the CSI-phy is a separate dt-node (e.g. x1e), only + * actually used phys are there. So no match on "msm_csiphy%d". + */ + + for (unsigned int i = 0; i < kMinCsiDecoders; i++) + camssDm.add("msm_csid" + std::to_string(i)); + + for (unsigned int i = 0; i < kMinVfes; i++) { + camssDm.add("msm_vfe" + std::to_string(i) + "_rdi0"); + camssDm.add("msm_vfe" + std::to_string(i) + "_rdi1"); + camssDm.add("msm_vfe" + std::to_string(i) + "_rdi2"); + } + + camssMediaDev_ = pipe->acquireMediaDevice(enumerator, camssDm); + if (!camssMediaDev_) + return {}; + + /* + * Disable all links that are enabled to start with a clean state, + * CamssCsiCamera::configure() enables links as necessary. + * \todo instead only disable links on used entities, to allow + * 2 separate libcamera instances to drive 2 different sensors. + * This will also require changes to PipelineHandler::acquire() to + * allow a more fine grained version of that locking a list of + * subdevs associated with a Camera instead of the mediactl node. + */ + if (camssMediaDev_->disableLinks()) + return {}; + + getEntities(phys_, "msm_csiphy%d", kMaxCsiPhys); + getEntities(csids_, "msm_csid%d", kMaxCsiDecoders); + /* Only RDI0 is used for now */ + getEntities(vfes_, "msm_vfe%d_rdi0", kMaxVfes); + + LOG(Camss, Info) << "Found " + << phys_.size() << " CSI phy(s) " + << csids_.size() << " CSI decoders " + << vfes_.size() << " VFEs"; + + for (auto &phy : phys_) { + std::unique_ptr camera = enumCamera(phy); + if (camera) + cameras.push_back(std::move(camera)); + } + + return cameras; +} + +std::unique_ptr CamssCsi::enumCamera(MediaEntity *phy) +{ + std::unique_ptr cam = std::make_unique(); + int ret; + + /* CSI phy has a sink pad for the sensor at index 0. */ + if (phy->pads().empty() || phy->pads()[0]->links().empty()) + return nullptr; + + MediaEntity *sensor = + phy->pads()[0]->links()[0]->source()->entity(); + cam->sensor_ = CameraSensorFactoryBase::create(sensor); + if (!cam->sensor_) + return nullptr; + + if (csids_.empty()) { + LOG(Camss, Warning) + << "Not enough CSI decoders to enumerate all cameras\n"; + return nullptr; + } + + if (vfes_.empty()) { + LOG(Camss, Warning) + << "Not enough VFEs to enumerate all cameras\n"; + return nullptr; + } + + MediaEntity *csid = csids_.front(); + MediaEntity *vfe = vfes_.front(); + + for (unsigned int i = 0; i < CamssCsiCamera::LinkCount; i++) { + auto &link = cam->links_[i]; + + switch (i) { + case CamssCsiCamera::SensorPhyLink: + link.link = camssMediaDev_->link(sensor, 0, phy, 0); + link.sinkSubdev = std::make_unique(phy); + break; + case CamssCsiCamera::PhyCsidLink: + link.link = camssMediaDev_->link(phy, 1, csid, 0); + link.sinkSubdev = std::make_unique(csid); + break; + case CamssCsiCamera::CsidVfeLink: + link.link = camssMediaDev_->link(csid, 1, vfe, 0); + link.sinkSubdev = std::make_unique(vfe); + break; + } + + if (!link.link) { + LOG(Camss, Error) << "Error enumerating links"; + return nullptr; + } + + ret = link.sinkSubdev->open(); + if (ret) + return nullptr; + } + + /* VFE has a source pad to its /dev/video# node at index 1. */ + if (vfe->pads().size() < 2 || vfe->pads()[1]->links().empty()) + return nullptr; + + MediaEntity *output = + vfe->pads()[1]->links()[0]->sink()->entity(); + cam->output_ = std::make_unique(output); + ret = cam->output_->open(); + if (ret) + return nullptr; + + LOG(Camss, Info) + << "Sensor " << cam->sensor_->entity()->name() + << " phy " << phy->name() + << " decoder " << csid->name() + << " VFE " << vfe->name(); + + csids_.erase(csids_.begin()); + vfes_.erase(vfes_.begin()); + return cam; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/camss_csi.h b/src/libcamera/pipeline/camss/camss_csi.h new file mode 100644 index 000000000..d78175a06 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_csi.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS CSI phy/decoder and VFE handling + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * Partially based on other pipeline-handlers which are: + * Copyright (C) 2020, Laurent Pinchart + * Copyright (C) 2019, Martijn Braam + * Copyright (C) 2019, Google Inc. + */ + +#pragma once + +#include +#include +#include + +#include + +#include "libcamera/internal/device_enumerator.h" +#include "libcamera/internal/pipeline_handler.h" +#include "libcamera/internal/v4l2_subdevice.h" +#include "libcamera/internal/v4l2_videodevice.h" + +namespace libcamera { + +class CameraSensor; +class CamssIsp; +class FrameBuffer; +class MediaDevice; +class PixelFormat; +class Request; +class Size; +class SizeRange; +struct StreamConfiguration; +enum class Transform; + +class CamssCsiCamera +{ +public: + CamssCsiCamera(); + + StreamConfiguration generateConfiguration(void) const; + StreamConfiguration validate(const StreamConfiguration &req) const; + int configure(const StreamConfiguration &cfg, const Transform &transform); + int exportBuffers(unsigned int count, + std::vector> *buffers); + + PixelFormat mbusCodeToPixelFormat(unsigned int code) const; + unsigned int PixelFormatToMbusCode(const PixelFormat &format) const; + + void setIsp(CamssIsp *isp) { isp_ = isp; } + + int start(); + void stop(); + + CameraSensor *sensor() { return sensor_.get(); } + const CameraSensor *sensor() const { return sensor_.get(); } + + FrameBuffer *queueBuffer(Request *request, FrameBuffer *rawBuffer); + void tryReturnBuffer(FrameBuffer *buffer); + Signal &bufferReady() { return output_->bufferReady; } + /* + * \todo camss kernel driver does not support this atm. Once supported + * this needs to take frameStart signal from the csi-decoder. + */ + Signal &frameStart() { return links_[0].sinkSubdev->frameStart; } + bool supportsFrameStart() { return false; } + + Signal<> bufferAvailable; + Stream rawStream_; + +private: + friend class CamssCsi; + + static constexpr unsigned int kBufferCount = 4; + + /* 3 links: sensor -> phy, phy -> csid, csid->vfe */ + enum LinkIndex { + SensorPhyLink, + PhyCsidLink, + CsidVfeLink, + LinkCount + }; + + struct linkInfo { + MediaLink *link; + std::unique_ptr sinkSubdev; + }; + + V4L2PixelFormat mbusCodeToV4L2PixelFormat(unsigned int code) const; + V4L2SubdeviceFormat getSensorFormat(Size size = {}, + const PixelFormat &format = {}, + bool useIspMargins = false) const; + int tryFormat(const V4L2SubdeviceFormat &sensorFormat, StreamConfiguration &cfg) const; + void freeBuffers(); + + std::unique_ptr sensor_; + std::unique_ptr output_; + std::array links_; + CamssIsp *isp_; + + std::vector> buffers_; + std::queue availableBuffers_; + unsigned int bufferCount_; +}; + +class CamssCsi +{ +public: + using Cameras = std::vector>; + + CamssCsi(); + Cameras match(PipelineHandler *pipe, DeviceEnumerator *enumerator); +private: + void getEntities(std::vector &ents, const char *fmt, unsigned int max); + std::unique_ptr enumCamera(MediaEntity *phy); + + static constexpr unsigned int kMaxCsiPhys = 5; + static constexpr unsigned int kMinCsiDecoders = 2; + static constexpr unsigned int kMaxCsiDecoders = 7; + static constexpr unsigned int kMinVfes = 2; + static constexpr unsigned int kMaxVfes = 7; + std::shared_ptr camssMediaDev_; + std::vector phys_; + std::vector csids_; + std::vector vfes_; +}; + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/camss_frames.cpp b/src/libcamera/pipeline/camss/camss_frames.cpp new file mode 100644 index 000000000..3daaaff10 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_frames.cpp @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Camss Frames helper + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * Based on the IPU3 pipeline-handler which is: + * Copyright (C) 2020, Google Inc. + */ + +#include "camss_frames.h" + +#include + +#include +#include + +#include "libcamera/internal/framebuffer.h" +#include "libcamera/internal/pipeline_handler.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(Camss) + +CamssFrames::CamssFrames() +{ +} + +void CamssFrames::init(const std::vector> ¶mBuffers, + const std::vector> &statBuffers, + bool metadataRequired) +{ + for (const std::unique_ptr &buffer : paramBuffers) + availableParamBuffers_.push(buffer.get()); + + for (const std::unique_ptr &buffer : statBuffers) + availableStatBuffers_.push(buffer.get()); + + frameInfo_.clear(); + + paramsRequired_ = !availableParamBuffers_.empty(); + statsRequired_ = !availableStatBuffers_.empty(); + metadataRequired_ = metadataRequired; +} + +void CamssFrames::clear() +{ + availableParamBuffers_ = {}; + availableStatBuffers_ = {}; +} + +CamssFrames::Info *CamssFrames::create(Request *request) +{ + unsigned int id = request->sequence(); + FrameBuffer *paramBuffer = nullptr; + FrameBuffer *statBuffer = nullptr; + + if (paramsRequired_ && availableParamBuffers_.empty()) { + LOG(Camss, Warning) << "Parameters buffer underrun"; + return nullptr; + } + + if (statsRequired_ && availableStatBuffers_.empty()) { + LOG(Camss, Warning) << "Statistics buffer underrun"; + return nullptr; + } + + if (paramsRequired_) { + paramBuffer = availableParamBuffers_.front(); + paramBuffer->_d()->setRequest(request); + availableParamBuffers_.pop(); + } + + if (statsRequired_) { + statBuffer = availableStatBuffers_.front(); + statBuffer->_d()->setRequest(request); + availableStatBuffers_.pop(); + } + + auto [it, inserted] = frameInfo_.try_emplace(id); + ASSERT(inserted); + + auto &info = it->second; + + info.id = id; + info.request = request; + info.rawBuffer = nullptr; + info.paramBuffer = paramBuffer; + info.statBuffer = statBuffer; + info.paramDequeued = false; + info.metadataProcessed = false; + + return &info; +} + +void CamssFrames::remove(CamssFrames::Info *info) +{ + /* Return params and stat buffer for reuse. */ + availableParamBuffers_.push(info->paramBuffer); + availableStatBuffers_.push(info->statBuffer); + + /* Delete the extended frame information. */ + frameInfo_.erase(info->id); +} + +bool CamssFrames::tryComplete(CamssFrames::Info *info) +{ + Request *request = info->request; + + if (request->hasPendingBuffers()) + return false; + + if (metadataRequired_ && !info->metadataProcessed) + return false; + + if (paramsRequired_ && !info->paramDequeued) + return false; + + remove(info); + + bufferAvailable.emit(); + + return true; +} + +CamssFrames::Info *CamssFrames::find(unsigned int id) +{ + const auto &itInfo = frameInfo_.find(id); + + if (itInfo != frameInfo_.end()) + return &itInfo->second; + + LOG(Camss, Fatal) << "Can't find tracking information for frame " << id; + + return nullptr; +} + +CamssFrames::Info *CamssFrames::find(FrameBuffer *buffer) +{ + for (auto &[id, info] : frameInfo_) { + for (const auto &[stream, buf] : info.request->buffers()) + if (buf == buffer) + return &info; + + if (info.rawBuffer == buffer || info.paramBuffer == buffer || + info.statBuffer == buffer) + return &info; + } + + LOG(Camss, Fatal) << "Can't find tracking information from buffer"; + + return nullptr; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/camss_frames.h b/src/libcamera/pipeline/camss/camss_frames.h new file mode 100644 index 000000000..936b907e4 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_frames.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Camss Frames helper + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * Based on the IPU3 pipeline-handler which is: + * Copyright (C) 2020, Google Inc. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +#include + +namespace libcamera { + +class FrameBuffer; +class Request; + +class CamssFrames +{ +public: + struct Info { + unsigned int id; + Request *request; + + FrameBuffer *rawBuffer; + FrameBuffer *paramBuffer; + FrameBuffer *statBuffer; + + ControlList effectiveSensorControls; + + bool paramDequeued; + bool metadataProcessed; + }; + + CamssFrames(); + + void init(const std::vector> ¶mBuffers, + const std::vector> &statBuffers, + bool metadataRequired); + void clear(); + + Info *create(Request *request); + void remove(Info *info); + bool tryComplete(Info *info); + + Info *find(unsigned int id); + Info *find(FrameBuffer *buffer); + + Signal<> bufferAvailable; + +private: + std::queue availableParamBuffers_; + std::queue availableStatBuffers_; + + std::map frameInfo_; + + bool paramsRequired_; + bool statsRequired_; + bool metadataRequired_; +}; + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/camss_isp.cpp b/src/libcamera/pipeline/camss/camss_isp.cpp new file mode 100644 index 000000000..c452568d6 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_isp.cpp @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS ISP virtual base class + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include "camss_isp.h" + +namespace libcamera { + +/** + * \var CamssIsp::inputBufferReady + * \brief A signal emitted when the input frame buffer completes + */ + +/** + * \var CamssIsp::outputBufferReady + * \brief A signal emitted when the output frame buffer completes + */ + +CamssIsp::~CamssIsp() +{ +} + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/camss_isp.h b/src/libcamera/pipeline/camss/camss_isp.h new file mode 100644 index 000000000..36adf99cc --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_isp.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS ISP virtual base class + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#pragma once + +#include +#include + +#include + +#include +#include + +namespace libcamera { + +class ControlList; +class FrameBuffer; +struct StreamConfiguration; +struct V4L2DeviceFormat; + +class CamssIsp +{ +public: + virtual ~CamssIsp() = 0; + + virtual bool isValid() = 0; + virtual Size getMargins(PixelFormat inputFormat) = 0; + + virtual StreamConfiguration generateConfiguration(const StreamConfiguration &raw) const = 0; + virtual StreamConfiguration validate(const StreamConfiguration &raw, const StreamConfiguration &req) const = 0; + virtual int configure(const StreamConfiguration &inputCfg, + const StreamConfiguration &outputCfg) = 0; + + virtual int allocateBuffers(unsigned int bufferCount) = 0; + virtual void freeBuffers() = 0; + virtual int exportOutputBuffers([[maybe_unused]] const Stream *stream, + [[maybe_unused]] unsigned int count, + [[maybe_unused]] std::vector> *buffers) { return 0; } + virtual void queueBuffers(Request *request, FrameBuffer *input) = 0; + + virtual void processStats(const uint32_t frame, const uint32_t statsBufferId, + const ControlList &sensorControls) = 0; + + virtual int start() = 0; + virtual void stop() = 0; + + static constexpr unsigned int kBufferCount = 4; + + Signal inputBufferReady; + Signal outputBufferReady; + Signal paramBufferReady; + Signal statsReady; + Signal metadataReady; + Signal setSensorControls; + Stream outStream_; +}; + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/camss_isp_soft.cpp b/src/libcamera/pipeline/camss/camss_isp_soft.cpp new file mode 100644 index 000000000..9b359b825 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_isp_soft.cpp @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS softISP class + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include "camss_isp_soft.h" + +#include + +#include +#include +#include +#include + +#include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/pipeline_handler.h" +#include "libcamera/internal/software_isp/software_isp.h" +#include "libcamera/internal/v4l2_videodevice.h" + +#include "camss_frames.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(Camss) + +/** + * \class CamssIspSoft + * \brief CAMSS ISP class using the Software ISP + */ + +/** + * \brief Constructs CamssIspSoft object + * \param[in] pipe The pipeline handler in use + * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline + * \param[in] frameInfos Pointer to CamssFrames instance owned by the pipeline + * \param[out] ControlInfoMap to which to add ISP provided controls + */ +CamssIspSoft::CamssIspSoft(PipelineHandler *pipe, const CameraSensor *sensor, + CamssFrames *frameInfos, ControlInfoMap *ispControls) + : sensor_(sensor), frameInfos_(frameInfos) +{ + swIsp_ = std::make_unique(pipe, sensor, ispControls); + + swIsp_->inputBufferReady.connect(this, + [&](FrameBuffer *f) { inputBufferReady.emit(f); }); + swIsp_->outputBufferReady.connect(this, + [&](FrameBuffer *f) { outputBufferReady.emit(f); }); + swIsp_->ispStatsReady.connect(this, + [&](uint32_t frame, uint32_t statsBufferId) { + statsReady.emit(frame, statsBufferId); + }); + swIsp_->metadataReady.connect(this, + [&](uint32_t frame, const ControlList &metadata) { + metadataReady.emit(frame, metadata); + }); + swIsp_->setSensorControls.connect(this, + [&](const ControlList &sensorControls) { + setSensorControls.emit(sensorControls); + }); +} + +CamssIspSoft::~CamssIspSoft() = default; + +bool CamssIspSoft::isValid() +{ + return swIsp_->isValid(); +} + +Size CamssIspSoft::getMargins(PixelFormat inputFormat) +{ + Size trySize(1024, 1024); + SizeRange sizes = swIsp_->sizes(inputFormat, trySize); + + return Size(1024 - sizes.max.width, 1024 - sizes.max.height); +} + +StreamConfiguration CamssIspSoft::generateConfiguration(const StreamConfiguration &raw) const +{ + /* + * The raw stream config may contain multiple format <-> sizes tupples. + * Since the softIsp can always crop / downscale and since it supports + * the same set of output pixel-formats for all supported input pixel- + * formats, simply use the best size, which should be preset in raw.size + * and also use the input pixel-format which matches that. + */ + SizeRange sizes = swIsp_->sizes(raw.pixelFormat, raw.size); + std::vector pixelFormats = swIsp_->formats(raw.pixelFormat); + + if (sizes.max.isNull() || pixelFormats.empty()) + return {}; + + std::vector sizesVector = { sizes }; + std::map> formats; + + for (unsigned int i = 0; i < pixelFormats.size(); i++) + formats[pixelFormats[i]] = sizesVector; + + StreamConfiguration cfg{ StreamFormats{ formats } }; + cfg.size = sizes.max; + cfg.pixelFormat = pixelFormats[0]; + cfg.bufferCount = kBufferCount; + + return cfg; +} + +namespace { + +/* + * \todo copy-pasted from src/libcamera/pipeline/simple/simple.cpp turn this + * into a member of SizeRange ? + * \todo also see V4L2M2MConverter::adjustSizes() which is also similar. + */ +static Size adjustSize(const Size &requestedSize, const SizeRange &supportedSizes) +{ + ASSERT(supportedSizes.min <= supportedSizes.max); + + if (supportedSizes.min == supportedSizes.max) + return supportedSizes.max; + + unsigned int hStep = supportedSizes.hStep; + unsigned int vStep = supportedSizes.vStep; + + if (hStep == 0) + hStep = supportedSizes.max.width - supportedSizes.min.width; + if (vStep == 0) + vStep = supportedSizes.max.height - supportedSizes.min.height; + + Size adjusted = requestedSize.boundedTo(supportedSizes.max) + .expandedTo(supportedSizes.min); + + return adjusted.shrunkBy(supportedSizes.min) + .alignedDownTo(hStep, vStep) + .grownBy(supportedSizes.min); +} + +} /* namespace */ + +StreamConfiguration CamssIspSoft::validate(const StreamConfiguration &raw, const StreamConfiguration &req) const +{ + StreamConfiguration cfg; + + SizeRange sizes = swIsp_->sizes(raw.pixelFormat, raw.size); + std::vector formats = swIsp_->formats(raw.pixelFormat); + + cfg.size = adjustSize(req.size, sizes); + + if (cfg.size.isNull() || formats.empty()) + return {}; + + for (unsigned int i = 0; i < formats.size(); i++) { + if (formats[i] == req.pixelFormat) + cfg.pixelFormat = req.pixelFormat; + } + + if (!cfg.pixelFormat.isValid()) + cfg.pixelFormat = formats[0]; + + std::tie(cfg.stride, cfg.frameSize) = + swIsp_->strideAndFrameSize(cfg.pixelFormat, cfg.size); + + cfg.bufferCount = std::max(kBufferCount, req.bufferCount); + + return cfg; +} + +int CamssIspSoft::configure(const StreamConfiguration &inputCfg, + const StreamConfiguration &outputCfg) +{ + std::vector> outputCfgs; + outputCfgs.push_back(outputCfg); + + /* \todo refactor SoftwareIsp to remove the need to pass this */ + ipa::soft::IPAConfigInfo configInfo; + configInfo.sensorControls = sensor_->controls(); + + return swIsp_->configure(inputCfg, outputCfgs, configInfo); +} + +int CamssIspSoft::allocateBuffers([[maybe_unused]] unsigned int bufferCount) +{ + frameInfos_->init({}, {}, true); + return 0; +} + +void CamssIspSoft::freeBuffers() +{ + frameInfos_->clear(); +} + +int CamssIspSoft::exportOutputBuffers(const Stream *stream, unsigned int count, + std::vector> *buffers) +{ + return swIsp_->exportBuffers(stream, count, buffers); +} + +void CamssIspSoft::queueBuffers(Request *request, FrameBuffer *input) +{ + std::map outputs; + for (const auto &[stream, outbuffer] : request->buffers()) { + if (stream == &outStream_) + outputs[stream] = outbuffer; + } + + swIsp_->queueRequest(request->sequence(), request->controls()); + swIsp_->queueBuffers(request->sequence(), input, outputs); +} + +void CamssIspSoft::processStats(const uint32_t frame, const uint32_t statsBufferId, + const ControlList &sensorControls) +{ + swIsp_->processStats(frame, statsBufferId, sensorControls); +} + +int CamssIspSoft::start() +{ + return swIsp_->start(); +} + +void CamssIspSoft::stop() +{ + swIsp_->stop(); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/camss_isp_soft.h b/src/libcamera/pipeline/camss/camss_isp_soft.h new file mode 100644 index 000000000..8adc6c59c --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_isp_soft.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS softISP class + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#pragma once + +#include +#include + +#include "camss_isp.h" + +namespace libcamera { + +class CameraSensor; +class CamssFrames; +class ControlInfoMap; +class PipelineHandler; +class SoftwareIsp; + +class CamssIspSoft : public CamssIsp +{ +public: + CamssIspSoft(PipelineHandler *pipe, const CameraSensor *sensor, + class CamssFrames *frameInfos, ControlInfoMap *ispControls); + ~CamssIspSoft() override; + + bool isValid() override; + Size getMargins(PixelFormat inputFormat) override; + + StreamConfiguration generateConfiguration(const StreamConfiguration &raw) const override; + StreamConfiguration validate(const StreamConfiguration &raw, const StreamConfiguration &req) const override; + int configure(const StreamConfiguration &inputCfg, + const StreamConfiguration &outputCfg) override; + + int allocateBuffers(unsigned int bufferCount) override; + void freeBuffers() override; + int exportOutputBuffers(const Stream *stream, unsigned int count, + std::vector> *buffers) override; + void queueBuffers(Request *request, FrameBuffer *input) override; + + void processStats(const uint32_t frame, const uint32_t statsBufferId, + const ControlList &sensorControls) override; + + int start() override; + void stop() override; + +private: + std::unique_ptr swIsp_; + const CameraSensor *sensor_; + class CamssFrames *frameInfos_; +}; + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/meson.build b/src/libcamera/pipeline/camss/meson.build new file mode 100644 index 000000000..2f3931dd1 --- /dev/null +++ b/src/libcamera/pipeline/camss/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: CC0-1.0 + +libcamera_internal_sources += files([ + 'camss.cpp', + 'camss_csi.cpp', + 'camss_frames.cpp', + 'camss_isp.cpp', + 'camss_isp_soft.cpp', + 'camss_util.cpp', +]) diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index e26f438d9..9ef9491bf 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -263,7 +263,6 @@ static const SimplePipelineInfo supportedDevices[] = { { "j721e-csi2rx", {}, true }, { "mtk-seninf", { { "mtk-mdp", 3 } }, false }, { "mxc-isi", {}, false }, - { "qcom-camss", {}, true }, { "sun6i-csi", {}, false }, }; From patchwork Tue Jul 21 14:59:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 27429 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 70910C3264 for ; Tue, 21 Jul 2026 15:00:14 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D2EF567E85; Tue, 21 Jul 2026 17:00:13 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="Jp6pkKdh"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="RahsNq+s"; dkim-atps=neutral Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3122467E65 for ; Tue, 21 Jul 2026 17:00:10 +0200 (CEST) Received: from pps.filterd (m0279873.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66LDg6Kq1805622 for ; Tue, 21 Jul 2026 15:00:08 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= kwpxxBqYpHpqHAiEJI/Sbk6Cz2awXOwuK4gNWTj9AUw=; b=Jp6pkKdhEfcuk5dv 5vPzO8O1ryusJcc0rm5kzN8v3GMwrvEmx78YhYWCn0FQiNDHOOb7ThZAOzy9gsUr IwDLECrlPA3hmxS2jIR7jloziYZo3+fb1zwBs13L6wZHyWgUkX3YT9/H0ZbBBdM7 zNxm1tlWlgv2oOXxaEJ1LD/dx7thLJesrEdvilf+M6+ZhMh3fAzGD04Zgtcpb6D3 +Ey1fjQjub/Lo09YlceBOwCqb5vMgc5WQ/TE2YTVC3IS2jyvfbB4gUS8RP8O1FHA cOrn1XSlCA8dyP4im9u5zrmPsSvUqEtXjEHQ30Q88Dngh8tB0ILCS3wgbemR97Rz DKzW4w== Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fj6pa9bmy-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Tue, 21 Jul 2026 15:00:08 +0000 (GMT) Received: by mail-qt1-f197.google.com with SMTP id d75a77b69052e-51c075c1e25so163187221cf.3 for ; Tue, 21 Jul 2026 08:00:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1784646008; x=1785250808; darn=lists.libcamera.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=kwpxxBqYpHpqHAiEJI/Sbk6Cz2awXOwuK4gNWTj9AUw=; b=RahsNq+sYFK5gEmjRDF6H/L+7ttwU+hyfbett8YDlDJ4FuI9IhIw0md/Kvw/6pN4mo CBk5HEvsRVcDABBW0AhXCBeZryxfzdT18nnvaJx/Qk7xQfaMPAB2eZyXzRqSEI7XxfpX I3riBZZE/AzIkESDU43x/gwZWe3jKg4q8Zq4lm5Xf4wOfBgRs/XH1qA/wSwTECxZBF+3 3D53A/Mxfh7OyXp7Hcsqdn/NH47TcOgYF6VKiSRig+vvWAocWwWHI0zE+UWY3I89YiZy StPAocDm/4OxnR83TZnEHtEt+c9vQitXrFw6khf27xSkFATQgtpr5WXt1I4SgPwZL7Vw YBtQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784646008; x=1785250808; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=kwpxxBqYpHpqHAiEJI/Sbk6Cz2awXOwuK4gNWTj9AUw=; b=GJZYJLIYutKRGhBAcXF1oHE0CXuG1RqeJdTf8MfT+OOY7fyI3M2B7QaJP2W2IctvUS hLdyZlloL6UF8gRXBXruSYzFWiGovqw6Ur2MOOTb7mhda1eN2eWH/75LOTk3k6rxr5Xw uWJbxATFchq3fmWleQ+5pMoPGTPW00bWv+yS4yTYm74OUqegPfOfmO0UxA6rZdvX+lB0 JSk33HIcWdS8jzWEHpspIhC/9vEmKGKQQyaQn0wcCyo6h2x59q2un8EQ+U9aDjoXwzGC RatgJMOE5NbxPFzxvHCbuig8kG+w9aKQ6hOuuKyB+FxtdcV6zv3ZtuYMGo3geyPeRas5 H20w== X-Gm-Message-State: AOJu0YxKtKMH8ihYW+oFQ5p6AZ4ncJyLp1HWdzFEZPqYxt4/xcABx3MV TG2uVcidQwPmxbemd8pLCA3TB3TGW2GE5HHOob30VkbU/muDuKInvyDFb+0pzX4/fLpYUtkeGPy 7JQ7VGflw9boBFeEuDkd+D/mO1wDxbf2+dCcKGp5ObhDGQGwUZMZMt3XBGqqHNsRuqBL8wUUBV8 dnkrOA3bAM X-Gm-Gg: AfdE7clXMxqEtc6NOMtJ+8zyC6ywDmEZcnR0D9vKbTMLBsiEXhddniDrBCmjkzA0lAF GsE5HFyW1c3c2oT0kDwSECFsG0z8ForbJyNKv2vatzkbPIuuxujoic9EX+SDzNz2hFunetFhWXp j+C2MSaVglo/ZDlwKpkDHTfAjsoCMn0yK2ackc5c7DCASBJisq4Q5QmGnjEjs012ydCv3bpVCdk 2GsVfrUglK7xina+wWQ7zjtIL0jZ0a+RfurBb4orbdE9mEjATAkoHByJgmhjQ7mppNMGGA1DSLF z7Kw+6tM0u10jqRGmBQTdCTb+TjblGioYfj84srA1N/FYNCimNVqn+hCfuj3NizTiE8RxcO9hrl 2oIRZns9vs+cn1ThnjrSpJ9VN/rM8jl8sxN/R+Dp0Bjd0v4dGehjoCd5ANPj4sr9UcjXsvbxDUx 4ZeOLEkt6HIxbAQas= X-Received: by 2002:a05:622a:11cf:b0:517:61d5:2f7b with SMTP id d75a77b69052e-5213e28e1c4mr176705991cf.56.1784646007946; Tue, 21 Jul 2026 08:00:07 -0700 (PDT) X-Received: by 2002:a05:622a:11cf:b0:517:61d5:2f7b with SMTP id d75a77b69052e-5213e28e1c4mr176704921cf.56.1784646007224; Tue, 21 Jul 2026 08:00:07 -0700 (PDT) Received: from shalem (2001-1c00-0c32-7800-5bfa-a036-83f0-f9ec.cable.dynamic.v6.ziggo.nl. [2001:1c00:c32:7800:5bfa:a036:83f0:f9ec]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c1712a5f837sm615968066b.32.2026.07.21.08.00.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Jul 2026 08:00:06 -0700 (PDT) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Loic Poulain , Hans de Goede Subject: [PATCH v2 5/7] camss: Add include/linux/camss-config.h Date: Tue, 21 Jul 2026 16:59:55 +0200 Message-ID: <20260721145958.68489-6-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> References: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Proofpoint-GUID: orhapHO2dJdF-t4mtJ3Vsk3fTFVbtJdV X-Proofpoint-ORIG-GUID: orhapHO2dJdF-t4mtJ3Vsk3fTFVbtJdV X-Authority-Analysis: v=2.4 cv=eYYNubEH c=1 sm=1 tr=0 ts=6a5f8978 cx=c_pps a=EVbN6Ke/fEF3bsl7X48z0g==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=rJkE3RaqiGZ5pbrm-msn:22 a=EUspDBNiAAAA:8 a=kFo2aqxNIXtk6PDOeyQA:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 a=a_PwQJl-kcHnX1M80qC6:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfX4lT9PHHfJAXf Nd0txgLwWYlA5kYi5UUK0gBL42LQM485MY6HWh6PI4E26nOb5CE7FCv9EB+byCBXN2W1HF99Vbw WfvKRWk1fZETDBa7yB5u1s+fFFkuczifDc16GrxhEcVTIgOHhnkLOAclTutBCrmsNEBmqON0s6w /rx+7NZBcQWh0sSJhOF+AKunerzzstTx9S+GuU3XyRFuVept2WkH2LeqAQzaaoOLEmXgrniZBDG BYYjumKzngJRVqkGF5EPnun5tjwhYTdZOcWGvzPMehnFc3E4HCnUzUO503KEDHhc2j9DMSSKd5b zNgRPmnHpAKJxY219RlCvrcH5GyTWlKKyENdS35EN6UkuT7ZBixSsUSU5tSaM38tvttxQWmJcyY 68Tu7LPtLoIUINlkL96Ix9d3ho6S58365dbKesCI8xIc9+OHD72hZ8qTPjUOrD2U+p7OBk0UbVE OHUc1cH3XwWOWZpaI4A== X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfX3etln9sm1b5B uJvY9vjTGxjMFSR3MlcUmBeg7Ai7teTmn4f9/6DC9NlrMJmKePvuiM2EQIBUEcIZB/kiVfgodbb SbabFlzVlbtj1pFBcfXjgo5gRcmbVQk= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1143, Hydra:6.1.134, FMLib:17.12.100.49 definitions=2026-07-21_03,2026-07-20_03,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 lowpriorityscore=0 malwarescore=0 priorityscore=1501 spamscore=0 bulkscore=0 impostorscore=0 phishscore=0 adultscore=0 clxscore=1015 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607210158 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 include/linux/camss-config.h for OPE ISP parameter definitions. Signed-off-by: Hans de Goede --- include/linux/camss-config.h | 161 +++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 include/linux/camss-config.h diff --git a/include/linux/camss-config.h b/include/linux/camss-config.h new file mode 100644 index 000000000..d0d087eea --- /dev/null +++ b/include/linux/camss-config.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * Qualcomm CAMSS ISP parameters UAPI + * + * Uses the generic V4L2 extensible ISP parameters buffer format defined in + * . + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _UAPI_LINUX_CAMSS_CONFIG_H +#define _UAPI_LINUX_CAMSS_CONFIG_H + +#include +#include + +/** + * enum camss_params_block_type - CAMSS ISP parameter block identifiers + * + * Each value identifies one ISP processing block. The value is placed in + * the @type field of &struct v4l2_isp_params_block_header. + */ +enum camss_params_block_type { + CAMSS_PARAMS_WB_GAIN = 1, + CAMSS_PARAMS_CHROMA_ENHAN = 2, + CAMSS_PARAMS_COLOR_CORRECT = 3, + CAMSS_PARAMS_MAX, +}; + +/** + * struct camss_params_wb_gain - White Balance gains + * + * Implements the CLC_WB pipeline module. The pipeline applies three + * sequential operations per channel: + * 1. Subtract sub-offset (black-level substraction) + * 2. Multiply by gain (colour balance) + * 3. Add add-offset (output pedestal) + * + * Gains are 15uQ10 (15-bit unsigned, 10 fractional bits). Offsets + * are 16-bit unsigned, normalised to full input scale (65535 = 1.0) + * + * @header: block header; @header.type = CAMSS_PARAMS_WB_GAIN + * @g_gain: green channel gain (15uQ10, 1024 = 1.0) + * @b_gain: blue channel gain (15uQ10, 1024 = 1.0) + * @r_gain: red channel gain (15uQ10, 1024 = 1.0) + * @g_sub: green sub-offset, subtracted before gain (16u) + * @b_sub: blue sub-offset, subtracted before gain (16u) + * @r_sub: red sub-offset, subtracted before gain (16u) + * @g_add: green add-offset, added after gain (16u) + * @b_add: blue add-offset, added after gain (16u) + * @r_add: red add-offset, added after gain (16u) + */ +struct camss_params_wb_gain { + struct v4l2_isp_params_block_header header; + __u16 g_gain; + __u16 b_gain; + __u16 r_gain; + __u16 g_sub; + __u16 b_sub; + __u16 r_sub; + __u16 g_add; + __u16 b_add; + __u16 r_add; + __u16 _pad[3]; +} __attribute__((aligned(8))); + +/** + * struct camss_params_chroma_enhan - RGB to YUV colour transfer matrix + * + * Implements the CLC_CHROMA_ENHAN pipeline module. All coefficients are + * signed 12-bit fixed-point Q3.8 (range roughly -8.0 to +7.996). + * + * RGB2Y - Luma (Y) ceofficients + * Y = v0 * R + v1 * G + v2 * B + * + * @luma_v0: R-to-Y coefficient (12sQ8) + * @luma_v1: G-to-Y coefficient (12sQ8) + * @luma_v2: B-to-Y coefficient (12sQ8) + * @luma_k: Y output offset (9s, 0 = no offset) + * + * RGB2Cb - Chroma (Cb) coefficients + * Cb = a x ((B - G) + b(R - G)) + KCb + * with: + * a = ap, when (B-G) + b(R-G) > 0; a = am, when (B-G) + b(R-G) ≤ 0; + * b = bp when (R-G) > 0; b = bm when (R-G) ≤ 0 + * + * @coeff_ap: Cb positive coefficient (12sQ8) + * @coeff_am: Cb negative coefficient (12sQ8) + * @coeff_bp: Cb positive coefficient (12sQ8) + * @coeff_bm: Cb negative coefficient (12sQ8) + * @kcb: Cb output offset (11s) + * + * RGB2Cr - Chroma (Cr) coefficients: + * Cr = c x ((R - G) + d(B - G)) + KCr + * with: + * c = cp, when (R-G) + d(B-G) > 0; c = cm, when (R-G) + d(B-G) ≤ 0 + * d = dp when (B-G) > 0; d = dm when (B-G) ≤ 0 + * + * @coeff_cp: Cr positive coefficient (12sQ8) + * @coeff_cm: Cr negative coefficient (12sQ8) + * @coeff_dp: Cr positive coefficient (12sQ8) + * @coeff_dm: Cr negative coefficient (12sQ8) + * @kcr: Cr output offset (11s) + * + * @header: generic block header; @header.type = CAMSS_PARAMS_CHROMA_ENHAN + */ +struct camss_params_chroma_enhan { + struct v4l2_isp_params_block_header header; + __u16 luma_v0; + __u16 luma_v1; + __u16 luma_v2; + __u16 luma_k; + __u16 coeff_ap; + __u16 coeff_am; + __u16 coeff_bp; + __u16 coeff_bm; + __u16 coeff_cp; + __u16 coeff_cm; + __u16 coeff_dp; + __u16 coeff_dm; + __u16 kcb; + __u16 kcr; + __u16 _pad[2]; +} __attribute__((aligned(8))); + +/** + * struct camss_params_color_correct - colour correction matrix + * + * Implements the CLC_CC pipeline module. The matrix computes: + * Out_ch0 (G) = a0*G + b0*B + c0*R + k0 + * Out_ch1 (B) = a1*G + b1*B + c1*R + k1 + * Out_ch2 (R) = a2*G + b2*B + c2*R + k2 + * + * @header: block header; @header.type = CAMSS_PARAMS_COLOR_CORRECT + * @a: G-input coefficients per output channel (12s; + * a[0]=Out_G, a[1]=Out_B, a[2]=Out_R) + * @b: B-input coefficients (12s) + * @c: R-input coefficients (12s) + * @k: per-output-channel offsets (typically 9s effective) + * @qfactor: Q-format selector (2u): + * 0 = 12sQ7 (range ~-256.0 .. +255.992) + * 1 = 12sQ8 (range ~-128.0 .. +127.996) + * 2 = 12sQ9 (range ~-64.0 .. +63.998) + * 3 = 12sQ10 (range ~-32.0 .. +31.999) + */ +struct camss_params_color_correct { + struct v4l2_isp_params_block_header header; + __u16 a[3]; + __u16 b[3]; + __u16 c[3]; + __u16 k[3]; + __u16 qfactor; + __u16 _pad[3]; +} __attribute__((aligned(8))); + +#define CAMSS_PARAMS_MAX_PAYLOAD \ + (sizeof(struct camss_params_wb_gain) +\ + sizeof(struct camss_params_chroma_enhan) +\ + sizeof(struct camss_params_color_correct)) + +#endif /* _UAPI_LINUX_CAMSS_CONFIG_H */ From patchwork Tue Jul 21 14:59:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 27430 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 C8C0CBE080 for ; Tue, 21 Jul 2026 15:00:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8434367E79; Tue, 21 Jul 2026 17:00:15 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="GN/Z2Fyc"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="UF8gf35T"; dkim-atps=neutral Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0BB4667E69 for ; Tue, 21 Jul 2026 17:00:12 +0200 (CEST) Received: from pps.filterd (m0279871.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66LDg7mG1958770 for ; Tue, 21 Jul 2026 15:00:11 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=lt5eIUJH5xj I6MnIwob+2vA6v5a0okvKVlguBHiiFfc=; b=GN/Z2FycXFFlfRapZEahKRtJLzb /nJehQ/9jFkhuULwWIg3OaY6To9vMCaXP/61s02JgIIOkfC0bVAqtA6WBTEi10xu DVkjEB9YvuUTJHfSzAXAikAkDPESxm2BuM0OHxTUz/LSWZFcNLcFbfIY+HCVUTSk LBNh3kp5ICh7kbqzocTY1bOJ5o/gEwpXhMd5d6YJLM642XsxyPSGxXUZ8oRhNcTX TJIkHUBmPuAs9eOuxF6Zq0ah03GTAKgOk6SKdiEexxQdhipy+ngyQ1GsKc0FYB1D /0Hzj1i22/amBN9+kw1agulwUjGj9LahX6pWhExIelbT39IIVtjHnfTtK6Q== Received: from mail-qt1-f199.google.com (mail-qt1-f199.google.com [209.85.160.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fj1bvu2r4-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Tue, 21 Jul 2026 15:00:11 +0000 (GMT) Received: by mail-qt1-f199.google.com with SMTP id d75a77b69052e-51c20762d21so111087501cf.0 for ; Tue, 21 Jul 2026 08:00:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1784646011; x=1785250811; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=lt5eIUJH5xjI6MnIwob+2vA6v5a0okvKVlguBHiiFfc=; b=UF8gf35TcDGfRfDxvCuIoccHVieLEQRGdBTTctfhu62hbXr0sHv7fhdsFTcg0CDroc JgpU9PrBL0GuRkASefKw6YG1HQKxQwsJ6NX4m3vVcABmaYC3Q3tUqdRy+RI5BB6+Sf+1 4v595br+UPcSlDsLj4Xag/+J/f4S7jx4MUfsvpXs7dbkpgWJ8BzRZHrROVQEu2/naKYb oe9geCyi6vcZUxfdeiSVb52yLh3csvYZ1CgtLRKaOq2xSpy13AoMWf/yOwjjuf3GhF3i BqRbkNFdXD/URgmUFOo0R12flyJps0mXt2b5AoMAnu9x2wlOkXk/7iSSXSNukFMTnm0j aKkg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784646011; x=1785250811; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=lt5eIUJH5xjI6MnIwob+2vA6v5a0okvKVlguBHiiFfc=; b=jGhMLJ9UIrJwxr4BkneSoVx0IInSTK+qPbTK8+Xx3Gf6CbehwQrqRqhH03bCLL/7zJ K8cx8UXhAhcZn3HbeGG7J1Cm2rhXEJ/3JZ/64JbrcwqxgSOL2XCRfpxEqtRbjNiPX6PV HaMpcWIc5tx46J3y/+kOroTLTIY/foCXP1OU4epqcKtoyGsHpI8eG1pjRLpokvtyRraL 988kdxp70mQuEmag7xNF3/LBgesNZsueryRylP/0Amce7wT8oQ/YN1CYL22/DpIlnpaE /pEPqjhQ5qJd/V7fhYd6VC894qsUyBEkRsVWDX+GMgA2mERzumaFZUQwE2Xel99Inbq7 D/fg== X-Gm-Message-State: AOJu0Ywr8sxnYqk3MgtNP8By3drb76Y0pJCCVwyhMf6CWQ14hkkKeJS1 a/PW2NSHQ7TgSMVuvXgKC2wa5o+UwKVge022Up65Fcz+V2HmBg70LFblljPGnmOIWZjEMTS5Nr9 ojK8fgc/JpP0nniRfe9UNbXa4ZGhojfiODN1E6T+B3ACBbsTN7I8uUYhCtwJwR9Xnjdau8UeO5C ku2Co6p4dh X-Gm-Gg: AfdE7ckQroNWpR2FUdEtzFiQC57ZiE/S20FTAaoguBJLpOFmGI/ZGFTzg5BuBA1SF2K He2feCTmioOfHPieSlDDMAkLGFBz6CVNb7JYSJFixIFzD5cOQaMjzAMezRoO2mxpTgJ6gogaSvC f9S9WM3wk9ii0ZFfwju9vE48OptCs6Z6nGr5nuEQb4pUQfwtRj54VYBV8+LRvtpHz3Fn+DdWyXK 8wt33z8SVArmVLzE2WuJ0aIVZ3+1q+yh2Qd6oorQTixtQifM4PDjgmYegecV2PFAWyXkIc7byi+ ZZGwrs+bZ7Ul4Rma4/qdxbPfWAUuwsp61HsNJzHjDfi749TsvMU9ZLOMTeWG1xrS4ALNovcsfac EyJ6ewO0bV18dwGwpJTjYafV3xNdMRxpAFM+CZbnx3Lx6ow6n7oFBBjTJ12Sc4dezUbuKGljfX7 3ccpg+MN9ZZh3e5Qo= X-Received: by 2002:a05:622a:1353:b0:51c:af4a:9d50 with SMTP id d75a77b69052e-5213eb800a4mr187525691cf.68.1784646009782; Tue, 21 Jul 2026 08:00:09 -0700 (PDT) X-Received: by 2002:a05:622a:1353:b0:51c:af4a:9d50 with SMTP id d75a77b69052e-5213eb800a4mr187524541cf.68.1784646008855; Tue, 21 Jul 2026 08:00:08 -0700 (PDT) Received: from shalem (2001-1c00-0c32-7800-5bfa-a036-83f0-f9ec.cable.dynamic.v6.ziggo.nl. [2001:1c00:c32:7800:5bfa:a036:83f0:f9ec]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c1712a5f837sm615968066b.32.2026.07.21.08.00.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Jul 2026 08:00:07 -0700 (PDT) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Loic Poulain , Hans de Goede Subject: [PATCH v2 6/7] camss: Add camss_params.h Date: Tue, 21 Jul 2026 16:59:56 +0200 Message-ID: <20260721145958.68489-7-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> References: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: DWrrIqA5MzfVzlnzcfrSpNad9VJBSTO0 X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfXy21S14ok3lMj 1EnYqhqO+SSvo4T4Lm/jNpO/f6B7HhA2woT7PZGTEYopzGAACFwhurn+qb+bnDZIvFEfwzFw42Q 04AV2o5ztffBbozhG/3ZmnzcmrwXx1U= X-Proofpoint-GUID: DWrrIqA5MzfVzlnzcfrSpNad9VJBSTO0 X-Authority-Analysis: v=2.4 cv=Sb7HsPRu c=1 sm=1 tr=0 ts=6a5f897b cx=c_pps a=WeENfcodrlLV9YRTxbY/uA==:117 a=xqWC_Br6kY4A:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=3WHJM1ZQz_JShphwDgj5:22 a=EUspDBNiAAAA:8 a=EZgyBINV0eAsl0jEDIsA:9 a=kacYvNCVWA4VmyqE58fU:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfXyhYpLovqVf+x 9JdmOz9l9aDhbDQSeQ19x39NaH6FgbUwvbsHOEatmC6G6pbT2UrURRDffJHlzp3JG/4OnUBoxn5 3z7UynqDeK8Ji8jFF9xPIqTIbfMRWCM7bjaKdwF3dcGg34iyytk8Q9Q9o62NCzQoh/BlAohWe+r hi+1hr1ABBarziXeHU9pPY9Ut4j54YEKGS2XwGPY5yw44rwk0bmtTJts5E7pRmfmFbRZSWl3U2/ acKvgLEsdqqFuBxIQnRGQloD6F5HiJXDGZdtEuHikfp6mEcawNIKDjBd4IxD76WY9E3Zga+lSPO U5hzojinICnnYw4+mh5OU6PA/sX4h6eslbk/T153CS8T5sxjkmu6CDVdas4kS/xEXojM0luQtbB 0FHrG9XOmq9Eomb3SC2OE9vTvV2vKifHIKSvdahI2s4uYrerX/jPbwHIlqL90COyDFW8eHDlIBX JN/XRALO8yMFKbH2KCw== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1143, Hydra:6.1.134, FMLib:17.12.100.49 definitions=2026-07-21_03,2026-07-20_03,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 malwarescore=0 spamscore=0 suspectscore=0 clxscore=1015 lowpriorityscore=0 impostorscore=0 adultscore=0 phishscore=0 priorityscore=1501 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607210158 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 header file definging the V4L2Params derived CamssParams class for OPE extensible ISP parameters support. Signed-off-by: Hans de Goede --- src/libcamera/pipeline/camss/camss_params.h | 67 +++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 src/libcamera/pipeline/camss/camss_params.h diff --git a/src/libcamera/pipeline/camss/camss_params.h b/src/libcamera/pipeline/camss/camss_params.h new file mode 100644 index 000000000..6dec85fbc --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_params.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS ISP Parameters + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#pragma once + +#include +#include + +/* + * FIXME must be moved to a to-be-written camss IPA and then this should be: + * #include + */ +#include "../../../ipa/libipa/v4l2_params.h" + +namespace libcamera { + +namespace ipa::camss { + +enum class CamssBlocks { + AwbGains, + ChromaEnh, + ColorCorrect, +}; + +namespace details { + +template +struct block_type { +}; + +#define CAMSS_DEFINE_BLOCK_TYPE(id, cfgType, blkType) \ + template<> \ + struct block_type { \ + using type = struct camss_params_##cfgType; \ + static constexpr camss_params_block_type blockType = \ + camss_params_block_type::CAMSS_PARAMS_##blkType; \ + } + +CAMSS_DEFINE_BLOCK_TYPE(AwbGains, wb_gain, WB_GAIN); +CAMSS_DEFINE_BLOCK_TYPE(ChromaEnh, chroma_enhan, CHROMA_ENHAN); +CAMSS_DEFINE_BLOCK_TYPE(ColorCorrect, color_correct, COLOR_CORRECT); + +struct param_traits { + using id_type = CamssBlocks; + + template + using id_to_details = block_type; +}; + +} /* namespace details */ + +class CamssParams : public V4L2Params +{ +public: + CamssParams(Span data) + : V4L2Params(data, V4L2_ISP_PARAMS_VERSION_V1) + { + } +}; + +} /* namespace ipa::camss */ + +} /* namespace libcamera */ From patchwork Tue Jul 21 14:59:57 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 27431 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 6036BBDE4C for ; Tue, 21 Jul 2026 15:00:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0CDD967E78; Tue, 21 Jul 2026 17:00:19 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="aY4pvB1O"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="h+HQHBUL"; dkim-atps=neutral Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 047E967E72 for ; Tue, 21 Jul 2026 17:00:16 +0200 (CEST) Received: from pps.filterd (m0279870.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66LDfwtK1912530 for ; Tue, 21 Jul 2026 15:00:15 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=iBY8Jj61JpQ 7zLskBS7JWNts/YX873WTeG1EgV3TanE=; b=aY4pvB1OHe/WYH3xrTFvFsCXcvC L7zXv1WrTiS6KuAnuCJ6MJY4YbJYhS7E3icrygRk+zPoHirEiC21mF8v/WNaag4T VijMT6eArkLtkZYUufCZM3KaAlTNFrBsKIbSMEutr1Ye84/qQPUiUSbhaN5fl5R8 gTpUMWQ+ipIHhEiJkDmaMNYPOPC4RKtzx8bfJQ325zQaP2rFMYzAOLIiwzccLgbq HRxxcAb4u5GofiEpS0qx0TDa88cTUA9fsVb2QpZR8W5Tc8awG9/8ARVueHTF73IL gCryPtJv7rvprh0PBr/HD3wUySVQtAFA+dJTr42DN2T18uwvB+1bryclLqw== Received: from mail-qt1-f198.google.com (mail-qt1-f198.google.com [209.85.160.198]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fj1duk0k7-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Tue, 21 Jul 2026 15:00:15 +0000 (GMT) Received: by mail-qt1-f198.google.com with SMTP id d75a77b69052e-51c075c1e25so163191061cf.3 for ; Tue, 21 Jul 2026 08:00:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1784646015; x=1785250815; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=iBY8Jj61JpQ7zLskBS7JWNts/YX873WTeG1EgV3TanE=; b=h+HQHBUL0RqYyrF66XQD/XsoEd3kRl5u0MmUOMreHhS4LmCHv5Tj7mmn2B53u8o4AS /nREOwwz3VmWWpvniabM8N4kFj6sU4xGjJzlbRh9pR4H4ZAJjeeBvVGP653al9oYqwVE VBhmpCrd665U3o00xjjbN3uum2Qr0+jMmg0Dshwom2uHgIeErMYbRPtB9IfnrbalzROQ 1BMjs2gz4cLVStilnGWfw7OtHSyKMwbtvJnTuq17jRZ8TyAgzSO6Q8C8IzlIidvFmmne lbF8DtYLy1KCS8Di3osdvGcVrU4GQkHBzi88p4zHYMOaOpnq231DZpUvW9agwO9TP2PP 54TQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784646015; x=1785250815; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=iBY8Jj61JpQ7zLskBS7JWNts/YX873WTeG1EgV3TanE=; b=hxsURIYRkxPTwzpp1xnr7JAgZy5dA+A3pCEKZ4+YbMGrQesd3REABq2NhvDTXm6HiI bqHXdfAl0S/NdhrudZpnQzQJRTv7y96f0IYe4SvTwcLMw3u+MaGV3M+ZsWH42eMjm6dL a/fDj90+/pSXcmjtALvtQxFXcGItqJQ2Cg6rOcZYaNMQREGnvg/byaNFI1Y2xGWM1CcX dnvSXLck6Z/FO5BViKBWDMWTbqWpdwnXSiPTIS36hvItr90Nsgj2LP2tuiS3sjh4K38t 1wFMnfi6l/YBnECMMcoIZUxuNB6ZBEDzenbBPMbqVDTb2Nv4ybrAWuZVgJ/Wgg8VDsk8 O2uQ== X-Gm-Message-State: AOJu0YyI39sNo2f9cMgX0K6fyGV7tUpAC7pWUg9t7ffI8k7fOy3D7TOF FOJjLliYDnZe683SPBjEgIo12AwEim8Pu4Ap+/diYjwGiHW/+cnycBBRmrE45Am32DR5qPtE7lK EbL5PvsyhKTKwzgFY1yGSqmKsExrfesB+wAugEZwIuh++OIGbv/6Od7KmCv1vyb60Ctja0KiGiM Kr08PdIQMl X-Gm-Gg: AfdE7clE9WUchXpGkYXXaTFfRbGfv80SwEoSHnRe2vIeBvzFAL921GnUdRtc8Td3ynw 5ceXi0qifpRBWgiP47CU51jlzc1SMovNlc1OSsFZpFVjwug6Im8z/T3ODvhlNHwOaPB1Xne4sDo pLsiAAiKf+YRwMFd5sIMpUKxTsCFkD6AH9cXuHWS41v/lMsdDjxG3sf110/jF7LmDfeCiD7qWyW oXiK73XWkPd8qMaW7V6Hb1TGdt3nKXSYPF/a8676N8KWB1Ltz8ii350Iv1v1HYqjVlVCqG4cics fhRkPkhK/X0RC8osJT4UzBBSSMEQI1K7jH1lK5ilyILhCeNB116KISLfMZsJgC8GmIEooLiIrDA 4KvMklATSDgwcltkEUnEeFlAJbLR8v1oUKIVEV+s7cBBfntOFhOdlRBkGlBB2K6BefIQbIuS5wn RxgEwbJfR2uL1n09o= X-Received: by 2002:a05:622a:1ba2:b0:51c:6e:ef23 with SMTP id d75a77b69052e-5213c9e3db3mr177448231cf.32.1784646012314; Tue, 21 Jul 2026 08:00:12 -0700 (PDT) X-Received: by 2002:a05:622a:1ba2:b0:51c:6e:ef23 with SMTP id d75a77b69052e-5213c9e3db3mr177445831cf.32.1784646010484; Tue, 21 Jul 2026 08:00:10 -0700 (PDT) Received: from shalem (2001-1c00-0c32-7800-5bfa-a036-83f0-f9ec.cable.dynamic.v6.ziggo.nl. [2001:1c00:c32:7800:5bfa:a036:83f0:f9ec]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c1712a5f837sm615968066b.32.2026.07.21.08.00.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Jul 2026 08:00:09 -0700 (PDT) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Loic Poulain , Hans de Goede Subject: [PATCH v2 7/7] camss: Add Offline Processing Engine ISP support Date: Tue, 21 Jul 2026 16:59:57 +0200 Message-ID: <20260721145958.68489-8-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> References: <20260721145958.68489-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: ib5jDCn2Hczbq5p99WW5TufhQDw1XYnN X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfX0BcZoi8n/r3f QSPfa5rw2rltU9N5luq+gHWqEyDnBGRZ1MdSbQ/t4W2o+AToLha+hQeRVNNdviwhX4bg4BcKfni gTKFzUCdH78vLE1LgwkN9794xkEBbH8qErpkM7iKWc+xJUJ8MOpDzdrWoC9Sy39bBskypR0zpGf mbbBZmP7qcYnGWjB1LiKYHBQffeNibDOFon3EksorkbyubISUEsKZls6RM5rYY6ZttRFHgWI7Vo z0eU0ETrWPl1w9dm5ee6AEWkF5cHeUqDMDrnGDD3vWBZenatKWYSRHvUhVs9mkv0mRKqAg1XXVu 6HZ5KyUz59P+0mhYJYkjyShaHjVNxI43CBO9LhuBDXzk5q2/BKBfO2n+BAzKgoztYKhTHm9trwp XxSGizbCGB+jkgTyPelX5ilzTA9SPuaTRc5Bz99jzuanXIQcAHi2aZhQHwVjDCCNGrItQ3wEqR+ VUux9xkR9+MMO5IEbCw== X-Authority-Analysis: v=2.4 cv=I9JVgtgg c=1 sm=1 tr=0 ts=6a5f897f cx=c_pps a=mPf7EqFMSY9/WdsSgAYMbA==:117 a=xqWC_Br6kY4A:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=gowsoOTTUOVcmtlkKump:22 a=VwQbUJbxAAAA:8 a=EUspDBNiAAAA:8 a=ISZBYy2OhyTthe2ftDAA:9 a=dawVfQjAaf238kedN5IG:22 X-Proofpoint-GUID: ib5jDCn2Hczbq5p99WW5TufhQDw1XYnN X-Proofpoint-Spam-Info: AW1haW4tMjYwNzIxMDE1OCBTYWx0ZWRfX3k1IkMrien8y zrpwEDxqMzLvTdbtp9nh7mcmiJ/e9xns2yOZRE5IGVWR5orH57f/mWykAgRwrnuyw/3Z4n0BkJ+ uZ8HNOaowCxFC8AlrottFwpVau5no/E= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1143, Hydra:6.1.134, FMLib:17.12.100.49 definitions=2026-07-21_03,2026-07-20_03,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 adultscore=0 spamscore=0 clxscore=1015 suspectscore=0 lowpriorityscore=0 phishscore=0 priorityscore=1501 impostorscore=0 malwarescore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607210158 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" This adds support for the OPE through the v3 kernel driver series posted upstream recently (See Link tag). This is still in the proof of concept phase atm. With the biggest todo item being this using the softISP simple IPA atm, while it really should come with its own IPA. Link: https://lore.kernel.org/linux-media/20260508-camss-isp-ope-v3-0-bb1055274603@oss.qualcomm.com/ Signed-off-by: Hans de Goede --- src/libcamera/pipeline/camss/camss.cpp | 19 +- .../pipeline/camss/camss_isp_ope.cpp | 679 ++++++++++++++++++ src/libcamera/pipeline/camss/camss_isp_ope.h | 120 ++++ src/libcamera/pipeline/camss/camss_params.h | 2 +- src/libcamera/pipeline/camss/meson.build | 3 + 5 files changed, 816 insertions(+), 7 deletions(-) create mode 100644 src/libcamera/pipeline/camss/camss_isp_ope.cpp create mode 100644 src/libcamera/pipeline/camss/camss_isp_ope.h diff --git a/src/libcamera/pipeline/camss/camss.cpp b/src/libcamera/pipeline/camss/camss.cpp index 371d4f8a2..6b53bdacd 100644 --- a/src/libcamera/pipeline/camss/camss.cpp +++ b/src/libcamera/pipeline/camss/camss.cpp @@ -38,6 +38,7 @@ #include "camss_csi.h" #include "camss_frames.h" #include "camss_isp.h" +#include "camss_isp_ope.h" #include "camss_isp_soft.h" namespace libcamera { @@ -522,12 +523,18 @@ bool PipelineHandlerCamss::match(DeviceEnumerator *enumerator) data->delayedCtrls_ = std::make_unique(sensor->device(), params); - data->isp_ = std::make_unique(this, sensor, - &data->frameInfos_, - &data->controlInfo_); - if (!data->isp_->isValid()) { - LOG(Camss, Error) << "Failed to create software ISP"; - continue; + data->isp_ = CamssIspOpe::match(this, enumerator, sensor, + &data->frameInfos_, + &data->controlInfo_); + if (data->isp_ == nullptr) { + data->isp_ = + std::make_unique(this, sensor, + &data->frameInfos_, + &data->controlInfo_); + if (!data->isp_->isValid()) { + LOG(Camss, Error) << "Failed to create software ISP"; + continue; + } } data->isp_->inputBufferReady.connect(data->csi_.get(), diff --git a/src/libcamera/pipeline/camss/camss_isp_ope.cpp b/src/libcamera/pipeline/camss/camss_isp_ope.cpp new file mode 100644 index 000000000..454c72795 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_isp_ope.cpp @@ -0,0 +1,679 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS OPE ISP class + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include "camss_isp_ope.h" + +#include + +#include + +#include +#include +#include +#include +#include + +#include "libcamera/internal/camera_manager.h" +#include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/device_enumerator.h" +#include "libcamera/internal/dma_buf_allocator.h" +#include "libcamera/internal/framebuffer.h" // FIXME drop once softIPA (ab)use is gone +#include "libcamera/internal/ipa_manager.h" +#include "libcamera/internal/media_device.h" +#include "libcamera/internal/pipeline_handler.h" +#include "libcamera/internal/software_isp/debayer_params.h" +#include "libcamera/internal/software_isp/swstats_cpu.h" +#include "libcamera/internal/v4l2_subdevice.h" +#include "libcamera/internal/v4l2_videodevice.h" + +#include "camss_frames.h" +#include "camss_params.h" +#include "camss_util.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(Camss) + +namespace { + +static const std::map inputFormatToMediaBus = { + { formats::SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8 }, + { formats::SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8 }, + { formats::SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8 }, + { formats::SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8 }, + { formats::SBGGR10_CSI2P, MEDIA_BUS_FMT_SBGGR10_1X10 }, + { formats::SGBRG10_CSI2P, MEDIA_BUS_FMT_SGBRG10_1X10 }, + { formats::SGRBG10_CSI2P, MEDIA_BUS_FMT_SGRBG10_1X10 }, + { formats::SRGGB10_CSI2P, MEDIA_BUS_FMT_SRGGB10_1X10 }, +}; + +static const std::map outputFormatToMediaBus = { + { formats::NV12, MEDIA_BUS_FMT_YUYV8_1_5X8 }, + { formats::NV21, MEDIA_BUS_FMT_YVYU8_1_5X8 }, + { formats::NV16, MEDIA_BUS_FMT_YUYV8_2X8 }, + { formats::NV61, MEDIA_BUS_FMT_YUYV8_2X8 }, + { formats::NV24, MEDIA_BUS_FMT_YUV8_1X24 }, + { formats::NV42, MEDIA_BUS_FMT_YUV8_1X24 }, + { formats::R8, MEDIA_BUS_FMT_Y8_1X8 }, +}; + +} /* namespace */ + +/** + * \class CamssIspOpe + * \brief CAMSS ISP class for the Offline Processing Engine (OPE) ISP + */ + +/** + * \brief Constructs CamssIspOpe object + * \param[in] pipe The pipeline handler in use + * \param[in] opeMediaDev The OPE MediaDevice + * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline + * \param[in] frameInfos Pointer to CamssFrames instance owned by the pipeline + * \param[out] ControlInfoMap to which to add ISP provided controls + */ +CamssIspOpe::CamssIspOpe(PipelineHandler *pipe, std::shared_ptr opeMediaDev, + const CameraSensor *sensor, CamssFrames *frameInfos, + ControlInfoMap *ispControls) + : opeMediaDev_(std::move(opeMediaDev)), sensor_(sensor), frameInfos_(frameInfos) +{ + sharedParams_ = SharedMemObject("softIsp_params"); + if (!sharedParams_) { + LOG(Camss, Error) << "Failed to create shared memory for parameters"; + return; + } + + const CameraManager &cm = *pipe->cameraManager(); + + stats_ = std::make_unique(cm); + if (!stats_->isValid()) { + LOG(Camss, Error) << "Failed to create SwStatsCpu object"; + return; + } + + stats_->statsReady.connect(this, + [&](uint32_t frame, uint32_t statsBufferId) { + statsReady.emit(frame, statsBufferId); + }); + + ipa_ = pipe->createIPA("simple", 0, 0); + if (!ipa_) { + LOG(Camss, Error) << "Creating IPA failed"; + return; + } + + /* + * The API tuning file is made from the sensor name. If the tuning file + * isn't found, fall back to the 'uncalibrated' file. + */ + std::string ipaTuningFile = + ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml"); + + IPACameraSensorInfo sensorInfo{}; + int ret = sensor->sensorInfo(&sensorInfo); + if (ret) { + LOG(Camss, Error) << "Camera sensor information not available"; + ipa_.reset(); + return; + } + + bool ccmEnabled; + ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() }, + stats_->getStatsFD(), + sharedParams_.fd(), + sensorInfo, + sensor->controls(), + ispControls, + &ccmEnabled); + if (ret) { + LOG(Camss, Error) << "IPA init failed"; + ipa_.reset(); + return; + } + + ipa_->paramsComputed.connect(this, &CamssIspOpe::paramsComputed); + ipa_->metadataReady.connect(this, + [&](uint32_t frame, const ControlList &metadata) { + metadataReady.emit(frame, metadata); + }); + ipa_->setSensorControls.connect(this, + [&](const ControlList &sensorControls) { + setSensorControls.emit(sensorControls); + }); +} + +CamssIspOpe::~CamssIspOpe() = default; + +bool CamssIspOpe::isValid() +{ + return !!ipa_; +} + +Size CamssIspOpe::getMargins([[maybe_unused]] PixelFormat inputFormat) +{ + /* TODO: Do we really not need any margins on OPE? */ + return Size(); +} + +int CamssIspOpe::init() +{ + MediaEntity *paramsEnt, *inputEnt, *procEnt, *dispEnt, *dispOutputEnt; + int ret; + + if (!isValid()) + return -ENOMEM; + + paramsEnt = opeMediaDev_->getEntityByName("ope_params"); + inputEnt = opeMediaDev_->getEntityByName("ope_input"); + procEnt = opeMediaDev_->getEntityByName("ope_proc"); + dispEnt = opeMediaDev_->getEntityByName("ope_disp"); + dispOutputEnt = opeMediaDev_->getEntityByName("ope_disp_output"); + + if (!paramsEnt || !inputEnt || !procEnt || !dispEnt || !dispOutputEnt) { + LOG(Camss, Error) << "Did not find expected entities"; + return -EINVAL; + } + + params_ = std::make_unique(paramsEnt); + ret = params_->open(); + if (ret) + return ret; + + params_->bufferReady.connect(this, &CamssIspOpe::parameterBufferReady); + + input_ = std::make_unique(inputEnt); + ret = input_->open(); + if (ret) + return ret; + + input_->bufferReady.connect(this, [&](FrameBuffer *f) { + inputBufferReady.emit(f); + }); + + proc_ = std::make_unique(procEnt); + ret = proc_->open(); + if (ret) + return ret; + + disp_ = std::make_unique(dispEnt); + ret = disp_->open(); + if (ret) + return ret; + + output_ = std::make_unique(dispOutputEnt); + ret = output_->open(); + if (ret) + return ret; + + output_->bufferReady.connect(this, [&](FrameBuffer *f) { + outputBufferReady.emit(f); + }); + + return 0; +} + +/* Note cfg is const / not modified when update_stride_and_size is false */ +int CamssIspOpe::trySetVideoCfg(V4L2VideoDevice *v4l2Dev, StreamConfiguration &cfg, + bool set, bool update_stride_and_size, + const char *msgPrefix) const +{ + int ret; + + V4L2DeviceFormat v4l2Format; + v4l2Format.fourcc = v4l2Dev->toV4L2PixelFormat(cfg.pixelFormat); + v4l2Format.size = cfg.size; + v4l2Format.planes[0].bpl = cfg.stride; + v4l2Format.planesCount = 1; + + if (set) + ret = v4l2Dev->setFormat(&v4l2Format); + else + ret = v4l2Dev->tryFormat(&v4l2Format); + + if (ret < 0) { + LOG(Camss, Error) << msgPrefix << " error: " << strerror(-ret); + return ret; + } + + if (update_stride_and_size) { + cfg.stride = v4l2Format.planes[0].bpl; + cfg.frameSize = v4l2Format.planes[0].size; + } + + if (!camssV4L2DeviceFormatMatchesStreamConfig(v4l2Format, cfg, msgPrefix)) + return -EINVAL; + + return 0; +} + +StreamConfiguration CamssIspOpe::generateConfiguration(const StreamConfiguration &raw) const +{ + if (!inputFormatToMediaBus.count(raw.pixelFormat)) { + LOG(Camss, Warning) << "Unsupported pixel format " << raw.pixelFormat; + return {}; + } + + if (trySetVideoCfg(input_.get(), const_cast(raw), + false, false, "OPE input try format")) + return {}; + + /* OPE always supports all output formats */ + std::vector sizesVector = { SizeRange(kMinOutputSize, raw.size, 2, 2) }; + std::map> formats; + for (auto it = outputFormatToMediaBus.begin(); it != outputFormatToMediaBus.end(); it++) + formats[it->first] = sizesVector; + + StreamConfiguration cfg{ StreamFormats{ formats } }; + cfg.size = raw.size; + cfg.pixelFormat = formats::NV12; /* Default to NV12 output */ + cfg.bufferCount = kBufferCount; + + return cfg; +} + +int CamssIspOpe::trySetSubdevFormat(V4L2Subdevice *subdev, + unsigned int pad, + const V4L2SubdeviceFormat &fmt, + V4L2Subdevice::Whence whence, + const char *msgPrefix) const +{ + V4L2SubdeviceFormat subdevFormat = fmt; + + int ret = subdev->setFormat(pad, &subdevFormat, whence); + if (ret < 0) { + LOG(Camss, Error) << msgPrefix << " try/set format error: " << strerror(-ret); + return ret; + } + + if (subdevFormat.size != fmt.size || subdevFormat.code != fmt.code) { + LOG(Camss, Error) << msgPrefix << " mismatch asked " + << fmt.size << "/" << fmt.code << " got " + << subdevFormat.size << "/" << subdevFormat.code; + return -EINVAL; + } + + return 0; +} + +int CamssIspOpe::trySetSubdevSelection(V4L2Subdevice *subdev, + unsigned int pad, + unsigned int target, + const Rectangle &rect, + V4L2Subdevice::Whence whence, + const char *msgPrefix) const +{ + struct Rectangle r = rect; + + int ret = subdev->setSelection(pad, target, &r, whence); + if (ret < 0) { + LOG(Camss, Error) << msgPrefix << " set selection error: " << strerror(-ret); + return ret; + } + + if (r != rect) { + LOG(Camss, Error) << msgPrefix << " mismatch asked " + << rect << " got " << r; + return -EINVAL; + } + + return 0; +} + +int CamssIspOpe::trySetPipelineConfig(const StreamConfiguration &inputCfg, + [[maybe_unused]] const StreamConfiguration &outputCfg, + V4L2Subdevice::Whence whence) const +{ + if (!inputFormatToMediaBus.count(inputCfg.pixelFormat)) { + LOG(Camss, Warning) << "Unsupported pixel format " << inputCfg.pixelFormat; + return -EINVAL; + } + + V4L2SubdeviceFormat subdevFormat; + subdevFormat.code = inputFormatToMediaBus.at(inputCfg.pixelFormat); + subdevFormat.size = inputCfg.size; + + int ret = trySetSubdevFormat(proc_.get(), PROC_PAD_INPUT, subdevFormat, + whence, "OPE proc input"); + if (ret < 0) + return ret; + + /* + * No input-crop, downscale instead to keep full field of view. + * \todo crop in x or y dimension to match requested output aspect, + * to keep square pixels after scaling. + */ + ret = trySetSubdevSelection(proc_.get(), PROC_PAD_INPUT, V4L2_SEL_TGT_CROP, + Rectangle(inputCfg.size), whence, "OPE input crop"); + if (ret < 0) + return ret; + + ret = trySetSubdevSelection(proc_.get(), PROC_PAD_DISP, V4L2_SEL_TGT_COMPOSE, + Rectangle(outputCfg.size), whence, "OPE output compose"); + if (ret < 0) + return ret; + + subdevFormat.code = outputFormatToMediaBus.at(outputCfg.pixelFormat); + subdevFormat.size = outputCfg.size; + + ret = trySetSubdevFormat(proc_.get(), PROC_PAD_DISP, subdevFormat, + whence, "OPE proc disp out"); + if (ret < 0) + return ret; + + ret = trySetSubdevFormat(disp_.get(), DISP_PAD_PROC, subdevFormat, + whence, "OPE disp proc in"); + if (ret < 0) + return ret; + + ret = trySetSubdevFormat(disp_.get(), DISP_PAD_OUTPUT, subdevFormat, + whence, "OPE disp out"); + if (ret < 0) + return ret; + + return 0; +} + +StreamConfiguration CamssIspOpe::validate(const StreamConfiguration &raw, const StreamConfiguration &req) const +{ + if (trySetVideoCfg(input_.get(), const_cast(raw), + false, false, "OPE input try format")) + return {}; + + StreamConfiguration cfg; + + for (auto it = outputFormatToMediaBus.begin(); it != outputFormatToMediaBus.end(); it++) { + if (it->first == req.pixelFormat) + cfg.pixelFormat = req.pixelFormat; + } + + if (!cfg.pixelFormat.isValid()) + cfg.pixelFormat = formats::NV12; /* Default to NV12 output */ + + if (SizeRange(kMinOutputSize, raw.size, 2, 2).contains(req.size)) + cfg.size = req.size; + else + cfg.size = raw.size; + + int ret = trySetPipelineConfig(raw, cfg, V4L2Subdevice::TryFormat); + if (ret < 0) + return {}; + + ret = trySetVideoCfg(output_.get(), cfg, false, true, "OPE output try format"); + if (ret < 0) + return {}; + + cfg.bufferCount = std::max(kBufferCount, req.bufferCount); + cfg.setStream(const_cast(&outStream_)); + + return cfg; +} + +int CamssIspOpe::configure(const StreamConfiguration &inputCfg, + const StreamConfiguration &outputCfg) +{ + ipa::soft::IPAConfigInfo configInfo; + configInfo.sensorControls = sensor_->controls(); + + int ret = ipa_->configure(configInfo); + if (ret < 0) + return ret; + + ret = stats_->configure(inputCfg); + if (ret < 0) + return ret; + + /* Use 2/3 center of image to reduce CPU load */ + Rectangle statsWindow; + statsWindow.width = inputCfg.size.width * 2 / 3; + statsWindow.height = inputCfg.size.height * 2 / 3; + statsWindow.x = (inputCfg.size.width - statsWindow.width) / 2; + statsWindow.y = (inputCfg.size.height - statsWindow.height) / 2; + /* stats_->setWindow() takes care of necessary alignment itself */ + stats_->setWindow(statsWindow); + + ret = trySetVideoCfg(input_.get(), const_cast(inputCfg), + true, false, "OPE input set format"); + if (ret < 0) + return ret; + + ret = trySetPipelineConfig(inputCfg, outputCfg, V4L2Subdevice::ActiveFormat); + if (ret < 0) + return ret; + + ret = trySetVideoCfg(output_.get(), const_cast(outputCfg), + true, false, "OPE output set format"); + if (ret < 0) + return ret; + + inputBufferCount_ = inputCfg.bufferCount; + outputBufferCount_ = outputCfg.bufferCount; + + return 0; +} + +int CamssIspOpe::allocateBuffers(unsigned int bufferCount) +{ + int ret; + + ret = input_->importBuffers(inputBufferCount_); + if (ret < 0) + return ret; + + ret = output_->importBuffers(outputBufferCount_); + if (ret < 0) { + freeBuffers(); + return ret; + } + + ret = params_->allocateBuffers(bufferCount, ¶mBuffers_); + if (ret < 0) { + freeBuffers(); + return ret; + } + + /* Map buffers to the IPA. */ + unsigned int ipaBufferId = 1; + + /* Lambda function for future re-use with statsBuffers. */ + auto pushBuffers = [&](const std::vector> &buffers) { + for (const std::unique_ptr &buffer : buffers) { + Span planes = buffer->planes(); + + buffer->setCookie(ipaBufferId++); + ipaBuffers_.emplace_back(buffer->cookie(), + std::vector{ planes.begin(), + planes.end() }); + } + }; + + pushBuffers(paramBuffers_); + + /* FIXME drop once using own camss IPA, which will map on IPA side */ + for (const IPABuffer &buffer : ipaBuffers_) { + const FrameBuffer fb(buffer.planes); + mappedIpaBuffers_.emplace(buffer.id, + MappedFrameBuffer(&fb, MappedFrameBuffer::MapFlag::ReadWrite)); + } + + frameInfos_->init(paramBuffers_, {}, true); + return 0; +} + +void CamssIspOpe::freeBuffers() +{ + frameInfos_->clear(); + + /* FIXME drop once using own camss IPA, which will map on IPA side */ + mappedIpaBuffers_.clear(); + + ipaBuffers_.clear(); + paramBuffers_.clear(); + params_->releaseBuffers(); + output_->releaseBuffers(); + input_->releaseBuffers(); +} + +int CamssIspOpe::exportOutputBuffers([[maybe_unused]] const Stream *stream, unsigned int count, + std::vector> *buffers) +{ + return output_->exportBuffers(count, buffers); +} + +void CamssIspOpe::queueBuffers(Request *request, FrameBuffer *inputBuffer) +{ + uint32_t frame = request->sequence(); + + DmaSyncer inputDmaSync(inputBuffer->planes()[0].fd, DmaSyncer::SyncType::Read); + + MappedFrameBuffer input(inputBuffer, MappedFrameBuffer::MapFlag::Read); + if (!input.isValid()) { + LOG(Camss, Error) << "mmap-ing buffer(s) failed"; + return; + } + + ipa_->queueRequest(frame, request->controls()); + stats_->processFrame(frame, 0, input); + ipa_->computeParams(frame); + /* paramsComputed() will queue all the buffers once the params are known. */ +} + +void CamssIspOpe::paramsComputed(uint32_t frame) +{ + CamssFrames::Info *info = frameInfos_->find(frame); + if (!info) + return; + + FrameBuffer *outputBuffer = nullptr; + for (const auto &[stream, buffer] : info->request->buffers()) { + if (stream == &outStream_) + outputBuffer = buffer; + } + if (!outputBuffer) { + LOG(Camss, Error) << "Missing output buffer"; + return; + } + + /* + * FIXME drop once using own camss IPA, which will do this on IPA side. + * IPA paramsComputed signal should also pass a bytesused value back. + */ + uint32_t bufferId = info->paramBuffer->cookie(); + ipa::camss::CamssParams params(mappedIpaBuffers_.at(bufferId).planes()[0]); + auto awbGains = params.block(); + DebayerParams debayerParams = *sharedParams_; + /* Convert gains to 15uQ10 and store */ + awbGains->r_gain = std::clamp(debayerParams.gains.r(), 0.0f, 31.0f) * 1024; + awbGains->g_gain = std::clamp(debayerParams.gains.g(), 0.0f, 31.0f) * 1024; + awbGains->b_gain = std::clamp(debayerParams.gains.b(), 0.0f, 31.0f) * 1024; + /* Convert blacklevel to 0-65535 (u16) and store */ + awbGains->r_sub = debayerParams.blackLevel.r() * 65535; + awbGains->g_sub = debayerParams.blackLevel.g() * 65535; + awbGains->b_sub = debayerParams.blackLevel.b() * 65535; + /* Unused */ + awbGains->r_add = 0; + awbGains->g_add = 0; + awbGains->b_add = 0; + + info->paramBuffer->_d()->metadata().planes()[0].bytesused = params.bytesused(); + params_->queueBuffer(info->paramBuffer); + output_->queueBuffer(outputBuffer); + input_->queueBuffer(info->rawBuffer); +} + +void CamssIspOpe::parameterBufferReady(FrameBuffer *f) +{ + CamssFrames::Info *info = frameInfos_->find(f); + if (!info) + return; + + info->paramDequeued = true; + + paramBufferReady.emit(f); +} + +void CamssIspOpe::processStats(const uint32_t frame, const uint32_t statsBufferId, + const ControlList &sensorControls) +{ + ipa_->processStats(frame, statsBufferId, sensorControls); +} + +int CamssIspOpe::start() +{ + int ret = ipa_->start(); + if (ret) + return ret; + + ret = input_->streamOn(); + if (ret < 0) { + stop(); + return ret; + } + + ret = output_->streamOn(); + if (ret < 0) { + stop(); + return ret; + } + + ret = params_->streamOn(); + if (ret < 0) { + stop(); + return ret; + } + + return 0; +} + +void CamssIspOpe::stop() +{ + params_->streamOff(); + output_->streamOff(); + input_->streamOff(); + ipa_->stop(); +} + +/** + * \brief Match media devices and create OPE instance if found + * \param[in] pipe The pipeline handler in use + * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline + * \param[in] frameInfos Pointer to CamssFrames instance owned by the pipeline + * \param[out] ControlInfoMap to which to add ISP provided controls + */ +std::unique_ptr CamssIspOpe::match(PipelineHandler *pipe, + DeviceEnumerator *enumerator, + const CameraSensor *sensor, + CamssFrames *frameInfos, + ControlInfoMap *ispControls) +{ + std::shared_ptr opeMediaDev; + DeviceMatch opeDm("qcom-camss-ope"); + + opeDm.add("ope_params"); + opeDm.add("ope_input"); + opeDm.add("ope_proc"); + opeDm.add("ope_disp"); + opeDm.add("ope_disp_output"); + + opeMediaDev = pipe->acquireMediaDevice(enumerator, opeDm); + if (!opeMediaDev) { + LOG(Camss, Info) << "No OPE match for " << sensor->entity()->name(); + return nullptr; + } + + std::unique_ptr ope = + std::make_unique(pipe, std::move(opeMediaDev), sensor, + frameInfos, ispControls); + + if (ope->init() != 0) + return nullptr; + + LOG(Camss, Info) << "Using OPE for " << sensor->entity()->name(); + + return ope; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/camss_isp_ope.h b/src/libcamera/pipeline/camss/camss_isp_ope.h new file mode 100644 index 000000000..8909f9bac --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_isp_ope.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS OPE ISP class + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#pragma once + +#include +#include + +#include +#include + +/* FIXME drop once using own camss IPA, which will map on IPA side */ +#include "libcamera/internal/mapped_framebuffer.h" +#include "libcamera/internal/shared_mem_object.h" +#include "libcamera/internal/software_isp/debayer_params.h" +#include "libcamera/internal/v4l2_subdevice.h" + +#include "camss_isp.h" + +namespace libcamera { + +class CameraSensor; +class CamssFrames; +class ControlInfoMap; +class DeviceEnumerator; +class MediaDevice; +class PipelineHandler; +class Stream; +class SwStatsCpu; +class V4L2VideoDevice; + +class CamssIspOpe : public CamssIsp +{ +public: + static std::unique_ptr match(PipelineHandler *pipe, + DeviceEnumerator *enumerator, + const CameraSensor *sensor, + CamssFrames *frameInfos, + ControlInfoMap *ispControls); + + bool isValid() override; + Size getMargins(PixelFormat inputFormat) override; + + CamssIspOpe(PipelineHandler *pipe, std::shared_ptr opeMediaDev, + const CameraSensor *sensor, CamssFrames *frameInfos, + ControlInfoMap *ispControls); + ~CamssIspOpe() override; + + StreamConfiguration generateConfiguration(const StreamConfiguration &raw) const override; + StreamConfiguration validate(const StreamConfiguration &raw, const StreamConfiguration &req) const override; + int configure(const StreamConfiguration &inputCfg, + const StreamConfiguration &outputCfg) override; + + int allocateBuffers(unsigned int bufferCount) override; + void freeBuffers() override; + int exportOutputBuffers(const Stream *stream, unsigned int count, + std::vector> *buffers) override; + void queueBuffers(Request *request, FrameBuffer *input) override; + + void processStats(const uint32_t frame, const uint32_t statsBufferId, + const ControlList &sensorControls) override; + + void paramsComputed(uint32_t frame); + void parameterBufferReady(FrameBuffer *f); + + int start() override; + void stop() override; + +private: + static constexpr Size kMinOutputSize = Size(24, 16); + static constexpr unsigned int PROC_PAD_INPUT = 0; + static constexpr unsigned int PROC_PAD_PARAMS = 1; + static constexpr unsigned int PROC_PAD_DISP = 2; + static constexpr unsigned int DISP_PAD_PROC = 0; + static constexpr unsigned int DISP_PAD_OUTPUT = 1; + + int init(); + int trySetVideoCfg(V4L2VideoDevice *v4l2Dev, StreamConfiguration &cfg, + bool set, bool update_stride_and_size, + const char *msgPrefix) const; + int trySetSubdevFormat(V4L2Subdevice *subdev, + unsigned int pad, + const V4L2SubdeviceFormat &fmt, + V4L2Subdevice::Whence whence, + const char *msgPrefix) const; + int trySetSubdevSelection(V4L2Subdevice *subdev, + unsigned int pad, + unsigned int target, + const Rectangle &rect, + V4L2Subdevice::Whence whence, + const char *msgPrefix) const; + int trySetPipelineConfig(const StreamConfiguration &inputCfg, + const StreamConfiguration &outputCfg, + V4L2Subdevice::Whence whence) const; + + std::shared_ptr opeMediaDev_; + const CameraSensor *sensor_; + const Stream *rawStream_; + class CamssFrames *frameInfos_; + SharedMemObject sharedParams_; + std::unique_ptr stats_; + std::unique_ptr proc_; + std::unique_ptr disp_; + std::unique_ptr params_; + std::unique_ptr input_; + std::unique_ptr output_; + std::unique_ptr ipa_; + std::vector> paramBuffers_; + std::vector ipaBuffers_; + /* FIXME drop once using own camss IPA, which will map on IPA side */ + std::map mappedIpaBuffers_; + unsigned int inputBufferCount_; + unsigned int outputBufferCount_; +}; + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/camss_params.h b/src/libcamera/pipeline/camss/camss_params.h index 6dec85fbc..c09d2c4c2 100644 --- a/src/libcamera/pipeline/camss/camss_params.h +++ b/src/libcamera/pipeline/camss/camss_params.h @@ -20,7 +20,7 @@ namespace libcamera { namespace ipa::camss { -enum class CamssBlocks { +enum class CamssBlocks : uint16_t { AwbGains, ChromaEnh, ColorCorrect, diff --git a/src/libcamera/pipeline/camss/meson.build b/src/libcamera/pipeline/camss/meson.build index 2f3931dd1..cffb53ef5 100644 --- a/src/libcamera/pipeline/camss/meson.build +++ b/src/libcamera/pipeline/camss/meson.build @@ -5,6 +5,9 @@ libcamera_internal_sources += files([ 'camss_csi.cpp', 'camss_frames.cpp', 'camss_isp.cpp', + 'camss_isp_ope.cpp', 'camss_isp_soft.cpp', 'camss_util.cpp', + # FIXME hack + '../../../ipa/libipa/v4l2_params.cpp' ])