From patchwork Fri Nov 6 15:13:35 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: 28790 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 A6BA8C3304 for ; Fri, 6 Nov 2026 15:13:57 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 757796A7D5; Fri, 6 Nov 2026 16:13:54 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="MRfJkANy"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="cRnL0G1N"; 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 9296D6917D for ; Fri, 6 Nov 2026 16:13:52 +0100 (CET) Received: from pps.filterd (m0279864.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 6A6F05QC2671820 for ; Fri, 6 Nov 2026 15:13:51 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= zEuDfvNyBI/3kEaxhabUhBY45YJLppM+BpglngAuO7Y=; b=MRfJkANy0AZCVDRK D8xjUtOzqcrDmMzlD/Z4UHWbofKjwVygjTvUgXfvbyuKt0IDFupe0hhJiJk7UUgB /rXTt75T2luYGxJvS4IbmLxqwVCkCqarL18T4LWMtNkoRteApBTJvxu5WqTVX5ij iWFwqyA6Ni1yN5YBzxxIrrPTpd7+sul/8acq6TBO05txVS43IlDtbUK2LoPlg5JU 2BlzPSyKsLlve4lFf2uNsMmSCSniuzPPm4xUwIzNfjR1iaAl+h0WBmdmuWj7jE08 zNtyOzNGK4SJ9WEjh6DoJ4QRhyl31ZV/z4X2Bjxq30cf6V7GEIwnVaOGjMaDvd/s uaTMIQ== Received: from mail-qk1-f198.google.com (mail-qk1-f198.google.com [209.85.222.198]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4hs3pcbad1-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 06 Nov 2026 15:13:50 +0000 (GMT) Received: by mail-qk1-f198.google.com with SMTP id af79cd13be357-93f322ef7d3so177650485a.0 for ; Fri, 06 Nov 2026 07:13:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1793978030; x=1794582830; 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=zEuDfvNyBI/3kEaxhabUhBY45YJLppM+BpglngAuO7Y=; b=cRnL0G1Nhwe2vtz18zBjo9bOKNdBeyRCcv1Bsy0uwm16wYlgnSZW7ziHqILhI9ibw8 dD+nXauEUbvwGqc426I3ArWjQSaVEwoklnT1jWULulrGrWSRyWapHNHRFR872sCsD2L+ Q/dJG5e9k9vqrGD1cVEwY6UmwqSgWy1rqwG4vnrl/dTz9GJU2WX+fZgNL14yhux8nHXe xaicPg0hssPqyLGGzcXWOCTk5Fn2Acg6lxptoqyTIKNVxJsk2bjC2nDN1dVzVJJB5J9e +R72jpwihN42yOvBNm+fK0uOoqA0K80p25eA8nqpwWowl58Tv/FuUaAbtyBak/jMTvrc tsNQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793978030; x=1794582830; 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=zEuDfvNyBI/3kEaxhabUhBY45YJLppM+BpglngAuO7Y=; b=Pex0jgdzfSfp2E1vowWDOEPe0EsUdSKeVCFfF0UpFpoRdglHVgzys5nyCp/FfgYB/B j0bbrGhTa9P4+g85L9X0BFCR6o0AqKF8neijQQchlSmMqaPR0g5yonDhOJNMqAVVO8d2 c4eHId0m7VLSZBz5hxsDy7urIB+yxFre+obVSflSyfagNJwpK7L52W0bjzt3oZRv3ok2 tdE4a4Iz54CAM/Gxm4ycDrjaVRLkkqqlDRvElNjHGowj79Esw1k/y1kRR7y6II5LL7PY mp2XQ8GnGQjwN19JIh7ap66wvoqHmStFPN8rh8zTyz1A8lw0hxXqKhzPAab8w5V9w3DE IWfg== X-Gm-Message-State: APuU4Z/+YvhWJNdVZvDt+qYDF1oTmz4JQT5m5UvEVL1L7zETzepwenwr MnWxhL4QRD3KXCSWgWw5snVohcHRLQgp2vtE7TORiFjPivE8BGZ0uzRGtV6FirHa4fz29a13+ws 9dxODXSmNq1q6rHOPGOAU03OQAeQkj/UP28+kqUc4Q3YXTONmfzpIHWQMsKLqeLcg9vt6kOTBkn 0BygV4VlRR X-Gm-Gg: AYBFou3pfgBxxdKVZlT2K2MlrOofRNAFScObWpNpdHlHtvl29mjq9BQvq69ByQQe/yc /EUoS0q/bDnsBVLzkSnY4yBfqqSHYJoOCuF6s1XG6PR2BWQpVuDLxnj39/XP6JUGs0isAxc77x4 x4CykgeCFOFOtivKHgOZj9kwxKwoQDHrbAeh8nkvANVOS8u3hV0GTIia/pzKlXsAlLomonG5IRu XAi8oI356Jof+UMDCHBvBj5TbZrH+NX5H1w1fx7OFnd/1jLR8YkFbCeSQmMAArgQ5Znyg/Bb/dW ZyMilweBHZJlH+PzEtbj2h8WrUC7Q9Dlr7ETT3nl8x6HXS3uUzLD2CVADCMLKlvuEx1MT1AVkPp r88nak6PIFqPGZloKIc6scpL8xLMWpkxj+bzBloKxwVgTl1TOodHNMjUwvbtNovkt7zK1nV/BlG vAZIABy366sykPsGKd+tSN5lmYDZ4t+AjiMaee2W4hkUde8Il9ayCZ3dYiygHQdAaV X-Received: by 2002:a05:620a:2b9a:b0:945:e23a:455c with SMTP id af79cd13be357-945f34d7864mr382186585a.20.1793978029650; Fri, 06 Nov 2026 07:13:49 -0800 (PST) X-Received: by 2002:a05:620a:2b9a:b0:945:e23a:455c with SMTP id af79cd13be357-945f34d7864mr382181385a.20.1793978029122; Fri, 06 Nov 2026 07:13:49 -0800 (PST) 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-c4d3569b856sm86042466b.46.2026.11.06.07.13.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 06 Nov 2026 07:13:48 -0800 (PST) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Gjorgji Rosikopulos , Loic Poulain , Hans de Goede , Kieran Bingham , =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= Subject: [PATCH v3 1/7] libcamera: V4L2Subdevice: Add whence argument to [get|set]Selection() Date: Fri, 6 Nov 2026 16:13:35 +0100 Message-ID: <20261106151345.32491-2-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> References: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: SFSux-AVOd02OsP9JW4vORclnYr8DumB X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfX6mM0N1nbHZar aVfk1NYqzIjvqkNBII1pVtvIotfCTAyTA5iQYG0r17nO6XC+hOr9BJpO8jgZKQL1vn4fGs4H50K WrkkNhEUigNc5o2Nj3VUwSw+pRqKQPhGil+v1Xv4nMioq5xgpo8YK6DUhn1r48upq5K0msXPXd1 C1IizRHh3MmMG2V/R26deco4m5H7ktG0OF/wwx48SkAPUqGF4VJUaqPSF3Q2BApwrnNUMcu4Db3 k6DerIUOO9h7OqWx5goNkQAiOMEYCRjmGuPt8LmmihnP2zysIDoepsSY+847uykt2r9gJ+mlLF8 /NW1vox8/9wrUGTEzeTfkmTRr+P/TAd2KCJM/EoDKRTZ0Kw7cYIwfVRJMX8W2egzTttCNInV6hS cdDnAYPO20bP8wv9FJX1ikkj142d/tbJW1RQYjH8zZNdjATlhEXGt3W36oFgcfUuHe2q+9ei9/y dVr5DQQ7Fk8SL279wlQ== X-Proofpoint-GUID: SFSux-AVOd02OsP9JW4vORclnYr8DumB X-Authority-Analysis: v=2.4 cv=TI3Q2Fla c=1 sm=1 tr=0 ts=6aedeeae cx=c_pps a=qKBjSQ1v91RyAK45QCPf5w==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=zJRp6_cAMcgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=DJpcGTmdVt4CTyJn9g5Z:22 a=P1BnusSwAAAA:8 a=EUspDBNiAAAA:8 a=BV3TIWPM9q02PJwuZ20A:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 a=NFOGd7dJGGMPyQGDc5-O:22 a=D0XLA9XvdZm18NrgonBM:22 X-Proofpoint-Spam-Info: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfXzRtlvTSf/ng/ uiYaD4XNhZcr+f83MsphwqF9wvNOp0RRpJDjT9K1BgFBjsk7WFiLwj6cNci+meECTSmt8pHvpcr S9F2G4h1Q7tIP+3VZCk5xY1BiPknJ2c= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1176, Hydra:6.1.138, FMLib:17.12.100.49 definitions=2026-11-06_05,2026-11-06_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 suspectscore=0 adultscore=0 clxscore=1015 lowpriorityscore=0 spamscore=0 phishscore=0 malwarescore=0 priorityscore=1501 impostorscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2611040001 definitions=main-2611060059 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. Reviewed-by: Kieran Bingham Reviewed-by: Barnabás Pőcze 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 Fri Nov 6 15:13:36 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: 28791 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 11336C3304 for ; Fri, 6 Nov 2026 15:13:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 459C16A7DF; Fri, 6 Nov 2026 16:13:56 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="jqA2c50D"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="B7mr4X6H"; 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 B367E6A7CE for ; Fri, 6 Nov 2026 16:13:53 +0100 (CET) Received: from pps.filterd (m0279863.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 6A6Exs98044150 for ; Fri, 6 Nov 2026 15:13: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=EJeHoEk6HAM HrU+Nj1H9vN5Yo7pEoQ9CMxnSmnnW0VY=; b=jqA2c50DYRrvNuv4PmKm+gqpVqR CJSjTZEM9d6TF/SfsEkDPMCq+tsGhEsZ5rk3aGXrkywmiZxIgSYQ0Mq52pf+ugjs NTwrnEphCOFCcVvqbuHScHKF06HNMZWjPUkwCrOGqgvDzaZMenuzF4ZLFDTp4YtO Lx3AhSbvN2Ze+2r+MwJDXwZan4ZoH+DECgE9IJP2jv9S5JcW5XRzaEn8DgUPiesT 8liX6QyoMTFRRIL1eSRPSnmVL4FAx8yV3EOfBWjCLUH9HdCshMQhRH4StbOmln8G DXvsbJQS3HFhfdJGSXmLKmvRyJXp4QL32lZxiET6/q+lBGo0yUczEqc1QHA== Received: from mail-qk1-f199.google.com (mail-qk1-f199.google.com [209.85.222.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4hs3pa39e3-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 06 Nov 2026 15:13:51 +0000 (GMT) Received: by mail-qk1-f199.google.com with SMTP id af79cd13be357-93f26dd2165so254093085a.3 for ; Fri, 06 Nov 2026 07:13:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1793978031; x=1794582831; 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=EJeHoEk6HAMHrU+Nj1H9vN5Yo7pEoQ9CMxnSmnnW0VY=; b=B7mr4X6HK2axJKvzFWlTZ5bSuDag8pQXkM5AmS+q42JiTXkBwC6lqTQR+CGRZ5DizF C5E87I6f7vZjcUaTiW/JkFHsh/lorUIkMHp/9hZbdu32fXQvZMuKyFkivj+TEElSbBim XVHBUvU/05cVI4InTnDqql/N+yU32UeRuy7+YxDZataAtijuZvEFOONmnLyqiMZflDl1 ratPySRPnI+r2201ANv5DG1Rl0aJOix90eF/EguqMxh0MKvWo3V4MpFNXCkYr9efyQFZ uPm9bQZIMmu4ctCZ9vI/EcD6PMGm3IZ1aqxj3fxyqm+JiIhmqAKXt4BcRTgLYN8FbmzC 05zg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793978031; x=1794582831; 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=EJeHoEk6HAMHrU+Nj1H9vN5Yo7pEoQ9CMxnSmnnW0VY=; b=Z5IwRvrOKf9FRdaSQBoDYGxoOyCdX1ijRrR1JKObFD2jUPrMv36nLVAA1b45baipQL sQxt1UpIuTm99Io8l+Qy+viWIAWrCXga+4upSWiJ7gk9j87McJkKTEgWGUbewRGYBmtQ SM78GQWWHaYSgNYWI2lSm/SkpbyufD/0LzCQK72TrrAXA7Hb3Lq8eJvK+44JFK7YMwbj MKh3wrTWVf1MfmZ2dXOCCm1iRlcd69LaNJnHM6FyCo59mYzBzzVvJRUZGEig1xal6p/5 XdkCkDsTohxFJ5MEhpKa5sGx3nZlAPob5x4cbX4uW/r7sdMlDAAsQSPv7YZ7HuuJ/gR1 3l+A== X-Gm-Message-State: APuU4Z+Muy8uBPZ5sIpRTdyIUh6/fkd3iNjWTysw4oE3svqVO/wLLE7+ F0cLEU04rSvWcF192YfSIoJGF0SXorsMybeJcSbc9gqOeZtRGwrMymtw30idb2oG9MdyrcLKssT gcyx2vQcd+DmNb61Py01N9j70OxcW9QJ+ffwAwJCrT8btLGmhyEUGs3HmbleGsK49VKHbb/+Vdb 8DxtIsEoZd X-Gm-Gg: AYBFou2TS32CXceqqCKQtPgiD2Cg3bLtXtWjaH3cXZoz1pyd82NHsE6ZzfHnKjcUPtD +MrWRuON/w/wdxueLu73hn7jN2BhYS5mJEjiiyUWeMMmvIqrSdu7jXqQwNqF4I2a7wugZUWGaiK esnPu6MnSHJc1j6A7YE3kNsQjh5so6NN1NhXuCKnJWfiIdOqAblm3AhM7O/HaKRKY9qjiyG8Eze 2820ik4Ju6lUMNTNs3Ccz6uCszXCwuNJD8Ds+pZCpTgIk8DFrKg8mFuC6nJ5d8DeoXRbIuInVRd CF7b/fUXJWdM/2BwawLZzR4lqOV7rmibFAeqvi34fma0ZCodunevoFpYmNQ2c1ysEsA9oI/tv7v ckLxMoDSClksBjn/rMI/ae6b/BrM+h1Iiw4HNa2gnpHG6iCKkA1yRr1u2l42bK9bMfkDZMgzW4p +bH3/FgbmzRISJ0KnbFIXgmI2RbZtKIWq9so89AtBlqU4/MpeZ6zBuEkYamCRaKZPX X-Received: by 2002:a05:620a:84c9:b0:945:d1ed:5d83 with SMTP id af79cd13be357-945f3562e34mr418647985a.54.1793978030859; Fri, 06 Nov 2026 07:13:50 -0800 (PST) X-Received: by 2002:a05:620a:84c9:b0:945:d1ed:5d83 with SMTP id af79cd13be357-945f3562e34mr418641785a.54.1793978030270; Fri, 06 Nov 2026 07:13:50 -0800 (PST) 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-c4d3569b856sm86042466b.46.2026.11.06.07.13.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 06 Nov 2026 07:13:49 -0800 (PST) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Gjorgji Rosikopulos , Loic Poulain , Hans de Goede Subject: [PATCH v3 2/7] camss: Add camss utility functions Date: Fri, 6 Nov 2026 16:13:36 +0100 Message-ID: <20261106151345.32491-3-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> References: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Authority-Analysis: v=2.4 cv=PLeaavqC c=1 sm=1 tr=0 ts=6aedeeaf cx=c_pps a=HLyN3IcIa5EE8TELMZ618Q==:117 a=xqWC_Br6kY4A:10 a=zJRp6_cAMcgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=yOCtJkima9RkubShWh1s:22 a=EUspDBNiAAAA:8 a=Z-vQv52y2RNpiqyDFZQA:9 a=bTQJ7kPSJx9SKPbeHEYW:22 X-Proofpoint-Spam-Info: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfX0FAGgodlfxp0 fYfwSwDjhirZz9UwIQl3tduXoLWIK4evipLB0wmKANWTP/iw1aj9uAZf9VmnU2drPm2Fh4hZmpw uO5mhRDUrP0iC7W/ynYxqteOia11tis= X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfX7gAPJSh/lUf3 CYX4Nt4LLTNX0G1j2fwvrYHoIJf2gF2f+a0xgrN327l8ZnRfpXbKvY/K/9Lk82Qg3KgyorLsNKS hMDYCaMdK2Z7TyyEfW0JdP6KChSfFX5SHwHOinTglTJVqkXCVfqURCNljRmQENdJ+fVG65gIMJH 9ymi/nxp8UuMxyTUxOuimwYONDIzKGcbkx1w9JRzoNsfHLjyxAcnCIOUY+mDDGi685JHnCiwk/+ sZkhjPOqFFXyT3zFFwpjy1UgfsF+HWjgLnnSVA+tN/yQ8WwjkxX5CGAaUVdxzjXKsji9Wrm2ReZ NXa94qdRFuNR3O7C6tFEAV1irTUXujCydhUW/9d7hMHRCcydl8DjNw8UcZsZOId/wtCKB0or6fP 5PMCV5bkldrQzt1+UOK+Dxf8s1bsfug90tK+/wdtOXMkQB0Tdpzo7tc35io8MqTGTwPHpB9cY4y Te/6cV3vKC16CkXZn3g== X-Proofpoint-GUID: ekZ0WRMZkYXvx02AHTpmmq0LLaXH1H4B X-Proofpoint-ORIG-GUID: ekZ0WRMZkYXvx02AHTpmmq0LLaXH1H4B X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1176, Hydra:6.1.138, FMLib:17.12.100.49 definitions=2026-11-06_05,2026-11-06_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 lowpriorityscore=0 impostorscore=0 malwarescore=0 clxscore=1015 bulkscore=0 priorityscore=1501 spamscore=0 phishscore=0 adultscore=0 suspectscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2611040001 definitions=main-2611060059 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 Fri Nov 6 15:13:37 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: 28793 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 C3915C3304 for ; Fri, 6 Nov 2026 15:14:01 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B5C386A7E3; Fri, 6 Nov 2026 16:14:00 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="lizIXh+g"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="UQqilKty"; 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 50DEC690F7 for ; Fri, 6 Nov 2026 16:13:57 +0100 (CET) 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 6A6ExrCa3722866 for ; Fri, 6 Nov 2026 15:13:55 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=jeHCxKcdqFC 3MVniB5sn/DM3cjQgSMypuWlll1AMYB4=; b=lizIXh+geEZRsOLuHg9WeLdArkF SVf4/cq4TCUA9LB2bIMyGExJyq2d1R+CLBKWhaopiw4yUY0kiMPSF9W6nDfbpLPt 9hsFmICG0voVV+bhxXMy4FB8Saox1tjoEpbFnY2gnM+9qwzV5+GwXPdWiPrdGzDu 6HGX9Hm6ZSBjsP7iCT5ssSxcoGkXHENjRY5C/l0VvisKhCzyx8WzqmzaeB6hssPu uYNExf8iqFUWL2fF3CG2Bo6pSewBxTYDRBQ8J8dYvCl0MNz4aOO0mu3du9YOjQAd wWrp3LW4TSVleziqLwGRztarKQ7b9DKBmL3ae6eIcqbWAUytIiMS42sHX+g== Received: from mail-qk1-f199.google.com (mail-qk1-f199.google.com [209.85.222.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4hs3p9k96v-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 06 Nov 2026 15:13:54 +0000 (GMT) Received: by mail-qk1-f199.google.com with SMTP id af79cd13be357-93f5844cb60so157131785a.0 for ; Fri, 06 Nov 2026 07:13:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1793978034; x=1794582834; 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=jeHCxKcdqFC3MVniB5sn/DM3cjQgSMypuWlll1AMYB4=; b=UQqilKtyv2WeCJw5n10ozQAuikZbHbYYDZLCmVLzPzE4U6QG9xoGMwG1YuuEikTw1N PDY9UcfR76K4TapldBBAk5x+M4rkGi+KU+eqi6BWsHhG6IOMAQqr4+K98Z+AV9I6Fw8R OMj5ACeXzy2hk8kKrLN/DB7aO7MQytIiTIfx3XToh3UtiZuajYEDX5ntkPv+wcibh2vM R6uiITt4lpR83+DUBSF8eGOFAFt69Ooyfvf5Rdv7FV/PsjKydyDue6AOR2d270z0CfCe FGfas2aLQrpB5UUkPTMRkNwkjsdnsOrdjqpQ6AEUIcpa9ouMV7oKaUaju5R7z3bW9FWM gWRA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793978034; x=1794582834; 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=jeHCxKcdqFC3MVniB5sn/DM3cjQgSMypuWlll1AMYB4=; b=UBhNwld0ZsHr773t/a0GtmJNQfOcVNFHuMf77yj/ngFX7bp1oPF7j7fSFETaAp6L7M uGW0gLdfgj4nByKZqmDyNJdgffxEo6o6+Y3WybSFc0n7Nng1Z/ElNEu0quICh/s4zGtw TY0gGDHNvL7s7YL+tNjofce8bdOUD9O7lcIe7fe3oLCl3Bp+20Ut/TgvNfGY9hwmx8D3 pDzphvih/WnPJUjm0x596QPmqowYffuqKpuapySlTSUMUz2acU3JV/6mmiJ9EUds2cJC /Fy4R5+jFwb9EZRmY3gAIPu2KBnAW3N15C+7kHf5RO4X9di8bDm6K44zSrg9aK8rkJEQ KavA== X-Gm-Message-State: APuU4Z84XGj2gr8e9Ik0BAJhCFCWq88K+KiG9YZymVt6EBgnGheF2zuZ fo6Brd9B9XVSAOcv40N/qNMREcD/SR6lHraLDuOC/UB637mvu9Hke/QeJtwiiWbJohQ44NV1ipP KtCDXDvFC9kGZweT9FLcghQJLVs3CU1uKD4eTj+1NH6cU4K95wAhzGO36ODHstxJjy9lL6Hd7cx GmI1dCfcgV X-Gm-Gg: AYBFou0Ti1bsEuImgkhaQ4pdbZcu1ZsH/R58ZPH8bfVImqnz+EvvwuG+OHGDqqXexsK biwDo8DO64PLjiUKyuEH/WfhH4BFIU7RGxlSgicTvCkH//Flas/qGxh/dRupME72IC1fwQWBaub 6GRxFwaOBJx0ccyjHlv5sWcau6pDGOLtbuo6uRiO+1Y8Isead0Bib2LUxn6ToXZ8TcNmbqtgJew qi8BooGyjvmJylbdEfXmYMVIkiH/6e1Y8PNe6wPN+cLJRRX8MF+IbA0G/IKtC7KKVcFfswsg0ij kJ4l31y1KVuoWtM2N7mHBDrjw5oZck4RJk6UGj70DWMcj+RNqGzUwCoF6F6Bbr8f/p+NuNjcumL rVR+emOSAdGpD0GWDSq29fDi35jbxA+6kuBkSKrI4kA7dksVVMWHNvgoPbHNFcTJz0itAVSVbNt KoBf/WVCtMt+iS/PqrO/zTu2fv847quV0eSi8UmD+JqIICEogae71tiHNHp8o627Hg X-Received: by 2002:a05:620a:2788:b0:943:e872:f521 with SMTP id af79cd13be357-945e16aa3d0mr835374085a.9.1793978033353; Fri, 06 Nov 2026 07:13:53 -0800 (PST) X-Received: by 2002:a05:620a:2788:b0:943:e872:f521 with SMTP id af79cd13be357-945e16aa3d0mr835359985a.9.1793978032015; Fri, 06 Nov 2026 07:13:52 -0800 (PST) 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-c4d3569b856sm86042466b.46.2026.11.06.07.13.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 06 Nov 2026 07:13:50 -0800 (PST) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Gjorgji Rosikopulos , Loic Poulain , Hans de Goede Subject: [PATCH v3 3/7] camss: Add camss pipeline handler Date: Fri, 6 Nov 2026 16:13:37 +0100 Message-ID: <20261106151345.32491-4-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> References: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: VCjytHig5UGX4QDvQpWvI-viMwRWcnNQ X-Proofpoint-GUID: VCjytHig5UGX4QDvQpWvI-viMwRWcnNQ X-Proofpoint-Spam-Info: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfX827a9WwIFkca hcLDKfFyMk2oYAqsP1lQjclUvg0i4Lul0y+DxrBtHG7hRMh6juHFfTGjRi97oi+fgJQdX6KzVF1 zPF6De7+JxfC05F9xlgmujyzUjLn298= X-Authority-Analysis: v=2.4 cv=XvNvqlF9 c=1 sm=1 tr=0 ts=6aedeeb3 cx=c_pps a=HLyN3IcIa5EE8TELMZ618Q==:117 a=xqWC_Br6kY4A:10 a=zJRp6_cAMcgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=_K5XuSEh1TEqbUxoQ0s3:22 a=EUspDBNiAAAA:8 a=ErEb1ksjjALba-Z9VpUA:9 a=P-oGHa0Z2j53quwT:21 a=bTQJ7kPSJx9SKPbeHEYW:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfX9kLzjBUoDm86 bsLFjNruWzLrdeUXc/ebDxryQ2U9Yhh+pYlNIB95RhIw/0VdRvPQfaKWUeyM8m+yW3XqrXkHpKy cSMTd1xIjRb5Y6T1vWWHiLW+GjC4zXMcbP8cHDWzSwBsr9MBn6PWLj0WCiaJrb5EzHiTjWlQhhU snhKX69c/r45nZlhj0/fRfiC1XBw7K7dAgiFOwEjtFb/AYDoDE//lWCqCA8Rr8e9EATI+qVB6f3 k6FydZ0AOycuF/j2Z9QmP/fTMbvO0HJ3EeGxqQk7JO1vcgUUp8jdvDmczuBwRkoLSjVtv4whepf ASFJPwzFHPrZSyRBK5foAD0ED8xLdkAvhj8MQlorcujK2Lx9UIqKhW3XaQ85aKfnMHBgut04GUD Yb0DDz8g4rZdCpdGjB3yInu5m/XIgjXeKGPHohzf51Ue+jFPNoQQQ60Z36Vv2u6fxy+S+hVp2xP I0CnBQXf4yhGPo+58BA== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1176, Hydra:6.1.138, FMLib:17.12.100.49 definitions=2026-11-06_05,2026-11-06_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 suspectscore=0 adultscore=0 malwarescore=0 impostorscore=0 phishscore=0 spamscore=0 lowpriorityscore=0 bulkscore=0 priorityscore=1501 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2611040001 definitions=main-2611060059 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 --- include/libcamera/ipa/meson.build | 1 + meson.build | 1 + meson_options.txt | 1 + src/ipa/meson.build | 1 + src/libcamera/pipeline/camss/camss.cpp | 688 ++++++++++++++++++ src/libcamera/pipeline/camss/camss_csi.cpp | 571 +++++++++++++++ src/libcamera/pipeline/camss/camss_csi.h | 129 ++++ src/libcamera/pipeline/camss/camss_frames.cpp | 153 ++++ src/libcamera/pipeline/camss/camss_frames.h | 70 ++ src/libcamera/pipeline/camss/camss_isp.cpp | 26 + src/libcamera/pipeline/camss/camss_isp.h | 64 ++ .../pipeline/camss/camss_isp_soft.cpp | 227 ++++++ src/libcamera/pipeline/camss/camss_isp_soft.h | 57 ++ src/libcamera/pipeline/camss/meson.build | 10 + src/libcamera/pipeline/simple/simple.cpp | 1 - 15 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/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build index fb518526a..a8337a0e1 100644 --- a/include/libcamera/ipa/meson.build +++ b/include/libcamera/ipa/meson.build @@ -63,6 +63,7 @@ libcamera_ipa_headers += custom_target('core_ipa_serializer_h', # Mapping from pipeline handler name to mojom file pipeline_ipa_mojom_mapping = { + 'camss': 'softisp.mojom', 'ipu3': 'ipu3.mojom', 'mali-c55': 'mali-c55.mojom', 'rkisp1': 'rkisp1.mojom', diff --git a/meson.build b/meson.build index 6d54fc7a9..cce543804 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 e829ebe60..845765103 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 ab4a1897f..77a7a5455 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': 'softisp', '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..bcb372601 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss.cpp @@ -0,0 +1,688 @@ +/* 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, + std::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, data_->isp_.get()); + 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, std::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_, + &data->controlInfo_); +} + +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); + + /* 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..9fc355b8f --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_csi.cpp @@ -0,0 +1,571 @@ +/* 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] isp Optional CamssIsp pointer for taking ISP debayering margins 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, + CamssIsp *isp) 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 (isp) + 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, + CamssIsp *isp) const +{ + StreamConfiguration cfg; + + /* Query the sensor static information for closest match. */ + V4L2SubdeviceFormat sensorFormat = getSensorFormat(req.size, req.pixelFormat, isp); + + 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); + /* 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..fd5cf29d2 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_csi.h @@ -0,0 +1,129 @@ +/* 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, + CamssIsp *isp) 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; + + 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 = {}, + CamssIsp *isp = nullptr) const; + int tryFormat(const V4L2SubdeviceFormat &sensorFormat, StreamConfiguration &cfg) const; + void freeBuffers(); + + std::unique_ptr sensor_; + std::unique_ptr output_; + std::array links_; + + 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..75561d7d9 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_frames.cpp @@ -0,0 +1,153 @@ +/* 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) +{ + 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(); +} + +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 (!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..cd5e3dfcf --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_frames.h @@ -0,0 +1,70 @@ +/* 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); + 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..55e967b36 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_isp.h @@ -0,0 +1,64 @@ +/* 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 ControlInfoMap; +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, + ControlInfoMap *ispControls) = 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..cc683a080 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_isp_soft.cpp @@ -0,0 +1,227 @@ +/* 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, + ControlInfoMap *ispControls) +{ + std::vector> outputCfgs; + outputCfgs.push_back(outputCfg); + + /* \todo refactor SoftwareIsp to remove the need to pass this */ + ipa::softisp::IPAConfigInfo configInfo; + configInfo.sensorControls = sensor_->controls(); + + return swIsp_->configure(inputCfg, outputCfgs, configInfo, ispControls); +} + +int CamssIspSoft::allocateBuffers([[maybe_unused]] unsigned int bufferCount) +{ + frameInfos_->init({}, {}); + 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..a05545f54 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_isp_soft.h @@ -0,0 +1,57 @@ +/* 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, + ControlInfoMap *ispControls) 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 35e066499..a0c2ead43 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -264,7 +264,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 Fri Nov 6 15:13:38 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: 28792 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 71A7DC335C for ; Fri, 6 Nov 2026 15:14:00 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1BD6E690F7; Fri, 6 Nov 2026 16:14:00 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="PrxQ4j76"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="AeegwllL"; 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 D3CC86A7DA for ; Fri, 6 Nov 2026 16:13:56 +0100 (CET) Received: from pps.filterd (m0279872.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 6A6F09bk3185114 for ; Fri, 6 Nov 2026 15:13:55 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=dGInKJs5zRn AaSa5XlI2F3kU4oNkZp3bZ/XQfvSamD8=; b=PrxQ4j76/mBskLFsZiH0m8+AD1z fNcA1qJutfkTBpU0lmOZc2rsyauz0KWz4yCDTb5IByxlOoEl0v2JV7o8ffeSc48V jSSMAlyvn0blWDE/CSLzJ0XgFtIauJeMBVM8xTbaMUVew2szHMsRtIrZziOSAXGN /JXyIm/o3LMyzLP50tSE9nOtHu9Aw4bBNAXuDkF9/L5GSo58PANVZAXSBVG6dlbN f5Z5NmHteKt9NqPRN9KVp+u7W5kvjsdcfbLWTxqTDhLq5ZFvhDDs+S7ilr56O4e7 lz1hXn6+vHDlyJalRZ/2TunRekhUWIvW29edXo9bTRxm0CIpMnHF8uxewnw== Received: from mail-qk1-f197.google.com (mail-qk1-f197.google.com [209.85.222.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4hs3pab8u8-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 06 Nov 2026 15:13:55 +0000 (GMT) Received: by mail-qk1-f197.google.com with SMTP id af79cd13be357-943e8ff02d0so83739185a.2 for ; Fri, 06 Nov 2026 07:13:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1793978034; x=1794582834; 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=dGInKJs5zRnAaSa5XlI2F3kU4oNkZp3bZ/XQfvSamD8=; b=AeegwllLe3Ak2f1jYCTHR9bCGgBQfXTsENNACniEWwk3ckyqZ2asOvTxEGGTilx7vR vkz0rCLTqKbcOooBBC9X32SXzamC+7h9sng2mxjVahq4+f7xk7zCwZzRRxS9C98KcuH5 rho6JzAVBAqK4+5933QZkwlQBEF5TPiEiQiG1FfKKhZ4gQYQz8BoYLSyrdg5Q3my8xdJ J2XhvjpTdAw1jtpXApAe3qMzrtOhLEIkM4WR55Ve4+cSbnbFxFFU/RydXSkJoWTAKcD5 QIjQIBDDYWZshFXxDc/qzAdwLu8zZ2D9TfLEYhqWyqFW/KY1BtBn03GhUNacU7DQUVuG G9wA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793978034; x=1794582834; 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=dGInKJs5zRnAaSa5XlI2F3kU4oNkZp3bZ/XQfvSamD8=; b=B9a9mJ3mfgdzHTLWXZHg9PpAzrG/szbEVsbupoRZfSlbVA2/8pSoEzrx+s/fdwuOb/ 5sfLeODp9mjE+2DPAD9C3kOGdCn80wqTQZSTY3ayWRKeMsICy9wYk3FPEPj4aDRcnkij mUPNjKwLKo+GQC5cJmgYmU0oNtF1GKM92ZCxT0Nxg2bYrnpAWPixRS1y/63ktFyb7611 Qy3lE9XDi+2kSDBf1e3XNGLz4VwOJRNW9UM9fCKgtIXiE13upzInvcijiZDwydZDiaJG E15WL2CIxU+vbdi0muE7WN+oaemNzPIiXJ5l7N/6rqJJhFPQjMggKhM1l+WoRFraub9L VGUA== X-Gm-Message-State: APuU4Z/4MYMApt579a2VovaZmULbpODM2iafhm6ehD6VgnfrjiivALfw E/Pw1ehh5SHADHUiJWPwmh7vemkaLsThFPIZxcnoWjJFSoVLwLWcD2HMa5UOXycfp6OC7g6fwZ7 qKXq5wpjD6RavcGuEnt0vTaSCf/DZS+ySokT/OKgQj9bqQuFUml2upT1/AfPa32rmyjr7Pfa/Qz YX+9MuimbJ X-Gm-Gg: AYBFou2sJh7sFOPU0++jWssmtdUAVprBSe7bXfxVvkpbt0qJd5XpSprzyGs27VzP88N mGEhC517Uuq2ciPRNx9uWirkFtCgU4B8Y4DjUPb3iTvvu+RUyc1+KKr0nTfAzYqaZnMIyHZniD1 tiI3wk7Jk0Xyhzc1g7TxhfM4HQvRKUKRFX4Wpkkmbocbbmg72SXaQFdxmduQp1U5r3r7gJDsTlJ tkvlA1uCRv5DEjIoCbDXxZsgJBcd7+rcDD3E3hm8lbNVLtCidEEcLE7lhoxnwCvWqPYzoEITpEO jFAM7N0o/bu0E6SyVk5mLFgpWA6MCL7lynJwMMY9/mexg6Io4GqiM4WYUJ+OpML6CfE6BtBdwNS fbC2SmgZqT3qo3eY4rjGpi3PW1pPrtr+kBDbtL3JhRrBwBS0JvW+bufOhadveZ0oBPmYnO0XfEA OttCp/MfhEMG+0J49vS9633wvonRYX0heYg1pnqSk3VjW+MCeT7YgWrpjUXPBn7hIO X-Received: by 2002:a05:620a:3704:b0:93f:571b:c72e with SMTP id af79cd13be357-945f34b1170mr429962085a.14.1793978034223; Fri, 06 Nov 2026 07:13:54 -0800 (PST) X-Received: by 2002:a05:620a:3704:b0:93f:571b:c72e with SMTP id af79cd13be357-945f34b1170mr429953585a.14.1793978033329; Fri, 06 Nov 2026 07:13:53 -0800 (PST) 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-c4d3569b856sm86042466b.46.2026.11.06.07.13.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 06 Nov 2026 07:13:52 -0800 (PST) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Gjorgji Rosikopulos , Loic Poulain , Hans de Goede Subject: [PATCH v3 4/7] camss: Add dummy ISP class for raw only image capture Date: Fri, 6 Nov 2026 16:13:38 +0100 Message-ID: <20261106151345.32491-5-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> References: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Proofpoint-GUID: gA2axqNpCnEAKckNqoky3WDzYhD-bSHa X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfX6XP534vQMEsr LTTA0YrWqVIKZt5Hs19xt1nF7miqi+9Dr6uRvD3TfxvM+STdnfS7feAT6Fy/ZsiETBOa/Sb/CqX K917KJH8RI1/Pjxc/MNo/8wYF6eGW8lQAmyQQl39IH4Z3aaJm9AVDkYZ9o21uapP9fPfC1np9DE 42tlugEKuAHSBPFCVnaKMrrcAFyWtdtf59a7MvsI6UE9xTLDkf5OlKiGUAZTjflpyPpd+AE53uE ymb7fT0ZVJvXSX0OJlt5T1A5MHaPRX/7fYVAPFXnJ+Qow+pRWjXngj0YgX4KzFJGVHefd7pmmNZ lqexyrGFCxdFoK+fSoqcJC/j0/48ert21wCf1k3TRAFRVMbnsyioH1+q3WtplR3s7XniupNkcaK o8t2VKkkaaZRmr8PwBheFk/tp9h5rTRzRPbhixcbxdloqew/snqCBp4eLB7PhtykOTaTbwHrfFj rnQF9zNcn+2k5A6DCZQ== X-Proofpoint-ORIG-GUID: gA2axqNpCnEAKckNqoky3WDzYhD-bSHa X-Authority-Analysis: v=2.4 cv=X7rHu2Te c=1 sm=1 tr=0 ts=6aedeeb3 cx=c_pps a=50t2pK5VMbmlHzFWWp8p/g==:117 a=xqWC_Br6kY4A:10 a=zJRp6_cAMcgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=yx91gb_oNiZeI1HMLzn7:22 a=EUspDBNiAAAA:8 a=T6x4K98W11HLE5YqqdAA:9 a=IoWCM6iH3mJn3m4BftBB:22 X-Proofpoint-Spam-Info: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfX6nHhYkB26IYB rVMjjYUwfOZv8hnAigHNfvSaszlFLyeGjz4Jx8mCzzEy0j2c+cA/hsixQxkGV4ikpCaH+OWZe75 v6aC2EPCIyyW+CGNaRYaT9MbmVqvmb0= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1176, Hydra:6.1.138, FMLib:17.12.100.49 definitions=2026-11-06_05,2026-11-06_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 clxscore=1015 impostorscore=0 phishscore=0 malwarescore=0 bulkscore=0 spamscore=0 priorityscore=1501 lowpriorityscore=0 adultscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2611040001 definitions=main-2611060059 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" In order to still get gain / exposure metadata added for raw image capture the code still needs to call into the IPA, this also enables autogain / exposure support for raw capture. This implements a new CamssIspRaw class which uses software-statistics + the software ISP IPA for this, while simply passing the raw buffers directly to the application after running the software-statistics. Signed-off-by: Hans de Goede --- src/libcamera/pipeline/camss/camss.cpp | 70 +++--- .../pipeline/camss/camss_isp_raw.cpp | 228 ++++++++++++++++++ src/libcamera/pipeline/camss/camss_isp_raw.h | 72 ++++++ src/libcamera/pipeline/camss/meson.build | 1 + 4 files changed, 342 insertions(+), 29 deletions(-) create mode 100644 src/libcamera/pipeline/camss/camss_isp_raw.cpp create mode 100644 src/libcamera/pipeline/camss/camss_isp_raw.h diff --git a/src/libcamera/pipeline/camss/camss.cpp b/src/libcamera/pipeline/camss/camss.cpp index bcb372601..20b64ea49 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_raw.h" #include "camss_isp_soft.h" namespace libcamera { @@ -61,7 +62,10 @@ public: void cancelPendingRequests(); std::unique_ptr csi_; - std::unique_ptr isp_; + std::unique_ptr processedIsp_; + std::unique_ptr rawIsp_; + /* Points to either processedIsp_ or rawIsp_ */ + CamssIsp *isp_; std::unique_ptr delayedCtrls_; CamssFrames frameInfos_; @@ -172,23 +176,16 @@ CameraConfiguration::Status CamssCameraConfiguration::validate() } } - 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"; + if (!rawCount && !processedCount) return Invalid; - } if (!rawCount) { rawConfig.size = processedConfig.size; rawConfig.bufferCount = processedConfig.bufferCount; } - csiConfig_ = data_->csi_->validate(rawConfig, data_->isp_.get()); + CamssIsp *isp = processedCount ? data_->processedIsp_.get() : data_->rawIsp_.get(); + csiConfig_ = data_->csi_->validate(rawConfig, isp); if (!csiConfig_.pixelFormat.isValid()) return Invalid; @@ -196,7 +193,7 @@ CameraConfiguration::Status CamssCameraConfiguration::validate() << " stride " << csiConfig_.stride << " frameSize " << csiConfig_.frameSize; - ispConfig_ = data_->isp_->validate(csiConfig_, processedConfig); + ispConfig_ = isp->validate(csiConfig_, processedConfig); if (!ispConfig_.pixelFormat.isValid()) return Invalid; @@ -216,7 +213,7 @@ CameraConfiguration::Status CamssCameraConfiguration::validate() stream = &data_->csi_->rawStream_; } else { hwCfg = &ispConfig_; - stream = &data_->isp_->outStream_; + stream = &isp->outStream_; } StreamConfiguration &cfg = config_[i]; @@ -321,7 +318,7 @@ PipelineHandlerCamss::generateConfiguration(Camera *camera, std::spanaddConfiguration(csiConfig); if (processedRequested) { - cfg = data->isp_->generateConfiguration(csiConfig); + cfg = data->processedIsp_->generateConfiguration(csiConfig); if (!cfg.pixelFormat.isValid()) return nullptr; @@ -346,6 +343,11 @@ int PipelineHandlerCamss::configure(Camera *camera, CameraConfiguration *c) if (ret) return ret; + if (config->ispConfig_.pixelFormat == config->csiConfig_.pixelFormat) + data->isp_ = data->rawIsp_.get(); + else + data->isp_ = data->processedIsp_.get(); + return data->isp_->configure(config->csiConfig_, config->ispConfig_, &data->controlInfo_); } @@ -426,7 +428,7 @@ void PipelineHandlerCamss::stopDevice(Camera *camera) } CamssCameraData::CamssCameraData(PipelineHandler *pipe) - : Camera::Private(pipe) + : Camera::Private(pipe), isp_(nullptr) { frameInfos_.bufferAvailable.connect(this, &CamssCameraData::queuePendingRequests); } @@ -523,27 +525,37 @@ 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()) { + data->processedIsp_ = std::make_unique(this, sensor, + &data->frameInfos_, + &data->controlInfo_); + if (!data->processedIsp_->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->rawIsp_ = std::make_unique(this, sensor, + &data->frameInfos_, + &data->controlInfo_); + if (!data->rawIsp_->isValid()) { + LOG(Camss, Error) << "Failed to create raw ISP"; + continue; + } + + for (auto isp : { data->processedIsp_.get(), data->rawIsp_.get() }) { + isp->inputBufferReady.connect(data->csi_.get(), + &CamssCsiCamera::tryReturnBuffer); + isp->outputBufferReady.connect(data.get(), + &CamssCameraData::ispOutputBufferReady); + isp->paramBufferReady.connect(data.get(), + &CamssCameraData::ispParameterBufferReady); + isp->statsReady.connect(data.get(), &CamssCameraData::statsReady); + isp->metadataReady.connect(data.get(), &CamssCameraData::metadataReady); + isp->setSensorControls.connect(data.get(), &CamssCameraData::setSensorControls); + } /* Create and register the Camera instance. */ std::set streams = { - &data->isp_->outStream_, + &data->processedIsp_->outStream_, &data->csi_->rawStream_, }; std::shared_ptr camera = diff --git a/src/libcamera/pipeline/camss/camss_isp_raw.cpp b/src/libcamera/pipeline/camss/camss_isp_raw.cpp new file mode 100644 index 000000000..cd9226d7b --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_isp_raw.cpp @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS dummy ISP class for raw only image capture. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include "camss_isp_raw.h" + +#include + +#include + +#include +#include +#include +#include +#include + +#include "libcamera/internal/camera_manager.h" +#include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/dma_buf_allocator.h" +#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" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(Camss) + +/** + * \class CamssIspRaw + * \brief CAMSS dummy ISP class for raw only image capture + */ + +/** + * \brief Constructs CamssIspRaw 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 + */ +CamssIspRaw::CamssIspRaw(PipelineHandler *pipe, const CameraSensor *sensor, + CamssFrames *frameInfos, ControlInfoMap *ispControls) + : 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(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; + bool lscEnabled; + ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() }, + stats_->getStatsFD(), + sharedParams_.fd(), + sensorInfo, + sensor->controls(), + ispControls, + &ccmEnabled, + &lscEnabled); + if (ret) { + LOG(Camss, Error) << "IPA init failed"; + ipa_.reset(); + return; + } + + ipa_->metadataReady.connect(this, + [&](uint32_t frame, const ControlList &metadata) { + metadataReady.emit(frame, metadata); + }); + ipa_->setSensorControls.connect(this, + [&](const ControlList &sensorControls) { + setSensorControls.emit(sensorControls); + }); +} + +CamssIspRaw::~CamssIspRaw() = default; + +bool CamssIspRaw::isValid() +{ + return !!ipa_; +} + +Size CamssIspRaw::getMargins([[maybe_unused]] PixelFormat inputFormat) +{ + return Size(); +} + +StreamConfiguration CamssIspRaw::generateConfiguration(const StreamConfiguration &raw) const +{ + return raw; +} + +StreamConfiguration CamssIspRaw::validate(const StreamConfiguration &raw, + [[maybe_unused]] const StreamConfiguration &req) const +{ + return raw; +} + +int CamssIspRaw::configure(const StreamConfiguration &inputCfg, + const StreamConfiguration &outputCfg, + ControlInfoMap *ispControls) +{ + ipa::softisp::IPAConfigInfo configInfo; + configInfo.sensorControls = sensor_->controls(); + + if (inputCfg.pixelFormat != outputCfg.pixelFormat || + inputCfg.size != outputCfg.size) + return -EINVAL; + + int ret = ipa_->configure(configInfo, ispControls); + 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); + + return 0; +} + +int CamssIspRaw::allocateBuffers([[maybe_unused]] unsigned int bufferCount) +{ + frameInfos_->init({}, {}); + return 0; +} + +void CamssIspRaw::freeBuffers() +{ + frameInfos_->clear(); +} + +int CamssIspRaw::exportOutputBuffers([[maybe_unused]] const Stream *stream, + [[maybe_unused]] unsigned int count, + [[maybe_unused]] std::vector> *buffers) +{ + /* Must never be called when there only is a raw stream */ + return -EINVAL; +} + +void CamssIspRaw::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()); + /* + * This emits statsReady which calls ipa_->processStats(), which emits + * metadataReady which makes camss.cpp mark the request as completed. + */ + stats_->processFrame(frame, 0, input); +} + +void CamssIspRaw::processStats(const uint32_t frame, const uint32_t statsBufferId, + const ControlList &sensorControls) +{ + ipa_->processStats(frame, statsBufferId, sensorControls); +} + +int CamssIspRaw::start() +{ + return ipa_->start(); +} + +void CamssIspRaw::stop() +{ + ipa_->stop(); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/camss_isp_raw.h b/src/libcamera/pipeline/camss/camss_isp_raw.h new file mode 100644 index 000000000..76b9d1af8 --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_isp_raw.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Qualcomm CAMSS dummy ISP class for raw only image capture. + * + * 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 CamssIspRaw : public CamssIsp +{ +public: + CamssIspRaw(PipelineHandler *pipe, const CameraSensor *sensor, + CamssFrames *frameInfos, ControlInfoMap *ispControls); + ~CamssIspRaw() 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, + ControlInfoMap *ispControls) 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: + const CameraSensor *sensor_; + class CamssFrames *frameInfos_; + SharedMemObject sharedParams_; + std::unique_ptr stats_; + std::unique_ptr ipa_; +}; + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/camss/meson.build b/src/libcamera/pipeline/camss/meson.build index 2f3931dd1..518d85478 100644 --- a/src/libcamera/pipeline/camss/meson.build +++ b/src/libcamera/pipeline/camss/meson.build @@ -5,6 +5,7 @@ libcamera_internal_sources += files([ 'camss_csi.cpp', 'camss_frames.cpp', 'camss_isp.cpp', + 'camss_isp_raw.cpp', 'camss_isp_soft.cpp', 'camss_util.cpp', ]) From patchwork Fri Nov 6 15:13:39 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: 28795 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 1AA78C335E for ; Fri, 6 Nov 2026 15:14:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8B3336A7E9; Fri, 6 Nov 2026 16:14:03 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="KPf53pBY"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="A+KLEp78"; 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 81CAD6A7D4 for ; Fri, 6 Nov 2026 16:13:58 +0100 (CET) Received: from pps.filterd (m0279864.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 6A6F03Yn2671664 for ; Fri, 6 Nov 2026 15:13:56 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= ObPjx0Y03ltx7pI207VTZMS8Hk07tO1wtc5qngRTeF4=; b=KPf53pBYRDMMlkSi VK1welMOzYgbAZ8zyry6AC6tKyO0P+PUD9KmO0k0AWfQufk3t7jmrISBQtAG6eYV wIJWQOopxsy6HKWPqKKkJheUrxmD7AM1wV0MuXdT2OGDKkZd1rwbzFQOn0/GQGh1 zk+pcAC0zO//ICnj2qlcM7073tezOiv+LK04BxZYHuKjLZxUfkmPS85+HNCDoz7N 9vCueYFqNbYZXbfV3jGxy1AhBxrvtYsR5IC6quKWtNLkKAiMpgQD1eIA2fYjptBl g/vTCh/OVAC/1ETE9XnkllfrOztudrx4wmldTxyAPmftAah7KGi0QxrNGrn7UogJ chAOOg== Received: from mail-qk1-f199.google.com (mail-qk1-f199.google.com [209.85.222.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4hs3pcbadn-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 06 Nov 2026 15:13:56 +0000 (GMT) Received: by mail-qk1-f199.google.com with SMTP id af79cd13be357-93f6f4fe531so138857685a.0 for ; Fri, 06 Nov 2026 07:13:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1793978036; x=1794582836; 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=ObPjx0Y03ltx7pI207VTZMS8Hk07tO1wtc5qngRTeF4=; b=A+KLEp78Ea6O1QJs++qGroHnMjPCXsiaOLK64bV2G9+t2QilKC5FpJGSglxeDxd+d2 1cRhXirb8ak7XAKbmwEY9UHL3FL8xTSJOnVWn1a3btqTNNbpldT0TygmdUleCVTY9q+v tUAl6mh7+QRyKkkdAKGQUcLVSXXtuBf+mco5jqpDlsa4bfsZ+t84SlCyfamMLRCS3jg7 6594R0ECrbQdMzoQg90blJKy8L/FiWJVZSDG/rJpiax2J6OFB0WJ0h6ooZS3ikYIrgML dFNfjXOPxgSAHaQs172+gxf8cjhwNEHWhyLD4hgPE2tf9DtzgC8JVfPGyR8O6vFn1xo+ ywPw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793978036; x=1794582836; 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=ObPjx0Y03ltx7pI207VTZMS8Hk07tO1wtc5qngRTeF4=; b=O/ertO8a3UGjXKz8c5YufLYLu7hXL6aNJx9aYr+OOTc2GMHveGnMPMWqYNbfU8oyih fWaVQr5+2PkW9HqyE3rnqwx29lZqSienpnr5aO4z5RVEK4eG5YgAmgLzCNpBEcFmhxAf UWq5ha9ojNIkMGlEFwB0KQhJOq8tgS/zTjTcBr9XoBQq4WfqFJqJjaoXxBOK3ydpdtA9 u9YeGw0ZytrAfTowAN8iG+2qzPSgGsR2kBRBk65V5CKbG/HupN55+rSc9FAUSuVX7laN TdfyiK+zhH/HDbzWksDT4ddgKGdkKXlAk8/r45G4kaESJXkWLYswwY4rED3i/9CJlEDI GN8A== X-Gm-Message-State: APuU4Z/Di36f1DfR5lOieQX1E6UAwk/5Iq1XWuiwE08dgi0WINloh2lE p3t697z31cez68hEQXCfx3aZmVw35YgeBXldNmnsZ1q2/HlDWtCAlRYxVV4I1HZopl/kfi/ThXx bJGZ3UKYjsJyHKtpbaFsKeJ3ZBS5e9wZraeqPVrbnrxwrawVMpAoGOmMXjEQjhlyTHWNZtlqCYu WW6uA6vqXj X-Gm-Gg: AYBFou0kIAcXfbPJOmTAJRnMVp/RQKy0aHkmLWTuKE1WYgi+DDI7puyxe7Zh6Ecn6M/ oO2aR2cE7VUY2yZw+h2oNDWe0w7XYSPX5faCFiWZJ9GWm+Be18593NFQKykmy8Xp+S92An3kXgx JccXUn9v6c9wHamrjSqXpH5gmvw9a6ZtJE3Gs4dYxY2lzrzfgnp600cdfY7sHqyT4boqzC3k08L RGeO2XON1i7Q+gLHAFNT7/0/KZosdhcH23lGauxFvWHBJfdg0NG0+Mvy9uodN2WnkD53FhxuxSR bjBekzrnikw4pTYtseyeHzQ6WCcEQYlXb8wpDUU/8yAPG7NY4INElPFn2yLNBGxOV4R8gQ0xbkd arZN+/49McjCc1dnfErpDO4YJ1JSMUgmKfMyQisw0dUfJ6eIvF5YJhOEoTGj/BGhdGi/wRjqjh4 iYMymTfaxGFs8w0He0FJzz9eRP3m5dVSMjU5kgeE0zS0KZKAO9OyKpe1ni8+mbwYYN X-Received: by 2002:a05:620a:1a29:b0:943:f31a:c0a7 with SMTP id af79cd13be357-945f34c9990mr456779985a.31.1793978035399; Fri, 06 Nov 2026 07:13:55 -0800 (PST) X-Received: by 2002:a05:620a:1a29:b0:943:f31a:c0a7 with SMTP id af79cd13be357-945f34c9990mr456772085a.31.1793978034699; Fri, 06 Nov 2026 07:13:54 -0800 (PST) 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-c4d3569b856sm86042466b.46.2026.11.06.07.13.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 06 Nov 2026 07:13:53 -0800 (PST) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Gjorgji Rosikopulos , Loic Poulain , Hans de Goede Subject: [PATCH v3 5/7] camss: Add include/linux/qcom-camss-config.h Date: Fri, 6 Nov 2026 16:13:39 +0100 Message-ID: <20261106151345.32491-6-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> References: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: KeUN610vB4fco_MH_L8qB_rsNnymCWFU X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfX2cNq5Zdjy14p wq0fsz7d8QyJrumD+JJarlZqrgZqSl7Rhhf9pLfY+pZZtMMwjl7kKcUitjT6dmhFGeqOQdO1LI+ seGxneG+9o9FT0w2P+mMYonCjLSbtxWESEM5b/A3yvAVm7ANYwQNaLBtJrhNlqT+C7Ee+6+cyuD bJbQ13G8bQHXH81Ng/oafbMBXLkwHS4LUl3zd4Wy7w2YFnjMwwRlUbK9OKrQX/HYlQTBv2azzQA 0MGQsMbnCyqH9zBRpGAIual5dPCh+hFiA8EjCMD602J4pLHKFKkgt08y4hdlotPCHvGFEKOJTWU WdGkmI3nUjbplIz0UKzTDPjglUWVnVCdCHBxlpgsvwUVjh/OtEZxCjMAzCP6F5lrRI+KPmVOKzh eTxqFi4U62tufx76DuHrOAgr9HZ/YJYJm02YZR0FSJFhP9iIix54fAEapRPah7L0hu5xPyZ3oSj LrV+XtpckGMxgk88O/Q== X-Proofpoint-GUID: KeUN610vB4fco_MH_L8qB_rsNnymCWFU X-Authority-Analysis: v=2.4 cv=TI3Q2Fla c=1 sm=1 tr=0 ts=6aedeeb4 cx=c_pps a=HLyN3IcIa5EE8TELMZ618Q==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=zJRp6_cAMcgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=DJpcGTmdVt4CTyJn9g5Z:22 a=VwQbUJbxAAAA:8 a=EUspDBNiAAAA:8 a=cBt3T3tByBXynNCq78MA:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 a=bTQJ7kPSJx9SKPbeHEYW:22 X-Proofpoint-Spam-Info: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfXx2/8N55Gt56Y A7ZSUahCqBaCRr9/f+u19eCs+JMfNRBWFqXtC4YWo/47vhwpY/e33d0bj/X7d0YNhTqp7mYSU3C WeYk7lCCumYsLYEGnpRbfVA0pVfkyJA= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1176, Hydra:6.1.138, FMLib:17.12.100.49 definitions=2026-11-06_05,2026-11-06_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 suspectscore=0 adultscore=0 clxscore=1015 lowpriorityscore=0 spamscore=0 phishscore=0 malwarescore=0 priorityscore=1501 impostorscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2611040001 definitions=main-2611060059 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/qcom-camss-config.h for OPE ISP parameter definitions. Link: https://lore.kernel.org/linux-media/20261103-camss-isp-ope-v13-4-c2cbf15a39b1@oss.qualcomm.com/ Signed-off-by: Hans de Goede --- include/linux/qcom-camss-config.h | 217 ++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 include/linux/qcom-camss-config.h diff --git a/include/linux/qcom-camss-config.h b/include/linux/qcom-camss-config.h new file mode 100644 index 000000000..543c1682f --- /dev/null +++ b/include/linux/qcom-camss-config.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * Qualcomm CAMSS ISP Driver - Userspace API + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _UAPI_LINUX_QCOM_CAMSS_CONFIG_H +#define _UAPI_LINUX_QCOM_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_block_header. + * + * @CAMSS_PARAMS_WB_GAIN: white balance gains and offsets (CLC_WB), + * &struct camss_params_wb_gain + * @CAMSS_PARAMS_CHROMA_ENHAN: RGB to YUV colour transfer matrix + * (CLC_CHROMA_ENHAN), &struct camss_params_chroma_enhan + * @CAMSS_PARAMS_COLOR_CORRECT: colour correction matrix (CLC_CC), + * &struct camss_params_color_correct + * @CAMSS_PARAMS_GAMMA: per-channel gamma correction curves (CLC_GLUT), + * &struct camss_params_gamma + */ +enum camss_params_block_type { + CAMSS_PARAMS_WB_GAIN = 1, + CAMSS_PARAMS_CHROMA_ENHAN = 2, + CAMSS_PARAMS_COLOR_CORRECT = 3, + CAMSS_PARAMS_GAMMA = 4, +}; + +/* Number of entries in each gamma channel LUT. */ +#define CAMSS_GAMMA_LUT_SIZE 256 + +/** + * 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 subtraction) + * 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) + * @_pad: padding for 64-bit alignment, must be zero + */ +struct camss_params_wb_gain { + struct v4l2_isp_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) coefficients:: + * + * Y = v0 * R + v1 * G + v2 * B + * + * 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 + * + * 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 + * + * @header: generic block header; @header.type = CAMSS_PARAMS_CHROMA_ENHAN + * @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) + * @coeff_ap: Cb positive coefficient (12sQ8) + * @coeff_am: Cb negative coefficient (12sQ8) + * @coeff_bp: Cb positive coefficient (12sQ8) + * @coeff_bm: Cb negative coefficient (12sQ8) + * @coeff_cp: Cr positive coefficient (12sQ8) + * @coeff_cm: Cr negative coefficient (12sQ8) + * @coeff_dp: Cr positive coefficient (12sQ8) + * @coeff_dm: Cr negative coefficient (12sQ8) + * @kcb: Cb output offset (11s) + * @kcr: Cr output offset (11s) + * @_pad: padding for 64-bit alignment, must be zero + */ +struct camss_params_chroma_enhan { + struct v4l2_isp_block_header header; + __s16 luma_v0; + __s16 luma_v1; + __s16 luma_v2; + __s16 luma_k; + __s16 coeff_ap; + __s16 coeff_am; + __s16 coeff_bp; + __s16 coeff_bm; + __s16 coeff_cp; + __s16 coeff_cm; + __s16 coeff_dp; + __s16 coeff_dm; + __s16 kcb; + __s16 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 + * + * The @qfactor member selects the fixed-point format of all coefficients:: + * + * 0 = 12sQ7 (range ~-16.0 .. +15.992) + * 1 = 12sQ8 (range ~-8.0 .. +7.996) + * 2 = 12sQ9 (range ~-4.0 .. +3.998) + * 3 = 12sQ10 (range ~-2.0 .. +1.999) + * + * @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), see above + * @_pad: padding for 64-bit alignment, must be zero + */ +struct camss_params_color_correct { + struct v4l2_isp_block_header header; + __s16 a[3]; + __s16 b[3]; + __s16 c[3]; + __s16 k[3]; + __u16 qfactor; + __u16 _pad[3]; +} __attribute__((aligned(8))); + +/** + * struct camss_params_gamma - per-channel gamma correction curves + * + * Implements the CLC_GLUT pipeline module, applied in the RGB domain. It + * holds one independent lookup table per colour channel. Each table is a + * direct (not segmented) map of input level to output level. + * + * Each table has @CAMSS_GAMMA_LUT_SIZE (256) entries of 16-bit unsigned + * output. The number of significant output bits follows the width of the + * engine's internal bus; the upper bits are ignored by the hardware. + * + * The module maps a 12-bit input to an 8-bit output: the top 8 input bits + * index the table and the low 4 bits are used to linearly interpolate + * between adjacent entries. Entry i therefore represents the output for + * input level i/255 of full scale. + * Output range: 0x00 = black, 0xFF = white. + * + * How to fill a curve (same on all three channels for pure luminance + * gamma; different curves per channel additionally shift colour balance):: + * + * Identity (pass-through, gamma 1.0): + * lut[i] = i; // i = 0..255 + * + * Encode with gamma g (e.g. sRGB-like, g = 2.2): + * lut[i] = round(pow(i / 255.0, 1.0 / g) * 255.0); + * + * @header: block header; @header.type = CAMSS_PARAMS_GAMMA + * @glut: green channel gamma curve + * @blut: blue channel gamma curve + * @rlut: red channel gamma curve + */ +struct camss_params_gamma { + struct v4l2_isp_block_header header; + __u16 glut[CAMSS_GAMMA_LUT_SIZE]; + __u16 blut[CAMSS_GAMMA_LUT_SIZE]; + __u16 rlut[CAMSS_GAMMA_LUT_SIZE]; +} __attribute__((aligned(8))); + +#define CAMSS_PARAMS_OPE_MAX_PAYLOAD \ + (sizeof(struct camss_params_wb_gain) +\ + sizeof(struct camss_params_chroma_enhan) +\ + sizeof(struct camss_params_color_correct) +\ + sizeof(struct camss_params_gamma)) + +#endif /* _UAPI_LINUX_QCOM_CAMSS_CONFIG_H */ From patchwork Fri Nov 6 15:13:40 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: 28794 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 4076FC335D for ; Fri, 6 Nov 2026 15:14:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D71656A7ED; Fri, 6 Nov 2026 16:14:02 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="iGOi48ji"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="GMkT4vyf"; 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 63FC36A7CD for ; Fri, 6 Nov 2026 16:13:58 +0100 (CET) Received: from pps.filterd (m0279872.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 6A6F09bl3185114 for ; Fri, 6 Nov 2026 15:13:57 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=84llMaYkBgA 8YNzqL5iJy5ES6nGOrnVbUi9M7p0W0pU=; b=iGOi48jiDvv7IaMrH1hp32xYUg3 0oAJHKidzQLqG8TcQW7qdHVaLhADkUU68M8MNGt1iBMBdXU77lBOmwQj4bf8M1xy c9U39hobrVwnT3QmVoSPh79ok1/JWEippRyftSn5AB8mifoF6q+VyjZKLaJMxVVW QhifUSkexDhMOKE7PS6bLtQgcrka390PBT/qHBztWDa+NOB4Rnw2v6+1guOfG/Qp EkHt6FVLzj8aOwv/bl4Qp0XLab2vtimJEqbYzy3iD3qU/S9RLeZWJD8aOwe9IWz0 Dk+6v99LUUpqSDt4S6Q02GHHYXDud81ucKKPv8hIDDZ5JACPbYxDUmZLEkQ== Received: from mail-qk1-f197.google.com (mail-qk1-f197.google.com [209.85.222.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4hs3pab8ub-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 06 Nov 2026 15:13:57 +0000 (GMT) Received: by mail-qk1-f197.google.com with SMTP id af79cd13be357-93ecdc701a2so106774685a.3 for ; Fri, 06 Nov 2026 07:13:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1793978036; x=1794582836; 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=84llMaYkBgA8YNzqL5iJy5ES6nGOrnVbUi9M7p0W0pU=; b=GMkT4vyfGO1vFEp26zzZNN/7Rrpzo+zaj2z/Wq6oXO6HeWfvlrYkAgIutDk6MD0NHH fHISTTIg+PJodWE5xkgJKOOnVeUDFkpCZZmk2Rw6S1skMvxpss7YxdKjEAJheepCf2Dz nd3t+ir6sFCGQHDtpmu381tCSxtmILPBuh/1u6DBbmknPFVD3UpV/4BGbchOqsCns995 ZjnP1ZB931wiyJLk2doHbnBSd/j63Pcrx/NZgD0XyD68Jzh58Kp+pQiQKG1zZvdpVQzv WsbKZ/F0wZejpGaWtpXPdp2PSL6EJ9AefFQf+J77hknUei/znrA3KrTFy3jhmHd+rdc8 fhLg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793978036; x=1794582836; 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=84llMaYkBgA8YNzqL5iJy5ES6nGOrnVbUi9M7p0W0pU=; b=pc/Qsj0cAU1y5qI7qQtbfSNQ1I5BWN+tzsbb63Py7mcx9YHGmLOXIN8V3QH/8q1Iv7 MrQm8q9Q/TDR+7gQQD6aGAErC+rOKQaFF7gYA1mEGnia1Sv5M5uHm+X7YrQMd0G/2wzt HeyPFMDEVQeT3F6P286mt/ryrMo399dUE3mgbQK02O0sVI7ZqPY9kSu/YKc37+2OMeaf 6lKBmluUy629WqOFDTnmvZDNWXsTH2FHYIteZwJufk1SzFv4c0xnRjM6CQKo6Peck3Xk 0I6TUn6O+Wl7/WYpSzFBBIjtNSXOsoIh2ztiGVLwMERVxlO5Up1HbvFwD25m+vLXGU2U iHXw== X-Gm-Message-State: APuU4Z+Z4xRq80kVewfEq+61IhMQgWMYR5VdQN0sQX/FL8QBN47H+o0m 3DXDY9oxgOUOVL7ndGVEPR6FmswIY+v7e2v7022wtFWvL01hRcdcqJaqYPkARj7cEEdzRqgLO19 wqutb/AnZc7MQG5m/Dve8yrXFdYGRD6hBWjmTMIu+4nOtVTR9eJXBHhVZExtN6jHoJjrnaxzRkS /zpyRqxvsI X-Gm-Gg: AYBFou17JWkz9wXncrq2Lb2nbRqD7802S2riB34Es081X34KbtK8CMdElR7e59t/y/W 7UwpBV3TCtOK+GrAjqH+9O2urmu3UzulRdS7iWKWPDJVQG83PEDXlhWFXGkN3RH+MFwX/ZfW09/ kRiPvQSUYTxJO/EML3VdDI+2S5s0Do1WFyJZLSgA9O7cUQdtAGTY7HYNFsqSM3wdCEtmKnMskGg 0joxx7skJ8/JB4hzyYivS6+dn+aUpmTHzI3NIVdh4O+fTOaGUuf3Bt4Wqetfbm1xbkge1ycuuQF mBt2roBUCOVjPFkxl7396UIpMU/q+D8SBAAotBmGDpCuwwdyysoyT38vLgi3uH0sG5NrKg5+IQk Y5y1HIAYl4w5bcj+yJcjgNc2YfLiguy5LCnXIoF7I7RH6BVy++u+AF5ouPp/TRwoZhcxAcIx5Ll qaOOW37dslBTRfJQilSgrns2b/jgAI1B5cNHOYRYNO3on2bm5zfTiNcD84eNq0etky X-Received: by 2002:a05:620a:4387:b0:93f:34c9:9252 with SMTP id af79cd13be357-945f355b906mr400106485a.55.1793978036533; Fri, 06 Nov 2026 07:13:56 -0800 (PST) X-Received: by 2002:a05:620a:4387:b0:93f:34c9:9252 with SMTP id af79cd13be357-945f355b906mr400101085a.55.1793978035928; Fri, 06 Nov 2026 07:13:55 -0800 (PST) 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-c4d3569b856sm86042466b.46.2026.11.06.07.13.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 06 Nov 2026 07:13:55 -0800 (PST) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Gjorgji Rosikopulos , Loic Poulain , Hans de Goede Subject: [PATCH v3 6/7] camss: Add camss_params.h Date: Fri, 6 Nov 2026 16:13:40 +0100 Message-ID: <20261106151345.32491-7-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> References: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Proofpoint-GUID: O9PQlxt5sicKhPQ_3SVcduKxI2VCvlDD X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfX7uYHMn1kj16F 6na82pu7wU+3G8JND5J+bzQhwePrcyAhiPb0MqYhml45PAORLFm42U3/5Hzuu7uHjZb9IpoqLwN L8ESFukpKks7ltUM9l6LL+UjCbOMVj0YM0ZoG85uBBHTiPTL4V+9/AcGs0u4JhBjpydfs1q06C0 FaHWSm8U6jQlAT1UaEYEZSbnDPrlwvF+R4OXeC+1KpUKnIMpkVi0pQOP+4wO8TsACVoVO80sxqt EXNtZsRJKbpGihWHwTRGFUkj+cCFF+W6VwzA9TWDvGPyhpj/O5b4vJngAtVifMnizq1HC+Guwx7 hpS9je2ZU0sAPfMR8PJhgR8ZcXdtolOkQdaBWtr2QP6YKKSv0QQEsu/2j2Edcu9KCAqGfOUR2jz tSqti6IYHubyCm/kOK361O4xq6aUpShIIdJlxuit0TjduumSQ4marRkY/ky2pYnjhLhpE1mj4eQ c/7fbzP5OxcM386wmDw== X-Proofpoint-ORIG-GUID: O9PQlxt5sicKhPQ_3SVcduKxI2VCvlDD X-Authority-Analysis: v=2.4 cv=X7rHu2Te c=1 sm=1 tr=0 ts=6aedeeb5 cx=c_pps a=50t2pK5VMbmlHzFWWp8p/g==:117 a=xqWC_Br6kY4A:10 a=zJRp6_cAMcgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=yx91gb_oNiZeI1HMLzn7:22 a=EUspDBNiAAAA:8 a=9Q4bkKXLEDaXOvrWDlIA:9 a=IoWCM6iH3mJn3m4BftBB:22 X-Proofpoint-Spam-Info: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfXwZIDq/qL/UaQ PGG9Z8e9XaGeEkxkVxbbooEKyeFhKVVT1RqLh3W/21ozM6EjklJ+L0qfzZenGEjTFpzQ0Q1/c/2 SfkdDsAljpfK9grWRxgU41rk7NeTQ/k= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1176, Hydra:6.1.138, FMLib:17.12.100.49 definitions=2026-11-06_05,2026-11-06_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 clxscore=1015 impostorscore=0 phishscore=0 malwarescore=0 bulkscore=0 spamscore=0 priorityscore=1501 lowpriorityscore=0 adultscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2611040001 definitions=main-2611060059 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 | 71 +++++++++++++++++++++ 1 file changed, 71 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..51e6e639c --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_params.h @@ -0,0 +1,71 @@ +/* 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 +#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, + Gamma, +}; + +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); +CAMSS_DEFINE_BLOCK_TYPE(Gamma, gamma, GAMMA); + +struct param_traits { + using id_type = CamssBlocks; + + template + using id_to_details = block_type; +}; + +} /* namespace details */ + +class CamssParams : public V4L2Params +{ +public: + CamssParams(std::span data) + : V4L2Params(data, V4L2_ISP_PARAMS_VERSION_V1) + { + } +}; + +} /* namespace ipa::camss */ + +} /* namespace libcamera */ From patchwork Fri Nov 6 15:13:41 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: 28796 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 0E587C335C for ; Fri, 6 Nov 2026 15:14:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A65746A7E1; Fri, 6 Nov 2026 16:14:04 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=qualcomm.com header.i=@qualcomm.com header.b="gKHcxEu5"; dkim=pass (2048-bit key; unprotected) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="KXqBczv2"; 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 631F66A7E1 for ; Fri, 6 Nov 2026 16:14:00 +0100 (CET) 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 6A6F0Fft087780 for ; Fri, 6 Nov 2026 15:13:59 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=/PX1YKa+HYv ZBGow/hQN9LE9bVXsPOugmPY594g/Tdk=; b=gKHcxEu5XpOZd9xK1B933BRaABQ cnWEXz5MNrhIGfjmFVNyKe3stMAlfyZ3T4x64Cqwp8/tZzzmq5MQkP5zbEs2zFhU deti17VQBtxgRvXTjzuI5gnEkHpmbpzeU3xU76s3ptI4MCtvgYgwIYH3dawGdK95 8RGl9AZmdXY1h2LYIFObZed8B3SBtQdfx++ArCftVjKu8iH2YAiZhJgW5+sfWCvp NzVCg+EKum4S0bRpZ28dYSBIlzErLank0kLaCkeNfDAIqVi2lhrXd5bklLPKpWQ2 Hl0K74MNnoojw1sBy+UYiMUObkcg3yU5dy6UMWahECeWHT6Sy4oMAxIUH2g== Received: from mail-qk1-f199.google.com (mail-qk1-f199.google.com [209.85.222.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4hs3pa3ak5-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 06 Nov 2026 15:13:58 +0000 (GMT) Received: by mail-qk1-f199.google.com with SMTP id af79cd13be357-93f2373e9b4so92953085a.1 for ; Fri, 06 Nov 2026 07:13:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1793978038; x=1794582838; 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=/PX1YKa+HYvZBGow/hQN9LE9bVXsPOugmPY594g/Tdk=; b=KXqBczv2mSNyPYL9xufAJIE2nqp4NvfOkE1lotYBzO+8TNm1+Z1osiwpQCN6ShybUX egAAkYMueoGD42LFw7/9fFambRcO1eul/fvWVk+5lSIddnjrILq4qZMwUPpyjFTFY1+c sb7sp+4CRypFavxsJ4juu8AF9Bzu9RiNGwzuTd8WE2aQmtlAI/ec3jcvhVfHjbMnt8WH h+Hu52pW2FnqKrunwokzV/iqJ8ViO10o6+ykzw66ptqyF++EZO88WFzscdn5mb4+tBJV vW9R0Begn8z/KKCBJwVsfY+aW+nOnDQKccvzkleFgLipcGvgPcwFAiuOBC5yAeg3truG HZ+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793978038; x=1794582838; 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=/PX1YKa+HYvZBGow/hQN9LE9bVXsPOugmPY594g/Tdk=; b=wQ4cWk4YJkZb4RORiNEKaGaY2DkHo8KazMtY2gXpxdZ1IMf5R6nKlW7cWsPDnBAz8R 8keNvFXrdfurnyB7LAMX5ewacEOFjkVxoj1XPH+hYWZOaEgT20Itp2jmAdPCoC0CzC/m yK1eypHyIAzAqk8fY+3hH3bjW+KAP5ar4iP95IrtpkxRfr93Y6BR4D19ytcdW6cZJGt0 Y2EXtg14GcwMSG3ZECFviBlMVCmCOc4iPEVaO50s8qNnjx8M/S5DxvNPHD+xSX3p8IVI v46gxKDfcrtpFGtD6WKGeHWHLGWyff+0k/3cJ1DzKDXEkSykuHGiJdDMxRrc3Rc8YIXj qltA== X-Gm-Message-State: APuU4Z/rHF1neM/Ev9aLafnRUD+CfD73QV3p6knThpj7gxFa9nuZL8MW tyhNa/0g8gfHHAaDpu84lFR+jX/+cdC2ECtjTv3ThdbqQuzajMrf1ln40pkoTBJr99i6Mcs0h4p ud2aAhOv1dzISy2GNnkwwqAqitz9t3/oul6pZ1ZTzejvzbD7LvnkVfH1RfqeeAYfi67nxb0g7iF ueNuxxhKvx X-Gm-Gg: AYBFou07CZgZv8jC+4I3D/MxeOXHvmJbDM/9bDQBVH2bnCwZzqq6WCbvadY2CQB9rxg KKO60Bld7FmiWitAaxI1HyPJbLMsbW+Bf/fKb2DuJB35QXyu4OpMVpVluk1aKPSG6C/J9bzx0ZB uf0RqrsImb2aE8NgZrK/FbDWHB+FWQK2AFAKl4+I0ANC3IUs/WPQcYYk+1/22EvX7VN+SR1vNiQ evf7pI0J5pB+81/AWMoFyjRcbbQDTbkXXEA66PmBFg86P9Nt2l/UEBqtH3XwP7oMUAWSexTgKGJ /6a/lj11RcM7/DH18sUtpH1UyEg1EwWYUE4hAiM0SDAdJGl/j2E99pqGyq8XaheUcWO8aPGudUZ PQ7DtbXr3Z+SZoAGiVzX2uyCHt3t+5KMmECPea46+MW/LMGuG0VTVz2H6rcJDEZpE3I9oBcsKeW v+4LFthNGW4lUNJY1Z3FykvAYGZcFQ8HDOk7DJklyy6tT9X4vLMOrcNvR22lcywkWz X-Received: by 2002:a05:620a:4449:b0:945:c50d:569e with SMTP id af79cd13be357-945f34fb975mr383916785a.36.1793978038104; Fri, 06 Nov 2026 07:13:58 -0800 (PST) X-Received: by 2002:a05:620a:4449:b0:945:c50d:569e with SMTP id af79cd13be357-945f34fb975mr383910185a.36.1793978037359; Fri, 06 Nov 2026 07:13:57 -0800 (PST) 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-c4d3569b856sm86042466b.46.2026.11.06.07.13.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 06 Nov 2026 07:13:56 -0800 (PST) From: Hans de Goede To: libcamera-devel@lists.libcamera.org Cc: Gjorgji Rosikopulos , Loic Poulain , Hans de Goede Subject: [PATCH v3 7/7] camss: Add Offline Processing Engine ISP support Date: Fri, 6 Nov 2026 16:13:41 +0100 Message-ID: <20261106151345.32491-8-johannes.goede@oss.qualcomm.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> References: <20261106151345.32491-1-johannes.goede@oss.qualcomm.com> MIME-Version: 1.0 X-Proofpoint-GUID: Lq6L25evKqJb9Z-36IB8Id14Pmc9VURG X-Proofpoint-Spam-Info: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfX71YRJo6nGwt0 r3cdvBLvDqwGooc6mxdTjZ2GapKXDJHZyomYi7lHS8EsPfVH378FHlIN+8MBFyradxq0AretShJ 90F5jQD3PsKuuoWodUd/CXFh+7bVzdw= X-Authority-Analysis: v=2.4 cv=e7WT2qp/ c=1 sm=1 tr=0 ts=6aedeeb6 cx=c_pps a=HLyN3IcIa5EE8TELMZ618Q==:117 a=xqWC_Br6kY4A:10 a=zJRp6_cAMcgA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=rJkE3RaqiGZ5pbrm-msn:22 a=VwQbUJbxAAAA:8 a=EUspDBNiAAAA:8 a=P511DZv0kOVs7XiUdvQA:9 a=bTQJ7kPSJx9SKPbeHEYW:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYxMTA2MDA1OSBTYWx0ZWRfX3oMFfwIANR2B A2kXcQWKPicTWWsgJKQVvQeBkYBrcg+ctsl/e93ugwInCdOxPm5xSKWjViaPAI7+/lL6oWDeNZN crpdWzqFSApl+91Oi+K8qiwpekdzJPdHau+FSaxFoMeMR452qRZjeX32yYggLWwZn+sEk5nRoGK yuYdQ7YjE12eN8OSu3v+079p/beXbHztXqVAt7VnB7RnIlVbJGe8iPdn4FOYxnNLk8EmYljjkP+ rHe9ZwXLlpAHVVFzVKTWraZ8+yygXrSGBpk3lnHUTELqovqcgOZtvzBoGy2ESFf4T99Tx4w3X/F RT2kLk4lJmajJZCHo+IAmjR2drnL4bN+KOz4+bio+oSgaIN7UNEwCdhaXqhWM0CKWP6ikoyqCBW d7EZU3J5zqAXdFA2uUZPjkVrrWcv7Tca0TWbXFFbULOUauE9sBNRLEpPgqalyI6XTXSfVpCycfM +nQfu8uHSAJicMcNlMg== X-Proofpoint-ORIG-GUID: Lq6L25evKqJb9Z-36IB8Id14Pmc9VURG X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293, Aquarius:18.0.1176, Hydra:6.1.138, FMLib:17.12.100.49 definitions=2026-11-06_05,2026-11-06_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 impostorscore=0 suspectscore=0 clxscore=1015 priorityscore=1501 malwarescore=0 adultscore=0 lowpriorityscore=0 bulkscore=0 phishscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2611040001 definitions=main-2611060059 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 v11 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/20261023-camss-isp-ope-v11-0-0c2937195298@oss.qualcomm.com/ Signed-off-by: Hans de Goede --- src/libcamera/pipeline/camss/camss.cpp | 19 +- .../pipeline/camss/camss_isp_ope.cpp | 704 ++++++++++++++++++ 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, 841 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 20b64ea49..e34d1dbf7 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_raw.h" #include "camss_isp_soft.h" @@ -525,12 +526,18 @@ bool PipelineHandlerCamss::match(DeviceEnumerator *enumerator) data->delayedCtrls_ = std::make_unique(sensor->device(), params); - data->processedIsp_ = std::make_unique(this, sensor, - &data->frameInfos_, - &data->controlInfo_); - if (!data->processedIsp_->isValid()) { - LOG(Camss, Error) << "Failed to create software ISP"; - continue; + data->processedIsp_ = CamssIspOpe::match(this, enumerator, sensor, + &data->frameInfos_, + &data->controlInfo_); + if (data->processedIsp_ == nullptr) { + data->processedIsp_ = + std::make_unique(this, sensor, + &data->frameInfos_, + &data->controlInfo_); + if (!data->processedIsp_->isValid()) { + LOG(Camss, Error) << "Failed to create software ISP"; + continue; + } } data->rawIsp_ = std::make_unique(this, sensor, 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..d9dcb254a --- /dev/null +++ b/src/libcamera/pipeline/camss/camss_isp_ope.cpp @@ -0,0 +1,704 @@ +/* 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(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; + bool lscEnabled; + ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() }, + stats_->getStatsFD(), + sharedParams_.fd(), + sensorInfo, + sensor->controls(), + ispControls, + &ccmEnabled, + &lscEnabled); + 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, + ControlInfoMap *ispControls) +{ + ipa::softisp::IPAConfigInfo configInfo; + configInfo.sensorControls = sensor_->controls(); + + int ret = ipa_->configure(configInfo, ispControls); + 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) { + std::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_, {}); + 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.0, 31.0) * 1024; + awbGains->g_gain = std::clamp(debayerParams.gains.g(), 0.0, 31.0) * 1024; + awbGains->b_gain = std::clamp(debayerParams.gains.b(), 0.0, 31.0) * 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; + + /* + * a[] contains green input contributions to GBR output + * b[] contains blue input contributions to GBR output + * c[] contains red input contributions to GBR output + * k[] contains offset for GBR output + */ + auto colorCorrect = params.block(); + colorCorrect->qfactor = 1; /* Select 12sQ8 format for matrix values */ + colorCorrect->a[0] = std::clamp(debayerParams.combinedMatrix[1][1], -8.0f, 7.9f) * 256; + colorCorrect->a[1] = std::clamp(debayerParams.combinedMatrix[2][1], -8.0f, 7.9f) * 256; + colorCorrect->a[2] = std::clamp(debayerParams.combinedMatrix[0][1], -8.0f, 7.9f) * 256; + colorCorrect->b[0] = std::clamp(debayerParams.combinedMatrix[1][2], -8.0f, 7.9f) * 256; + colorCorrect->b[1] = std::clamp(debayerParams.combinedMatrix[2][2], -8.0f, 7.9f) * 256; + colorCorrect->b[2] = std::clamp(debayerParams.combinedMatrix[0][2], -8.0f, 7.9f) * 256; + colorCorrect->c[0] = std::clamp(debayerParams.combinedMatrix[1][0], -8.0f, 7.9f) * 256; + colorCorrect->c[1] = std::clamp(debayerParams.combinedMatrix[2][0], -8.0f, 7.9f) * 256; + colorCorrect->c[2] = std::clamp(debayerParams.combinedMatrix[0][0], -8.0f, 7.9f) * 256; + colorCorrect->k[0] = 0; + colorCorrect->k[1] = 0; + colorCorrect->k[2] = 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..b2134b00e --- /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, + ControlInfoMap *ispControls) 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_; + 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 51e6e639c..9244de7e0 100644 --- a/src/libcamera/pipeline/camss/camss_params.h +++ b/src/libcamera/pipeline/camss/camss_params.h @@ -22,7 +22,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 518d85478..88a70546d 100644 --- a/src/libcamera/pipeline/camss/meson.build +++ b/src/libcamera/pipeline/camss/meson.build @@ -5,7 +5,10 @@ libcamera_internal_sources += files([ 'camss_csi.cpp', 'camss_frames.cpp', 'camss_isp.cpp', + 'camss_isp_ope.cpp', 'camss_isp_raw.cpp', 'camss_isp_soft.cpp', 'camss_util.cpp', + # FIXME hack + '../../../ipa/libipa/v4l2_params.cpp' ])