From nobody Sun Jul 26 01:48:11 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8D2553DA5DA for ; Fri, 10 Jul 2026 09:04:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674302; cv=none; b=f/Qo6RfIZL+dATFtfmwMYjV9VMuFwsH26TsvwEjP5pPsMtKvcTNbwrO6PM143eZ8OwRMYPEbIaBuYWJCvE6RvA0MLkD/sAm0SxsKDZeNyy25i5exsGHsjN3TFnOMVDP2HbAd8ZCh06F3v3q8Fqi4X0CgSb9xNjixHuccpXjYYYs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674302; c=relaxed/simple; bh=4EixHxIwIvTdS6db1jIzl2G1P8b1qVfh0RRT47S+Ffo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Y3b7u7/ItbPxC6uyX9Qs5kEKeHYMf6WZ9YaakusN7DawoTSxrxw1WLiRefBVLLxn7qTRIVDH/XoeGtgukzBGTt1lyU0DMFlayJgszE4BYw3jpBsxKEkH3QjTpoBrXoB2mMbCVXPFEc/bqMDpYhwAUv2KycP/Sv0hlW7gJoJEIbU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=MSsYgEqo; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=Vg+cli97; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="MSsYgEqo"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="Vg+cli97" Received: from pps.filterd (m0279871.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66A7eEcK190650 for ; Fri, 10 Jul 2026 09:04:57 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= Gk7F2deuwS5Hpkt4gopFDx6kPDKlbTgeIq7RxuJd1Ac=; b=MSsYgEqoAmMIswjA zoDcbJUECXBTEnynPhlebOjs0vII5ilyvPd37IMJIr67ScqGjY31mUoGWU+xXUC6 M3uvX2pOtuE9r0Uc2qV4VTg2uLHU14wtDIHtuWy/JWVxVVY/e+wNHfn3nGS+A2Ox TCljq0yClp4gF1cmhczWf4NDC8B1ISU6EdrDV9sbA1QMRYtoqlCTSj1MOVekql9W +fTBRaI3TTsXOsS78ZCwPDPO0WPMdKifHBJP9d4IovSzKfJgY0byELPCgUfb3UpG MKJiYFBkHntOnZ6h3wnxh43F1JJM5HVU1q8MvmhGozXiVvfsNCI3GtPOsrT0LBKN Z0skLg== Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fas6n14ag-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 10 Jul 2026 09:04:57 +0000 (GMT) Received: by mail-qt1-f197.google.com with SMTP id d75a77b69052e-51a8b0a08d2so5946571cf.0 for ; Fri, 10 Jul 2026 02:04:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1783674297; x=1784279097; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Gk7F2deuwS5Hpkt4gopFDx6kPDKlbTgeIq7RxuJd1Ac=; b=Vg+cli9729OVnct8Nr9TePS+dLJ66CM4pnmt3HpWzGJKcFx/NNxNtnDfam1TB3tehc 7v0dOD9iKek5lNLTuaiFQYQ4udx+ROVntYYIEps9oPEwrVzUHCj2W7lnZXvTgNj07+ct TbDbJgzHxjPICY8Xozaq5W6Pb4RslYBW66Y67K0Q9EdJ/LBgyS2eO5WE1UeAMCfXK+mf t2EvynrBY5WqmZK79/z3/yNRic+jwgTGI6D0MtTkwsL9P5NPSpP025GSFrQLhXOoMxfX F3r3NWc6mHnzElHqousH1WX8QjPrx/cGEaKmP6LojmO0R4B7kThsyyVolWx7qKePXvst 6Syw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783674297; x=1784279097; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=Gk7F2deuwS5Hpkt4gopFDx6kPDKlbTgeIq7RxuJd1Ac=; b=p0MOdtQvITZcMAFkk64FHqDIu9x3yEk/S7oymoZ5UDygX7FpUz213pVdw2kYRUSKsL p+k6ETX2yl4SYNuO1jr7kJWAzxBs5zfdztWrYhBPANvhA7+AGx+JW9v+lhu8aS8dVdpO KrFXb14T1eKIbyb0mqdm1frTVVZoyNHQW78WlR7E7kkrhBnN70UKTzZ1QjNvomUj6ZJJ k5wbtGDzqbIdO9sABBNSHXjKlYrVQUL6gv3HXyHrHvE8nCikwAXizsXP9N6O/zSlEkfQ 7ouXpTNzh+Es5ceudgqYEY+ZrXfuA6TEb/Ks+lxRUfaqeFXwisP1LK+OeE6A1zbUurTE 3VQw== X-Gm-Message-State: AOJu0Yyq7iJuYaiuhC2qaFbmNumuOYp5OeGptYoxDlFWcgqyq3V0BtVk bXGbTGx8z+iIUIk//YCgixHPFu9ZYUdiAcsaJ0ZgTJmbbf1AiQpc5pJvr3OK6PBTRH89WJCUr5T /kZ0Kuwrzx4CadYSj0R5SXls2K3oYz+mfDvnGDtI0Bclas4fNJ/gkr5W3tu+jHrRceKQ= X-Gm-Gg: AfdE7cnLpki0/ORm3mEiGsCWOX4HJz1ayef52k64o3powqbFxFyE7EDE34F6lBcIlIM WWgv6hbkSGX9R3sm3QVnHnzyi9zBLgc+MQNPAD6qgNgvcL5o9me1XBtjxyBNRaMOV4Ixjsl7NPK CNsCaRW4F1QtGQczp5kPSNwQ2xkHsm41T28uQBCM2VDAuaH3kBY+jYdp4QSHKWZrlivIOJLYqJo AcmNMHFUwAwHBKC8bMVDi1DZUlksmLeXs9jZpAtM4fSW0IXG4DnWyfInSthYtKgQyCAPW9KEgMz I+KYY0UVVAf5jrF2UpVMCqqsz/s2wjCeDw1rGUaMiWTynFIrsMY2KHuzW4YvndNOT5KTfef99c6 8SKvNp5vUQOebL5LrEbi7nT74WmaYpsWpFhlyd4k0lRYurY21vsKF/n3AsAMerxUkD9Q2lvXclm IkSldrwMxmhYrRjVrN2NQxMqad0fOn1cs2vrV5++1o0iik0LjcyokZpRRnvZo+7w== X-Received: by 2002:a05:622a:a0b:b0:51c:ebd:fb40 with SMTP id d75a77b69052e-51c8b2e3a0fmr110685341cf.31.1783674296461; Fri, 10 Jul 2026 02:04:56 -0700 (PDT) X-Received: by 2002:a05:622a:a0b:b0:51c:ebd:fb40 with SMTP id d75a77b69052e-51c8b2e3a0fmr110684991cf.31.1783674295724; Fri, 10 Jul 2026 02:04:55 -0700 (PDT) Received: from QCOM-eG0v1AUPpu.na.qualcomm.com ([82.64.236.198]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c15dfda815dsm259932266b.36.2026.07.10.02.04.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 02:04:54 -0700 (PDT) From: Loic Poulain Date: Fri, 10 Jul 2026 11:04:47 +0200 Subject: [PATCH v4 1/7] media: qcom: camss: Add camss-pipeline helper Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260710-camss-isp-ope-v4-1-51207a0319d8@oss.qualcomm.com> References: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> In-Reply-To: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> To: Bryan O'Donoghue , Vladimir Zapolskiy , Loic Poulain , Mauro Carvalho Chehab , Kees Cook , "Gustavo A. R. Silva" , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio Cc: linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-hardening@vger.kernel.org, devicetree@vger.kernel.org X-Mailer: b4 0.14.2 X-Proofpoint-Spam-Info: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfXzITxdmo+sVXm Re3luaAYBnx7SvuGmzs8jq3s4PVUynVuDvQnD0Nu5JlR6Clm52fHO2j2YeBov1KDL9xO+ThLtfi ZR2x1/hUNBdt1p+QlOgepaa/LhwCL1o= X-Authority-Analysis: v=2.4 cv=DYgnbPtW c=1 sm=1 tr=0 ts=6a50b5b9 cx=c_pps a=EVbN6Ke/fEF3bsl7X48z0g==:117 a=MDeckJw97qnk8wCBExTehA==:17 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=3WHJM1ZQz_JShphwDgj5:22 a=EUspDBNiAAAA:8 a=nv4yEDu1kATSZ9vQL9cA:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 a=a_PwQJl-kcHnX1M80qC6:22 X-Proofpoint-ORIG-GUID: ZIciy_y37iloaCsR4TX2D-LSmkAVnsv7 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfXxJEGe0tdFk9z waCxTGQrwcu7xr7I4r78g5j/hK8RXd6IyhJHBe/51GwbNgNkWe6ohhWPPR2WzDTFcrxb2iv84qm h9tPYzhdYqlVRCkxjKO9QvtJjlywB6rTWiwB/xD9mkVICettFTX+v7+aAXYt+VP3eXgev0zTar3 Wm7wyFYEJrXXRc4qZ4I7sKlJu8Docr9h2yheos0MDPJmQFGDxzKfQZ7iE+1norEAuSxvktq50PV mzBHtl/I2Cb+1fk1rcwvC9YYEuEe8oq0PE5pfvvEYBAw95fnPNgwBBzutNhJzcgtZWkxwJqRyw6 U/O/tiHwkSvF995rp6pp3M1GbBhriDL1zzsgflr8lDb7DmdRlJbX5vWBUbMMkCmSPuyOXpRiUQZ sPKfDtFEQlpGm8KF3kjtVaCEw+Ejq7+Ny3pgvyHc7Q2rY7pCLclKiUKIKErjHT7RThlGT+WaXNf 6L5NGQu5+g1pj84DLBw== X-Proofpoint-GUID: ZIciy_y37iloaCsR4TX2D-LSmkAVnsv7 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-10_02,2026-07-09_04,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 lowpriorityscore=0 adultscore=0 suspectscore=0 bulkscore=0 clxscore=1015 phishscore=0 malwarescore=0 spamscore=0 impostorscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607100088 Add a declarative MC topology builder for CAMSS offline ISP drivers. Drivers describe their entire media graph, entities (video devices, subdevs, or base entities), their pads, and the links between them in a static descriptor table. The builder validates the table, allocates and registers all entities, and creates all MC pad links. Signed-off-by: Loic Poulain --- drivers/media/platform/qcom/camss/Makefile | 1 + drivers/media/platform/qcom/camss/camss-pipeline.c | 386 +++++++++++++++++= ++++ drivers/media/platform/qcom/camss/camss-pipeline.h | 232 +++++++++++++ 3 files changed, 619 insertions(+) diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/pla= tform/qcom/camss/Makefile index 5e349b4915130c71dbff90e73102e46dfede1520..0d133ddc08be088678994820c0a= c64e761884611 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -27,5 +27,6 @@ qcom-camss-objs +=3D \ camss-vfe.o \ camss-video.o \ camss-format.o \ + camss-pipeline.o \ =20 obj-$(CONFIG_VIDEO_QCOM_CAMSS) +=3D qcom-camss.o diff --git a/drivers/media/platform/qcom/camss/camss-pipeline.c b/drivers/m= edia/platform/qcom/camss/camss-pipeline.c new file mode 100644 index 0000000000000000000000000000000000000000..3cdc0df7123d718efc0ad0c6d01= a25fb13cf6ca9 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-pipeline.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * CAMSS ISP pipeline helper =E2=80=94 declarative MC topology builder + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include + +#include +#include +#include +#include +#include +#include "camss-pipeline.h" + +#if !IS_ENABLED(CONFIG_MEDIA_CONTROLLER) +static inline int media_entity_pads_init(struct media_entity *e, u16 n, + struct media_pad *p) { return 0; } +static inline void media_entity_remove_links(struct media_entity *e) {} +static inline int media_create_pad_link(struct media_entity *src, u16 sp, + struct media_entity *sink, u16 dp, + u32 flags) { return 0; } +#endif + +/* -------- Internal elpers -------- */ + +static enum vfl_devnode_direction isp_caps_to_vfl_dir(u32 caps) +{ + if (caps & (V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE)) + return VFL_DIR_M2M; + if (caps & (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE | + V4L2_CAP_META_OUTPUT | V4L2_CAP_VBI_OUTPUT | V4L2_CAP_SDR_OUTPUT)) + return VFL_DIR_TX; + return VFL_DIR_RX; +} + +static unsigned int isp_count_pads(const struct camss_isp_pad_desc *pads) +{ + unsigned int n =3D 0; + + if (!pads) + return 0; + while (pads[n].flags) + n++; + return n; +} + +static struct media_entity *isp_pipeline_media_entity(struct camss_isp_pip= eline *pipeline, + unsigned int idx) +{ + struct camss_isp_pipeline_entity *slot =3D &pipeline->entities[idx]; + + switch (slot->obj_type) { + case MEDIA_ENTITY_TYPE_VIDEO_DEVICE: + return &slot->vdev.entity; + case MEDIA_ENTITY_TYPE_V4L2_SUBDEV: + return &slot->subdev.entity; + default: + return &slot->entity; + } +} + +/* -------- Validation -------- */ + +static int isp_pipeline_validate(struct device *dev, + const struct camss_isp_entity_desc *descs, + unsigned int num_entities) +{ + unsigned int i, pi; + + for (i =3D 0; i < num_entities; i++) { + const struct camss_isp_pad_desc *pads =3D descs[i].pads; + unsigned int num_pads =3D isp_count_pads(pads); + + for (pi =3D 0; pi < num_pads; pi++) { + const struct camss_isp_pad_desc *pad =3D &pads[pi]; + const struct camss_isp_pad_desc *peer_pad; + unsigned int peer_num_pads; + int peer_ent =3D pad->peer_entity; + + if (peer_ent < 0) + continue; + + if ((unsigned int)peer_ent >=3D num_entities) { + dev_err(dev, "entity[%u].p%u: peer_entity %d out of range\n", + i, pi, peer_ent); + return -EINVAL; + } + + peer_num_pads =3D isp_count_pads(descs[peer_ent].pads); + if (pad->peer_pad >=3D peer_num_pads) { + dev_err(dev, "entity[%u].p%u: peer_pad %u out of range\n", + i, pi, pad->peer_pad); + return -EINVAL; + } + + peer_pad =3D &descs[peer_ent].pads[pad->peer_pad]; + + /* Links are SOURCE->SINK; reject SOURCE->SOURCE or SINK->SINK */ + if (((pad->flags & MEDIA_PAD_FL_SOURCE) && + (peer_pad->flags & MEDIA_PAD_FL_SOURCE)) || + ((pad->flags & MEDIA_PAD_FL_SINK) && + (peer_pad->flags & MEDIA_PAD_FL_SINK))) { + dev_err(dev, "entity[%u].p%u -> entity[%d].p%u: invalid\n", + i, pi, peer_ent, pad->peer_pad); + return -EINVAL; + } + + /* Verify back-reference consistency */ + if (peer_pad->peer_entity >=3D 0 && + ((unsigned int)peer_pad->peer_entity !=3D i || + peer_pad->peer_pad !=3D pi)) { + dev_err(dev, "entity[%u].p%u <-> entity[%d].p%u: mismatch\n", + i, pi, peer_ent, pad->peer_pad); + return -EINVAL; + } + } + } + + return 0; +} + +/* -------- Allocation / Release -------- */ + +struct camss_isp_pipeline *camss_isp_pipeline_alloc(unsigned int num_entit= ies) +{ + struct camss_isp_pipeline *pipeline; + + pipeline =3D kzalloc(struct_size(pipeline, entities, num_entities), + GFP_KERNEL); + if (!pipeline) + return ERR_PTR(-ENOMEM); + + pipeline->num_entities =3D num_entities; + return pipeline; +} +EXPORT_SYMBOL_GPL(camss_isp_pipeline_alloc); + +void camss_isp_pipeline_free(struct camss_isp_pipeline *pipeline) +{ + kfree(pipeline); +} +EXPORT_SYMBOL_GPL(camss_isp_pipeline_free); + +/* -------- Registration -------- */ + +void camss_isp_pipeline_unregister(struct camss_isp_pipeline *pipeline) +{ + int i; + + /* Unregister entities in reverse order */ + for (i =3D (int)pipeline->num_entities - 1; i >=3D 0; i--) { + struct camss_isp_pipeline_entity *slot =3D &pipeline->entities[i]; + + switch (slot->obj_type) { + case MEDIA_ENTITY_TYPE_VIDEO_DEVICE: + if (slot->vdev.name[0]) + video_unregister_device(&slot->vdev); + break; + case MEDIA_ENTITY_TYPE_V4L2_SUBDEV: + if (slot->subdev.name[0]) { + v4l2_subdev_cleanup(&slot->subdev); + v4l2_device_unregister_subdev(&slot->subdev); + } + break; + case MEDIA_ENTITY_TYPE_BASE: + if (slot->entity.name) { + media_entity_remove_links(&slot->entity); + media_device_unregister_entity(&slot->entity); + } + break; + } + + kfree(slot->pads); + slot->pads =3D NULL; + } + + pipeline->v4l2_dev =3D NULL; +} +EXPORT_SYMBOL_GPL(camss_isp_pipeline_unregister); + +static int isp_register_vdev(struct camss_isp_pipeline_entity *slot, + const struct camss_isp_entity_desc *desc, + struct v4l2_device *v4l2_dev) +{ + struct video_device *vdev =3D &slot->vdev; + int ret; + + strscpy(vdev->name, desc->name, sizeof(vdev->name)); + vdev->vfl_dir =3D isp_caps_to_vfl_dir(desc->vdev.caps); + vdev->v4l2_dev =3D v4l2_dev; + vdev->device_caps =3D desc->vdev.caps | V4L2_CAP_IO_MC; + vdev->release =3D video_device_release_empty; + if (desc->vdev.fops) + vdev->fops =3D desc->vdev.fops; + if (desc->vdev.ioctl_ops) + vdev->ioctl_ops =3D desc->vdev.ioctl_ops; + if (desc->vdev.entity_ops) + vdev->entity.ops =3D desc->vdev.entity_ops; + + vdev->entity.obj_type =3D MEDIA_ENTITY_TYPE_VIDEO_DEVICE; + vdev->entity.function =3D desc->function ? desc->function : MEDIA_ENT_F_I= O_V4L; + + ret =3D media_entity_pads_init(&vdev->entity, slot->num_pads, slot->pads); + if (ret) + return ret; + + ret =3D video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) + return ret; + + video_set_drvdata(vdev, desc->vdev.drvdata); + + return 0; +} + +static int isp_register_subdev(struct camss_isp_pipeline_entity *slot, + const struct camss_isp_entity_desc *desc, + struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd =3D &slot->subdev; + int ret; + + v4l2_subdev_init(sd, desc->subdev.ops); + strscpy(sd->name, desc->name, sizeof(sd->name)); + sd->entity.function =3D desc->function ? + desc->function : MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; + /* Create a /dev/v4l-subdevN node so userspace can query pad formats */ + sd->flags |=3D V4L2_SUBDEV_FL_HAS_DEVNODE; + if (desc->subdev.internal_ops) + sd->internal_ops =3D desc->subdev.internal_ops; + if (desc->subdev.entity_ops) + sd->entity.ops =3D desc->subdev.entity_ops; + + ret =3D media_entity_pads_init(&sd->entity, slot->num_pads, slot->pads); + if (ret) + return ret; + + ret =3D v4l2_device_register_subdev(v4l2_dev, sd); + if (ret) + return ret; + + ret =3D v4l2_subdev_init_finalize(sd); + if (ret) { + v4l2_device_unregister_subdev(sd); + return ret; + } + + return 0; +} + +static int isp_register_base_entity(struct camss_isp_pipeline_entity *slot, + const struct camss_isp_entity_desc *desc, + struct v4l2_device *v4l2_dev) +{ + struct media_entity *entity =3D &slot->entity; + int ret; + + entity->obj_type =3D MEDIA_ENTITY_TYPE_BASE; + entity->name =3D desc->name; + entity->function =3D desc->function; + + ret =3D media_entity_pads_init(entity, slot->num_pads, slot->pads); + if (ret) + return ret; + + return media_device_register_entity(v4l2_dev->mdev, entity); +} + +static int isp_alloc_pads(struct camss_isp_pipeline_entity *slot, + const struct camss_isp_entity_desc *desc) +{ + unsigned int num_pads =3D isp_count_pads(desc->pads); + unsigned int i; + + if (!num_pads) + goto done; + + slot->pads =3D kcalloc(num_pads, sizeof(*slot->pads), GFP_KERNEL); + if (!slot->pads) + return -ENOMEM; + + for (i =3D 0; i < num_pads; i++) + slot->pads[i].flags =3D desc->pads[i].flags; +done: + slot->num_pads =3D num_pads; + return 0; +} + +int camss_isp_pipeline_register(struct camss_isp_pipeline *pipeline, + struct v4l2_device *v4l2_dev, + const struct camss_isp_entity_desc *descs, + unsigned int num_entities) +{ + unsigned int i, pi; + int ret; + + if (WARN_ON(num_entities !=3D pipeline->num_entities)) + return -EINVAL; + + if (WARN_ON(!v4l2_dev || !v4l2_dev->mdev)) + return -EINVAL; + + ret =3D isp_pipeline_validate(v4l2_dev->dev, descs, num_entities); + if (ret) + return ret; + + pipeline->v4l2_dev =3D v4l2_dev; + + /* Register each entity */ + for (i =3D 0; i < num_entities; i++) { + const struct camss_isp_entity_desc *desc =3D &descs[i]; + struct camss_isp_pipeline_entity *slot =3D &pipeline->entities[i]; + + slot->obj_type =3D desc->obj_type; + + ret =3D isp_alloc_pads(slot, desc); + if (ret) + goto err_unregister; + + switch (desc->obj_type) { + case MEDIA_ENTITY_TYPE_VIDEO_DEVICE: + ret =3D isp_register_vdev(slot, desc, v4l2_dev); + break; + case MEDIA_ENTITY_TYPE_V4L2_SUBDEV: + ret =3D isp_register_subdev(slot, desc, v4l2_dev); + break; + case MEDIA_ENTITY_TYPE_BASE: + default: + ret =3D isp_register_base_entity(slot, desc, v4l2_dev); + break; + } + if (ret) + goto err_unregister; + } + + /* Create links =E2=80=94 only from SOURCE side to avoid duplicates */ + for (i =3D 0; i < num_entities; i++) { + const struct camss_isp_entity_desc *desc =3D &descs[i]; + unsigned int num_pads =3D isp_count_pads(desc->pads); + + for (pi =3D 0; pi < num_pads; pi++) { + const struct camss_isp_pad_desc *pad =3D &desc->pads[pi]; + struct media_entity *src_entity, *sink_entity; + unsigned int src_pad_idx, sink_pad_idx; + u32 lflags; + + if (!(pad->flags & MEDIA_PAD_FL_SOURCE)) + continue; + if (pad->peer_entity < 0) + continue; + + src_entity =3D isp_pipeline_media_entity(pipeline, i); + sink_entity =3D isp_pipeline_media_entity(pipeline, + (unsigned int)pad->peer_entity); + src_pad_idx =3D pi; + sink_pad_idx =3D pad->peer_pad; + + lflags =3D pad->link_flags ? + pad->link_flags : + (MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + + ret =3D media_create_pad_link(src_entity, src_pad_idx, + sink_entity, sink_pad_idx, + lflags); + if (ret) + goto err_unregister; + } + } + + /* Create /dev/v4l-subdevN nodes for all registered subdevs */ + ret =3D v4l2_device_register_subdev_nodes(v4l2_dev); + if (ret) + goto err_unregister; + + return 0; + +err_unregister: + camss_isp_pipeline_unregister(pipeline); + return ret; +} +EXPORT_SYMBOL_GPL(camss_isp_pipeline_register); + +MODULE_DESCRIPTION("CAMSS ISP pipeline topology builder"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/qcom/camss/camss-pipeline.h b/drivers/m= edia/platform/qcom/camss/camss-pipeline.h new file mode 100644 index 0000000000000000000000000000000000000000..e3082fb138a141f0955c6c74200= 143c4550419ae --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-pipeline.h @@ -0,0 +1,232 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * CAMSS ISP pipeline helper =E2=80=94 declarative MC topology builder + * + * Drivers describe their entire media graph =E2=80=94 entities (video dev= ices, + * subdevs, or base entities), their pads, and the links between them =E2= =80=94 + * in a single static descriptor table. The builder validates the table, + * allocates and registers all entities, and creates all MC links. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _CAMSS_PIPELINE_H +#define _CAMSS_PIPELINE_H + +#include +#include +#include +#include +#include +#include + +/** + * struct camss_isp_pad_desc - descriptor for one pad and its optional link + * + * @flags: Pad flags: MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE, + * MEDIA_PAD_FL_MUST_CONNECT. A zero @flags value acts as + * the sentinel that terminates the pad list. + * @peer_entity: Index of the peer entity in the descriptor array, or -1 + * if this pad has no link. + * @peer_pad: Pad index on the peer entity to link to. + * @link_flags: MC link flags (MEDIA_LNK_FL_*). Defaults to + * MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED when zero. + * + * Links are described from both sides (each endpoint references the other= ), + * but the builder only creates each link once =E2=80=94 from the SOURCE s= ide. + */ +struct camss_isp_pad_desc { + u32 flags; + int peer_entity; + unsigned int peer_pad; + u32 link_flags; +}; + +/** + * struct camss_isp_entity_desc - descriptor for one entity in the pipeline + * + * @name: Human-readable entity name (also used as video device name + * suffix when @obj_type is MEDIA_ENTITY_TYPE_VIDEO_DEVICE). + * @obj_type: MEDIA_ENTITY_TYPE_VIDEO_DEVICE, MEDIA_ENTITY_TYPE_V4L2_SUBD= EV, + * or MEDIA_ENTITY_TYPE_BASE. + * @function: MEDIA_ENT_F_* function identifier. + * @pads: Sentinel-terminated (flags =3D=3D 0) array of pad descripto= rs. + * + * Fields used only for MEDIA_ENTITY_TYPE_VIDEO_DEVICE: + * @vdev.caps: V4L2_CAP_* device capabilities. + * The video device direction (VFL_DIR_RX/TX/M2M) is derived + * automatically from @caps by the builder. + * @vdev.drvdata: Opaque pointer set via video_set_drvdata() after registr= ation. + * @vdev.fops: File operations (may be NULL to use kernel defaults). + * @vdev.ioctl_ops: ioctl operations (may be NULL). + * + * Fields used only for MEDIA_ENTITY_TYPE_V4L2_SUBDEV: + * @subdev.ops: Subdev operations (may be NULL). + * @subdev.internal_ops: Internal subdev operations (may be NULL). + */ +struct camss_isp_entity_desc { + const char *name; + u32 obj_type; + u32 function; + const struct camss_isp_pad_desc *pads; + + union { + /* MEDIA_ENTITY_TYPE_VIDEO_DEVICE */ + struct { + u32 caps; + void *drvdata; + const struct v4l2_file_operations *fops; + const struct v4l2_ioctl_ops *ioctl_ops; + const struct media_entity_operations *entity_ops; + } vdev; + /* MEDIA_ENTITY_TYPE_V4L2_SUBDEV */ + struct { + const struct v4l2_subdev_ops *ops; + const struct v4l2_subdev_internal_ops *internal_ops; + const struct media_entity_operations *entity_ops; + } subdev; + }; +}; + +/** + * struct camss_isp_pipeline_entity - one registered entity slot + * + * Internal to the pipeline; drivers access entities via the accessor help= ers. + * + * @obj_type: mirrors the descriptor's @obj_type. + * @pads: allocated pad array for this entity. + * @num_pads: number of entries in @pads. + * @vdev: valid when @obj_type =3D=3D MEDIA_ENTITY_TYPE_VIDEO_DEVICE. + * @subdev: valid when @obj_type =3D=3D MEDIA_ENTITY_TYPE_V4L2_SUBDEV. + * @entity: valid when @obj_type =3D=3D MEDIA_ENTITY_TYPE_BASE. + */ +struct camss_isp_pipeline_entity { + u32 obj_type; + struct media_pad *pads; + unsigned int num_pads; + union { + struct video_device vdev; + struct v4l2_subdev subdev; + struct media_entity entity; + }; +}; + +/** + * struct camss_isp_pipeline - registered ISP pipeline topology + * + * Allocate with camss_isp_pipeline_alloc(), register with + * camss_isp_pipeline_register(), tear down with + * camss_isp_pipeline_unregister(), free with camss_isp_pipeline_free(). + * + * @v4l2_dev: Pointer to the caller-provided V4L2 device. + * @drv_priv: Driver-private pointer; not touched by the framework. + * @num_entities: Number of entries in @entities. + * @entities: Per-entity state; flexible array. + */ +struct camss_isp_pipeline { + struct v4l2_device *v4l2_dev; + void *drv_priv; + + unsigned int num_entities; + struct camss_isp_pipeline_entity entities[] __counted_by(num_entities); +}; + +/** + * camss_isp_pipeline_alloc() - allocate a pipeline for @num_entities enti= ties + * + * Returns a pointer to the new pipeline or ERR_PTR on failure. + * Free with camss_isp_pipeline_free() if never registered, or call + * camss_isp_pipeline_unregister() followed by camss_isp_pipeline_free(). + */ +struct camss_isp_pipeline *camss_isp_pipeline_alloc(unsigned int num_entit= ies); + +/** + * camss_isp_pipeline_free() - free an unregistered pipeline + * @pipeline: pipeline to free (may be NULL) + */ +void camss_isp_pipeline_free(struct camss_isp_pipeline *pipeline); + +/** + * camss_isp_pipeline_register() - validate descriptors and register the g= raph + * @pipeline: pipeline (allocated with camss_isp_pipeline_alloc()) + * @v4l2_dev: caller-owned and already-registered V4L2 device; its + * associated media_device (v4l2_dev->mdev) must also be + * initialised and registered before this call. + * @descs: array of @num_entities entity descriptors + * @num_entities: number of entities; must equal pipeline->num_entities + * + * Validates the descriptor table (link direction consistency, index bound= s), + * then registers all entities into the provided v4l2_device / media_device + * and creates all MC pad links. + * + * Returns 0 on success or a negative error code. + */ +int camss_isp_pipeline_register(struct camss_isp_pipeline *pipeline, + struct v4l2_device *v4l2_dev, + const struct camss_isp_entity_desc *descs, + unsigned int num_entities); + +/** + * camss_isp_pipeline_unregister() - tear down a registered pipeline + * @pipeline: pipeline to unregister + */ +void camss_isp_pipeline_unregister(struct camss_isp_pipeline *pipeline); + +/** + * camss_isp_pipeline_get_vdev() - return the video_device for entity @idx + * @pipeline: registered pipeline + * @idx: entity index (must be MEDIA_ENTITY_TYPE_VIDEO_DEVICE) + * + * Returns NULL if @idx is out of range or the entity is not a video devic= e. + */ +static inline struct video_device * +camss_isp_pipeline_get_vdev(struct camss_isp_pipeline *pipeline, + unsigned int idx) +{ + if (WARN_ON(idx >=3D pipeline->num_entities)) + return NULL; + if (WARN_ON(pipeline->entities[idx].obj_type !=3D + MEDIA_ENTITY_TYPE_VIDEO_DEVICE)) + return NULL; + return &pipeline->entities[idx].vdev; +} + +/** + * camss_isp_pipeline_get_subdev() - return the v4l2_subdev for entity @idx + * @pipeline: registered pipeline + * @idx: entity index (must be MEDIA_ENTITY_TYPE_V4L2_SUBDEV) + * + * Returns NULL if @idx is out of range or the entity is not a subdev. + */ +static inline struct v4l2_subdev * +camss_isp_pipeline_get_subdev(struct camss_isp_pipeline *pipeline, + unsigned int idx) +{ + if (WARN_ON(idx >=3D pipeline->num_entities)) + return NULL; + if (WARN_ON(pipeline->entities[idx].obj_type !=3D + MEDIA_ENTITY_TYPE_V4L2_SUBDEV)) + return NULL; + return &pipeline->entities[idx].subdev; +} + +/** + * camss_isp_pipeline_get_entity() - return the media_entity for entity @i= dx + * @pipeline: registered pipeline + * @idx: entity index (must be MEDIA_ENTITY_TYPE_BASE) + * + * Returns NULL if @idx is out of range or the entity is not a base entity. + */ +static inline struct media_entity * +camss_isp_pipeline_get_entity(struct camss_isp_pipeline *pipeline, + unsigned int idx) +{ + if (WARN_ON(idx >=3D pipeline->num_entities)) + return NULL; + if (WARN_ON(pipeline->entities[idx].obj_type !=3D + MEDIA_ENTITY_TYPE_BASE)) + return NULL; + return &pipeline->entities[idx].entity; +} + +#endif /* _CAMSS_PIPELINE_H */ --=20 2.34.1 From nobody Sun Jul 26 01:48:11 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A18E23D6CB7 for ; Fri, 10 Jul 2026 09:04:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674301; cv=none; b=qUkv67xdohr0/bp5QH552G7/WxGLYPuceiTfmk1gHyQuumbIrj7RIaQytEjlMmktMxQmH2rgEC89pVlaFNJdALeGrf5JiwXg/uTeK++TYpaIRtuptEot6Fzof7z+oGrg2OcrUoSg67mRzYb2P5jtQT6OZKS9GJiUyzYAzzWZo2k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674301; c=relaxed/simple; bh=QBbw+M6NM/7xN/mb/n4EhswLHY92P8G06BHphqlIqXQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=SUgYZHmiT1l5cJFbyuOKVMg5A5dfBm3mOnw3ThuzMnf2+hfCIIdqvKRwtRri5xO+iro/gj4BcBJOBicHIGvT8+piEoL/apnvs2k1Rjr+V7dL7QKPjoQVqg18eiHBUzSvtw0joloH7/1VUEDb9W00ao5DAB2afVYuqjlS3R0aH9Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=irvF8Mfa; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=bVBgYIOG; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="irvF8Mfa"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="bVBgYIOG" 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 66A7dWxO4194181 for ; Fri, 10 Jul 2026 09:04:58 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= 9beixJbeJJsoRWLUTkdRDRxoNyd2rNMiM72fJceYNRM=; b=irvF8MfaPyLrVmL6 M+CIBFXO5yeirklARUCZDXTEV6g6ouCgEvsMrvecDmv2fuHYF87HhvhWG354WAcE ggjegVmBklfvX/KO1Dw80cTdXKCy3ITk1+COfVBsNv49dI8UnA5HvIaRMOayOat9 9zXpJ9Ngj/aQVo/oUdU3Jy3mCsEMwxOIntmq//yI/qGYhN2d7MQ95PVxMa72caYH k8uC4wsfUOEtTsrvHf7WpNN4m+aRvTOVh26MXbdiRnuoIVkg2xjN34Sqj/jZOzSS gN88zk48iHQchDsngOTdLgn54H8Wy+CAaBLgdC7I9xKXKdApXeFcL+kJnvhkYDYS 7ljBqA== Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4faf24ugpr-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 10 Jul 2026 09:04:58 +0000 (GMT) Received: by mail-qt1-f197.google.com with SMTP id d75a77b69052e-51c12e43b98so8195011cf.1 for ; Fri, 10 Jul 2026 02:04:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1783674298; x=1784279098; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=9beixJbeJJsoRWLUTkdRDRxoNyd2rNMiM72fJceYNRM=; b=bVBgYIOG321ZEjaVPvSyq62iCy7sc4GLVP6A1/3zUq7k8gVEFn01mKPVdpx2bwv5u3 7wkuujmxc4hdarqnBm33lRI2U3YiIpqjPNbPhFOLL8mRO16kQ7QrpkensCsJcoYVU6Z7 5Q/b6fauXiRgvs21bxKf0n+0l+QsXDOQk/dFJqlo2PDr0RQNIZt5o5nv+Bl/0PbgawmJ OkEQfnQ2vMNfgiHaI5DV2EaqV1qAZ8RQqcuDvFd4RDPP3oGjc3LnwArS9s4KImkZWr6S b/eGg6YCRAAFDGINdlqXzEtyOcURDk3C92dIqigVgUwqLHEP09ltqAvkd0zJ3KYFRWuZ 9z4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783674298; x=1784279098; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=9beixJbeJJsoRWLUTkdRDRxoNyd2rNMiM72fJceYNRM=; b=InswyGD+bWjKJN0NsMac2KoyhzMwCCwgCwPnjaZH7m8S6tqSlfTyVFVRg9OVJrA//w ZSzTHRDyaBUZ36woB/RHuLeOUV9bTB/GaUGAueSNgDWLM4EG416n7cldhKP1SZFqQ4qJ KBRk4gEb3R1Z2l6Di21Zzam0US1fVdzs+o+RUBeZL9uYBgNwMsI/fNkGH1sfDbZ1jhzx jQTLEFgOpKMfZfa6wD7KmyGgNWJFropnE90O5W5lkiOK2Bao2sB6b7ZEyFFx4JZpGkzd qc1ZbHDqqtrpyywcgWHnCSENMlaeYMJ+RK/7o9R8H6U4s/8nb9tcEXApw2zl3ouxVC+v I6ZQ== X-Gm-Message-State: AOJu0YyphlCpuQp68/9gyaAnBeEm9i0yy0FmHnAvOBpi6qMztZ0U2ZnP vGsdwaiNoic27igeugQKQPiBUUAtOtHQbDaTzv3SQ/Xp8Wbmy0E0ABRGEMB8Qh+uzAd5o1vpy8i h1ePM55RK+/nJye1AzWrW4XVHfsxMIgB0W5w3EDrNnru4b29/nJlEXYqHFqaoRNsNyRAM0Z+1Z3 E/tQ== X-Gm-Gg: AfdE7ckpQY6ga9Akn2sYJNsFIMVsdDHK6JDZo1NTu81NfymVjbkRdY5Ve6NrMaLjtK6 KOZQieJFIzW65GIVP7xgMKk/iS1UNZXBadB28VYq87GLWGPjys3/mOjEmRUh/WN7aNVOnaCw4ap YoB5Rmfaec5578TnlINPLUzNzrYRZ9muSLYLL+5JSnJZOtokkG3Cgy0lX4kS7NmHdmx7mPJt0Xj gUz17EYP1/gp08qU6pk97C/yU0/byihtTxkpw/xgWlFVRfh2MR1uCJeGkizsTfNvdiruo1MVIsD UTkwksoZZXDDrxGFcQ2Oy11W478sZGmOdZCPFHSTcPNEzoc3dfrjDCIUE97bzF//18FfysV+Y4I QVz64jHnRUIS1MZgfoW55w8MT6BpLDs39T9aUAx0OX+it2QLgUjtP52qPEEfaSLOlmQ6QafOIEw cdkYtTSFHJw5TEvCjq6oKDoOGlAJ0BD8fCcoQrTmXAVMjx6ymG8ll4PF6UIaBA5Q== X-Received: by 2002:ac8:7d85:0:b0:51c:7b13:62f8 with SMTP id d75a77b69052e-51c8b438a5cmr111756851cf.78.1783674298043; Fri, 10 Jul 2026 02:04:58 -0700 (PDT) X-Received: by 2002:ac8:7d85:0:b0:51c:7b13:62f8 with SMTP id d75a77b69052e-51c8b438a5cmr111756501cf.78.1783674297528; Fri, 10 Jul 2026 02:04:57 -0700 (PDT) Received: from QCOM-eG0v1AUPpu.na.qualcomm.com ([82.64.236.198]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c15dfda815dsm259932266b.36.2026.07.10.02.04.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 02:04:56 -0700 (PDT) From: Loic Poulain Date: Fri, 10 Jul 2026 11:04:48 +0200 Subject: [PATCH v4 2/7] media: qcom: camss: Add camss-params helper Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260710-camss-isp-ope-v4-2-51207a0319d8@oss.qualcomm.com> References: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> In-Reply-To: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> To: Bryan O'Donoghue , Vladimir Zapolskiy , Loic Poulain , Mauro Carvalho Chehab , Kees Cook , "Gustavo A. R. Silva" , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio Cc: linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-hardening@vger.kernel.org, devicetree@vger.kernel.org X-Mailer: b4 0.14.2 X-Proofpoint-GUID: a7Qd0eeQDvmPkkgCZ_z3RD-iNwbuU7yf X-Proofpoint-ORIG-GUID: a7Qd0eeQDvmPkkgCZ_z3RD-iNwbuU7yf X-Proofpoint-Spam-Info: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfX3FKPNB8XiaJa OUbSl8hhsyZQuVsgn5ye7Zb+fx9UjAEl7VV27C08N4Tr+5bfqMKs7hL8GVu0z1rsfBOPlu+8cxm Qc9sW3q/TZfLFPIE1P88R4zsk+/sFN4= X-Authority-Analysis: v=2.4 cv=daKwG3Xe c=1 sm=1 tr=0 ts=6a50b5ba cx=c_pps a=EVbN6Ke/fEF3bsl7X48z0g==:117 a=MDeckJw97qnk8wCBExTehA==:17 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=yx91gb_oNiZeI1HMLzn7:22 a=EUspDBNiAAAA:8 a=ELVW7ba-Z2avb-AJsJIA:9 a=QEXdDO2ut3YA:10 a=a_PwQJl-kcHnX1M80qC6:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfX7doar/nthcT1 UsZgwVNBDMVvJFdFhT5vy3dB7k/VwumptVn6DnIXer10SnOwTeW8rqkXHRNq7lUf5/eJ7tpQHbG zfykTuL8hZLSxdG0ZlvUDNrYSys6ibgMAbt07PJhqIVzL7s9k1JjCayO4VC+ghGePeC/DQLdy5D xj0ZBNkPRDfkojNgM1PujobpkSoHpqbOD7Cc1zJc3ZX5egc0kKD/nXxLHoIdjQcOmTjldf7+ZIq FmIa5MmuE0A7WVs4GmkgbQm+HAQE7nDphXZSw4R1UoMsdckECIY4ZQoMg3q5l2jQCoM2bMdwtZM UDueE2u3dBHutRz/UUfm/YNMUEgK9hKlEWfsH7ul/wtS4TW4DhhFL34NmtDOZ1A5u+hqSDSRV18 fsfYkFgz5oq4t2GZKjMgzq/xsHZM2gf4UhOM3uC8EcxBCu/z2RqBDmvlIegC/VBnF2WRKw6kK1i btoAJ2d8OF30DKYy+Xg== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-10_02,2026-07-09_04,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 phishscore=0 adultscore=0 suspectscore=0 lowpriorityscore=0 clxscore=1015 malwarescore=0 priorityscore=1501 bulkscore=0 impostorscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607100088 Add V4L2 ISP parameter buffer validation helpers for CAMSS ISP drivers. Provides buffer size validation and per-block type validation for the extensible V4L2 ISP parameters buffer format. Signed-off-by: Loic Poulain --- drivers/media/platform/qcom/camss/Makefile | 1 + drivers/media/platform/qcom/camss/camss-params.c | 67 ++++++++++++++++++++= ++++ drivers/media/platform/qcom/camss/camss-params.h | 62 ++++++++++++++++++++= ++ 3 files changed, 130 insertions(+) diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/pla= tform/qcom/camss/Makefile index 0d133ddc08be088678994820c0ac64e761884611..5678621efb6780b67a043ec8a2e= 914cce02d9b98 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -28,5 +28,6 @@ qcom-camss-objs +=3D \ camss-video.o \ camss-format.o \ camss-pipeline.o \ + camss-params.o \ =20 obj-$(CONFIG_VIDEO_QCOM_CAMSS) +=3D qcom-camss.o diff --git a/drivers/media/platform/qcom/camss/camss-params.c b/drivers/med= ia/platform/qcom/camss/camss-params.c new file mode 100644 index 0000000000000000000000000000000000000000..ebb75a191d37551b4616a048ed3= 9408f219d7daa --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-params.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-params.c + * + * CAMSS ISP parameter buffer parser. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include "camss-params.h" + +int camss_isp_params_apply(struct device *dev, + struct vb2_buffer *vb, + const struct v4l2_isp_params_block_type_info *type_info, + const camss_isp_params_handler_fn *handlers, + unsigned int num_handlers, + void *priv) +{ + const struct v4l2_isp_params_buffer *buf; + unsigned int remaining; + unsigned int offset =3D 0; + int ret; + + ret =3D v4l2_isp_params_validate_buffer_size(dev, vb, + v4l2_isp_params_buffer_size(CAMSS_PARAMS_MAX_PAYLOAD)); + if (ret) + return ret; + + buf =3D vb2_plane_vaddr(vb, 0); + + ret =3D v4l2_isp_params_validate_buffer(dev, vb, buf, type_info, num_hand= lers); + if (ret) + return ret; + + dev_dbg(dev, "params: version=3D%u data_size=3D%u\n", buf->version, buf->= data_size); + + remaining =3D buf->data_size; + + while (remaining >=3D sizeof(struct v4l2_isp_params_block_header)) { + const union camss_isp_params_block *block =3D + (const union camss_isp_params_block *)&buf->data[offset]; + u16 type =3D block->header.type; + u32 bsize =3D block->header.size; + + if (type < num_handlers && handlers[type]) + handlers[type](priv, block); + else + dev_dbg(dev, "params: no handler for block type %u\n", type); + + offset +=3D bsize; + remaining -=3D bsize; + } + + dev_dbg(dev, "params: buffer parsed successfully\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(camss_isp_params_apply); + +MODULE_DESCRIPTION("CAMSS ISP parameter buffer parser"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/qcom/camss/camss-params.h b/drivers/med= ia/platform/qcom/camss/camss-params.h new file mode 100644 index 0000000000000000000000000000000000000000..058e3f158b42d39512301d412a9= 83fb2f34dab21 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-params.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * camss-params.h + * + * CAMSS ISP parameter buffer parser. + * + * Wraps the upstream v4l2_isp_params_validate_buffer() validation and adds + * a dispatch layer: after validation each block is forwarded to a + * driver-supplied handler. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef CAMSS_PARAMS_H +#define CAMSS_PARAMS_H + +#include +#include +#include + +#define CAMSS_ISP_PARAMS_FMT_INIT \ + { .fourcc =3D V4L2_META_FMT_QCOM_ISP_PARAMS, .depth =3D 8, .align =3D 0, = .num_planes =3D 1 } + +#define CAMSS_ISP_PARAMS_FL_BLOCK_DIRTY (1U << V4L2_ISP_PARAMS_FL_DRIVER= _FLAGS(0)) + +struct device; +struct vb2_buffer; +struct camss_isp_fmt; + +union camss_isp_params_block { + struct v4l2_isp_params_block_header header; + struct camss_params_wb_gain wb_gain; + struct camss_params_chroma_enhan chroma_enhan; + struct camss_params_color_correct color_correct; +}; + +typedef void (*camss_isp_params_handler_fn)(void *priv, const union camss_= isp_params_block *block); + +/** + * camss_isp_params_apply - validate and dispatch a params buffer + * + * @dev: device for error logging + * @vb: the vb2 buffer (used for size validation) + * @type_info: per-block-type validation info, indexed by block type + * @handlers: per-block-type handlers, indexed by block type + * @num_handlers: number of entries in @type_info and @handlers + * @priv: opaque pointer forwarded to each handler + * + * Calls v4l2_isp_params_validate_buffer_size(), then + * v4l2_isp_params_validate_buffer(), then walks the validated block stream + * dispatching each block to its handler. + * + * Returns 0 on success, negative errno on validation failure. + */ +int camss_isp_params_apply(struct device *dev, + struct vb2_buffer *vb, + const struct v4l2_isp_params_block_type_info *type_info, + const camss_isp_params_handler_fn *handlers, + unsigned int num_handlers, + void *priv); + +#endif /* CAMSS_PARAMS_H */ --=20 2.34.1 From nobody Sun Jul 26 01:48:11 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0B5A03DA7F5 for ; Fri, 10 Jul 2026 09:05:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674303; cv=none; b=lBA9YuLspUEsiIOMcJEpMPGRIE5yE6js2d9tAMARxpYOWFT5cO8hX8fdV7E2grCv1nkFD8JVclI6HxlZDhAzDLMOS0HGbJys8GPLyl0yUJaW5RP761ls02a0ZIWwPYj1IXbw3VM4Yy2AI4TPVqyh/V2eYNlGSGlcYXXXpiOgOlc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674303; c=relaxed/simple; bh=njrkKm3aiN5Zc7CJQtaFlW6IU/Xu6yimySfqW99zRic=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ZYwBO5JYzYPG53RTiJxImYxn5pS5DBdZLt9ej1cUwHahhx+6/zJoGZA0bqPFEC7Zp8emX5hFo3MuvhjN0TW6+TMAs+hhts72d3gQ91ZhZM4o2H2V1BrbLqCzQN90/l55q/ii1ZBOCyq2Y5cttYueLrAZgIBsj5iCruUWCvZqZiw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=Cawtc6ER; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=Lyq80u5K; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="Cawtc6ER"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="Lyq80u5K" Received: from pps.filterd (m0279865.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66A7dW66235369 for ; Fri, 10 Jul 2026 09:05:01 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= hGScOXxHIxlcFY4BY/Y1OfU858ahh8r8M2ytmxZhzuY=; b=Cawtc6ERus/ZEH6Z crkdPCDD7Sf9Inkuuwb+pM/J+Ahd7qgTlt9mv6SPtS+PiIYRjfkJK4DKb85G8Yqj DQ5NUQBdBMaZFzBMRZtKy3BUKYbj2y/OcSD3XlqtshVQJgnC3GqeSHMRChw9lIzN M3rRaQPM0j7K4tO70Jv3HG1JMCJ4BLVw4KeIrlwOb1QsJD46ithUCnEiCduP9ydM kC9pjlJgDQg5J8doFCzJI+HL7SnUsjreGlAjEKj1fhyUI0oL3lcnT3RmjSd+c9JH 5QOViw6yhoxIwjZpeEDsF5e2FzpDO7DZOL9DuaYdCEhYMWrA6Ykd9VWm+kluXYqd /dsbXQ== Received: from mail-qt1-f199.google.com (mail-qt1-f199.google.com [209.85.160.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4faeg3kq4m-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 10 Jul 2026 09:05:00 +0000 (GMT) Received: by mail-qt1-f199.google.com with SMTP id d75a77b69052e-51c1a97644aso6267221cf.2 for ; Fri, 10 Jul 2026 02:05:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1783674300; x=1784279100; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=hGScOXxHIxlcFY4BY/Y1OfU858ahh8r8M2ytmxZhzuY=; b=Lyq80u5KnKFWh6QAVgFsML8rolGPzuivTf0gEqjI3XPf8uBTvE+DskKCs4niKQeSSI jMNmnNzw+osWelcYaGVuWETuJ1EUFTrqVhrZCVrTG9NTlJgvLePxITnu52ESDafk/rW2 YvffJ/ffTeZx0/kOlJjBkqCjyBlhPTIsf6An+hd7rGPSTMUFi3kdnt8712EWt6m17k4m XEEm/zvcMInQ3DAfF5Mub2o+A9NXFngGt2GZIfTwOkCztRatJudOuYxkI0wCnQtyVCqq 8LdmimtoQgFhHWzcwb1XItBCDBpY+hPhXocAGdm7Y2ygmUSJyMHYRxSmLA/8avk3fnzR TbvQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783674300; x=1784279100; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=hGScOXxHIxlcFY4BY/Y1OfU858ahh8r8M2ytmxZhzuY=; b=dtSLJngH+q9a4BZhnkTlJG4vyIXqPsFRINQR+gZu04XjJdPA9ke85U1DdQUY7qO5yB OjE5fc9//LjOENj09GXzyZrg0qVVKPWnksv415cSzFSze34vdNpiL8fKIxrophf+W6Zf qphvoDEvKO2/3zbI0Uj7y5UwTAsF09OfJ72NqV5U33JMfQI6ItJvcAKOuR7hd1ewQNl+ 2gfsOYySQjyeoY/n8Vq/GpsdbRPlcfo+7xVxBEae6IbTnsscusEvMR/Xr1Xb4si+/tKC xllPbNTysjaoatO+HH2fDAi4e4lExIAoXWBBqc+rmgKvW4J0f3rE8jjVAA2wnnQ2VTZ0 4LZg== X-Gm-Message-State: AOJu0Yy5xNiTe9B8b3DptAO2/1F5hmxr+UzRPy2H+B6Wi1sSSlayRx4i 9orkVD7x0U6SgXJUd2SpXdmsEr1vceh+fJKPHFID8Oo4y0lSebOTfQ3xY0o0YZivYHn943n2esu uBrpkaqgBRj5jphLyF6kD6rFLEHD1hxLMgjAL7fIqBtljRBm3H6Zi/hjcgZGowVe6sKE= X-Gm-Gg: AfdE7ck4TaspU0LsTLD9cAItmr1Tm3vglurDgJe+CsKwucULWxxzPBLyvSu8zX3v1w8 22/72iQLq3Pj8QJ6Y+ds0NnY6+52pCb/ApVpYAGEkwvJf6aKGgQ9FmfCePSkYEpihaNGt9wZ98q qFlVVYu5jwIgoFpBruUt0zrNJO1XryOobkn7Q7ANaYF3BhWveegxd/2i5HPrSejmdcdeHs0Mx9z S7IwXwBpJ2DmRRoH4XvJV7RHnzCnHs3DhYNT+HJr1iMPBV3ERrmTqCacdIVaNKSDesxgN0Fv388 hAGJKDOg0ZzfGpbdwPoR2nIvqjt4W2c1uWNJnPtmoVbLba6n2qO/PmFGtyJhQVZM5t2iFjYfybx jzqvSYFoXl3VIgAB1uBMeqzbhbiDf/eCbYrnOIXKRH+J83mQE5BJcDGPfnITCk1xOKfOflkSdUL jKx0ORKvHJ6FUKlmuEq5hAiOX8yed1GXmGMLpeQt4zTuYoxl08I5nKlPcSboX5oQ== X-Received: by 2002:ac8:5ccb:0:b0:51a:8d31:9a06 with SMTP id d75a77b69052e-51c8b403b76mr120422761cf.16.1783674299604; Fri, 10 Jul 2026 02:04:59 -0700 (PDT) X-Received: by 2002:ac8:5ccb:0:b0:51a:8d31:9a06 with SMTP id d75a77b69052e-51c8b403b76mr120422501cf.16.1783674299240; Fri, 10 Jul 2026 02:04:59 -0700 (PDT) Received: from QCOM-eG0v1AUPpu.na.qualcomm.com ([82.64.236.198]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c15dfda815dsm259932266b.36.2026.07.10.02.04.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 02:04:58 -0700 (PDT) From: Loic Poulain Date: Fri, 10 Jul 2026 11:04:49 +0200 Subject: [PATCH v4 3/7] media: qcom: camss: Add V4L2 meta format for CAMSS ISP parameters Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260710-camss-isp-ope-v4-3-51207a0319d8@oss.qualcomm.com> References: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> In-Reply-To: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> To: Bryan O'Donoghue , Vladimir Zapolskiy , Loic Poulain , Mauro Carvalho Chehab , Kees Cook , "Gustavo A. R. Silva" , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio Cc: linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-hardening@vger.kernel.org, devicetree@vger.kernel.org X-Mailer: b4 0.14.2 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfXxTs9DwQNSHoT 3jai1A7+ZhkqXKzWAsUDBs0maetUEAwVmPGOM8Nb9VeG42n4jvE4aKdEIHO5cgMv7ITTlE4W+Kp fAaBeDxst0K7aYqFMKkIlvZRRViBEWQaLiZSbIAmU5PxWBoXHft695gz3tm774vvw1M1fhJpZqf RDwO4n1W/DSmbYseaMQ9A8dFm9BWpBvzs3jlc+PC5qBZwNy7hzEPmgukORDDEr0A2E7k+EcyKwM tVfIT4GRfL1QCuW9NhQTjZunMSTb9gUnNWR+ZZ+DAMYgPz7KlfwdfZIDWtwECjtxY31gPp9Ycah W0ZC1Kq3sv2sZUBZ1AmPEW/P79IMV35fAQNGArAR03n+oJ/NuhrQJs6vQzbSMMR0NKkwd4nZ3c6 6RMAEn0jM1koUsr3FnA1AVGxd8YMrF86fiOwi2KV/wsI7FyzHq1YTV5cAbg3JQQ/nvimIF5zGg6 Y3gLGMwSGeMp6mzJv9A== X-Authority-Analysis: v=2.4 cv=ZcMt8MVA c=1 sm=1 tr=0 ts=6a50b5bc cx=c_pps a=WeENfcodrlLV9YRTxbY/uA==:117 a=MDeckJw97qnk8wCBExTehA==:17 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=Um2Pa8k9VHT-vaBCBUpS:22 a=EUspDBNiAAAA:8 a=NHZ7RztTnFrf7oJu4FoA:9 a=QEXdDO2ut3YA:10 a=kacYvNCVWA4VmyqE58fU:22 X-Proofpoint-Spam-Info: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfX7wSHYseHbQch BOM1lnbcA9typ5sLXNQ1R2LRdfd/I2CCycdOGEXFEIQ32dvx5UrElhfHA1u1gtVj5lMYRHGjV92 7RQXfS5hrecqdy8ViXcSeQ3uOt/9D2w= X-Proofpoint-GUID: anHyUhs5KCf_TCcj4hBFJcEhoRidwK90 X-Proofpoint-ORIG-GUID: anHyUhs5KCf_TCcj4hBFJcEhoRidwK90 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-10_02,2026-07-09_04,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 clxscore=1015 lowpriorityscore=0 malwarescore=0 bulkscore=0 suspectscore=0 priorityscore=1501 impostorscore=0 phishscore=0 adultscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607100088 Add a V4L2 meta format code (V4L2_META_FMT_QCOM_ISP_PARAMS) for the Qualcomm CAMSS ISP parameter buffer. This format is used by the params video node exposed by CAMSS offline ISP drivers (e.g. OPE) to carry per-frame ISP tuning data such as white balance, color correction and chroma enhancement settings. Signed-off-by: Loic Poulain --- drivers/media/v4l2-core/v4l2-ioctl.c | 3 +++ include/uapi/linux/videodev2.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core= /v4l2-ioctl.c index 98512ea4cc5b9d725e1851af2ed38df85bb4fa8c..01593493dff63491a36cfebe404= d26df34c95838 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1471,6 +1471,9 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_META_FMT_C3ISP_STATS: descr =3D "Amlogic C3 ISP Statistics"; b= reak; case V4L2_META_FMT_MALI_C55_PARAMS: descr =3D "ARM Mali-C55 ISP Parameter= s"; break; case V4L2_META_FMT_MALI_C55_STATS: descr =3D "ARM Mali-C55 ISP 3A Statist= ics"; break; + case V4L2_META_FMT_QCOM_ISP_PARAMS: + descr =3D "Qualcomm CAMSS ISP Parameters"; + break; case V4L2_PIX_FMT_NV12_8L128: descr =3D "NV12 (8x128 Linear)"; break; case V4L2_PIX_FMT_NV12M_8L128: descr =3D "NV12M (8x128 Linear)"; break; case V4L2_PIX_FMT_NV12_10BE_8L128: descr =3D "10-bit NV12 (8x128 Linear, = BE)"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index add08188f06890182a5c399a223c1ab0a546cae1..011026194a20cde22a374e6fb25= 70cbc6bced230 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -888,6 +888,9 @@ struct v4l2_pix_format { #define V4L2_META_FMT_MALI_C55_PARAMS v4l2_fourcc('C', '5', '5', 'P') /* A= RM Mali-C55 Parameters */ #define V4L2_META_FMT_MALI_C55_STATS v4l2_fourcc('C', '5', '5', 'S') /* AR= M Mali-C55 3A Statistics */ =20 +/* Vendor specific - used for Qualcomm CAMSS offline ISP */ +#define V4L2_META_FMT_QCOM_ISP_PARAMS v4l2_fourcc('Q', 'C', 'I', 'P') /* Q= ualcomm CAMSS ISP */ + #ifdef __KERNEL__ /* * Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when --=20 2.34.1 From nobody Sun Jul 26 01:48:11 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 523063DD870 for ; Fri, 10 Jul 2026 09:05:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674305; cv=none; b=B5femGZ0Epm44dCkroUFsv2jcZug/v+/SHvN5FSMdsSsAErTZUGaHJiiYdDxShPYxoSeNAVfwGYdb8vwT9wgJ9zpIUiNtboX04W1qkfVpjJ25ZxjT175pmVQOIoDl1fUAcQrtxrk7g33s1C/6VKyWQhcCar3gvJcdZ+4a7pL+x4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674305; c=relaxed/simple; bh=LkFnGFAWssGPIbolVdpYF874p9uyyDT0NSK2OsrJn1E=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=dIDhNjSbT0Yoq5R3kp7zu0hHMy508RpJP3rDhJVM9BLJdeDw8FPS5tKHHENKeNVsrb5ozrgba5DoWh2a2sfJynYNUU4XYesl5HsBlJ8wvq+aMdtwYb1k0LzkcsfklIJsw6rQK3aVOOLXWS+6thguilLm5tAme7Aatk7CHB8iPUs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=mwSt09Iz; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=HAym3anZ; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="mwSt09Iz"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="HAym3anZ" Received: from pps.filterd (m0279866.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66A7djM6041101 for ; Fri, 10 Jul 2026 09:05:02 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= kJoazphX4WE+SBI8eVQ4A5Wnlq+yoJNtNK+pdddGKWQ=; b=mwSt09IzPnNNl5TW WhAA/t5I8Bq7dJsk83S6TeAHUlzRb4hg72tsGMPvuhfvByTdPQAaMkAqpLfPqduZ VlG3P4PDwj9oKIZUBSbekhOo7Uk+Zoi7Cj4l3mbg78n0P2XF/u2foj6+bwGBczMK 3jsyrxjiN1Xoo0Azyt3C8upGzOtJvpInXAstG4FkGMzXb8qm1ylLVPDLEALPH5A4 4ZSngC1VzHNmIi/+zKkqhcyDuEQDSdJWnCkJT/JYzFpvmmzD/LvU/3uEdqNhgCO7 FjN9+UU0OYW0CR0SvR/ts30sx+Hw/uKffYlSvbKZ3dw59rTdugmdtZx07u1J/Q1D FMrgpg== Received: from mail-qt1-f199.google.com (mail-qt1-f199.google.com [209.85.160.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4faeg5kpur-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 10 Jul 2026 09:05:02 +0000 (GMT) Received: by mail-qt1-f199.google.com with SMTP id d75a77b69052e-51c0199faafso6368601cf.3 for ; Fri, 10 Jul 2026 02:05:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1783674301; x=1784279101; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=kJoazphX4WE+SBI8eVQ4A5Wnlq+yoJNtNK+pdddGKWQ=; b=HAym3anZIzCt/7LCqz0juTlrafRarEow98naC2vrtJFHg7c9PJcsGZeX0uKyaNrUy1 ZFwDGGdh3Pk3P2bjEk0k2ztCkfT+w9HeaMOR0KS/aCV/hrq++X9Z6qrgLnA2njmG85yb 8IsRaFa0EBBPyJLGbrLNce8hMZ3FD2tJDlP50hrCHFaUuBoTCjIPkCWxzYC9GQ7hjW+R tDM8lb6BFyHwEL1kJBG7tH5NmaE8gYPsCy/tlCoUJzwye8sFAPtb4J9KYMPLDzrcnumb 8b7FJgEBOBLy+w6DU5N9e/Cnhi0o1sBgcWzUohYVJSPRq6xrCxHnygMQyHOfReq5yoVR rA5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783674301; x=1784279101; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=kJoazphX4WE+SBI8eVQ4A5Wnlq+yoJNtNK+pdddGKWQ=; b=m1ufzHjs2UlEIVP4ZHanUs051sv6xWbZs4w2t56Pexn8efU3Rz4qYMi4OgeChWsvz+ ulMNMNKaQhvVrJqPSKY2vnYwgQVi8AtFkVQpIy5MQP4dKd5U0OfJNtsAmDAqKwsiwesY Iag8Wp104YT4NGJ1FwK2QL11185xAIIVyQEHgl5mukpbDFHj5k7aguz8atYyk5Tk4OhM Ht48LB07u0/q3WqTwfD0LK0V9v0WYTJGUt5RoQScHJky90DrFf/IFubVXjO55bqQ8ZkP SsT/VB+Y+CkkXwDYkF7wOs7aOypWeJNWVLIraQEJ3MZY7ObLh/4ZkmBxxTDXSqwnLdqi QxfA== X-Gm-Message-State: AOJu0YyT0oHLvJGUJ/NyFXEsMnC3MOt5PzwMo5SydsR+q1my4fciTF1C cgGW0LuUaqL9yYLDovS7rITm/e8cCThYbiL08lvE+Zzls2nk73zhVV8564UyajvuYhiZyWH6Tjk 98G+s2GLYLZA87Tmwe5xrARzOj0DIu3SS1a2RRdCe8UbmwKoqX1S7PUXPvmHPMPeQuj8= X-Gm-Gg: AfdE7cmt2DxR/l263WS3OCxsvxuCidcTJZ/EmaLEj5N76t5uM9tCWfEpBWkwuTXFQLH XdPj8TF2Es5cLx9FUWzXMIEVSoZ8PsdTWCSUAZpG5vuu063YVFXN1tOULYjBzQuy7kjnZVNHV4u Qx09oflOJgrH8wRo7HkdWkubKyxWWh8E8anDvV6YQZrB20yfnvoHE054wh1ZYF103Xdsek374kW qPdc9X45CIEZtRJX4tA5sM2ZwI2E+tN6OyNafpJ9UhI0MLzmCv9gfj9tqVqywwbV31Ap5iPwCje tloChYx0CTJQTZxApdseKPAgy6/a21bqLuet+zxOYDdgHjxfST119R89uc2Ar4I/TycwTXm+rFu iRLBVEJyQQKM3se3G1DCuKs43qx36ZbSNOaWK4Ad9F+q9IhjIwKeiUKXLOYxsIIaVyVIuhKBFag amgv/36YXRh37TK4ccQksZ+m7DD8ngliwUgreI/KxkHnfvtAn3Rirf58FWzYNxMQ== X-Received: by 2002:ac8:5f12:0:b0:51c:83ee:e429 with SMTP id d75a77b69052e-51c8b422c6fmr109839611cf.78.1783674301393; Fri, 10 Jul 2026 02:05:01 -0700 (PDT) X-Received: by 2002:ac8:5f12:0:b0:51c:83ee:e429 with SMTP id d75a77b69052e-51c8b422c6fmr109839171cf.78.1783674300888; Fri, 10 Jul 2026 02:05:00 -0700 (PDT) Received: from QCOM-eG0v1AUPpu.na.qualcomm.com ([82.64.236.198]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c15dfda815dsm259932266b.36.2026.07.10.02.04.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 02:05:00 -0700 (PDT) From: Loic Poulain Date: Fri, 10 Jul 2026 11:04:50 +0200 Subject: [PATCH v4 4/7] dt-bindings: media: qcom: Add CAMSS Offline Processing Engine (OPE) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260710-camss-isp-ope-v4-4-51207a0319d8@oss.qualcomm.com> References: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> In-Reply-To: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> To: Bryan O'Donoghue , Vladimir Zapolskiy , Loic Poulain , Mauro Carvalho Chehab , Kees Cook , "Gustavo A. R. Silva" , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio Cc: linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-hardening@vger.kernel.org, devicetree@vger.kernel.org, Krzysztof Kozlowski X-Mailer: b4 0.14.2 X-Authority-Analysis: v=2.4 cv=FN8rAeos c=1 sm=1 tr=0 ts=6a50b5be cx=c_pps a=WeENfcodrlLV9YRTxbY/uA==:117 a=MDeckJw97qnk8wCBExTehA==:17 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=YMgV9FUhrdKAYTUUvYB2:22 a=gEfo2CItAAAA:8 a=EUspDBNiAAAA:8 a=l64rWm3OqFMOLA4dSg0A:9 a=QEXdDO2ut3YA:10 a=kacYvNCVWA4VmyqE58fU:22 a=sptkURWiP4Gy88Gu7hUp:22 X-Proofpoint-Spam-Info: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfX8DfrxJVRo6CI eoK4Ud8cyLQlO6E+VATGPtVjuf3JQghHi5++inBsiIGtIX+WWsN0NoYBk7QsoZKxu/F5cghp94H NP+5lfnO77jarhopi8FiQYIBsFWW12g= X-Proofpoint-ORIG-GUID: XeYy4YrRYFQtc2KY13GpIbFpQ7dzPCk8 X-Proofpoint-GUID: XeYy4YrRYFQtc2KY13GpIbFpQ7dzPCk8 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfX9vcmuizs7lc/ HUDhonm3jdZqXug+NPeRXzG4eHxkLFa3jCNA2QzfY1tOgbCqzrSsE6Rpsu3+S/DwfF6sNe0sxUe FroPWfDk7sbJgKelMm9SjeuvXcgmpHWs9M09kzyrV/EBlhDDx3L6LP5ne+I5Z2YH0n9a2ELF19C hEyptkjcGxoM6RPLMLgSClYQzu1TlF22xB/sHKQmS4DSJwBd1Rhp3RZhTSlSnrTbGRQWms8m7Ls P1DIr74e1qAoAaE8JIynd145bqTXaLgiTJDKB2nLqtCaZKu9EDlkMPt9f8Hnnyu8AO1ODv3+r8j ee6QBiKuKk59UA/BGjjOZIOalGx3QPNphhddoEj6YAo1m28+4DokRaPFMkhIA5sfzkxXyCchqwX shDVUMLAynGtNSWRJG3xQU8rS5m4tXMW7b7lD94Xa+BiVqVKJcrO8R/DzyVKTrCUip2/sxn+9fi OXN0hdzLoMjrFLLiBqA== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-10_02,2026-07-09_04,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 suspectscore=0 phishscore=0 clxscore=1015 impostorscore=0 spamscore=0 priorityscore=1501 lowpriorityscore=0 adultscore=0 malwarescore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607100088 Add Devicetree binding documentation for the Qualcomm Camera Subsystem Offline Processing Engine (OPE) found on platforms such as Agatti. The OPE is a memory-to-memory image processing block which operates on frames read from and written back to system memory. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Loic Poulain --- .../bindings/media/qcom,qcm2290-camss-ope.yaml | 147 +++++++++++++++++= ++++ 1 file changed, 147 insertions(+) diff --git a/Documentation/devicetree/bindings/media/qcom,qcm2290-camss-ope= .yaml b/Documentation/devicetree/bindings/media/qcom,qcm2290-camss-ope.yaml new file mode 100644 index 0000000000000000000000000000000000000000..58bf79ca447f7173933f6c5641a= dd1f8035abc4a --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,qcm2290-camss-ope.yaml @@ -0,0 +1,147 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/qcom,qcm2290-camss-ope.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Camera Subsystem Offline Processing Engine + +maintainers: + - Loic Poulain + +description: + The Qualcomm Camera Subsystem (CAMSS) Offline Processing Engine (OPE) + is a memory-to-memory image processing block. It supports a range of + pixel-processing operations such as scaling, cropping, gain adjustments, + white-balancing, and various format conversions. The OPE does not + interface directly with image sensors, instead, it processes frames + sourced from and written back to system memory. + +properties: + compatible: + const: qcom,qcm2290-camss-ope + + reg: + maxItems: 5 + + reg-names: + items: + - const: top + - const: qos + - const: pipeline + - const: bus_read + - const: bus_write + + clocks: + maxItems: 5 + + clock-names: + items: + - const: axi + - const: core + - const: iface + - const: nrt + - const: top + + interrupts: + maxItems: 1 + + interconnects: + maxItems: 2 + + interconnect-names: + items: + - const: config + - const: data + + iommus: + items: + - description: OPE read (fetch engine) port. + - description: OPE write (write engine) port. + + operating-points-v2: true + + opp-table: + type: object + + power-domains: + items: + - description: GDSC CAMSS Block, Global Distributed Switch Controlle= r. + - description: CX power domain. + + power-domain-names: + items: + - const: camss + - const: cx + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - interconnects + - interconnect-names + - iommus + - power-domains + - power-domain-names + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + #include + + camss_ope: isp@5c42400 { + compatible =3D "qcom,qcm2290-camss-ope"; + + reg =3D <0x5c42400 0x200>, + <0x5c42600 0x200>, + <0x5c42800 0x4400>, + <0x5c46c00 0x190>, + <0x5c46d90 0xa00>; + reg-names =3D "top", "qos", "pipeline", + "bus_read", "bus_write"; + + clocks =3D <&gcc GCC_CAMSS_AXI_CLK>, + <&gcc GCC_CAMSS_OPE_CLK>, + <&gcc GCC_CAMSS_OPE_AHB_CLK>, + <&gcc GCC_CAMSS_NRT_AXI_CLK>, + <&gcc GCC_CAMSS_TOP_AHB_CLK>; + clock-names =3D "axi", "core", "iface", "nrt", "top"; + + interrupts =3D ; + + interconnects =3D <&bimc MASTER_APPSS_PROC RPM_ACTIVE_TAG + &config_noc SLAVE_CAMERA_CFG RPM_ACTIVE_TAG>, + <&mmnrt_virt MASTER_CAMNOC_SF RPM_ALWAYS_TAG + &bimc SLAVE_EBI1 RPM_ALWAYS_TAG>; + interconnect-names =3D "config", "data"; + + iommus =3D <&apps_smmu 0x820 0x0>, + <&apps_smmu 0x840 0x0>; + + operating-points-v2 =3D <&ope_opp_table>; + power-domains =3D <&gcc GCC_CAMSS_TOP_GDSC>, + <&rpmpd QCM2290_VDDCX>; + power-domain-names =3D "camss", "cx"; + + ope_opp_table: opp-table { + compatible =3D "operating-points-v2"; + + opp-200000000 { + opp-hz =3D /bits/ 64 <200000000>; + required-opps =3D <&rpmpd_opp_svs>; + }; + + opp-465000000 { + opp-hz =3D /bits/ 64 <465000000>; + required-opps =3D <&rpmpd_opp_nom>; + }; + }; + }; --=20 2.34.1 From nobody Sun Jul 26 01:48:11 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 105083DEAD1 for ; Fri, 10 Jul 2026 09:05:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674307; cv=none; b=hpCLZKy/jHkzXPNz8kL1cvGzKymXEm8wgBmAYeatzD6QerbOt0LPiwA0GUKSDRf+WtoxF+uhzCt2+lVJxfUDBMHSC1ZN7pCiJZL4AFlPe05lFnYd/xxE9bi3d8Mf/EjcVc6gAXkldQ4+281F707eZCLJTdXlspVHo6w1MXnspY8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674307; c=relaxed/simple; bh=VmSVbvVuTt4AN5C8oiuT9E98mX8DRT9XHfPGxKvhc/w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=K3DcLYG9WWiENSaDnHcN9fE5DosA2jK6ghFwIi8RmvzXC+grCfQO3HgCi+X+Mat9jOrhecORKgoTQ8YAa3JCfbTf7LS6/526m68hF/AC9MCYGrT1M1VL6C/yAXnbyiAlXUoW8dmsqN+KdmWknefxaAWWIiM/c77d7UgQunm7kg0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=MMYkQD6+; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=EAvhJrjj; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="MMYkQD6+"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="EAvhJrjj" 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 66A7dSVM4156464 for ; Fri, 10 Jul 2026 09:05:04 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= rzlf2ZtK9nuTY0MJJWVDEnSEIyJ35HZ7vs1BndCygtQ=; b=MMYkQD6+kI8HwVus v+lIuYYzSiya2DyC+iLQXcxhHHHM3KItB/1J6Ce391tk0SPnlpDVwFRr2+Br/rTB h6q6MX6ocPwBnqc/JCOZpsnjvmOUgM8awpXqpOeuEuPtbf/y/4ipDRMtFvgW+Bhz G8tRID+oL22dAYzIRw/XKe8bmpGfs/C4YHx6z4YOrmUivZIMj24fo8ELPv89hxHf Biwmd5xzLMwW4t9mopGddztqe2KBwIJ0RoBYvWvCHl2uER30pNlq/UbGAyO+GDFZ 2jPOrX2DPYBao1DdTvQKRnzu80FgbnIaVuAjpgTnrYONTCDqojJr6x9tL6cyMx0q tDdToQ== 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 4fahv8jhgy-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 10 Jul 2026 09:05:04 +0000 (GMT) Received: by mail-qk1-f197.google.com with SMTP id af79cd13be357-92e606e8f3eso55359485a.3 for ; Fri, 10 Jul 2026 02:05:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1783674303; x=1784279103; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=rzlf2ZtK9nuTY0MJJWVDEnSEIyJ35HZ7vs1BndCygtQ=; b=EAvhJrjjsXHBC6EPTI5i6zBVqEJBBhpwryjf15H/5p0OlrR43fRa0nWR8EUd/lFcyb CTM8BjG09LUxLwIAtNC+2zGKmTZGEDx/HiZLP/4hk6+HshHJZxpgxXq6hS7X5vANN4D2 ApfKcOKMWK8IstvLuo2oeXZLTiC6rRDGSV3gmFjDk9fsJ2l5dcw0+AMH03HRMKKi0pa+ CiKDdAfPPw8piw7eF/62RvSv3KiGA5O+c4QcjbKWQdq+VZslJOlXSyTeQn0yFkb71oqt 2xtem30VpxOriNXNsXNXOclJoeL1bch2PqaH25+fhKELDKgj3G/I9Ey0olRfA7UMFWQq waew== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783674303; x=1784279103; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=rzlf2ZtK9nuTY0MJJWVDEnSEIyJ35HZ7vs1BndCygtQ=; b=B35DVTTTSNwRctDdMw953BZuf1nWO9IFOH/bis6lN7+yrp4UJJImZhVsyKwPZwxqaP mtsRsnHkOtA8bJXDeVZfbpY9e1BkxKdfVkR58lgeAEMKD3dVxjlMmkE2ys+rPmZsCByh glV2YfY+fMzzPgmMFcoGVrbCgtqCozvFZBINRtH9XXxMNpGRTm8Nz07uzZ0w733I0KEq /u1pjpLmzRz7Jwqh5J/PMZXXasUbrSmArTxauzfW1u6deY0chveyx2eic5ezkwEq8PpO Z/SpIM/nIDNbjhRFCDef5Wi7nkXdWU6tWxpmZFAXcXQVdu1Chml4eEekx15W3Sa7pyNd xeHg== X-Gm-Message-State: AOJu0YzsUWdAOEaM/7NE+Dj8O7sH1A+36ozSyii8CfIanGUNDyMZqJ6k motwhsoPA+6r3jLDJSFzWJN01W13E0teRGyF+MmwL46+gmhIk1kMsWCtFvm7dlEWuAOM8Xrl399 pFigXuDvpJYGLk5YjgEE8cNSudsd8IMwHQDC9c0I7ljnAe6NTlkGAK+a+GaSr9mpNT80= X-Gm-Gg: AfdE7ckoKf9YK22chNGIHsmsCfGF0cDO1/xnHxhFk2eV8bcniugysPOyEFzxeqE822W 5wr1X2whj6sBgg057IWp4v5AaTLDOaFyplvpkFmvl5BBl69l9J+KbQFfSYsBbFO65O546Vo47KQ 5EM9JVQ/Cr6gGppALvIgPWXe1Y3q8Ntg//I/A2rFhcyfM4p8UeS5ehOikQu+4ZaYc27ULGk79Vn 12+UY0cB3NvSedtKkTnit8C+nGLoq8wnwxBnJasOWW4Joal+3LykdqspPYuWAHDQMCcSj0h+uON u/QjhNG1AoPHGfF0O/S+yWxGCFIKloVmLRrTSDnk8e9Aj9o8R6l9TAbUwMqykE4E90b7mUIfyHN WC19T6cX0lM8YXjmEdsIJoLO0wnMGAfSCHroF27rReMVNkGMXrC1tfGpcOPsmIw+6xsRhnzX8xm 4mxGwR4u6KBjGVc9DbMCQlYB+G0TFO5/q/d0qjBm1GSolGM58pdX6FUkuZBqlCgw== X-Received: by 2002:a05:622a:1819:b0:51c:1a4c:3c91 with SMTP id d75a77b69052e-51c8b2adf76mr112432841cf.1.1783674303205; Fri, 10 Jul 2026 02:05:03 -0700 (PDT) X-Received: by 2002:a05:622a:1819:b0:51c:1a4c:3c91 with SMTP id d75a77b69052e-51c8b2adf76mr112432391cf.1.1783674302574; Fri, 10 Jul 2026 02:05:02 -0700 (PDT) Received: from QCOM-eG0v1AUPpu.na.qualcomm.com ([82.64.236.198]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c15dfda815dsm259932266b.36.2026.07.10.02.05.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 02:05:01 -0700 (PDT) From: Loic Poulain Date: Fri, 10 Jul 2026 11:04:51 +0200 Subject: [PATCH v4 5/7] media: uapi: Add CAMSS ISP configuration definition Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260710-camss-isp-ope-v4-5-51207a0319d8@oss.qualcomm.com> References: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> In-Reply-To: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> To: Bryan O'Donoghue , Vladimir Zapolskiy , Loic Poulain , Mauro Carvalho Chehab , Kees Cook , "Gustavo A. R. Silva" , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio Cc: linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-hardening@vger.kernel.org, devicetree@vger.kernel.org X-Mailer: b4 0.14.2 X-Proofpoint-GUID: Qp__4MwroPikVeAs4DKn7Id2751jNtk6 X-Proofpoint-ORIG-GUID: Qp__4MwroPikVeAs4DKn7Id2751jNtk6 X-Proofpoint-Spam-Info: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfX/Z+Ob0mPYLnN t70TQ0YChoVenCwOOJ7YxiflZ9W6TlzIWg51c13oitrUL5gERUL0sIUb9sQCd5HZRUKtw7MIQAs DU3qxgDzKTMbWt3+VjFR0EjnaxCKRTQ= X-Authority-Analysis: v=2.4 cv=funsol4f c=1 sm=1 tr=0 ts=6a50b5c0 cx=c_pps a=50t2pK5VMbmlHzFWWp8p/g==:117 a=MDeckJw97qnk8wCBExTehA==:17 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=DJpcGTmdVt4CTyJn9g5Z:22 a=EUspDBNiAAAA:8 a=kFo2aqxNIXtk6PDOeyQA:9 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 a=IoWCM6iH3mJn3m4BftBB:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfX9qrtULE3e1Vr Su7n+dJ4d5ypGWCpKQQxjNz+y+YjZsctUvDdE23TMHFNWQ+tXY3suE6IZmSUc7xYiRRPQxEWYnc uVywpcBP8VFacJ4HWVaNEsWSt09e4wPSu5JZ3AmyE0o+vQWOpnGWkTiJJcfwCYbOaUAIhN68Iki uXczNn3Nwwp7YHm0OzhULdYdw8YHdquNg+HpcGsuBmWb3b3vHNM+jKAspRDxaBAeUjG/6ahzaIc xOXge+FbMoZZl1o/fhXljFuSfraHOsjO4RSbo5y4oZjL7xYeM2X+A1nK7UoXrCBlm7YeXfTW6Yr PRrpCDHT6iPztTHzrk00CliU+Tk0ST6Bmo6UUb7uN3U4PC3dqkBbJJtR6l15/ueocDYowGyBOkp V4H8trJkT08RYcguDtkPdazqPNxwDpSTCRrwFyOqybngRrfoNxKuPShY/dnLkdu5q6OT/mSQ4gs bBtrlWub8DrIL/aV+XQ== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-10_02,2026-07-09_04,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 phishscore=0 clxscore=1015 lowpriorityscore=0 adultscore=0 bulkscore=0 suspectscore=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-2606150000 definitions=main-2607100088 Add the uapi header camss-config.h defining the ISP parameter structures used by the CAMSS Offline Processing Engine (OPE) driver. This includes structures for white balance, chroma enhancement and color correction configuration. Signed-off-by: Loic Poulain --- include/uapi/linux/camss-config.h | 161 ++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 161 insertions(+) diff --git a/include/uapi/linux/camss-config.h b/include/uapi/linux/camss-c= onfig.h new file mode 100644 index 0000000000000000000000000000000000000000..d0d087eea08337fea14211d3561= 95cacea2fb050 --- /dev/null +++ b/include/uapi/linux/camss-config.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * Qualcomm CAMSS ISP parameters UAPI + * + * Uses the generic V4L2 extensible ISP parameters buffer format defined in + * . + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _UAPI_LINUX_CAMSS_CONFIG_H +#define _UAPI_LINUX_CAMSS_CONFIG_H + +#include +#include + +/** + * enum camss_params_block_type - CAMSS ISP parameter block identifiers + * + * Each value identifies one ISP processing block. The value is placed in + * the @type field of &struct v4l2_isp_params_block_header. + */ +enum camss_params_block_type { + CAMSS_PARAMS_WB_GAIN =3D 1, + CAMSS_PARAMS_CHROMA_ENHAN =3D 2, + CAMSS_PARAMS_COLOR_CORRECT =3D 3, + CAMSS_PARAMS_MAX, +}; + +/** + * struct camss_params_wb_gain - White Balance gains + * + * Implements the CLC_WB pipeline module. The pipeline applies three + * sequential operations per channel: + * 1. Subtract sub-offset (black-level substraction) + * 2. Multiply by gain (colour balance) + * 3. Add add-offset (output pedestal) + * + * Gains are 15uQ10 (15-bit unsigned, 10 fractional bits). Offsets + * are 16-bit unsigned, normalised to full input scale (65535 =3D 1.0) + * + * @header: block header; @header.type =3D CAMSS_PARAMS_WB_GAIN + * @g_gain: green channel gain (15uQ10, 1024 =3D 1.0) + * @b_gain: blue channel gain (15uQ10, 1024 =3D 1.0) + * @r_gain: red channel gain (15uQ10, 1024 =3D 1.0) + * @g_sub: green sub-offset, subtracted before gain (16u) + * @b_sub: blue sub-offset, subtracted before gain (16u) + * @r_sub: red sub-offset, subtracted before gain (16u) + * @g_add: green add-offset, added after gain (16u) + * @b_add: blue add-offset, added after gain (16u) + * @r_add: red add-offset, added after gain (16u) + */ +struct camss_params_wb_gain { + struct v4l2_isp_params_block_header header; + __u16 g_gain; + __u16 b_gain; + __u16 r_gain; + __u16 g_sub; + __u16 b_sub; + __u16 r_sub; + __u16 g_add; + __u16 b_add; + __u16 r_add; + __u16 _pad[3]; +} __attribute__((aligned(8))); + +/** + * struct camss_params_chroma_enhan - RGB to YUV colour transfer matrix + * + * Implements the CLC_CHROMA_ENHAN pipeline module. All coefficients are + * signed 12-bit fixed-point Q3.8 (range roughly -8.0 to +7.996). + * + * RGB2Y - Luma (Y) ceofficients + * Y =3D v0 * R + v1 * G + v2 * B + * + * @luma_v0: R-to-Y coefficient (12sQ8) + * @luma_v1: G-to-Y coefficient (12sQ8) + * @luma_v2: B-to-Y coefficient (12sQ8) + * @luma_k: Y output offset (9s, 0 =3D no offset) + * + * RGB2Cb - Chroma (Cb) coefficients + * Cb =3D a x ((B - G) + b(R - G)) + KCb + * with: + * a =3D ap, when (B-G) + b(R-G) > 0; a =3D am, when (B-G) + b(R-G) =E2= =89=A4 0; + * b =3D bp when (R-G) > 0; b =3D bm when (R-G) =E2=89=A4 0 + * + * @coeff_ap: Cb positive coefficient (12sQ8) + * @coeff_am: Cb negative coefficient (12sQ8) + * @coeff_bp: Cb positive coefficient (12sQ8) + * @coeff_bm: Cb negative coefficient (12sQ8) + * @kcb: Cb output offset (11s) + * + * RGB2Cr - Chroma (Cr) coefficients: + * Cr =3D c x ((R - G) + d(B - G)) + KCr + * with: + * c =3D cp, when (R-G) + d(B-G) > 0; c =3D cm, when (R-G) + d(B-G) =E2= =89=A4 0 + * d =3D dp when (B-G) > 0; d =3D dm when (B-G) =E2=89=A4 0 + * + * @coeff_cp: Cr positive coefficient (12sQ8) + * @coeff_cm: Cr negative coefficient (12sQ8) + * @coeff_dp: Cr positive coefficient (12sQ8) + * @coeff_dm: Cr negative coefficient (12sQ8) + * @kcr: Cr output offset (11s) + * + * @header: generic block header; @header.type =3D CAMSS_PARAMS_CHROMA_ENH= AN + */ +struct camss_params_chroma_enhan { + struct v4l2_isp_params_block_header header; + __u16 luma_v0; + __u16 luma_v1; + __u16 luma_v2; + __u16 luma_k; + __u16 coeff_ap; + __u16 coeff_am; + __u16 coeff_bp; + __u16 coeff_bm; + __u16 coeff_cp; + __u16 coeff_cm; + __u16 coeff_dp; + __u16 coeff_dm; + __u16 kcb; + __u16 kcr; + __u16 _pad[2]; +} __attribute__((aligned(8))); + +/** + * struct camss_params_color_correct - colour correction matrix + * + * Implements the CLC_CC pipeline module. The matrix computes: + * Out_ch0 (G) =3D a0*G + b0*B + c0*R + k0 + * Out_ch1 (B) =3D a1*G + b1*B + c1*R + k1 + * Out_ch2 (R) =3D a2*G + b2*B + c2*R + k2 + * + * @header: block header; @header.type =3D CAMSS_PARAMS_COLOR_CORRECT + * @a: G-input coefficients per output channel (12s; + * a[0]=3DOut_G, a[1]=3DOut_B, a[2]=3DOut_R) + * @b: B-input coefficients (12s) + * @c: R-input coefficients (12s) + * @k: per-output-channel offsets (typically 9s effective) + * @qfactor: Q-format selector (2u): + * 0 =3D 12sQ7 (range ~-256.0 .. +255.992) + * 1 =3D 12sQ8 (range ~-128.0 .. +127.996) + * 2 =3D 12sQ9 (range ~-64.0 .. +63.998) + * 3 =3D 12sQ10 (range ~-32.0 .. +31.999) + */ +struct camss_params_color_correct { + struct v4l2_isp_params_block_header header; + __u16 a[3]; + __u16 b[3]; + __u16 c[3]; + __u16 k[3]; + __u16 qfactor; + __u16 _pad[3]; +} __attribute__((aligned(8))); + +#define CAMSS_PARAMS_MAX_PAYLOAD \ + (sizeof(struct camss_params_wb_gain) +\ + sizeof(struct camss_params_chroma_enhan) +\ + sizeof(struct camss_params_color_correct)) + +#endif /* _UAPI_LINUX_CAMSS_CONFIG_H */ --=20 2.34.1 From nobody Sun Jul 26 01:48:11 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4E6953DB992 for ; Fri, 10 Jul 2026 09:05:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674315; cv=none; b=C+ktNySnzQkPvXdK8uBZy33rRgQ0sKY8TtNUpkmXEmorKitH9g9fWgX4n/GP96RQ1j///wYtj0Icc2NKzjd14p/dI684qUatjVHAD+eiAcscNTCrjCTd6Rzb24zStGiZ9lxjqbCn3RsRseCyqz61lkZAWrzj0YZLh7BO/N1eK/w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674315; c=relaxed/simple; bh=pO2zT8MbT+NG32ut3W3DgTmy9r7KxDGd3qXfA/ML2F8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=d15ecpSCODJx9/T39KbSWK4hAhISXwP1w7Xxs1ueNXBxm7JjT2x4tj1LbvqVQ22kQYDIDK4RqZVRn78Wfy/aCG1IplK6yCU7jCElD0l8kKg8MZ3ropCXH1mf9HdnigsaNNCkEugihk+cXvk9M0QzpzScZtQ3Bib1h1u3WjgmMik= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=EAKRXGYb; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=MAyVAye5; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="EAKRXGYb"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="MAyVAye5" 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 66A7dqgk183375 for ; Fri, 10 Jul 2026 09:05:08 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= RcqmqIf8Y5TFYorESfDdEZh9cSZQ/cx3bJW4x7z39Y8=; b=EAKRXGYbiJLCYOAr XJkFpMkHe8qAAoreD8LFRzU6vdidEO3ttQM2FEcfFupPm4dUC5GYTbdN4I0TYz2G My4bWvg+4CnIn0vobh4YLAihLwJ9CsqVbE0LSSCxPfxkeZTnTPTmUFnxcTvxsU8v PXJri6W4q5q9oa+qEkb5z0ORiOCNBBddOT19ka0rPw3ztEeDi12kmuV90HtlaA1k D3rlMmomqLDlDRxhM01NPBsjA53+D+zeNtzeE+OV3e6Ronsb6tctCMnKZpNTNQjy wFggtGBdaDnJrkJ+ZHFdr2KmD1pyBm5ZdFFDsDYUAYTl2wULOt2nMd6n6z5iPRLf RE2cEA== Received: from mail-qv1-f70.google.com (mail-qv1-f70.google.com [209.85.219.70]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fajte2acg-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 10 Jul 2026 09:05:08 +0000 (GMT) Received: by mail-qv1-f70.google.com with SMTP id 6a1803df08f44-8eeba1d9e47so8791966d6.2 for ; Fri, 10 Jul 2026 02:05:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1783674307; x=1784279107; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=RcqmqIf8Y5TFYorESfDdEZh9cSZQ/cx3bJW4x7z39Y8=; b=MAyVAye5q0DnbVt4fOE4CoSVjWtRgPgt0reuQuOqmxw1FAu0nC9lPyw5qFCKXxhgFx 6GQX3zAAywThtsHxUOpdRSBtDKj2ZNfyQtArmXAswgehoL+T5ji5CfTQAmRcUjYYDtWU T+puRLPaCg+y8e1FuXRONmdC+XRRznDqIuHWLGMNx926AluQtvmDoasgruZzPFR41nbz RJXCR7w0ysq5/YIzerzUk/rdm8x7t2RRZyls06weOQ+2pWa+lEF6WGa0dF+0Nnxu+Z6r QTPRQK8JiEBtmSa3fWdEQz1K7IlMcidmw7VmH0j7Gjo/c+V6NwebV+ZJKtLvpbYX/Iq0 R3fg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783674307; x=1784279107; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=RcqmqIf8Y5TFYorESfDdEZh9cSZQ/cx3bJW4x7z39Y8=; b=WZznnzfZujMVEApIR25S9IOeRUCjf7CuBjH2PjMMi2V4MKRztKFr8JAmyrnVpN1uI7 2HPV+6fUtwh+DvfOiS9tZBiZxC0cdSsSBzFAxzYOBzLtrAQwwbj0uaMjFmQsuIzwE3Sd R0S2GmshFCQLxDEqV5bvE16VT/bYgAJdsB1EkpgMacuAC20xVnrIY6+5jKifYOsSwbv5 AzPkvZwr7y95B0mY1biD2qsBfZ8t1UM7EVOw7h9DFSNakOerijWWbYi3gEZ3DmqtoDJ/ lMB8ANkqWCjctLHenPoXJJuWn5gWcpHpaXqV4CBrOY3spLaT4eZpkDaRMyJtCvXVq5ul uf9w== X-Gm-Message-State: AOJu0YzNve33Ha6kk3pAwrBkJPrpMUanC17ivSVSJjFm7Dh+y/90LSFM 9gTitsV5RjcDUn9sryG5gjIoVxwqROQGuocGR8EMXJJnJpdFkohMVZR68Dl0XEcTf2CtbnuE8Tl df8gAWizJ72MnEYE2Yc4toN8rUSnEf8jIufujnjCFZmhcWyAOOEdT4Sj0wKajAUrL0Xc= X-Gm-Gg: AfdE7cloiAkIMUm106Ey409kgmsH+HdXXTPCsJOjgZwuZF88tLEqfZIXnl8q1LeAbyi Fo18/HkzlMbt3n4CxucqgbidV/u3K0t+I9L1lDa3yDFDJPtGjqR4V/nI7Q0Jrv0MvFAMsdSh2Ki F8r798q0SKaJKuAzU3RFDORKvL+RtZjWTYlfcDYoaT/p+04Cw+GtGn6zYS6hAO0J4UFfKLv7ux2 MRqATCOvJxOwtXds5T7UTq1WrHp79p48MhOlS5wX/un4TG0rZVu387GL9EjGem+6EUHBfWPuVrB aaQwjA2BIAgcO3jeC9q44fqpmnD9nHNXWtfeSbT21GVxdTwIOkzCDRYqOu38mZS/DC/1iX85lfr Sq9VR3vehDLKqnGJdg16/h9F4WNabh/guNFsswDVt1E1TE+Mx0431FtVYzz4GSBwXToC7fAw6tp a4ajEOpcudQ3EA/mhtt5FE3dhDX7dgL2IAd0eav9CH0h8KhoNy10yZgF+Abm2Hmw== X-Received: by 2002:a05:622a:411b:b0:51c:c6c:9d65 with SMTP id d75a77b69052e-51c8b2aabb4mr121599951cf.1.1783674305860; Fri, 10 Jul 2026 02:05:05 -0700 (PDT) X-Received: by 2002:a05:622a:411b:b0:51c:c6c:9d65 with SMTP id d75a77b69052e-51c8b2aabb4mr121598841cf.1.1783674304646; Fri, 10 Jul 2026 02:05:04 -0700 (PDT) Received: from QCOM-eG0v1AUPpu.na.qualcomm.com ([82.64.236.198]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c15dfda815dsm259932266b.36.2026.07.10.02.05.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 02:05:03 -0700 (PDT) From: Loic Poulain Date: Fri, 10 Jul 2026 11:04:52 +0200 Subject: [PATCH v4 6/7] media: qcom: camss: Add CAMSS Offline Processing Engine driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260710-camss-isp-ope-v4-6-51207a0319d8@oss.qualcomm.com> References: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> In-Reply-To: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> To: Bryan O'Donoghue , Vladimir Zapolskiy , Loic Poulain , Mauro Carvalho Chehab , Kees Cook , "Gustavo A. R. Silva" , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio Cc: linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-hardening@vger.kernel.org, devicetree@vger.kernel.org, Hans de Goede X-Mailer: b4 0.14.2 X-Proofpoint-GUID: DlbGT9HU8Sg9PomTw7Idw6h6Anjl2XAq X-Proofpoint-ORIG-GUID: DlbGT9HU8Sg9PomTw7Idw6h6Anjl2XAq X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfX7hqeraOmdynu U6ZCPGejTmAEeKmPfzICtt9ykwi4YKFl7KjlXA+sOYJmzrzmAMlMuMlX1PNyT+myXcHlWJKe5w5 TKidKls5F26DJ4D92AY/+Fuz9eFhzQ2xRVQVCuWBmNJNIBZoYtymQEPwAQ2N8B902C6q9XMCD59 IltBTeKc4APgFpY4NPeuhLmUCgO2UqmiL1xXFDpAJ21ZHMa78PrHqf8FgSo/57z7elt7gUkrjRV RfIbnv8JPXQGnjmp9BDoJma+PFDnjb90kA0B5ql7gHWvhNjmwQQ9KvAkrHsRThJH0yOJYXsQnUH u2YZQshpWBplvnhXXWBARh9fm0btSV4fUhAfrXaYBfk1lio24lnNypNls5NrmrdSRA6kwPFgKqu 2CBCgb8iPL31x/Z/+BHWzfGlPbFsaCGdlskExTmxgAUIYtzIBK5DkJ/mWuZbENmYtVqvTVdAt03 YiFR3w6oSrq0Re67SOg== X-Authority-Analysis: v=2.4 cv=N7MZ0W9B c=1 sm=1 tr=0 ts=6a50b5c4 cx=c_pps a=oc9J++0uMp73DTRD5QyR2A==:117 a=MDeckJw97qnk8wCBExTehA==:17 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=yOCtJkima9RkubShWh1s:22 a=EUspDBNiAAAA:8 a=-xheWclFLtP6Gnvlc4IA:9 a=dICAtHF08a6TQIwv:21 a=QEXdDO2ut3YA:10 a=O8hF6Hzn-FEA:10 a=iYH6xdkBrDN1Jqds4HTS:22 X-Proofpoint-Spam-Info: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfX7A3Zz6b7cfOY vny+DoyUXh/ivUnBRgkTpgoBLuOognPUS9Aa+rVeRWUR/nsjGw4HusuCrweZpXreVDOBd7P75ny 5Ng8pMOCo8XbRgq67+qXA664dhdjcJ4= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-10_02,2026-07-09_04,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 spamscore=0 impostorscore=0 suspectscore=0 adultscore=0 malwarescore=0 phishscore=0 lowpriorityscore=0 priorityscore=1501 bulkscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607100088 Add an image processing driver for the Qualcomm Offline Processing Engine (OPE). OPE is a memory-to-memory ISP block that converts raw Bayer frames to YUV, performing white balance, demosaic, chroma enhancement, color correction and downscaling. The hardware architecture consists of Fetch Engines and Write Engines, connected through intermediate pipeline modules for pix processing. The driver exposes three video nodes per pipeline instance: - ope_input: Bayer RAW input (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - ope_disp_output: YUV output (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - ope_params: ISP parameters (V4L2_BUF_TYPE_META_OUTPUT) Hardware features: - Stripe-based processing (up to 336 pixels wide per stripe) - White balance (CLC_WB) - Demosaic / Bayer-to-RGB (CLC_DEMO) - RGB-to-YUV conversion (CLC_CHROMA_ENHAN) - Color correction matrix (CLC_CC) - MN downscaler for chroma and luma planes Default configuration values are based on public standards such as BT.601. Processing Model: OPE processes frames in stripes of up to 336 pixels. Therefore, frames must be split into stripes for processing. Each stripe is configured after the previous one has been acquired (double buffered registers). To minimize inter-stripe latency, stripe configurations are generated ahead of time. Signed-off-by: Loic Poulain Co-developed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/media/platform/qcom/camss/Kconfig | 18 + drivers/media/platform/qcom/camss/Makefile | 4 + drivers/media/platform/qcom/camss/camss-ope.c | 3245 +++++++++++++++++++++= ++++ 3 files changed, 3267 insertions(+) diff --git a/drivers/media/platform/qcom/camss/Kconfig b/drivers/media/plat= form/qcom/camss/Kconfig index 4eda48cb1adf049a7fb6cb59b9da3c0870fe57f4..895fc57a679655fcb6f308be156= 5dc6b77bbbd67 100644 --- a/drivers/media/platform/qcom/camss/Kconfig +++ b/drivers/media/platform/qcom/camss/Kconfig @@ -7,3 +7,21 @@ config VIDEO_QCOM_CAMSS select VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_DMA_SG select V4L2_FWNODE + +config VIDEO_QCOM_CAMSS_OPE + tristate "Qualcomm Offline Processing Engine (OPE) driver" + depends on VIDEO_QCOM_CAMSS + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST + select V4L2_ISP + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + help + Enable support for the Qualcomm Offline Processing Engine (OPE). + OPE is a memory-to-memory ISP block that converts raw Bayer frames + to YUV, performing white balance, demosaic, chroma enhancement and + downscaling. Found on QCM2290 and related SoCs. + + To compile this driver as a module, choose M here: the module + will be called qcom-camss-ope. diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/pla= tform/qcom/camss/Makefile index 5678621efb6780b67a043ec8a2e914cce02d9b98..422eebc0a86301de3f39c743fbc= 06c437b17ac9a 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -31,3 +31,7 @@ qcom-camss-objs +=3D \ camss-params.o \ =20 obj-$(CONFIG_VIDEO_QCOM_CAMSS) +=3D qcom-camss.o + +qcom-camss-ope-objs :=3D camss-ope.o + +obj-$(CONFIG_VIDEO_QCOM_CAMSS_OPE) +=3D qcom-camss-ope.o diff --git a/drivers/media/platform/qcom/camss/camss-ope.c b/drivers/media/= platform/qcom/camss/camss-ope.c new file mode 100644 index 0000000000000000000000000000000000000000..2c0d68cf1a637c998ebe4d3afb1= fa6dbdb68f029 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-ope.c @@ -0,0 +1,3245 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-ope.c + * + * Qualcomm MSM Camera Subsystem - Offline Processing Engine + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/* + * This driver provides driver implementation for the Qualcomm Offline + * Processing Engine (OPE). OPE is a memory-to-memory hardware block + * designed for image processing on a source frame. Typically, the input + * frame originates from the SoC CSI capture path, though not limited to. + * + * The hardware architecture consists of Fetch Engines and Write Engines, + * connected through intermediate pipeline modules: + * [FETCH ENGINES] =3D> [Pipeline Modules] =3D> [WRITE ENGINES] + * + * Current Configuration: + * Fetch Engine: One fetch engine is used for Bayer frame input. + * Write Engines: Two display write engines for Y and UV planes output. + * + * Only a subset of the pipeline modules are enabled: + * CLC_WB: White balance for channel gain configuration + * CLC_DEMO: Demosaic for Bayer to RGB conversion + * CLC_CC: Color Correct, coefficient based RGB correction + * CLC_CHROMA_ENHAN: for RGB to YUV conversion + * CLC_DOWNSCALE*: Downscaling for UV (YUV444 -> YUV422/YUV420) and YUV = planes + * + * Default configuration values are based on public standards such as BT.6= 01. + * + * Processing Model: + * OPE processes frames in stripes of up to 336 pixels. Therefore, frames = must + * be split into stripes for processing. Each stripe is configured after t= he + * previous one has been acquired (double buffered registers). To minimize + * inter-stripe latency, the stripe configurations are generated ahead of = time. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "camss-pipeline.h" + +#include +#include + +#include + +#include "camss-params.h" + +#define OPE_NAME "qcom-camss-ope" + +/* Format descriptor */ +struct ope_fmt { + u32 fourcc; + unsigned int depth; + unsigned int align; + unsigned int num_planes; + u32 mbus_code; + unsigned int c_hsub; + unsigned int c_vsub; +}; + +/* Per-queue format state */ +struct ope_fmt_state { + const struct ope_fmt *fmt; + unsigned int width; + unsigned int height; + struct v4l2_rect crop; + unsigned int bytesperline; + unsigned int sizeimage; + enum v4l2_colorspace colorspace; + enum v4l2_xfer_func xfer_func; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + unsigned int sequence; + struct v4l2_fract timeperframe; +}; + +/* -------- Register layout -------- */ + +#define OPE_TOP_HW_VERSION 0x000 +#define OPE_TOP_HW_VERSION_STEP GENMASK(15, 0) +#define OPE_TOP_HW_VERSION_REV GENMASK(27, 16) +#define OPE_TOP_HW_VERSION_GEN GENMASK(31, 28) +#define OPE_TOP_RESET_CMD 0x004 +#define OPE_TOP_RESET_CMD_HW BIT(0) +#define OPE_TOP_RESET_CMD_SW BIT(1) +#define OPE_TOP_IRQ_STATUS 0x014 +#define OPE_TOP_IRQ_MASK 0x018 +#define OPE_TOP_IRQ_STATUS_RST_DONE BIT(0) +#define OPE_TOP_IRQ_STATUS_WE BIT(1) +#define OPE_TOP_IRQ_STATUS_FE BIT(2) +#define OPE_TOP_IRQ_STATUS_VIOL BIT(3) +#define OPE_TOP_IRQ_STATUS_IDLE BIT(4) +#define OPE_TOP_IRQ_CLEAR 0x01c +#define OPE_TOP_IRQ_CMD 0x024 +#define OPE_TOP_IRQ_CMD_CLEAR BIT(0) +#define OPE_TOP_VIOLATION_STATUS 0x028 + +/* Fetch engine */ +#define OPE_BUS_RD_INPUT_IF_IRQ_MASK 0x00c +#define OPE_BUS_RD_INPUT_IF_IRQ_CLEAR 0x010 +#define OPE_BUS_RD_INPUT_IF_IRQ_CMD 0x014 +#define OPE_BUS_RD_INPUT_IF_IRQ_CMD_CLEAR BIT(0) +#define OPE_BUS_RD_INPUT_IF_IRQ_STATUS 0x018 +#define OPE_BUS_RD_INPUT_IF_CMD 0x01c +#define OPE_BUS_RD_INPUT_IF_CMD_GO_CMD BIT(0) +#define OPE_BUS_RD_CLIENT_0_CORE_CFG 0x050 +#define OPE_BUS_RD_CLIENT_0_CORE_CFG_EN BIT(0) +#define OPE_BUS_RD_CLIENT_0_CCIF_META_DATA 0x054 +#define OPE_BUS_RD_CLIENT_0_CCIF_MD_PIX_PATTERN GENMASK(7, 2) +#define OPE_BUS_RD_CLIENT_0_ADDR_IMAGE 0x058 +#define OPE_BUS_RD_CLIENT_0_RD_BUFFER_SIZE 0x05c +#define OPE_BUS_RD_CLIENT_0_RD_STRIDE 0x060 +#define OPE_BUS_RD_CLIENT_0_UNPACK_CFG_0 0x064 + +/* Write engines */ +#define OPE_BUS_WR_INPUT_IF_IRQ_MASK_0 0x018 +#define OPE_BUS_WR_INPUT_IF_IRQ_MASK_1 0x01c +#define OPE_BUS_WR_INPUT_IF_IRQ_CLEAR_0 0x020 +#define OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0 0x028 +#define OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_RUP_DONE BIT(0) +#define OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_BUF_DONE BIT(8) +#define OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_CONS_VIOL BIT(28) +#define OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_VIOL BIT(30) +#define OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_IMG_SZ_VIOL BIT(31) +#define OPE_BUS_WR_INPUT_IF_IRQ_CMD 0x030 +#define OPE_BUS_WR_INPUT_IF_IRQ_CMD_CLEAR BIT(0) +#define OPE_BUS_WR_VIOLATION_STATUS 0x064 +#define OPE_BUS_WR_IMAGE_SIZE_VIOLATION_STATUS 0x070 +#define OPE_BUS_WR_CLIENT_CFG(c) (0x200 + (c) * 0x100) +#define OPE_BUS_WR_CLIENT_CFG_EN BIT(0) +#define OPE_BUS_WR_CLIENT_CFG_AUTORECOVER BIT(4) +#define OPE_BUS_WR_CLIENT_ADDR_IMAGE(c) (0x204 + (c) * 0x100) +#define OPE_BUS_WR_CLIENT_IMAGE_CFG_0(c) (0x20c + (c) * 0x100) +#define OPE_BUS_WR_CLIENT_IMAGE_CFG_1(c) (0x210 + (c) * 0x100) +#define OPE_BUS_WR_CLIENT_IMAGE_CFG_2(c) (0x214 + (c) * 0x100) +#define OPE_BUS_WR_CLIENT_PACKER_CFG(c) (0x218 + (c) * 0x100) +#define OPE_BUS_WR_CLIENT_MAX 4 + +/* Pipeline modules */ +#define OPE_PP_CLC_WB_GAIN_MODULE_CFG (0x200 + 0x60) +#define OPE_PP_CLC_WB_GAIN_MODULE_CFG_EN BIT(0) +#define OPE_PP_CLC_WB_GAIN_WB_CFG(ch) (0x200 + 0x68 + 4 * (ch)) +#define OPE_PP_CLC_WB_GAIN_WB_CFG_GAIN GENMASK(14, 0) +#define OPE_PP_CLC_WB_GAIN_WB_SUB_CFG(ch) (0x200 + 0x74 + 4 * (ch)) +#define OPE_PP_CLC_WB_GAIN_WB_SUB_CFG_VAL GENMASK(31, 20) +#define OPE_PP_CLC_WB_GAIN_WB_ADD_CFG(ch) (0x200 + 0x80 + 4 * (ch)) +#define OPE_PP_CLC_WB_GAIN_WB_ADD_CFG_VAL GENMASK(31, 20) + +#define OPE_PP_CLC_CC_BASE 0x400 +#define OPE_PP_CLC_CC_MODULE_CFG (OPE_PP_CLC_CC_BASE + 0x60) +#define OPE_PP_CLC_CC_MODULE_CFG_EN BIT(0) +#define OPE_PP_CLC_CC_COEFF_A_CFG_0 (OPE_PP_CLC_CC_BASE + 0x68) +#define OPE_PP_CLC_CC_COEFF_A_CFG_0_A0 GENMASK(11, 0) +#define OPE_PP_CLC_CC_COEFF_A_CFG_0_A1 GENMASK(27, 16) +#define OPE_PP_CLC_CC_COEFF_A_CFG_1 (OPE_PP_CLC_CC_BASE + 0x6c) +#define OPE_PP_CLC_CC_COEFF_A_CFG_1_A2 GENMASK(11, 0) +#define OPE_PP_CLC_CC_COEFF_B_CFG_0 (OPE_PP_CLC_CC_BASE + 0x70) +#define OPE_PP_CLC_CC_COEFF_B_CFG_0_B0 GENMASK(11, 0) +#define OPE_PP_CLC_CC_COEFF_B_CFG_0_B1 GENMASK(27, 16) +#define OPE_PP_CLC_CC_COEFF_B_CFG_1 (OPE_PP_CLC_CC_BASE + 0x74) +#define OPE_PP_CLC_CC_COEFF_B_CFG_1_B2 GENMASK(11, 0) +#define OPE_PP_CLC_CC_COEFF_C_CFG_0 (OPE_PP_CLC_CC_BASE + 0x78) +#define OPE_PP_CLC_CC_COEFF_C_CFG_0_C0 GENMASK(11, 0) +#define OPE_PP_CLC_CC_COEFF_C_CFG_0_C1 GENMASK(27, 16) +#define OPE_PP_CLC_CC_COEFF_C_CFG_1 (OPE_PP_CLC_CC_BASE + 0x7c) +#define OPE_PP_CLC_CC_COEFF_C_CFG_1_C2 GENMASK(11, 0) +#define OPE_PP_CLC_CC_COEFF_K_CFG_0 (OPE_PP_CLC_CC_BASE + 0x80) +#define OPE_PP_CLC_CC_COEFF_K_CFG_0_K0 GENMASK(12, 0) +#define OPE_PP_CLC_CC_COEFF_K_CFG_1 (OPE_PP_CLC_CC_BASE + 0x84) +#define OPE_PP_CLC_CC_COEFF_K_CFG_1_K1 GENMASK(12, 0) +#define OPE_PP_CLC_CC_COEFF_K_CFG_2 (OPE_PP_CLC_CC_BASE + 0x88) +#define OPE_PP_CLC_CC_COEFF_K_CFG_2_K2 GENMASK(12, 0) +#define OPE_PP_CLC_CC_COEFF_M_CFG (OPE_PP_CLC_CC_BASE + 0x8c) +#define OPE_PP_CLC_CC_COEFF_M_CFG_M GENMASK(11, 0) + +#define OPE_PP_CLC_DEMO_MODULE_CFG (0x800 + 0x60) +#define OPE_PP_CLC_DEMO_MODULE_CFG_EN BIT(0) +#define OPE_PP_CLC_DEMO_MODULE_CFG_DYN_G_CLAMP_EN BIT(4) +#define OPE_PP_CLC_DEMO_INTERP_COEFF_CFG (0x800 + 0x68) +#define OPE_PP_CLC_DEMO_INTERP_COEFF_CFG_LAMBDA_G GENMASK(15, 8) +#define OPE_PP_CLC_DEMO_INTERP_COEFF_CFG_LAMBDA_RB GENMASK(7, 0) +#define OPE_PP_CLC_DEMO_INTERP_CLASSIFIER_CFG_0 (0x800 + 0x6c) +#define OPE_PP_CLC_DEMO_INTERP_CLASSIFIER_CFG_0_AK GENMASK(15, 0) +#define OPE_PP_CLC_DEMO_INTERP_CLASSIFIER_CFG_1 (0x800 + 0x70) +#define OPE_PP_CLC_DEMO_INTERP_CLASSIFIER_CFG_1_WK GENMASK(15, 0) + +#define OPE_PP_CLC_DOWNSCALE_MN_DS_C_PRE_BASE 0x1c00 +#define OPE_PP_CLC_DOWNSCALE_MN_DS_Y_DISP_BASE 0x3000 +#define OPE_PP_CLC_DOWNSCALE_MN_DS_C_DISP_BASE 0x3200 +#define OPE_PP_CLC_CROP_RND_CLAMP_Y_DISP_BASE 0x3400 +#define OPE_PP_CLC_CROP_RND_CLAMP_C_DISP_BASE 0x3600 +#define OPE_PP_CLC_CROP_RND_CLAMP_HW_STATUS(base) ((base) + 0x04) +#define OPE_PP_CLC_CROP_RND_CLAMP_HEIGHT_VIOL BIT(2) +#define OPE_PP_CLC_CROP_RND_CLAMP_WIDTH_VIOL BIT(1) +#define OPE_PP_CLC_CROP_RND_CLAMP_MODULE_CFG(base) ((base) + 0x60) +#define OPE_PP_CLC_CROP_RND_CLAMP_EN BIT(0) +#define OPE_PP_CLC_CROP_RND_CLAMP_CROP_EN BIT(9) +#define OPE_PP_CLC_CROP_RND_CLAMP_CROP_LINE_CFG(base) ((base) + 0x68) +#define OPE_PP_CLC_CROP_RND_CLAMP_CROP_PIXEL_CFG(base) ((base) + 0x6c) +#define OPE_PP_CLC_CROP_RND_CLAMP_CROP_FIRST GENMASK(29, 16) +#define OPE_PP_CLC_CROP_RND_CLAMP_CROP_LAST GENMASK(13, 0) +#define OPE_PP_CLC_DOWNSCALE_MN_CFG(ds) ((ds) + 0x60) +#define OPE_PP_CLC_DOWNSCALE_MN_CFG_EN BIT(0) +#define OPE_PP_CLC_DOWNSCALE_MN_DS_CFG(ds) ((ds) + 0x64) +#define OPE_PP_CLC_DOWNSCALE_MN_DS_CFG_H_SCALE_EN BIT(9) +#define OPE_PP_CLC_DOWNSCALE_MN_DS_CFG_V_SCALE_EN BIT(10) +#define OPE_PP_CLC_DOWNSCALE_MN_DS_IMAGE_SIZE_CFG(ds) ((ds) + 0x68) +#define OPE_PP_CLC_DOWNSCALE_MN_DS_MN_H_CFG(ds) ((ds) + 0x6c) +#define OPE_PP_CLC_DOWNSCALE_MN_DS_MN_H_PHASE_CFG(ds) ((ds) + 0x70) +#define OPE_PP_CLC_DOWNSCALE_MN_DS_MN_V_CFG(ds) ((ds) + 0x74) +#define OPE_PP_CLC_DOWNSCALE_MN_DS_MN_V_PHASE_CFG(ds) ((ds) + 0x78) + +#define OPE_PP_CLC_CHROMA_ENHAN_MODULE_CFG (0x1200 + 0x60) +#define OPE_PP_CLC_CHROMA_ENHAN_MODULE_CFG_EN BIT(0) +#define OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_0 (0x1200 + 0x68) +#define OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_0_V0 GENMASK(11, 0) +#define OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_0_V1 GENMASK(27, 16) +#define OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_1 (0x1200 + 0x6c) +#define OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_1_K GENMASK(31, 23) +#define OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_2 (0x1200 + 0x70) +#define OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_2_V2 GENMASK(11, 0) +#define OPE_PP_CLC_CHROMA_ENHAN_COEFF_A_CFG (0x1200 + 0x74) +#define OPE_PP_CLC_CHROMA_ENHAN_COEFF_A_CFG_AP GENMASK(11, 0) +#define OPE_PP_CLC_CHROMA_ENHAN_COEFF_A_CFG_AM GENMASK(27, 16) +#define OPE_PP_CLC_CHROMA_ENHAN_COEFF_B_CFG (0x1200 + 0x78) +#define OPE_PP_CLC_CHROMA_ENHAN_COEFF_B_CFG_BP GENMASK(11, 0) +#define OPE_PP_CLC_CHROMA_ENHAN_COEFF_B_CFG_BM GENMASK(27, 16) +#define OPE_PP_CLC_CHROMA_ENHAN_COEFF_C_CFG (0x1200 + 0x7C) +#define OPE_PP_CLC_CHROMA_ENHAN_COEFF_C_CFG_CP GENMASK(11, 0) +#define OPE_PP_CLC_CHROMA_ENHAN_COEFF_C_CFG_CM GENMASK(27, 16) +#define OPE_PP_CLC_CHROMA_ENHAN_COEFF_D_CFG (0x1200 + 0x80) +#define OPE_PP_CLC_CHROMA_ENHAN_COEFF_D_CFG_DP GENMASK(11, 0) +#define OPE_PP_CLC_CHROMA_ENHAN_COEFF_D_CFG_DM GENMASK(27, 16) +#define OPE_PP_CLC_CHROMA_ENHAN_CHROMA_CFG_0 (0x1200 + 0x84) +#define OPE_PP_CLC_CHROMA_ENHAN_CHROMA_CFG_0_KCB GENMASK(31, 21) +#define OPE_PP_CLC_CHROMA_ENHAN_CHROMA_CFG_1 (0x1200 + 0x88) +#define OPE_PP_CLC_CHROMA_ENHAN_CHROMA_CFG_1_KCR GENMASK(31, 21) + +/* -------- OPE-specific constants -------- */ + +#define OPE_STRIPE_MAX_W 336 +#define OPE_STRIPE_MAX_H 8192 +#define OPE_STRIPE_MIN_W 16 +#define OPE_STRIPE_MIN_H OPE_STRIPE_MIN_W +#define OPE_MAX_STRIPE 16 +#define OPE_ALIGN_H 1 +#define OPE_ALIGN_W 1 +#define OPE_MIN_W 24 +#define OPE_MIN_H 16 +#define OPE_MAX_W (OPE_STRIPE_MAX_W * OPE_MAX_STRIPE) +#define OPE_MAX_H OPE_STRIPE_MAX_H +#define OPE_RESET_TIMEOUT_MS 100 +#define DEFAULT_FRAMERATE 60 + +/* + * AXI clock is shared across CAMSS blocks and not dynamically scaled, + * just request the highest supported rate. + */ +#define OPE_AXI_CLK_RATE (300 * HZ_PER_MHZ) + +/* Downscaler fixed-point helpers */ +#define Q21(v) (((uint64_t)(v)) << 21) +#define DS_Q21(n, d) ((uint32_t)(((uint64_t)(n) << 21) / (d))) +#define DS_RESOLUTION(in, out) \ + (((out) * 128 <=3D (in)) ? 0x0 : \ + ((out) * 16 <=3D (in)) ? 0x1 : \ + ((out) * 8 <=3D (in)) ? 0x2 : 0x3) +#define DS_OUTPUT_PIX(in, phase_init, phase_step) \ + ((Q21(in) - (phase_init)) / (phase_step)) +#define OPE_WB(n, d) (((n) << 10) / (d)) + +enum ope_downscaler { + OPE_DS_C_PRE, + OPE_DS_C_DISP, + OPE_DS_Y_DISP, + OPE_DS_MAX, +}; + +static const u32 ope_ds_base[OPE_DS_MAX] =3D { + OPE_PP_CLC_DOWNSCALE_MN_DS_C_PRE_BASE, + OPE_PP_CLC_DOWNSCALE_MN_DS_C_DISP_BASE, + OPE_PP_CLC_DOWNSCALE_MN_DS_Y_DISP_BASE, +}; + +enum ope_wr_client { + OPE_WR_CLIENT_VID_Y, + OPE_WR_CLIENT_VID_C, + OPE_WR_CLIENT_DISP_Y, + OPE_WR_CLIENT_DISP_C, + OPE_WR_CLIENT_MAX, +}; + +enum ope_pixel_pattern { + OPE_PIXEL_PATTERN_RGRGRG, + OPE_PIXEL_PATTERN_GRGRGR, + OPE_PIXEL_PATTERN_BGBGBG, + OPE_PIXEL_PATTERN_GBGBGB, + OPE_PIXEL_PATTERN_YCBYCR, + OPE_PIXEL_PATTERN_YCRYCB, + OPE_PIXEL_PATTERN_CBYCRY, + OPE_PIXEL_PATTERN_CRYCBY, +}; + +enum ope_stripe_location { + OPE_STRIPE_LOCATION_FULL, + OPE_STRIPE_LOCATION_LEFT, + OPE_STRIPE_LOCATION_RIGHT, + OPE_STRIPE_LOCATION_MIDDLE, +}; + +enum ope_unpacker_format { + OPE_UNPACKER_FMT_PLAIN_8 =3D 1, + OPE_UNPACKER_FMT_PLAIN_16_10BPP =3D 2, + OPE_UNPACKER_FMT_MIPI_10 =3D 13, +}; + +enum ope_packer_format { + OPE_PACKER_FMT_PLAIN_8 =3D 1, + OPE_PACKER_FMT_PLAIN_8_ODD_EVEN =3D 2, + OPE_PACKER_FMT_MIPI_10 =3D 12, +}; + +struct ope_hw_fmt { + u32 fourcc; + enum ope_pixel_pattern pattern; + enum ope_unpacker_format unpacker; + enum ope_packer_format packer; +}; + +static const struct ope_hw_fmt ope_hw_fmts[] =3D { + { V4L2_PIX_FMT_SBGGR10P, OPE_PIXEL_PATTERN_BGBGBG, + OPE_UNPACKER_FMT_MIPI_10, OPE_PACKER_FMT_MIPI_10 }, + { V4L2_PIX_FMT_SGBRG10P, OPE_PIXEL_PATTERN_GBGBGB, + OPE_UNPACKER_FMT_MIPI_10, OPE_PACKER_FMT_MIPI_10 }, + { V4L2_PIX_FMT_SGRBG10P, OPE_PIXEL_PATTERN_GRGRGR, + OPE_UNPACKER_FMT_MIPI_10, OPE_PACKER_FMT_MIPI_10 }, + { V4L2_PIX_FMT_SRGGB10P, OPE_PIXEL_PATTERN_RGRGRG, + OPE_UNPACKER_FMT_MIPI_10, OPE_PACKER_FMT_MIPI_10 }, + { V4L2_PIX_FMT_SRGGB8, OPE_PIXEL_PATTERN_RGRGRG, + OPE_UNPACKER_FMT_PLAIN_8, OPE_PACKER_FMT_PLAIN_8 }, + { V4L2_PIX_FMT_SBGGR8, OPE_PIXEL_PATTERN_BGBGBG, + OPE_UNPACKER_FMT_PLAIN_8, OPE_PACKER_FMT_PLAIN_8 }, + { V4L2_PIX_FMT_SGBRG8, OPE_PIXEL_PATTERN_GBGBGB, + OPE_UNPACKER_FMT_PLAIN_8, OPE_PACKER_FMT_PLAIN_8 }, + { V4L2_PIX_FMT_SGRBG8, OPE_PIXEL_PATTERN_GRGRGR, + OPE_UNPACKER_FMT_PLAIN_8, OPE_PACKER_FMT_PLAIN_8 }, + { V4L2_PIX_FMT_NV24, OPE_PIXEL_PATTERN_YCBYCR, + OPE_UNPACKER_FMT_PLAIN_8, OPE_PACKER_FMT_PLAIN_8 }, + { V4L2_PIX_FMT_NV42, OPE_PIXEL_PATTERN_YCRYCB, + OPE_UNPACKER_FMT_PLAIN_8, OPE_PACKER_FMT_PLAIN_8_ODD_EVEN }, + { V4L2_PIX_FMT_NV16, OPE_PIXEL_PATTERN_CBYCRY, + OPE_UNPACKER_FMT_PLAIN_8, OPE_PACKER_FMT_PLAIN_8 }, + { V4L2_PIX_FMT_NV61, OPE_PIXEL_PATTERN_CBYCRY, + OPE_UNPACKER_FMT_PLAIN_8, OPE_PACKER_FMT_PLAIN_8_ODD_EVEN }, + { V4L2_PIX_FMT_NV12, OPE_PIXEL_PATTERN_CBYCRY, + OPE_UNPACKER_FMT_PLAIN_8, OPE_PACKER_FMT_PLAIN_8 }, + { V4L2_PIX_FMT_NV21, OPE_PIXEL_PATTERN_CBYCRY, + OPE_UNPACKER_FMT_PLAIN_8, OPE_PACKER_FMT_PLAIN_8_ODD_EVEN }, + { V4L2_PIX_FMT_GREY, OPE_PIXEL_PATTERN_RGRGRG, + OPE_UNPACKER_FMT_PLAIN_8, OPE_PACKER_FMT_PLAIN_8 }, +}; + +static const struct ope_hw_fmt *ope_find_hw_fmt(u32 fourcc) +{ + for (unsigned int i =3D 0; i < ARRAY_SIZE(ope_hw_fmts); i++) + if (ope_hw_fmts[i].fourcc =3D=3D fourcc) + return &ope_hw_fmts[i]; + + return NULL; +} + +static const struct ope_fmt ope_input_fmts[] =3D { + { V4L2_PIX_FMT_SBGGR10P, 10, 2, 1, MEDIA_BUS_FMT_SBGGR10_1X10 }, + { V4L2_PIX_FMT_SGBRG10P, 10, 2, 1, MEDIA_BUS_FMT_SGBRG10_1X10 }, + { V4L2_PIX_FMT_SGRBG10P, 10, 2, 1, MEDIA_BUS_FMT_SGRBG10_1X10 }, + { V4L2_PIX_FMT_SRGGB10P, 10, 2, 1, MEDIA_BUS_FMT_SRGGB10_1X10 }, + { V4L2_PIX_FMT_SRGGB8, 8, 0, 1, MEDIA_BUS_FMT_SRGGB8_1X8 }, + { V4L2_PIX_FMT_SBGGR8, 8, 0, 1, MEDIA_BUS_FMT_SBGGR8_1X8 }, + { V4L2_PIX_FMT_SGBRG8, 8, 0, 1, MEDIA_BUS_FMT_SGBRG8_1X8 }, + { V4L2_PIX_FMT_SGRBG8, 8, 0, 1, MEDIA_BUS_FMT_SGRBG8_1X8 }, +}; + +static const struct ope_fmt ope_output_fmts[] =3D { + { V4L2_PIX_FMT_NV24, 24, 0, 1, MEDIA_BUS_FMT_YUV8_1X24, 1, 1 }, + { V4L2_PIX_FMT_NV42, 24, 0, 1, MEDIA_BUS_FMT_YUV8_1X24, 1, 1 }, + { V4L2_PIX_FMT_NV16, 16, 1, 1, MEDIA_BUS_FMT_YUYV8_2X8, 2, 1 }, + { V4L2_PIX_FMT_NV61, 16, 1, 1, MEDIA_BUS_FMT_YUYV8_2X8, 2, 1 }, + { V4L2_PIX_FMT_NV12, 12, 1, 1, MEDIA_BUS_FMT_YUYV8_1_5X8, 2, 2 }, + { V4L2_PIX_FMT_NV21, 12, 1, 1, MEDIA_BUS_FMT_YUYV8_1_5X8, 2, 2 }, + { V4L2_PIX_FMT_GREY, 8, 0, 1, MEDIA_BUS_FMT_Y8_1X8, 1, 1 }, +}; + +struct ope_dsc_config { + u32 input_width; + u32 input_height; + u32 output_width; + u32 output_height; + u32 phase_step_h; + u32 phase_step_v; + u32 crop_last_pixel; + u32 crop_last_line; +}; + +struct ope_stripe { + struct { + dma_addr_t addr; + u32 width; + u32 height; + u32 stride; + enum ope_stripe_location location; + enum ope_pixel_pattern pattern; + enum ope_unpacker_format format; + } src; + struct { + dma_addr_t addr; + u32 width; + u32 height; + u32 stride; + u32 x_init; + enum ope_packer_format format; + bool enabled; + } dst[OPE_WR_CLIENT_MAX]; + struct ope_dsc_config dsc[OPE_DS_MAX]; +}; + +/* OPE tuning parameter state */ + +/* Demosaic (CLC_DEMO) coefficients, internal only, not user-configurable.= */ +#define CAMSS_PARAMS_INTERNAL (-1) + +struct ope_params_demo { + struct v4l2_isp_params_block_header header; + u8 lambda_rb; + u8 lambda_g; + u16 a_k; + u16 w_k; + u16 _pad; +}; + +/* OPE tuning parameter state */ +struct ope_config { + struct camss_params_wb_gain wb_gain; + struct camss_params_chroma_enhan chroma_enhan; + struct camss_params_color_correct color_correct; + struct ope_params_demo demo; +}; + +enum ope_entity { + OPE_ENTITY_FRAME_IN, + OPE_ENTITY_PARAMS, + OPE_ENTITY_PROC, + OPE_ENTITY_DISP, + OPE_ENTITY_DISP_OUT, + OPE_ENTITY_COUNT +}; + +enum ope_queue_idx { + OPE_QUEUE_FRAME_IN, + OPE_QUEUE_DISP_OUT, + OPE_QUEUE_PARAMS, + OPE_QUEUE_COUNT +}; + +struct camss_isp_buf { + struct vb2_v4l2_buffer vb; /* must be first */ + struct list_head list; +}; + +struct ope_queue { + struct vb2_queue vq; + struct list_head rdy_queue; + spinlock_t rdy_spinlock; +}; + +enum ope_proc_pad { + OPE_PROC_PAD_SINK_IN, + OPE_PROC_PAD_SINK_PAR, + OPE_PROC_PAD_SOURCE, + OPE_PROC_PADS_NUM, +}; + +enum ope_disp_pad { + OPE_DISP_PAD_SINK, + OPE_DISP_PAD_SOURCE, + OPE_DISP_PADS_NUM, +}; + +/* per-context state */ +struct ope_ctx { + bool queued; + struct ope_dev *ope; + struct mutex vbq_lock; + struct ope_queue queues[OPE_QUEUE_COUNT]; + + unsigned int framerate; + struct ope_fmt_state fmt_in; + struct ope_fmt_state fmt_out; + + struct list_head list; + bool started; + bool first_start; + + struct ope_config config; + u8 current_stripe; + struct ope_stripe stripe[OPE_MAX_STRIPE]; +}; + +/* Per OPE device state */ +struct ope_dev { + struct device *dev; + struct v4l2_device v4l2_dev; + struct media_device mdev; + struct camss_isp_pipeline *pipeline; + struct mutex mutex; + struct work_struct run_work; + struct ope_ctx *curr_ctx; + struct ope_ctx *prev_ctx; + spinlock_t sched_lock; + + struct icc_path *icc_data; + struct icc_path *icc_config; + + void __iomem *base; + void __iomem *base_rd; + void __iomem *base_wr; + void __iomem *base_pp; + + struct completion reset_complete; + + struct list_head ctx_list; + struct ope_ctx *shared_ctx; + unsigned int open_count; + + /* Currently active hardware context (set at job start) */ + struct ope_ctx *hw_ctx; +}; + +/* -------- Register accessors -------- */ + +static inline u32 ope_read(struct ope_dev *ope, u32 reg) +{ + return readl(ope->base + reg); +} + +static inline void ope_write(struct ope_dev *ope, u32 reg, u32 val) +{ + writel(val, ope->base + reg); +} + +static inline void ope_write_wr(struct ope_dev *ope, u32 reg, u32 val) +{ + writel_relaxed(val, ope->base_wr + reg); +} + +static inline u32 ope_read_wr(struct ope_dev *ope, u32 reg) +{ + return readl_relaxed(ope->base_wr + reg); +} + +static inline void ope_write_rd(struct ope_dev *ope, u32 reg, u32 val) +{ + writel_relaxed(val, ope->base_rd + reg); +} + +static inline void ope_write_pp(struct ope_dev *ope, u32 reg, u32 val) +{ + writel_relaxed(val, ope->base_pp + reg); +} + +static inline void ope_start(struct ope_dev *ope) +{ + wmb(); /* ensure all register writes are visible before GO_CMD */ + ope_write_rd(ope, OPE_BUS_RD_INPUT_IF_CMD, OPE_BUS_RD_INPUT_IF_CMD_GO_CMD= ); +} + +/* -------- Stripe helpers -------- */ + +static inline enum ope_stripe_location ope_stripe_location(unsigned int id= x, unsigned int count) +{ + if (count =3D=3D 1) + return OPE_STRIPE_LOCATION_FULL; + if (idx =3D=3D 0) + return OPE_STRIPE_LOCATION_LEFT; + if (idx =3D=3D count - 1) + return OPE_STRIPE_LOCATION_RIGHT; + + return OPE_STRIPE_LOCATION_MIDDLE; +} + +static inline bool ope_stripe_is_last(const struct ope_stripe *s) +{ + return s && (s->src.location =3D=3D OPE_STRIPE_LOCATION_RIGHT || + s->src.location =3D=3D OPE_STRIPE_LOCATION_FULL); +} + +static inline struct ope_stripe *ope_current_stripe(struct ope_ctx *ctx) +{ + if (ctx->current_stripe >=3D OPE_MAX_STRIPE) + return NULL; + + return &ctx->stripe[ctx->current_stripe]; +} + +static inline unsigned int ope_stripe_index(struct ope_ctx *ctx, const str= uct ope_stripe *stripe) +{ + return stripe - &ctx->stripe[0]; +} + +static inline struct ope_stripe *ope_prev_stripe(struct ope_ctx *ctx, stru= ct ope_stripe *stripe) +{ + unsigned int idx =3D ope_stripe_index(ctx, stripe); + + return idx ? &ctx->stripe[idx - 1] : NULL; +} + + +static inline unsigned int ope_last_stripe_width(unsigned int src_width, u= nsigned int nstripes) +{ + unsigned int last_sw =3D src_width - (nstripes - 1) * OPE_STRIPE_MAX_W; + + if (last_sw < OPE_STRIPE_MIN_W) + last_sw +=3D OPE_STRIPE_MIN_W * 2; + + return last_sw; +} + +static unsigned int ope_calc_h_scale(unsigned int src, unsigned int dst, + unsigned int nstripes, unsigned int c_hsub, + unsigned int sw_align) +{ + unsigned int last_sw =3D ope_last_stripe_width(src, nstripes); + unsigned int denom; + + if (src =3D=3D dst) + return DS_Q21(src, src); + + /* Find the smallest h_scale denominator >=3D dst such that the + * last-stripe adjustment can satisfy both Y and C pixel counts. + */ + for (denom =3D dst; denom <=3D dst + 16 * nstripes; denom++) { + unsigned int hs =3D DS_Q21(src, denom); + unsigned int tw =3D (nstripes - 1) * DS_OUTPUT_PIX(OPE_STRIPE_MAX_W, 0, = hs); + unsigned int tc =3D (nstripes - 1) * DS_OUTPUT_PIX(OPE_STRIPE_MAX_W / c_= hsub, 0, hs); + unsigned int ry =3D dst - tw; + unsigned int rc =3D dst / c_hsub - tc; + unsigned int s_y =3D DIV_ROUND_UP((u64)ry * hs, Q21(1)); + unsigned int s_c =3D DIV_ROUND_UP((u64)rc * hs, Q21(1)) * c_hsub; + unsigned int s =3D ALIGN(max(s_y, s_c), sw_align); + + s =3D clamp(s, OPE_STRIPE_MIN_W, last_sw); + tw +=3D DS_OUTPUT_PIX(s, 0, hs); + tc +=3D DS_OUTPUT_PIX(s / c_hsub, 0, hs); + + if (tw =3D=3D dst && tc * c_hsub =3D=3D dst) + return hs; + } + + return DS_Q21(src, dst); +} + +static unsigned int ope_calc_v_scale(unsigned int src, unsigned int dst) +{ + return DS_Q21(src, dst); +} + +static void ope_gen_stripe_chroma_dsc(struct ope_ctx *ctx, + struct ope_stripe *stripe) +{ + struct ope_dsc_config *dsc =3D &stripe->dsc[OPE_DS_C_PRE]; + const struct ope_fmt *fmt =3D ctx->fmt_out.fmt; + + dsc->input_width =3D stripe->src.width; + dsc->input_height =3D stripe->src.height; + + dsc->output_width =3D dsc->input_width / fmt->c_hsub; + dsc->output_height =3D dsc->input_height / fmt->c_vsub; + + dsc->phase_step_h =3D DS_Q21(dsc->input_width, dsc->output_width); + dsc->phase_step_v =3D DS_Q21(dsc->input_height, dsc->output_height); +} + +static void ope_gen_stripe_dsc(struct ope_ctx *ctx, struct ope_stripe *str= ipe, + unsigned int h_scale, unsigned int v_scale) +{ + struct ope_dsc_config *dsc_c =3D &stripe->dsc[OPE_DS_C_DISP]; + struct ope_dsc_config *dsc_y =3D &stripe->dsc[OPE_DS_Y_DISP]; + unsigned int sw =3D stripe->src.width; + unsigned int sw_c =3D stripe->dsc[OPE_DS_C_PRE].output_width; + + dsc_c->phase_step_h =3D dsc_y->phase_step_h =3D h_scale; + dsc_c->phase_step_v =3D dsc_y->phase_step_v =3D v_scale; + + dsc_c->input_width =3D stripe->dsc[OPE_DS_C_PRE].output_width; + dsc_c->input_height =3D stripe->dsc[OPE_DS_C_PRE].output_height; + dsc_y->input_width =3D stripe->src.width; + dsc_y->input_height =3D stripe->src.height; + + /* Clip the output so the WE always receives the expected count */ + dsc_y->output_width =3D DS_OUTPUT_PIX(sw, 0, h_scale); + dsc_y->output_height =3D DS_OUTPUT_PIX(stripe->src.height, 0, v_scale); + dsc_c->output_width =3D DS_OUTPUT_PIX(sw_c, 0, h_scale); + dsc_c->output_height =3D DS_OUTPUT_PIX(stripe->dsc[OPE_DS_C_PRE].output_h= eight, 0, v_scale); + + dsc_y->crop_last_pixel =3D dsc_y->output_width - 1; + dsc_y->crop_last_line =3D dsc_y->output_height - 1; + dsc_c->crop_last_pixel =3D dsc_c->output_width - 1; + dsc_c->crop_last_line =3D dsc_c->output_height - 1; +} + +static void ope_gen_stripe_yuv_dst(struct ope_ctx *ctx, struct ope_stripe = *stripe, + dma_addr_t dst, const struct v4l2_rect *compose) +{ + const struct ope_fmt_state *fo =3D &ctx->fmt_out; + const struct ope_hw_fmt *hw =3D ope_find_hw_fmt(fo->fmt->fourcc); + struct ope_stripe *prev =3D ope_prev_stripe(ctx, stripe); + unsigned int bpl =3D fo->bytesperline; + unsigned int img_h =3D fo->height; + unsigned int c_stride; + unsigned int x_init; + dma_addr_t base; + + stripe->dst[OPE_WR_CLIENT_DISP_Y].enabled =3D true; + stripe->dst[OPE_WR_CLIENT_DISP_C].enabled =3D true; + + /* Y plane */ + base =3D dst + compose->top * bpl; + + if (prev) + x_init =3D prev->dst[OPE_WR_CLIENT_DISP_Y].x_init + + prev->dst[OPE_WR_CLIENT_DISP_Y].width; + else + x_init =3D compose->left; + + stripe->dst[OPE_WR_CLIENT_DISP_Y].addr =3D base; + stripe->dst[OPE_WR_CLIENT_DISP_Y].x_init =3D x_init; + stripe->dst[OPE_WR_CLIENT_DISP_Y].width =3D stripe->dsc[OPE_DS_Y_DISP].o= utput_width; + stripe->dst[OPE_WR_CLIENT_DISP_Y].height =3D stripe->dsc[OPE_DS_Y_DISP].o= utput_height; + stripe->dst[OPE_WR_CLIENT_DISP_Y].stride =3D bpl; + stripe->dst[OPE_WR_CLIENT_DISP_Y].format =3D OPE_PACKER_FMT_PLAIN_8; + + /* UV plane (appended to Y plane)*/ + c_stride =3D bpl * 2 / fo->fmt->c_hsub; + base =3D dst + bpl * img_h + (compose->top / fo->fmt->c_vsub) * c_stride; + + if (prev) + x_init =3D prev->dst[OPE_WR_CLIENT_DISP_C].x_init + + prev->dst[OPE_WR_CLIENT_DISP_C].width; + else + x_init =3D compose->left * 2 / fo->fmt->c_hsub; + + stripe->dst[OPE_WR_CLIENT_DISP_C].addr =3D base; + stripe->dst[OPE_WR_CLIENT_DISP_C].x_init =3D x_init; + stripe->dst[OPE_WR_CLIENT_DISP_C].format =3D hw ? hw->packer : OPE_PACKER= _FMT_PLAIN_8; + stripe->dst[OPE_WR_CLIENT_DISP_C].width =3D stripe->dsc[OPE_DS_C_DISP].o= utput_width * 2; + stripe->dst[OPE_WR_CLIENT_DISP_C].height =3D stripe->dsc[OPE_DS_C_DISP].o= utput_height; + + switch (fo->fmt->fourcc) { + case V4L2_PIX_FMT_GREY: + stripe->dst[OPE_WR_CLIENT_DISP_C].enabled =3D false; + break; + default: + stripe->dst[OPE_WR_CLIENT_DISP_C].stride =3D c_stride; + break; + } +} + +static void ope_gen_stripes(struct ope_ctx *ctx, dma_addr_t src, dma_addr_= t dst) +{ + const struct ope_fmt_state *fi =3D &ctx->fmt_in; + const struct ope_hw_fmt *src_hw =3D ope_find_hw_fmt(fi->fmt->fourcc); + unsigned int num_stripes, width, x_out, x_out_c, i; + const struct ope_fmt_state *fo =3D &ctx->fmt_out; + unsigned int c_hsub =3D fo->fmt->c_hsub; + struct v4l2_subdev_state *sd_state; + const struct v4l2_rect *compose; + const struct v4l2_rect *crop; + struct v4l2_subdev *proc_sd; + unsigned int h_scale, v_scale; + + proc_sd =3D camss_isp_pipeline_get_subdev(ctx->ope->pipeline, OPE_ENTITY_= PROC); + sd_state =3D v4l2_subdev_lock_and_get_active_state(proc_sd); + crop =3D v4l2_subdev_state_get_crop(sd_state, OPE_PROC_PAD_SINK_IN); + compose =3D v4l2_subdev_state_get_compose(sd_state, OPE_PROC_PAD_SOURCE); + + /* Advance source pointer to the crop origin */ + src +=3D crop->top * fi->bytesperline + crop->left * fi->fmt->depth / 8; + width =3D crop->width; + num_stripes =3D DIV_ROUND_UP(crop->width, OPE_STRIPE_MAX_W); + h_scale =3D ope_calc_h_scale(crop->width, compose->width, num_stripes, c= _hsub, + max(2u, 1u << fi->fmt->align)); + v_scale =3D ope_calc_v_scale(crop->height, compose->height); + + x_out =3D x_out_c =3D 0; + + for (i =3D 0; i < num_stripes; i++) { + struct ope_stripe *stripe =3D &ctx->stripe[i]; + + memset(stripe, 0, sizeof(*stripe)); + + stripe->src.addr =3D src; + stripe->src.width =3D width; + stripe->src.height =3D crop->height; + stripe->src.stride =3D fi->bytesperline; + stripe->src.location =3D ope_stripe_location(i, num_stripes); + stripe->src.pattern =3D src_hw ? src_hw->pattern : 0; + stripe->src.format =3D src_hw ? src_hw->unpacker : 0; + + /* Ensure last stripe is wide enough */ + if (width > OPE_STRIPE_MAX_W && width < OPE_STRIPE_MAX_W + OPE_STRIPE_MI= N_W) + stripe->src.width -=3D OPE_STRIPE_MIN_W * 2; + + v4l_bound_align_image(&stripe->src.width, + OPE_STRIPE_MIN_W, OPE_STRIPE_MAX_W, + fi->fmt->align, + &stripe->src.height, + OPE_STRIPE_MIN_H, OPE_STRIPE_MAX_H, + OPE_ALIGN_H, 0); + + width -=3D stripe->src.width; + src +=3D stripe->src.width * fi->fmt->depth / 8; + + /* Last-stripe adjustment: compute the minimum input width that + * delivers exactly the remaining output pixels for Y and C. + */ + if (ope_stripe_is_last(stripe)) { + unsigned int rem_y =3D compose->width - x_out; + unsigned int rem_c =3D compose->width / c_hsub - x_out_c; + unsigned int s_y =3D DIV_ROUND_UP((u64)rem_y * h_scale, Q21(1)); + unsigned int s_c =3D DIV_ROUND_UP((u64)rem_c * h_scale, Q21(1)) * c_hsu= b; + unsigned int s =3D ALIGN(max(s_y, s_c), max(2u, 1u << fi->fmt->align)); + + stripe->src.width =3D clamp(s, OPE_STRIPE_MIN_W, stripe->src.width); + } + + ope_gen_stripe_chroma_dsc(ctx, stripe); + ope_gen_stripe_dsc(ctx, stripe, h_scale, v_scale); + ope_gen_stripe_yuv_dst(ctx, stripe, dst, compose); + + x_out +=3D stripe->dsc[OPE_DS_Y_DISP].output_width; + x_out_c +=3D stripe->dsc[OPE_DS_C_DISP].output_width; + + /* Width in bytes for the fetch engine */ + stripe->src.width =3D stripe->src.width * fi->fmt->depth / 8; + } + + v4l2_subdev_unlock_state(sd_state); +} + +/* -------- Pipeline IQ module programming -------- */ + +static bool ope_module_update(struct ope_ctx *ctx, u32 module_cfg_reg, u32= enable_mask, + struct v4l2_isp_params_block_header *hdr, bool force) +{ + bool enable =3D !(hdr->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE); + bool dirty =3D hdr->flags & CAMSS_ISP_PARAMS_FL_BLOCK_DIRTY; + + /* skip if neither forced (new context) nor dirty (updated params) */ + if (!force && !dirty) + return false; + + hdr->flags &=3D ~CAMSS_ISP_PARAMS_FL_BLOCK_DIRTY; + + ope_write_pp(ctx->ope, module_cfg_reg, enable ? enable_mask : 0); + + /* If module is disabled, not need to update content */ + if (!enable) + return false; + + dev_dbg(ctx->ope->dev, "IQ module (%u) update (flags:0x%x)\n", hdr->type,= hdr->flags); + + return true; +} + +static void ope_prog_wb(struct ope_ctx *ctx, bool force) +{ + struct camss_params_wb_gain *wb =3D &ctx->config.wb_gain; + struct ope_dev *ope =3D ctx->ope; + + if (!ope_module_update(ctx, OPE_PP_CLC_WB_GAIN_MODULE_CFG, + OPE_PP_CLC_WB_GAIN_MODULE_CFG_EN, + &wb->header, force)) + return; + + ope_write_pp(ope, OPE_PP_CLC_WB_GAIN_WB_CFG(0), + FIELD_PREP(OPE_PP_CLC_WB_GAIN_WB_CFG_GAIN, wb->g_gain)); + ope_write_pp(ope, OPE_PP_CLC_WB_GAIN_WB_CFG(1), + FIELD_PREP(OPE_PP_CLC_WB_GAIN_WB_CFG_GAIN, wb->b_gain)); + ope_write_pp(ope, OPE_PP_CLC_WB_GAIN_WB_CFG(2), + FIELD_PREP(OPE_PP_CLC_WB_GAIN_WB_CFG_GAIN, wb->r_gain)); + ope_write_pp(ope, OPE_PP_CLC_WB_GAIN_WB_SUB_CFG(0), + FIELD_PREP(OPE_PP_CLC_WB_GAIN_WB_SUB_CFG_VAL, wb->g_sub)); + ope_write_pp(ope, OPE_PP_CLC_WB_GAIN_WB_SUB_CFG(1), + FIELD_PREP(OPE_PP_CLC_WB_GAIN_WB_SUB_CFG_VAL, wb->b_sub)); + ope_write_pp(ope, OPE_PP_CLC_WB_GAIN_WB_SUB_CFG(2), + FIELD_PREP(OPE_PP_CLC_WB_GAIN_WB_SUB_CFG_VAL, wb->r_sub)); + ope_write_pp(ope, OPE_PP_CLC_WB_GAIN_WB_ADD_CFG(0), + FIELD_PREP(OPE_PP_CLC_WB_GAIN_WB_ADD_CFG_VAL, wb->g_add)); + ope_write_pp(ope, OPE_PP_CLC_WB_GAIN_WB_ADD_CFG(1), + FIELD_PREP(OPE_PP_CLC_WB_GAIN_WB_ADD_CFG_VAL, wb->b_add)); + ope_write_pp(ope, OPE_PP_CLC_WB_GAIN_WB_ADD_CFG(2), + FIELD_PREP(OPE_PP_CLC_WB_GAIN_WB_ADD_CFG_VAL, wb->r_add)); +} + +static void ope_prog_bayer2rgb(struct ope_ctx *ctx, bool force) +{ + struct ope_params_demo *demo =3D &ctx->config.demo; + struct ope_dev *ope =3D ctx->ope; + + if (!ope_module_update(ctx, OPE_PP_CLC_DEMO_MODULE_CFG, + OPE_PP_CLC_DEMO_MODULE_CFG_EN | + OPE_PP_CLC_DEMO_MODULE_CFG_DYN_G_CLAMP_EN, + &demo->header, force)) + return; + + ope_write_pp(ope, OPE_PP_CLC_DEMO_INTERP_COEFF_CFG, + FIELD_PREP(OPE_PP_CLC_DEMO_INTERP_COEFF_CFG_LAMBDA_G, demo->lambda= _g) | + FIELD_PREP(OPE_PP_CLC_DEMO_INTERP_COEFF_CFG_LAMBDA_RB, demo->lambda= _rb)); + ope_write_pp(ope, OPE_PP_CLC_DEMO_INTERP_CLASSIFIER_CFG_0, + FIELD_PREP(OPE_PP_CLC_DEMO_INTERP_CLASSIFIER_CFG_0_AK, demo->a_k)); + ope_write_pp(ope, OPE_PP_CLC_DEMO_INTERP_CLASSIFIER_CFG_1, + FIELD_PREP(OPE_PP_CLC_DEMO_INTERP_CLASSIFIER_CFG_1_WK, demo->w_k)); +} + +static void ope_prog_rgb2yuv(struct ope_ctx *ctx, bool force) +{ + struct camss_params_chroma_enhan *cc =3D &ctx->config.chroma_enhan; + struct ope_dev *ope =3D ctx->ope; + + if (!ope_module_update(ctx, OPE_PP_CLC_CHROMA_ENHAN_MODULE_CFG, + OPE_PP_CLC_CHROMA_ENHAN_MODULE_CFG_EN, + &cc->header, force)) + return; + + ope_write_pp(ope, OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_0, + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_0_V0, cc->luma_v0) | + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_0_V1, cc->luma_v1)); + ope_write_pp(ope, OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_1, + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_1_K, cc->luma_k)); + ope_write_pp(ope, OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_2, + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_LUMA_CFG_2_V2, cc->luma_v2)); + ope_write_pp(ope, OPE_PP_CLC_CHROMA_ENHAN_COEFF_A_CFG, + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_COEFF_A_CFG_AP, cc->coeff_ap) | + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_COEFF_A_CFG_AM, cc->coeff_am)); + ope_write_pp(ope, OPE_PP_CLC_CHROMA_ENHAN_COEFF_B_CFG, + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_COEFF_B_CFG_BP, cc->coeff_bp) | + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_COEFF_B_CFG_BM, cc->coeff_bm)); + ope_write_pp(ope, OPE_PP_CLC_CHROMA_ENHAN_COEFF_C_CFG, + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_COEFF_C_CFG_CP, cc->coeff_cp) | + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_COEFF_C_CFG_CM, cc->coeff_cm)); + ope_write_pp(ope, OPE_PP_CLC_CHROMA_ENHAN_COEFF_D_CFG, + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_COEFF_D_CFG_DP, cc->coeff_dp) | + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_COEFF_D_CFG_DM, cc->coeff_dm)); + ope_write_pp(ope, OPE_PP_CLC_CHROMA_ENHAN_CHROMA_CFG_0, + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_CHROMA_CFG_0_KCB, cc->kcb)); + ope_write_pp(ope, OPE_PP_CLC_CHROMA_ENHAN_CHROMA_CFG_1, + FIELD_PREP(OPE_PP_CLC_CHROMA_ENHAN_CHROMA_CFG_1_KCR, cc->kcr)); +} + +static void ope_prog_color_correct(struct ope_ctx *ctx, bool force) +{ + struct camss_params_color_correct *cc =3D &ctx->config.color_correct; + struct ope_dev *ope =3D ctx->ope; + + if (!ope_module_update(ctx, OPE_PP_CLC_CC_MODULE_CFG, + OPE_PP_CLC_CC_MODULE_CFG_EN, + &cc->header, force)) + return; + + ope_write_pp(ope, OPE_PP_CLC_CC_COEFF_A_CFG_0, + FIELD_PREP(OPE_PP_CLC_CC_COEFF_A_CFG_0_A0, cc->a[0]) | + FIELD_PREP(OPE_PP_CLC_CC_COEFF_A_CFG_0_A1, cc->a[1])); + ope_write_pp(ope, OPE_PP_CLC_CC_COEFF_A_CFG_1, + FIELD_PREP(OPE_PP_CLC_CC_COEFF_A_CFG_1_A2, cc->a[2])); + ope_write_pp(ope, OPE_PP_CLC_CC_COEFF_B_CFG_0, + FIELD_PREP(OPE_PP_CLC_CC_COEFF_B_CFG_0_B0, cc->b[0]) | + FIELD_PREP(OPE_PP_CLC_CC_COEFF_B_CFG_0_B1, cc->b[1])); + ope_write_pp(ope, OPE_PP_CLC_CC_COEFF_B_CFG_1, + FIELD_PREP(OPE_PP_CLC_CC_COEFF_B_CFG_1_B2, cc->b[2])); + ope_write_pp(ope, OPE_PP_CLC_CC_COEFF_C_CFG_0, + FIELD_PREP(OPE_PP_CLC_CC_COEFF_C_CFG_0_C0, cc->c[0]) | + FIELD_PREP(OPE_PP_CLC_CC_COEFF_C_CFG_0_C1, cc->c[1])); + ope_write_pp(ope, OPE_PP_CLC_CC_COEFF_C_CFG_1, + FIELD_PREP(OPE_PP_CLC_CC_COEFF_C_CFG_1_C2, cc->c[2])); + ope_write_pp(ope, OPE_PP_CLC_CC_COEFF_K_CFG_0, + FIELD_PREP(OPE_PP_CLC_CC_COEFF_K_CFG_0_K0, cc->k[0])); + ope_write_pp(ope, OPE_PP_CLC_CC_COEFF_K_CFG_1, + FIELD_PREP(OPE_PP_CLC_CC_COEFF_K_CFG_1_K1, cc->k[1])); + ope_write_pp(ope, OPE_PP_CLC_CC_COEFF_K_CFG_2, + FIELD_PREP(OPE_PP_CLC_CC_COEFF_K_CFG_2_K2, cc->k[2])); + ope_write_pp(ope, OPE_PP_CLC_CC_COEFF_M_CFG, + FIELD_PREP(OPE_PP_CLC_CC_COEFF_M_CFG_M, cc->qfactor)); +} + +static void ope_prog_crop_rnd_clamp(struct ope_dev *ope, const struct ope_= stripe *stripe) +{ + static const u32 crop_bases[] =3D { + OPE_PP_CLC_CROP_RND_CLAMP_Y_DISP_BASE, + OPE_PP_CLC_CROP_RND_CLAMP_C_DISP_BASE, + }; + static const enum ope_downscaler ds_idx[] =3D { + OPE_DS_Y_DISP, + OPE_DS_C_DISP, + }; + + for (unsigned int i =3D 0; i < ARRAY_SIZE(crop_bases); i++) { + const struct ope_dsc_config *dsc =3D &stripe->dsc[ds_idx[i]]; + u32 cbase =3D crop_bases[i]; + + if (!dsc->output_width || !dsc->output_height) { + ope_write_pp(ope, OPE_PP_CLC_CROP_RND_CLAMP_MODULE_CFG(cbase), 0); + continue; + } + + ope_write_pp(ope, OPE_PP_CLC_CROP_RND_CLAMP_CROP_PIXEL_CFG(cbase), + FIELD_PREP(OPE_PP_CLC_CROP_RND_CLAMP_CROP_FIRST, 0) | + FIELD_PREP(OPE_PP_CLC_CROP_RND_CLAMP_CROP_LAST, + dsc->crop_last_pixel)); + ope_write_pp(ope, OPE_PP_CLC_CROP_RND_CLAMP_CROP_LINE_CFG(cbase), + FIELD_PREP(OPE_PP_CLC_CROP_RND_CLAMP_CROP_FIRST, 0) | + FIELD_PREP(OPE_PP_CLC_CROP_RND_CLAMP_CROP_LAST, dsc->crop_last_lin= e)); + ope_write_pp(ope, OPE_PP_CLC_CROP_RND_CLAMP_MODULE_CFG(cbase), + OPE_PP_CLC_CROP_RND_CLAMP_EN | OPE_PP_CLC_CROP_RND_CLAMP_CROP_EN); + } +} + +static void ope_prog_stripe(struct ope_ctx *ctx, struct ope_stripe *stripe) +{ + struct ope_dev *ope =3D ctx->ope; + + dev_dbg(ope->dev, "ctx=3D%p programming stripe %u\n", + ctx, (unsigned int)(stripe - ctx->stripe)); + + /* Fetch Engine */ + ope_write_rd(ope, OPE_BUS_RD_CLIENT_0_UNPACK_CFG_0, stripe->src.format); + ope_write_rd(ope, OPE_BUS_RD_CLIENT_0_RD_BUFFER_SIZE, + (stripe->src.width << 16) | stripe->src.height); + ope_write_rd(ope, OPE_BUS_RD_CLIENT_0_ADDR_IMAGE, stripe->src.addr); + ope_write_rd(ope, OPE_BUS_RD_CLIENT_0_RD_STRIDE, stripe->src.stride); + ope_write_rd(ope, OPE_BUS_RD_CLIENT_0_CCIF_META_DATA, + FIELD_PREP(OPE_BUS_RD_CLIENT_0_CCIF_MD_PIX_PATTERN, + stripe->src.pattern)); + ope_write_rd(ope, OPE_BUS_RD_CLIENT_0_CORE_CFG, + OPE_BUS_RD_CLIENT_0_CORE_CFG_EN); + + /* Write Engines */ + for (int i =3D 0; i < OPE_WR_CLIENT_MAX; i++) { + if (!stripe->dst[i].enabled) { + ope_write_wr(ope, OPE_BUS_WR_CLIENT_CFG(i), 0); + continue; + } + + ope_write_wr(ope, OPE_BUS_WR_CLIENT_ADDR_IMAGE(i), + stripe->dst[i].addr); + ope_write_wr(ope, OPE_BUS_WR_CLIENT_IMAGE_CFG_0(i), + (stripe->dst[i].height << 16) | stripe->dst[i].width); + ope_write_wr(ope, OPE_BUS_WR_CLIENT_IMAGE_CFG_1(i), + stripe->dst[i].x_init); + ope_write_wr(ope, OPE_BUS_WR_CLIENT_IMAGE_CFG_2(i), + stripe->dst[i].stride); + ope_write_wr(ope, OPE_BUS_WR_CLIENT_PACKER_CFG(i), + stripe->dst[i].format); + ope_write_wr(ope, OPE_BUS_WR_CLIENT_CFG(i), + OPE_BUS_WR_CLIENT_CFG_EN | + OPE_BUS_WR_CLIENT_CFG_AUTORECOVER); + } + + /* Downscalers */ + for (int i =3D 0; i < OPE_DS_MAX; i++) { + struct ope_dsc_config *dsc =3D &stripe->dsc[i]; + u32 base =3D ope_ds_base[i]; + u32 cfg =3D 0; + + if (dsc->input_width !=3D dsc->output_width) { + dsc->phase_step_h |=3D + DS_RESOLUTION(dsc->input_width, + dsc->output_width) << 30; + cfg |=3D OPE_PP_CLC_DOWNSCALE_MN_DS_CFG_H_SCALE_EN; + } + if (dsc->input_height !=3D dsc->output_height) { + dsc->phase_step_v |=3D + DS_RESOLUTION(dsc->input_height, + dsc->output_height) << 30; + cfg |=3D OPE_PP_CLC_DOWNSCALE_MN_DS_CFG_V_SCALE_EN; + } + + ope_write_pp(ope, OPE_PP_CLC_DOWNSCALE_MN_DS_CFG(base), cfg); + ope_write_pp(ope, OPE_PP_CLC_DOWNSCALE_MN_DS_IMAGE_SIZE_CFG(base), + ((dsc->input_width - 1) << 16) | (dsc->input_height - 1)); + ope_write_pp(ope, OPE_PP_CLC_DOWNSCALE_MN_DS_MN_H_CFG(base), + dsc->phase_step_h); + ope_write_pp(ope, OPE_PP_CLC_DOWNSCALE_MN_DS_MN_V_CFG(base), + dsc->phase_step_v); + ope_write_pp(ope, OPE_PP_CLC_DOWNSCALE_MN_CFG(base), + cfg ? OPE_PP_CLC_DOWNSCALE_MN_CFG_EN : 0); + + dev_dbg(ope->dev, "DS[%d] cfg=3D0x%x in=3D%ux%u out=3D%ux%u\n", + i, cfg, dsc->input_width, dsc->input_height, + dsc->output_width, dsc->output_height); + } + + ope_prog_crop_rnd_clamp(ope, stripe); +} + +static void ope_params_apply_wb(void *priv, const union camss_isp_params_b= lock *block) +{ + struct ope_ctx *ctx =3D priv; + + ctx->config.wb_gain =3D block->wb_gain; + ctx->config.wb_gain.g_sub =3D block->wb_gain.g_sub >> 4; + ctx->config.wb_gain.b_sub =3D block->wb_gain.b_sub >> 4; + ctx->config.wb_gain.r_sub =3D block->wb_gain.r_sub >> 4; + ctx->config.wb_gain.g_add =3D block->wb_gain.g_add >> 4; + ctx->config.wb_gain.b_add =3D block->wb_gain.b_add >> 4; + ctx->config.wb_gain.r_add =3D block->wb_gain.r_add >> 4; + ctx->config.wb_gain.header.flags |=3D CAMSS_ISP_PARAMS_FL_BLOCK_DIRTY; +} + +static void ope_params_apply_chroma_enhan(void *priv, const union camss_is= p_params_block *block) +{ + struct ope_ctx *ctx =3D priv; + + ctx->config.chroma_enhan =3D block->chroma_enhan; + ctx->config.chroma_enhan.header.flags |=3D CAMSS_ISP_PARAMS_FL_BLOCK_DIRT= Y; +} + +static void ope_params_apply_color_correct(void *priv, const union camss_i= sp_params_block *block) +{ + struct ope_ctx *ctx =3D priv; + + ctx->config.color_correct =3D block->color_correct; + ctx->config.color_correct.header.flags |=3D CAMSS_ISP_PARAMS_FL_BLOCK_DIR= TY; +} + +static const struct v4l2_isp_params_block_type_info ope_params_type_info[]= =3D { + [CAMSS_PARAMS_WB_GAIN] =3D { sizeof(struct camss_params_wb_gain) }, + [CAMSS_PARAMS_CHROMA_ENHAN] =3D { sizeof(struct camss_params_chroma_enhan= ) }, + [CAMSS_PARAMS_COLOR_CORRECT] =3D { sizeof(struct camss_params_color_corre= ct) }, +}; + +static const camss_isp_params_handler_fn ope_params_handlers[] =3D { + [CAMSS_PARAMS_WB_GAIN] =3D ope_params_apply_wb, + [CAMSS_PARAMS_CHROMA_ENHAN] =3D ope_params_apply_chroma_enhan, + [CAMSS_PARAMS_COLOR_CORRECT] =3D ope_params_apply_color_correct, +}; + +static struct vb2_v4l2_buffer *ope_queue_remove(struct ope_ctx *ctx, + unsigned int idx) +{ + struct ope_queue *q =3D &ctx->queues[idx]; + struct camss_isp_buf *buf; + + scoped_guard(spinlock_irqsave, &q->rdy_spinlock) { + buf =3D list_first_entry_or_null(&q->rdy_queue, struct camss_isp_buf, li= st); + if (buf) + list_del(&buf->list); + } + + return buf ? &buf->vb : NULL; +} + +static struct vb2_v4l2_buffer *ope_queue_peek(struct ope_ctx *ctx, + unsigned int idx) +{ + struct ope_queue *q =3D &ctx->queues[idx]; + struct camss_isp_buf *buf; + + scoped_guard(spinlock_irqsave, &q->rdy_spinlock) + buf =3D list_first_entry_or_null(&q->rdy_queue, struct camss_isp_buf, li= st); + + return buf ? &buf->vb : NULL; +} + +static void ope_queue_drain(struct ope_ctx *ctx, unsigned int idx, + enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *vbuf; + + while ((vbuf =3D ope_queue_remove(ctx, idx))) + vb2_buffer_done(&vbuf->vb2_buf, state); +} + +static void ope_apply_params(struct ope_ctx *ctx) +{ + struct vb2_v4l2_buffer *vbuf; + + vbuf =3D ope_queue_peek(ctx, OPE_QUEUE_PARAMS); + if (!vbuf) + return; + + camss_isp_params_apply(ctx->ope->dev, &vbuf->vb2_buf, + ope_params_type_info, + ope_params_handlers, + ARRAY_SIZE(ope_params_handlers), + ctx); +} + +/* Default/initial tuning parameters */ +static const struct ope_config ope_default_config =3D { + .wb_gain =3D { + .header.type =3D CAMSS_PARAMS_WB_GAIN, + .header.flags =3D V4L2_ISP_PARAMS_FL_BLOCK_ENABLE | + CAMSS_ISP_PARAMS_FL_BLOCK_DIRTY, + .g_gain =3D OPE_WB(1, 1), + .b_gain =3D OPE_WB(3, 2), + .r_gain =3D OPE_WB(3, 2), + }, + .demo =3D { + .header.type =3D CAMSS_PARAMS_INTERNAL, + .header.flags =3D V4L2_ISP_PARAMS_FL_BLOCK_ENABLE | + CAMSS_ISP_PARAMS_FL_BLOCK_DIRTY, + .lambda_rb =3D 0, + .lambda_g =3D 128, + .a_k =3D 128, + .w_k =3D 102, + }, + .chroma_enhan =3D { /* RGB -> YUV values from BT.601/Full-Range */ + .header.type =3D CAMSS_PARAMS_CHROMA_ENHAN, + .header.flags =3D V4L2_ISP_PARAMS_FL_BLOCK_ENABLE | + CAMSS_ISP_PARAMS_FL_BLOCK_DIRTY, + .luma_v0 =3D 0x04d, /* 0.299 */ + .luma_v1 =3D 0x096, /* 0.587 */ + .luma_v2 =3D 0x01d, /* 0.114 */ + .luma_k =3D 0, + .coeff_ap =3D 0x077, /* 0.464 */ + .coeff_am =3D 0x070, /* 0.438 */ + .coeff_bp =3D 0xfac, /* -0.329 */ + .coeff_bm =3D 0xfc4, /* -0.234 */ + .coeff_cp =3D 0x086, /* 0.524 */ + .coeff_cm =3D 0x089, /* 0.535 */ + .coeff_dp =3D 0xfff, /* -0.004 */ + .coeff_dm =3D 0xfdf, /* -0.130 */ + .kcb =3D 128, + .kcr =3D 128, + }, + .color_correct =3D { + .header.type =3D CAMSS_PARAMS_COLOR_CORRECT, + .header.flags =3D V4L2_ISP_PARAMS_FL_BLOCK_DISABLE | + CAMSS_ISP_PARAMS_FL_BLOCK_DIRTY, + } +}; + +static void ope_try_schedule(struct ope_ctx *ctx); + +/* -------- Job scheduling -------- */ +static bool ope_job_ready(void *priv) +{ + struct ope_ctx *ctx =3D priv; + + return ctx->started && + !list_empty(&ctx->queues[OPE_QUEUE_FRAME_IN].rdy_queue) && + !list_empty(&ctx->queues[OPE_QUEUE_DISP_OUT].rdy_queue); +} + +static void ope_job_finish(struct ope_ctx *ctx, enum vb2_buffer_state stat= e) +{ + struct vb2_v4l2_buffer *src, *dst, *params; + struct ope_dev *ope =3D ctx->ope; + + src =3D ope_queue_remove(ctx, OPE_QUEUE_FRAME_IN); + dst =3D ope_queue_remove(ctx, OPE_QUEUE_DISP_OUT); + params =3D ope_queue_remove(ctx, OPE_QUEUE_PARAMS); + + if (dst) { + dst->sequence =3D ctx->fmt_in.sequence++; + if (src) + dst->vb2_buf.timestamp =3D src->vb2_buf.timestamp; + } + + if (dst) + vb2_buffer_done(&dst->vb2_buf, state); + if (src) + vb2_buffer_done(&src->vb2_buf, state); + if (params) + vb2_buffer_done(¶ms->vb2_buf, state); + + scoped_guard(spinlock_irqsave, &ope->sched_lock) { + ope->hw_ctx =3D NULL; + ope->curr_ctx =3D NULL; + ctx->queued =3D false; + } + + dev_dbg(ctx->ope->dev, "done ctx=3D%p state=3D%u\n", ctx, state); + + if (state =3D=3D VB2_BUF_STATE_DONE) + ope_try_schedule(ctx); +} + +static dma_addr_t ope_buf_dma_addr(struct ope_ctx *ctx, unsigned int queue= _idx) +{ + struct vb2_v4l2_buffer *vbuf =3D ope_queue_peek(ctx, queue_idx); + + if (!vbuf) + return 0; + + return vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); +} + +static void ope_run_job(void *priv, bool ctx_changed) +{ + struct ope_ctx *ctx =3D priv; + struct ope_dev *ope =3D ctx->ope; + dma_addr_t src, dst; + + src =3D ope_buf_dma_addr(ctx, OPE_QUEUE_FRAME_IN); + dst =3D ope_buf_dma_addr(ctx, OPE_QUEUE_DISP_OUT); + + if (!src || !dst) { + dev_warn(ope->dev, "Job cannot run, missing buffer\n"); + ope_job_finish(ctx, VB2_BUF_STATE_ERROR); + return; + } + + dev_dbg(ope->dev, "start ctx=3D%p->%p src=3D%pad dst=3D%pad\n", + ope->hw_ctx, ctx, &src, &dst); + + if (ctx->first_start) { + ctx_changed =3D true; + ctx->first_start =3D false; + } + + ope_apply_params(ctx); + ope_gen_stripes(ctx, src, dst); + + ope_prog_wb(ctx, ctx_changed); + ope_prog_bayer2rgb(ctx, ctx_changed); + ope_prog_color_correct(ctx, ctx_changed); + ope_prog_rgb2yuv(ctx, ctx_changed); + + ctx->current_stripe =3D 0; + ope->hw_ctx =3D ctx; + ope_prog_stripe(ctx, &ctx->stripe[0]); + + ope_start(ope); +} + + +static void ope_sched_cancel(struct ope_ctx *ctx) +{ + struct ope_dev *ope =3D ctx->ope; + bool running; + + cancel_work_sync(&ope->run_work); + + scoped_guard(spinlock_irqsave, &ope->sched_lock) { + running =3D (ope->curr_ctx =3D=3D ctx); + if (!running) + ctx->queued =3D false; + } + + if (running) { + reinit_completion(&ope->reset_complete); + ope_write(ope, OPE_TOP_RESET_CMD, OPE_TOP_RESET_CMD_SW); + wait_for_completion(&ope->reset_complete); + } +} + +static void ope_run_work(struct work_struct *work) +{ + struct ope_dev *ope =3D container_of(work, struct ope_dev, run_work); + struct ope_ctx *ctx; + bool ctx_changed; + + scoped_guard(spinlock_irqsave, &ope->sched_lock) { + ctx =3D ope->shared_ctx; + if (!ctx || !ctx->queued || ope->curr_ctx) + return; + if (!ope_job_ready(ctx)) + return; + ctx_changed =3D (ope->prev_ctx !=3D ctx); + ope->prev_ctx =3D ctx; + ope->curr_ctx =3D ctx; + } + + ope_run_job(ctx, ctx_changed); +} + +static void ope_try_schedule(struct ope_ctx *ctx) +{ + struct ope_dev *ope =3D ctx->ope; + + if (!ope_job_ready(ctx)) + return; + + scoped_guard(spinlock_irqsave, &ope->sched_lock) { + if (ctx->queued || ope->curr_ctx) + return; + ctx->queued =3D true; + } + + schedule_work(&ope->run_work); +} + +/* -------- Interrupt handlers -------- */ + +static void ope_fe_irq(struct ope_dev *ope) +{ + u32 status =3D readl_relaxed(ope->base_rd + OPE_BUS_RD_INPUT_IF_IRQ_STATU= S); + + writel_relaxed(status, ope->base_rd + OPE_BUS_RD_INPUT_IF_IRQ_CLEAR); + writel_relaxed(OPE_BUS_RD_INPUT_IF_IRQ_CMD_CLEAR, + ope->base_rd + OPE_BUS_RD_INPUT_IF_IRQ_CMD); +} + +static void ope_we_irq(struct ope_dev *ope, struct ope_ctx *ctx) +{ + u32 status; + + status =3D ope_read_wr(ope, OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0); + ope_write_wr(ope, OPE_BUS_WR_INPUT_IF_IRQ_CLEAR_0, status); + ope_write_wr(ope, OPE_BUS_WR_INPUT_IF_IRQ_CMD, OPE_BUS_WR_INPUT_IF_IRQ_CM= D_CLEAR); + + if (!ctx) + return; + + if (status & OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_CONS_VIOL) { + u32 viol =3D ope_read_wr(ope, OPE_BUS_WR_VIOLATION_STATUS); + + dev_err_ratelimited(ope->dev, "constraint violation (clients=3D0x%x)\n",= viol); + ope_write(ctx->ope, OPE_TOP_RESET_CMD, OPE_TOP_RESET_CMD_SW); + } + + if (status & OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_IMG_SZ_VIOL) { + u32 viol =3D ope_read_wr(ope, OPE_BUS_WR_IMAGE_SIZE_VIOLATION_STATUS); + + for (int i =3D 0; i < OPE_WR_CLIENT_MAX; i++) { + if (BIT(i) & viol) + dev_err_ratelimited(ope->dev, "WE%d: image size violation\n", i); + } + ope_write(ctx->ope, OPE_TOP_RESET_CMD, OPE_TOP_RESET_CMD_SW); + } + + if (status & OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_VIOL) { + u32 viol =3D ope_read_wr(ope, OPE_BUS_WR_VIOLATION_STATUS); + + dev_err_ratelimited(ope->dev, "fatal violation (status=3D0x%08x)\n", vio= l); + ope_write(ctx->ope, OPE_TOP_RESET_CMD, OPE_TOP_RESET_CMD_SW); + } + + if (status & OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_RUP_DONE) { + /* Register update done: program next stripe (double-buffered) */ + struct ope_stripe *stripe =3D ope_current_stripe(ctx); + + if (stripe && !ope_stripe_is_last(stripe)) + ope_prog_stripe(ctx, stripe + 1); + } +} + + +static void __ope_irq_init(struct ope_dev *ope) +{ + ope_write(ope, OPE_TOP_IRQ_MASK, + OPE_TOP_IRQ_STATUS_RST_DONE | + OPE_TOP_IRQ_STATUS_WE | + OPE_TOP_IRQ_STATUS_VIOL | + OPE_TOP_IRQ_STATUS_IDLE); + ope_write_wr(ope, OPE_BUS_WR_INPUT_IF_IRQ_MASK_0, + OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_RUP_DONE | + OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_CONS_VIOL | + OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_VIOL | + OPE_BUS_WR_INPUT_IF_IRQ_STATUS_0_IMG_SZ_VIOL); +} + +static irqreturn_t ope_irq(int irq, void *dev_id) +{ + struct ope_dev *ope =3D dev_id; + struct ope_ctx *ctx; + u32 status; + + /* + * hw_ctx is safe to read here without a lock: ope_run_job() sets it + * before calling ope_start(), so the hardware cannot raise an IRQ + * before hw_ctx is visible. ope_job_finish() clears it in IRQ context + * here, so the clear is serialised with this read by the IRQ + * itself. + */ + ctx =3D ope->hw_ctx; + + status =3D ope_read(ope, OPE_TOP_IRQ_STATUS); + ope_write(ope, OPE_TOP_IRQ_CLEAR, status); + ope_write(ope, OPE_TOP_IRQ_CMD, OPE_TOP_IRQ_CMD_CLEAR); + + if (status & OPE_TOP_IRQ_STATUS_RST_DONE) { + dev_dbg(ope->dev, "reset done ctx=3D%p\n", ctx); + if (ctx) + ope_job_finish(ctx, VB2_BUF_STATE_ERROR); + complete(&ope->reset_complete); + } + + if (status & OPE_TOP_IRQ_STATUS_VIOL) + dev_warn(ope->dev, "OPE violation: 0x%08x\n", + ope_read(ope, OPE_TOP_VIOLATION_STATUS)); + + if (status & OPE_TOP_IRQ_STATUS_FE) + ope_fe_irq(ope); + + if (status & OPE_TOP_IRQ_STATUS_WE) + ope_we_irq(ope, ctx); + + if ((status & OPE_TOP_IRQ_STATUS_IDLE) && ctx) { + struct ope_stripe *stripe =3D ope_current_stripe(ctx); + + dev_dbg(ope->dev, "stripe %u done ctx=3D%p\n", ctx->current_stripe, ctx); + + if (ope_stripe_is_last(stripe)) { + ctx->current_stripe =3D 0; + ope_job_finish(ctx, VB2_BUF_STATE_DONE); + } else { + ctx->current_stripe++; + ope_start(ope); + } + } + + return IRQ_HANDLED; +} + +/* -------- vb2 queue private data -------- */ + +struct ope_vq_priv { + struct ope_ctx *ctx; + unsigned int queue_idx; + struct video_device *vdev; +}; + +static inline struct ope_ctx *ope_ctx_from_vq(struct vb2_queue *q) +{ + return ((struct ope_vq_priv *)vb2_get_drv_priv(q))->ctx; +} + +static inline unsigned int ope_idx_from_vq(struct vb2_queue *q) +{ + return ((struct ope_vq_priv *)vb2_get_drv_priv(q))->queue_idx; +} + +static inline struct video_device *ope_vdev_from_vq(struct vb2_queue *q) +{ + return ((struct ope_vq_priv *)vb2_get_drv_priv(q))->vdev; +} + +/* -------- vb2 ops -------- */ + +static int ope_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct ope_ctx *ctx =3D ope_ctx_from_vq(q); + unsigned int idx =3D ope_idx_from_vq(q); + unsigned int size; + + if (idx =3D=3D OPE_QUEUE_FRAME_IN) + size =3D ctx->fmt_in.sizeimage ? ctx->fmt_in.sizeimage : PAGE_SIZE; + else if (idx =3D=3D OPE_QUEUE_DISP_OUT) + size =3D ctx->fmt_out.sizeimage ? ctx->fmt_out.sizeimage : PAGE_SIZE; + else + size =3D v4l2_isp_params_buffer_size(CAMSS_PARAMS_MAX_PAYLOAD); + + if (*nplanes) { + if (*nplanes !=3D 1 || sizes[0] < size) + return -EINVAL; + } else { + *nplanes =3D 1; + sizes[0] =3D size; + } + + return 0; +} + +static int ope_buf_prepare(struct vb2_buffer *vb) +{ + struct ope_ctx *ctx =3D ope_ctx_from_vq(vb->vb2_queue); + unsigned int idx =3D ope_idx_from_vq(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf =3D to_vb2_v4l2_buffer(vb); + unsigned int sizeimage; + + if (idx =3D=3D OPE_QUEUE_FRAME_IN) + sizeimage =3D ctx->fmt_in.sizeimage; + else if (idx =3D=3D OPE_QUEUE_DISP_OUT) + sizeimage =3D ctx->fmt_out.sizeimage; + else + sizeimage =3D v4l2_isp_params_buffer_size(CAMSS_PARAMS_MAX_PAYLOAD); + + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vb->vb2_queue->type =3D=3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (vbuf->field =3D=3D V4L2_FIELD_ANY) + vbuf->field =3D V4L2_FIELD_NONE; + if (vbuf->field !=3D V4L2_FIELD_NONE) + return -EINVAL; + } + } + + if (vb2_plane_size(vb, 0) < sizeimage) + return -EINVAL; + + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) + vb2_set_plane_payload(vb, 0, sizeimage); + + return 0; +} + +static void ope_buf_queue(struct vb2_buffer *vb) +{ + struct ope_ctx *ctx =3D ope_ctx_from_vq(vb->vb2_queue); + unsigned int idx =3D ope_idx_from_vq(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf =3D to_vb2_v4l2_buffer(vb); + struct ope_queue *q =3D &ctx->queues[idx]; + struct camss_isp_buf *buf =3D container_of(vbuf, struct camss_isp_buf, vb= ); + + guard(spinlock_irqsave)(&q->rdy_spinlock); + list_add_tail(&buf->list, &q->rdy_queue); + + ope_try_schedule(ctx); +} + +/* -------- Power scaling -------- */ + +static inline unsigned long ope_pixclk(const struct ope_fmt_state *fs, uns= igned int fps) +{ + return (unsigned long)fs->width * fs->height * fps; +} + +static inline unsigned int ope_load_avg(const struct ope_fmt_state *fs, un= signed int fps) +{ + return mult_frac(ope_pixclk(fs, fps), fs->fmt->depth, 1000) / 8; +} + +static inline unsigned int ope_load_peak(const struct ope_fmt_state *fs, u= nsigned int fps) +{ + return ope_load_avg(fs, fps) * 2; +} + +static inline unsigned int ope_load_config(const struct ope_fmt_state *fs,= unsigned int fps) +{ + unsigned int stripe_count =3D fs->width / OPE_STRIPE_MAX_W + 1; + + /* In worst case we have ~50 32-bit registers to write */ + return mult_frac(stripe_count * 50 * 4, fps, 1000); +} + +static void ope_adjust_power(struct ope_dev *ope) +{ + unsigned int loadavg =3D 0, loadpeak =3D 0, loadconfig =3D 0; + unsigned long pixclk =3D 0; + struct dev_pm_opp *opp; + struct ope_ctx *ctx; + int ret; + + list_for_each_entry(ctx, &ope->ctx_list, list) { + unsigned int fps; + + if (!ctx->started) + continue; + + fps =3D ctx->framerate ? ctx->framerate : DEFAULT_FRAMERATE; + + pixclk +=3D ope_pixclk(&ctx->fmt_in, fps); + loadavg +=3D ope_load_avg(&ctx->fmt_in, fps); + loadavg +=3D ope_load_avg(&ctx->fmt_out, fps); + loadpeak +=3D ope_load_peak(&ctx->fmt_in, fps); + loadpeak +=3D ope_load_peak(&ctx->fmt_out, fps); + loadconfig +=3D ope_load_config(&ctx->fmt_in, fps); + } + + /* 30% margin for overhead */ + pixclk =3D mult_frac(pixclk, 13, 10); + + dev_dbg(ope->dev, "adjust power: clk=3D%luHz avg=3D%uKBps peak=3D%uKBps c= fg=3D%uKBps\n", + pixclk, loadavg, loadpeak, loadconfig); + + opp =3D dev_pm_opp_find_freq_ceil(ope->dev, &pixclk); + if (IS_ERR(opp)) + dev_warn(ope->dev, "Requested pixel clock %luHz exceeds hardware limit\n= ", pixclk); + else + dev_pm_opp_put(opp); + + ret =3D dev_pm_opp_set_rate(ope->dev, pixclk); + if (ret) + dev_warn(ope->dev, "Failed to set OPP rate: %d\n", ret); + + ret =3D icc_set_bw(ope->icc_data, loadavg, loadpeak); + if (ret) + dev_warn(ope->dev, "Failed to set data path BW: %d\n", ret); + + ret =3D icc_set_bw(ope->icc_config, loadconfig, loadconfig * 5); + if (ret) + dev_warn(ope->dev, "Failed to set config path BW: %d\n", ret); +} + +static int ope_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct ope_ctx *ctx =3D ope_ctx_from_vq(q); + struct video_device *vdev =3D ope_vdev_from_vq(q); + unsigned int idx =3D ope_idx_from_vq(q); + int ret; + + dev_dbg(ctx->ope->dev, "streaming start ctx=3D%p queue=3D%u\n", ctx, idx); + + ret =3D video_device_pipeline_alloc_start(vdev); + if (ret) + goto err_drain; + + ret =3D pm_runtime_resume_and_get(ctx->ope->dev); + if (ret) { + dev_err(ctx->ope->dev, "resume failed (%d)\n", ret); + goto err_pipeline_stop; + } + + if (idx =3D=3D OPE_QUEUE_FRAME_IN) { + ctx->fmt_in.sequence =3D 0; + ctx->started =3D true; + ctx->first_start =3D true; + ope_adjust_power(ctx->ope); + __ope_irq_init(ctx->ope); + } + + ope_try_schedule(ctx); + + return 0; + +err_pipeline_stop: + video_device_pipeline_stop(vdev); +err_drain: + ope_queue_drain(ctx, idx, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void ope_stop_streaming(struct vb2_queue *q) +{ + struct ope_ctx *ctx =3D ope_ctx_from_vq(q); + struct video_device *vdev =3D ope_vdev_from_vq(q); + unsigned int idx =3D ope_idx_from_vq(q); + + dev_dbg(ctx->ope->dev, "streaming stop ctx=3D%p queue=3D%u\n", ctx, idx); + + if (idx =3D=3D OPE_QUEUE_FRAME_IN) { + ctx->started =3D false; + ope_adjust_power(ctx->ope); + } + + ope_sched_cancel(ctx); + + ope_queue_drain(ctx, idx, VB2_BUF_STATE_ERROR); + + pm_runtime_put(ctx->ope->dev); + + video_device_pipeline_stop(vdev); +} + +static const struct vb2_ops ope_vb2_ops =3D { + .queue_setup =3D ope_queue_setup, + .buf_prepare =3D ope_buf_prepare, + .buf_queue =3D ope_buf_queue, + .start_streaming =3D ope_start_streaming, + .stop_streaming =3D ope_stop_streaming, + .wait_prepare =3D vb2_ops_wait_prepare, + .wait_finish =3D vb2_ops_wait_finish, +}; + +static const unsigned int ope_queue_to_entity[] =3D { + [OPE_QUEUE_FRAME_IN] =3D OPE_ENTITY_FRAME_IN, + [OPE_QUEUE_DISP_OUT] =3D OPE_ENTITY_DISP_OUT, + [OPE_QUEUE_PARAMS] =3D OPE_ENTITY_PARAMS, +}; + +static int ope_init_vq(struct ope_ctx *ctx, unsigned int idx) +{ + struct ope_dev *ope =3D ctx->ope; + struct ope_vq_priv *qpriv; + struct vb2_queue *q =3D &ctx->queues[idx].vq; + + qpriv =3D devm_kzalloc(ope->dev, sizeof(*qpriv), GFP_KERNEL); + if (!qpriv) + return -ENOMEM; + + qpriv->ctx =3D ctx; + qpriv->queue_idx =3D idx; + qpriv->vdev =3D camss_isp_pipeline_get_vdev(ope->pipeline, ope_queue_to_e= ntity[idx]); + q->drv_priv =3D qpriv; + q->ops =3D &ope_vb2_ops; + q->lock =3D &ctx->vbq_lock; + q->dev =3D ope->dev; + q->buf_struct_size =3D sizeof(struct camss_isp_buf); + q->timestamp_flags =3D V4L2_BUF_FLAG_TIMESTAMP_COPY; + q->min_queued_buffers =3D (idx =3D=3D OPE_QUEUE_PARAMS) ? 0 : 1; + + if (idx =3D=3D OPE_QUEUE_PARAMS) { + q->type =3D V4L2_BUF_TYPE_META_OUTPUT; + q->mem_ops =3D &vb2_vmalloc_memops; + q->io_modes =3D VB2_MMAP | VB2_USERPTR; + } else if (idx =3D=3D OPE_QUEUE_DISP_OUT) { + q->type =3D V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->mem_ops =3D &vb2_dma_contig_memops; + q->io_modes =3D VB2_MMAP | VB2_DMABUF; + } else { + q->type =3D V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + q->mem_ops =3D &vb2_dma_contig_memops; + q->io_modes =3D VB2_MMAP | VB2_DMABUF; + } + + return vb2_queue_init(q); +} + +/* -------- Format helpers -------- */ + +static const struct ope_fmt *ope_find_fmt(const struct ope_fmt *fmts, + unsigned int n, u32 fourcc) +{ + for (unsigned int i =3D 0; i < n; i++) + if (fmts[i].fourcc =3D=3D fourcc) + return &fmts[i]; + + return NULL; +} + +static const struct ope_fmt *ope_fmt_try(struct ope_dev *ope, bool is_outp= ut, + struct v4l2_pix_format_mplane *pix) +{ + const struct ope_fmt *fmts =3D is_output ? ope_output_fmts : ope_input_fm= ts; + unsigned int n =3D is_output ? ARRAY_SIZE(ope_output_fmts) : ARRAY_SIZE(o= pe_input_fmts); + unsigned int max_w =3D OPE_MAX_W, max_h =3D OPE_MAX_H; + const struct ope_fmt *fmt; + unsigned int bytesperline; + + fmt =3D ope_find_fmt(fmts, n, pix->pixelformat); + if (!fmt) { + fmt =3D &fmts[0]; + pix->pixelformat =3D fmt->fourcc; + } + + v4l_bound_align_image(&pix->width, OPE_MIN_W, max_w, fmt->align, + &pix->height, OPE_MIN_H, max_h, 0, 0); + + pix->num_planes =3D 1; + pix->field =3D V4L2_FIELD_NONE; + if (!pix->colorspace) + pix->colorspace =3D is_output ? V4L2_COLORSPACE_SRGB : V4L2_COLORSPACE_R= AW; + if (is_output) { + pix->ycbcr_enc =3D V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); + pix->quantization =3D V4L2_QUANTIZATION_FULL_RANGE; + } + + /* + * Output formats are semi-planar or grey-scale (Y plane only) always + * using 1 byte per Y value. pix->bytesperline stores the Y-plane bpl. + * Depth tracks the total storage size including the second combined + * Cb + Cr plane which directly follows the Y plane and is used for + * sizeimage calculations rather then for bytesperline. + */ + if (is_output) + bytesperline =3D pix->width; + else + bytesperline =3D pix->width * fmt->depth / 8; + + if (pix->plane_fmt[0].bytesperline > bytesperline) + bytesperline =3D pix->plane_fmt[0].bytesperline; + + /* + * The WE requires the C plane base address to be 16-byte aligned. + * c_base =3D bpl * img_h + (compose_top / c_vsub) * c_stride where + * c_stride =3D bpl * 2 / c_hsub. To guarantee alignment for any + * compose_top, bpl must be a multiple of 16 / c_hsub: + * NV12/NV16 (c_hsub=3D2): bpl must be multiple of 8... but c_stride=3D= bpl + * so we need bpl % 16 =3D=3D 0. + * NV24/NV42 (c_hsub=3D1): c_stride=3Dbpl*2, need bpl*2 % 16 =3D=3D 0, + * so bpl must be a multiple of 8. + * GREY: no C plane, no constraint. + */ + if (is_output && fmt->fourcc !=3D V4L2_PIX_FMT_GREY) + bytesperline =3D ALIGN(bytesperline, 8 * fmt->c_hsub); + + pix->plane_fmt[0].bytesperline =3D bytesperline; + + if (is_output) + pix->plane_fmt[0].sizeimage =3D (u64)bytesperline * pix->height * fmt->d= epth / 8; + else + pix->plane_fmt[0].sizeimage =3D bytesperline * pix->height; + + return fmt; +} + +/* -------- ioctl helpers/ops -------- */ + +static inline unsigned int ope_queue_idx_from_file(struct file *file) +{ + return (unsigned int)(uintptr_t)video_get_drvdata(video_devdata(file)); +} + +static struct vb2_queue *ope_vq_from_file(struct file *file, struct ope_ct= x *ctx) +{ + unsigned int idx =3D ope_queue_idx_from_file(file); + + if (idx >=3D OPE_QUEUE_COUNT) + return NULL; + + return &ctx->queues[idx].vq; +} + +static struct ope_ctx *ope_ctx_from_file(struct file *file) +{ + struct ope_dev *ope =3D container_of(video_devdata(file)->v4l2_dev, + struct ope_dev, v4l2_dev); + + return ope->shared_ctx; +} + +static int ope_querycap(struct file *file, void *priv, struct v4l2_capabil= ity *cap) +{ + strscpy(cap->driver, OPE_NAME, sizeof(cap->driver)); + strscpy(cap->card, "Qualcomm CAMSS OPE", sizeof(cap->card)); + + return 0; +} + +static int ope_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2= _fmtdesc *f) +{ + if (f->index >=3D ARRAY_SIZE(ope_input_fmts)) + return -EINVAL; + + f->pixelformat =3D ope_input_fmts[f->index].fourcc; + + return 0; +} + +static int ope_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_fo= rmat *f) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct ope_fmt_state *fs =3D &ctx->fmt_in; + struct v4l2_pix_format_mplane *pix =3D &f->fmt.pix_mp; + + pix->pixelformat =3D fs->fmt->fourcc; + pix->width =3D fs->width; + pix->height =3D fs->height; + pix->num_planes =3D 1; + pix->field =3D V4L2_FIELD_NONE; + pix->colorspace =3D fs->colorspace; + pix->xfer_func =3D fs->xfer_func; + pix->ycbcr_enc =3D fs->ycbcr_enc; + pix->quantization =3D fs->quantization; + pix->plane_fmt[0].bytesperline =3D fs->bytesperline; + pix->plane_fmt[0].sizeimage =3D fs->sizeimage; + + return 0; +} + +static int ope_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_= format *f) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + + ope_fmt_try(ctx->ope, false, &f->fmt.pix_mp); + + return 0; +} + +static int ope_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_fo= rmat *f) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + const struct ope_fmt *fmt; + + if (vb2_is_busy(&ctx->queues[OPE_QUEUE_FRAME_IN].vq)) + return -EBUSY; + + fmt =3D ope_fmt_try(ctx->ope, false, &f->fmt.pix_mp); + ctx->fmt_in.fmt =3D fmt; + ctx->fmt_in.width =3D f->fmt.pix_mp.width; + ctx->fmt_in.height =3D f->fmt.pix_mp.height; + ctx->fmt_in.bytesperline =3D f->fmt.pix_mp.plane_fmt[0].bytesperline; + ctx->fmt_in.sizeimage =3D f->fmt.pix_mp.plane_fmt[0].sizeimage; + ctx->fmt_in.colorspace =3D f->fmt.pix_mp.colorspace; + ctx->fmt_in.xfer_func =3D f->fmt.pix_mp.xfer_func; + ctx->fmt_in.ycbcr_enc =3D f->fmt.pix_mp.ycbcr_enc; + ctx->fmt_in.quantization =3D f->fmt.pix_mp.quantization; + + return 0; +} + +static int ope_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2= _fmtdesc *f) +{ + unsigned int i, n =3D 0; + + for (i =3D 0; i < ARRAY_SIZE(ope_output_fmts); i++) { + if (f->mbus_code && ope_output_fmts[i].mbus_code !=3D f->mbus_code) + continue; + if (n++ =3D=3D f->index) { + f->pixelformat =3D ope_output_fmts[i].fourcc; + return 0; + } + } + + return -EINVAL; +} + +static int ope_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fo= rmat *f) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct ope_fmt_state *fs =3D &ctx->fmt_out; + struct v4l2_pix_format_mplane *pix =3D &f->fmt.pix_mp; + + pix->pixelformat =3D fs->fmt->fourcc; + pix->width =3D fs->width; + pix->height =3D fs->height; + pix->num_planes =3D 1; + pix->field =3D V4L2_FIELD_NONE; + pix->colorspace =3D fs->colorspace; + pix->xfer_func =3D fs->xfer_func; + pix->ycbcr_enc =3D fs->ycbcr_enc; + pix->quantization =3D fs->quantization; + pix->plane_fmt[0].bytesperline =3D fs->bytesperline; + pix->plane_fmt[0].sizeimage =3D fs->sizeimage; + + return 0; +} + +static int ope_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_= format *f) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + + ope_fmt_try(ctx->ope, true, &f->fmt.pix_mp); + + return 0; +} + +static int ope_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fo= rmat *f) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + const struct ope_fmt *fmt; + + if (vb2_is_busy(&ctx->queues[OPE_QUEUE_DISP_OUT].vq)) + return -EBUSY; + + fmt =3D ope_fmt_try(ctx->ope, true, &f->fmt.pix_mp); + + ctx->fmt_out.fmt =3D fmt; + ctx->fmt_out.width =3D f->fmt.pix_mp.width; + ctx->fmt_out.height =3D f->fmt.pix_mp.height; + ctx->fmt_out.bytesperline =3D f->fmt.pix_mp.plane_fmt[0].bytesperline; + ctx->fmt_out.sizeimage =3D f->fmt.pix_mp.plane_fmt[0].sizeimage; + ctx->fmt_out.colorspace =3D f->fmt.pix_mp.colorspace; + ctx->fmt_out.xfer_func =3D f->fmt.pix_mp.xfer_func; + ctx->fmt_out.ycbcr_enc =3D f->fmt.pix_mp.ycbcr_enc; + ctx->fmt_out.quantization =3D f->fmt.pix_mp.quantization; + + return 0; +} + +static int ope_g_fmt_meta(struct file *file, void *priv, struct v4l2_forma= t *f) +{ + f->fmt.meta.dataformat =3D V4L2_META_FMT_QCOM_ISP_PARAMS; + f->fmt.meta.buffersize =3D v4l2_isp_params_buffer_size(CAMSS_PARAMS_MAX_P= AYLOAD); + + return 0; +} + +static int ope_enum_fmt_meta_out(struct file *file, void *priv, struct v4l= 2_fmtdesc *f) +{ + if (f->index > 0) + return -EINVAL; + + f->pixelformat =3D V4L2_META_FMT_QCOM_ISP_PARAMS; + + return 0; +} + +static int ope_g_parm(struct file *file, void *priv, struct v4l2_streampar= m *sp) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + + if (!V4L2_TYPE_IS_OUTPUT(sp->type)) + return -EINVAL; + + sp->parm.output.capability =3D V4L2_CAP_TIMEPERFRAME; + sp->parm.output.timeperframe =3D ctx->fmt_in.timeperframe; + + return 0; +} + +static int ope_s_parm(struct file *file, void *priv, struct v4l2_streampar= m *sp) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct v4l2_fract *tpf =3D &sp->parm.output.timeperframe; + + if (!V4L2_TYPE_IS_OUTPUT(sp->type)) + return -EINVAL; + + if (vb2_is_busy(&ctx->queues[OPE_QUEUE_FRAME_IN].vq)) + return -EBUSY; + + if (!tpf->denominator) + tpf->denominator =3D 1; + if (!tpf->numerator) + tpf->numerator =3D 1; + + ctx->fmt_in.timeperframe =3D *tpf; + ctx->framerate =3D tpf->denominator / tpf->numerator; + sp->parm.output.capability =3D V4L2_CAP_TIMEPERFRAME; + + ope_adjust_power(ctx->ope); + + return 0; +} + +static int ope_enum_framesizes(struct file *file, void *priv, struct v4l2_= frmsizeenum *fsize) +{ + unsigned int idx =3D ope_queue_idx_from_file(file); + const struct ope_fmt *fmts; + unsigned int n; + + if (idx =3D=3D OPE_QUEUE_FRAME_IN) { + fmts =3D ope_input_fmts; + n =3D ARRAY_SIZE(ope_input_fmts); + } else { + fmts =3D ope_output_fmts; + n =3D ARRAY_SIZE(ope_output_fmts); + } + + if (fsize->index > 0 || !ope_find_fmt(fmts, n, fsize->pixel_format)) + return -EINVAL; + + fsize->type =3D V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width =3D OPE_MIN_W; + fsize->stepwise.max_width =3D OPE_MAX_W; + fsize->stepwise.step_width =3D 1; + fsize->stepwise.min_height =3D OPE_MIN_H; + fsize->stepwise.max_height =3D OPE_MAX_H; + fsize->stepwise.step_height =3D 1; + + return 0; +} + +static int ope_reqbufs(struct file *file, void *priv, struct v4l2_requestb= uffers *rb) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct vb2_queue *vq =3D ope_vq_from_file(file, ctx); + int ret; + + if (!vq) + return -EINVAL; + + if (vb2_queue_is_busy(vq, file)) + return -EBUSY; + + ret =3D vb2_reqbufs(vq, rb); + if (!ret) + vq->owner =3D rb->count ? file->private_data : NULL; + + return ret; +} + +static int ope_querybuf(struct file *file, void *priv, struct v4l2_buffer = *buf) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct vb2_queue *vq =3D ope_vq_from_file(file, ctx); + + return vq ? vb2_querybuf(vq, buf) : -EINVAL; +} + +static int ope_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct video_device *vdev =3D video_devdata(file); + struct vb2_queue *vq =3D ope_vq_from_file(file, ctx); + + if (!vq) + return -EINVAL; + if (vb2_queue_is_busy(vq, file)) + return -EBUSY; + + return vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf); +} + +static int ope_dqbuf(struct file *file, void *priv, struct v4l2_buffer *bu= f) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct vb2_queue *vq =3D ope_vq_from_file(file, ctx); + + return vq ? vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK) : -EINVAL; +} + +static int ope_prepare_buf(struct file *file, void *priv, struct v4l2_buff= er *buf) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct video_device *vdev =3D video_devdata(file); + struct vb2_queue *vq =3D ope_vq_from_file(file, ctx); + + if (!vq) + return -EINVAL; + if (vb2_queue_is_busy(vq, file)) + return -EBUSY; + + return vb2_prepare_buf(vq, vdev->v4l2_dev->mdev, buf); +} + +static int ope_create_bufs(struct file *file, void *priv, struct v4l2_crea= te_buffers *create) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct vb2_queue *vq =3D ope_vq_from_file(file, ctx); + int ret; + + if (!vq) + return -EINVAL; + if (vb2_queue_is_busy(vq, file)) + return -EBUSY; + ret =3D vb2_create_bufs(vq, create); + if (!ret && create->count) + vq->owner =3D file->private_data; + + return ret; +} + +static int ope_expbuf(struct file *file, void *priv, struct v4l2_exportbuf= fer *eb) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct vb2_queue *vq =3D ope_vq_from_file(file, ctx); + + if (!vq) + return -EINVAL; + if (vb2_queue_is_busy(vq, file)) + return -EBUSY; + + return vb2_expbuf(vq, eb); +} + +static int ope_streamon(struct file *file, void *priv, enum v4l2_buf_type = type) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct vb2_queue *vq =3D ope_vq_from_file(file, ctx); + + if (!vq) + return -EINVAL; + if (vb2_queue_is_busy(vq, file)) + return -EBUSY; + + return vb2_streamon(vq, type); +} + +static int ope_streamoff(struct file *file, void *priv, enum v4l2_buf_type= type) +{ + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct vb2_queue *vq =3D ope_vq_from_file(file, ctx); + + if (!vq) + return -EINVAL; + + if (vb2_queue_is_busy(vq, file)) + return -EBUSY; + + return vb2_streamoff(vq, type); +} + +/* -------- proc subdev ops -------- */ + +static const struct v4l2_rect ope_sd_min_rect =3D { + .width =3D OPE_MIN_W, + .height =3D OPE_MIN_H, +}; + +static void ope_sd_adjust_crop_rect(struct v4l2_rect *crop, const struct v= 4l2_rect *bounds) +{ + v4l2_rect_set_min_size(crop, &ope_sd_min_rect); + v4l2_rect_map_inside(crop, bounds); +} + +static void ope_sd_adjust_crop(struct v4l2_rect *crop, const struct v4l2_m= bus_framefmt *bounds) +{ + struct v4l2_rect crop_bounds =3D { + .left =3D 0, .top =3D 0, + .width =3D bounds->width, .height =3D bounds->height, + }; + + ope_sd_adjust_crop_rect(crop, &crop_bounds); +} + +static void ope_proc_set_src_fmt(struct v4l2_subdev_state *state, + struct v4l2_mbus_framefmt *format) +{ + struct v4l2_mbus_framefmt *src_fmt; + const struct v4l2_rect *compose; + + src_fmt =3D v4l2_subdev_state_get_format(state, OPE_PROC_PAD_SOURCE); + compose =3D v4l2_subdev_state_get_compose(state, OPE_PROC_PAD_SOURCE); + + src_fmt->code =3D MEDIA_BUS_FMT_YUYV8_1_5X8; + for (unsigned int i =3D 0; i < ARRAY_SIZE(ope_output_fmts); i++) { + if (ope_output_fmts[i].mbus_code =3D=3D format->code) { + src_fmt->code =3D format->code; + break; + } + } + + src_fmt->width =3D compose->width; + src_fmt->height =3D compose->height; + src_fmt->field =3D V4L2_FIELD_NONE; + src_fmt->colorspace =3D V4L2_COLORSPACE_SRGB; + *format =3D *src_fmt; +} + +static void ope_proc_set_src_compose(struct v4l2_subdev_state *state, stru= ct v4l2_rect *r) +{ + struct v4l2_rect *compose =3D v4l2_subdev_state_get_compose(state, OPE_PR= OC_PAD_SOURCE); + struct v4l2_rect bounds; + + const struct v4l2_rect *sink_crop =3D + v4l2_subdev_state_get_crop(state, OPE_PROC_PAD_SINK_IN); + + bounds =3D (struct v4l2_rect){ 0, 0, sink_crop->width, sink_crop->height = }; + *compose =3D *r; + ope_sd_adjust_crop_rect(compose, &bounds); + *r =3D *compose; + + /* Propagate to out format */ + ope_proc_set_src_fmt(state, v4l2_subdev_state_get_format(state, OPE_PROC_= PAD_SOURCE)); +} + +static void ope_proc_set_sink_crop(struct v4l2_subdev_state *state, struct= v4l2_rect *r) +{ + struct v4l2_rect *crop; + + crop =3D v4l2_subdev_state_get_crop(state, OPE_PROC_PAD_SINK_IN); + + *crop =3D *r; + ope_sd_adjust_crop(crop, v4l2_subdev_state_get_format(state, OPE_PROC_PAD= _SINK_IN)); + *r =3D *crop; + + /* Propagate to out format */ + ope_proc_set_src_compose(state, v4l2_subdev_state_get_compose(state, OPE_= PROC_PAD_SOURCE)); +} + +static void ope_proc_set_sink_fmt(struct v4l2_subdev_state *state, + struct v4l2_mbus_framefmt *format) +{ + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_rect *crop; + + sink_fmt =3D v4l2_subdev_state_get_format(state, OPE_PROC_PAD_SINK_IN); + crop =3D v4l2_subdev_state_get_crop(state, OPE_PROC_PAD_SINK_IN); + + sink_fmt->code =3D ope_input_fmts[0].mbus_code; + for (unsigned int i =3D 0; i < ARRAY_SIZE(ope_input_fmts); i++) { + if (ope_input_fmts[i].mbus_code =3D=3D format->code) { + sink_fmt->code =3D format->code; + break; + } + } + + sink_fmt->width =3D clamp(format->width, (u32)OPE_MIN_W, (u32)OPE_MAX_W); + sink_fmt->height =3D clamp(format->height, (u32)OPE_MIN_H, (u32)OPE_MAX_H= ); + sink_fmt->field =3D V4L2_FIELD_NONE; + sink_fmt->colorspace =3D V4L2_COLORSPACE_RAW; + *format =3D *sink_fmt; + + /* Propagate to in crop */ + ope_proc_set_sink_crop(state, crop); +} + +static int ope_proc_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt fmt =3D { + .width =3D OPE_MIN_W, + .height =3D OPE_MIN_H, + .code =3D MEDIA_BUS_FMT_SRGGB8_1X8, + }; + + /* Params sink: fixed format, no geometry */ + *v4l2_subdev_state_get_format(state, OPE_PROC_PAD_SINK_PAR) =3D + (struct v4l2_mbus_framefmt){ + .code =3D MEDIA_BUS_FMT_FIXED, + .field =3D V4L2_FIELD_NONE, + .colorspace =3D V4L2_COLORSPACE_DEFAULT, + }; + + /* Initialise source compose default before cascade */ + *v4l2_subdev_state_get_compose(state, OPE_PROC_PAD_SOURCE) =3D + (struct v4l2_rect){ 0, 0, OPE_MIN_W, OPE_MIN_H }; + v4l2_subdev_state_get_format(state, OPE_PROC_PAD_SOURCE)->code =3D + MEDIA_BUS_FMT_YUYV8_1_5X8; + + /* Drive the full cascade from sink-in fmt */ + ope_proc_set_sink_fmt(state, &fmt); + + return 0; +} + +static int ope_proc_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + if (fmt->pad =3D=3D OPE_PROC_PAD_SINK_PAR) + return v4l2_subdev_get_fmt(sd, state, fmt); + + if (fmt->pad =3D=3D OPE_PROC_PAD_SINK_IN) { + ope_proc_set_sink_fmt(state, &fmt->format); + return 0; + } + + ope_proc_set_src_fmt(state, &fmt->format); + + return 0; +} + +static int ope_proc_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + unsigned int n =3D 0; + + if (code->pad !=3D OPE_PROC_PAD_SOURCE) + return -EINVAL; + + for (unsigned int i =3D 0; i < ARRAY_SIZE(ope_output_fmts); i++) { + u32 mc =3D ope_output_fmts[i].mbus_code; + bool seen =3D false; + + for (unsigned int j =3D 0; j < i; j++) { + if (ope_output_fmts[j].mbus_code =3D=3D mc) { + seen =3D true; + break; + } + } + if (!seen && n++ =3D=3D code->index) { + code->code =3D mc; + return 0; + } + } + + return -EINVAL; +} + +static int ope_proc_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + if (sel->pad =3D=3D OPE_PROC_PAD_SINK_IN) { + const struct v4l2_mbus_framefmt *sink_fmt =3D + v4l2_subdev_state_get_format(state, OPE_PROC_PAD_SINK_IN); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r =3D *v4l2_subdev_state_get_crop(state, OPE_PROC_PAD_SINK_IN); + return 0; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r =3D (struct v4l2_rect){ 0, 0, sink_fmt->width, sink_fmt->height = }; + return 0; + default: + return -EINVAL; + } + } + + if (sel->pad =3D=3D OPE_PROC_PAD_SOURCE) { + const struct v4l2_rect *crop =3D + v4l2_subdev_state_get_crop(state, OPE_PROC_PAD_SINK_IN); + + switch (sel->target) { + case V4L2_SEL_TGT_COMPOSE: + sel->r =3D *v4l2_subdev_state_get_compose(state, OPE_PROC_PAD_SOURCE); + return 0; + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r =3D *crop; + return 0; + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +static int ope_proc_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + if (sel->pad =3D=3D OPE_PROC_PAD_SINK_IN) { + if (sel->target !=3D V4L2_SEL_TGT_CROP) + return -EINVAL; + ope_proc_set_sink_crop(state, &sel->r); + return 0; + } + + if (sel->pad =3D=3D OPE_PROC_PAD_SOURCE) { + if (sel->target !=3D V4L2_SEL_TGT_COMPOSE) + return -EINVAL; + ope_proc_set_src_compose(state, &sel->r); + return 0; + } + + return -EINVAL; +} + +static const struct v4l2_subdev_pad_ops ope_proc_pad_ops =3D { + .get_fmt =3D v4l2_subdev_get_fmt, + .set_fmt =3D ope_proc_set_fmt, + .enum_mbus_code =3D ope_proc_enum_mbus_code, + .get_selection =3D ope_proc_get_selection, + .set_selection =3D ope_proc_set_selection, +}; + +static const struct v4l2_subdev_internal_ops ope_proc_internal_ops =3D { + .init_state =3D ope_proc_init_state, +}; + +static const struct v4l2_subdev_ops ope_proc_ops =3D { + .pad =3D &ope_proc_pad_ops, +}; + +/* -------- disp subdev ops -------- */ + +static int ope_disp_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *pad_fmt; + u32 code =3D fmt->format.code; + + fmt->format.code =3D MEDIA_BUS_FMT_YUYV8_1_5X8; + for (unsigned int i =3D 0; i < ARRAY_SIZE(ope_output_fmts); i++) { + if (ope_output_fmts[i].mbus_code =3D=3D code) { + fmt->format.code =3D code; + break; + } + } + + v4l_bound_align_image(&fmt->format.width, OPE_MIN_W, OPE_MAX_W, 0, + &fmt->format.height, OPE_MIN_H, OPE_MAX_H, 0, 0); + fmt->format.field =3D V4L2_FIELD_NONE; + fmt->format.colorspace =3D V4L2_COLORSPACE_SRGB; + + pad_fmt =3D v4l2_subdev_state_get_format(state, fmt->pad); + *pad_fmt =3D fmt->format; + return 0; +} + +static int ope_disp_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt def =3D { + .width =3D OPE_MIN_W, + .height =3D OPE_MIN_H, + .code =3D MEDIA_BUS_FMT_YUYV8_1_5X8, + .field =3D V4L2_FIELD_NONE, + .colorspace =3D V4L2_COLORSPACE_SRGB, + }; + + *v4l2_subdev_state_get_format(state, OPE_DISP_PAD_SINK) =3D def; + *v4l2_subdev_state_get_format(state, OPE_DISP_PAD_SOURCE) =3D def; + return 0; +} + +static const struct v4l2_subdev_pad_ops ope_disp_pad_ops =3D { + .get_fmt =3D v4l2_subdev_get_fmt, + .set_fmt =3D ope_disp_set_fmt, + .link_validate =3D v4l2_subdev_link_validate_default, +}; + +static const struct media_entity_operations ope_disp_entity_ops =3D { + .link_validate =3D v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ope_disp_internal_ops =3D { + .init_state =3D ope_disp_init_state, +}; + +static const struct v4l2_subdev_ops ope_disp_ops =3D { + .pad =3D &ope_disp_pad_ops, +}; + +/* -------- disp-output video ops -------- */ + +static int ope_disp_output_link_validate(struct media_link *link) +{ + struct video_device *vdev =3D media_entity_to_video_device(link->sink->en= tity); + struct v4l2_subdev *sd =3D media_entity_to_v4l2_subdev(link->source->enti= ty); + struct ope_dev *ope =3D container_of(vdev->v4l2_dev, struct ope_dev, v4l2= _dev); + struct ope_ctx *ctx =3D ope->shared_ctx; + struct v4l2_subdev_state *sd_state; + struct v4l2_subdev_format sd_fmt =3D { + .which =3D V4L2_SUBDEV_FORMAT_ACTIVE, + .pad =3D link->source->index, + }; + int ret; + + if (!ctx) + return 0; + + sd_state =3D v4l2_subdev_lock_and_get_active_state(sd); + ret =3D v4l2_subdev_call(sd, pad, get_fmt, sd_state, &sd_fmt); + v4l2_subdev_unlock_state(sd_state); + if (ret) + return ret; + + if (ctx->fmt_out.fmt->mbus_code !=3D sd_fmt.format.code) { + dev_dbg(ctx->ope->dev, "link validate: mbus 0x%04x incompatible with %p4= cc\n", + sd_fmt.format.code, &ctx->fmt_out.fmt->fourcc); + return -EPIPE; + } + + if (sd_fmt.format.width > ctx->fmt_out.width || + sd_fmt.format.height > ctx->fmt_out.height) { + dev_dbg(ctx->ope->dev, + "link validate: compose %ux%u exceeds output buffer %ux%u\n", + sd_fmt.format.width, sd_fmt.format.height, + ctx->fmt_out.width, ctx->fmt_out.height); + return -EPIPE; + } + + /* Check compose rect (including offset) fits within output buffer */ + { + struct v4l2_subdev *proc_sd =3D + camss_isp_pipeline_get_subdev(ctx->ope->pipeline, + OPE_ENTITY_PROC); + struct v4l2_subdev_state *proc_state =3D + v4l2_subdev_lock_and_get_active_state(proc_sd); + const struct v4l2_rect *compose =3D + v4l2_subdev_state_get_compose(proc_state, + OPE_PROC_PAD_SOURCE); + + if (compose->left + compose->width > ctx->fmt_out.width || + compose->top + compose->height > ctx->fmt_out.height) { + dev_dbg(ctx->ope->dev, + "link validate: compose %ux%u+%u+%u overflows output buffer %ux%u\n", + compose->width, compose->height, + compose->left, compose->top, + ctx->fmt_out.width, ctx->fmt_out.height); + v4l2_subdev_unlock_state(proc_state); + return -EPIPE; + } + v4l2_subdev_unlock_state(proc_state); + } + + return 0; +} + +static const struct media_entity_operations ope_disp_output_entity_ops =3D= { + .link_validate =3D ope_disp_output_link_validate, +}; + +static int ope_input_link_validate(struct media_link *link) +{ + struct video_device *vdev =3D media_entity_to_video_device(link->source->= entity); + struct v4l2_subdev *sd =3D media_entity_to_v4l2_subdev(link->sink->entity= ); + struct ope_dev *ope =3D container_of(vdev->v4l2_dev, struct ope_dev, v4l2= _dev); + struct ope_ctx *ctx =3D ope->shared_ctx; + struct v4l2_subdev_state *sd_state; + struct v4l2_subdev_format sd_fmt =3D { + .which =3D V4L2_SUBDEV_FORMAT_ACTIVE, + .pad =3D link->sink->index, + }; + int ret; + + if (!ctx) + return 0; + + sd_state =3D v4l2_subdev_lock_and_get_active_state(sd); + ret =3D v4l2_subdev_call(sd, pad, get_fmt, sd_state, &sd_fmt); + v4l2_subdev_unlock_state(sd_state); + if (ret) + return ret; + + if (ctx->fmt_in.fmt->mbus_code !=3D sd_fmt.format.code || + ctx->fmt_in.width !=3D sd_fmt.format.width || + ctx->fmt_in.height !=3D sd_fmt.format.height) { + dev_dbg(ctx->ope->dev, + "link validate: input %dx%d/0x%04x !=3D pad %dx%d/0x%04x\n", + ctx->fmt_in.width, ctx->fmt_in.height, + ctx->fmt_in.fmt->mbus_code, + sd_fmt.format.width, sd_fmt.format.height, + sd_fmt.format.code); + return -EPIPE; + } + + return 0; +} + +static const struct media_entity_operations ope_input_entity_ops =3D { + .link_validate =3D ope_input_link_validate, +}; + +static const struct v4l2_ioctl_ops ope_video_ioctl_ops =3D { + .vidioc_querycap =3D ope_querycap, + .vidioc_enum_fmt_vid_out =3D ope_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out_mplane =3D ope_g_fmt_vid_out, + .vidioc_try_fmt_vid_out_mplane =3D ope_try_fmt_vid_out, + .vidioc_s_fmt_vid_out_mplane =3D ope_s_fmt_vid_out, + .vidioc_enum_fmt_vid_cap =3D ope_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap_mplane =3D ope_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap_mplane =3D ope_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap_mplane =3D ope_s_fmt_vid_cap, + .vidioc_enum_framesizes =3D ope_enum_framesizes, + .vidioc_g_parm =3D ope_g_parm, + .vidioc_s_parm =3D ope_s_parm, + .vidioc_reqbufs =3D ope_reqbufs, + .vidioc_querybuf =3D ope_querybuf, + .vidioc_qbuf =3D ope_qbuf, + .vidioc_dqbuf =3D ope_dqbuf, + .vidioc_prepare_buf =3D ope_prepare_buf, + .vidioc_create_bufs =3D ope_create_bufs, + .vidioc_expbuf =3D ope_expbuf, + .vidioc_streamon =3D ope_streamon, + .vidioc_streamoff =3D ope_streamoff, + .vidioc_subscribe_event =3D v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event =3D v4l2_event_unsubscribe, +}; + +static const struct v4l2_ioctl_ops ope_meta_ioctl_ops =3D { + .vidioc_querycap =3D ope_querycap, + .vidioc_enum_fmt_meta_out =3D ope_enum_fmt_meta_out, + .vidioc_g_fmt_meta_out =3D ope_g_fmt_meta, + .vidioc_s_fmt_meta_out =3D ope_g_fmt_meta, + .vidioc_try_fmt_meta_out =3D ope_g_fmt_meta, + .vidioc_reqbufs =3D ope_reqbufs, + .vidioc_querybuf =3D ope_querybuf, + .vidioc_qbuf =3D ope_qbuf, + .vidioc_dqbuf =3D ope_dqbuf, + .vidioc_prepare_buf =3D ope_prepare_buf, + .vidioc_create_bufs =3D ope_create_bufs, + .vidioc_expbuf =3D ope_expbuf, + .vidioc_streamon =3D ope_streamon, + .vidioc_streamoff =3D ope_streamoff, +}; + +/* -------- File/Context ops -------- */ + +static struct ope_ctx *ope_ctx_create(struct ope_dev *ope) +{ + struct ope_ctx *ctx; + int ret; + + ctx =3D kvzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->ope =3D ope; + + mutex_init(&ctx->vbq_lock); + + ctx->config =3D ope_default_config; + + /* Default input format */ + ctx->fmt_in.fmt =3D &ope_input_fmts[0]; + ctx->fmt_in.width =3D OPE_MIN_W; + ctx->fmt_in.height =3D OPE_MIN_H; + ctx->fmt_in.bytesperline =3D OPE_MIN_W * ope_input_fmts[0].depth / 8; + ctx->fmt_in.sizeimage =3D ctx->fmt_in.bytesperline * OPE_MIN_H; + ctx->fmt_in.colorspace =3D V4L2_COLORSPACE_RAW; + ctx->fmt_in.timeperframe.numerator =3D 1; + ctx->fmt_in.timeperframe.denominator =3D DEFAULT_FRAMERATE; + + /* Default output format */ + ctx->fmt_out.fmt =3D &ope_output_fmts[0]; + ctx->fmt_out.width =3D OPE_MIN_W; + ctx->fmt_out.height =3D OPE_MIN_H; + ctx->fmt_out.bytesperline =3D OPE_MIN_W; + ctx->fmt_out.sizeimage =3D (u64)ope_output_fmts[0].depth * OPE_MIN_W * = OPE_MIN_H / 8; + ctx->fmt_out.colorspace =3D V4L2_COLORSPACE_SRGB; + + for (unsigned int i =3D 0; i < OPE_QUEUE_COUNT; i++) { + INIT_LIST_HEAD(&ctx->queues[i].rdy_queue); + spin_lock_init(&ctx->queues[i].rdy_spinlock); + ret =3D ope_init_vq(ctx, i); + if (ret) { + while (i--) + vb2_queue_release(&ctx->queues[i].vq); + kvfree(ctx); + return ERR_PTR(ret); + } + } + + INIT_LIST_HEAD(&ctx->list); + list_add(&ctx->list, &ope->ctx_list); + ope->shared_ctx =3D ctx; + + return ctx; +} + +static void ope_ctx_destroy(struct ope_ctx *ctx) +{ + list_del(&ctx->list); + ope_sched_cancel(ctx); + for (unsigned int i =3D 0; i < OPE_QUEUE_COUNT; i++) + vb2_queue_release(&ctx->queues[i].vq); + mutex_destroy(&ctx->vbq_lock); + kvfree(ctx); +} + +static int ope_open(struct file *file) +{ + struct video_device *vdev =3D video_devdata(file); + struct ope_dev *ope =3D container_of(vdev->v4l2_dev, struct ope_dev, v4l2= _dev); + struct ope_ctx *ctx; + struct v4l2_fh *fh; + int ret =3D 0; + + fh =3D kzalloc(sizeof(*fh), GFP_KERNEL); + if (!fh) + return -ENOMEM; + + if (mutex_lock_interruptible(&ope->mutex)) { + kfree(fh); + return -ERESTARTSYS; + } + + /* + * For now, only a single shared context is supported, + * until media multi-context support is available. + */ + if (!ope->shared_ctx) { + ctx =3D ope_ctx_create(ope); + if (IS_ERR(ctx)) { + ret =3D PTR_ERR(ctx); + goto unlock; + } + } else { + ctx =3D ope->shared_ctx; + } + + v4l2_fh_init(fh, vdev); + v4l2_fh_add(fh, file); + ope->open_count++; + +unlock: + if (ret) + kfree(fh); + mutex_unlock(&ope->mutex); + return ret; +} + +static int ope_release(struct file *file) +{ + struct v4l2_fh *fh =3D file_to_v4l2_fh(file); + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct ope_dev *ope =3D ctx->ope; + + v4l2_fh_del(fh, file); + v4l2_fh_exit(fh); + kfree(fh); + + scoped_guard(mutex, &ope->mutex) { + if (--ope->open_count !=3D 0) + return 0; + ope->shared_ctx =3D NULL; + } + + ope_ctx_destroy(ctx); + + return 0; +} + +static __poll_t ope_poll(struct file *file, poll_table *wait) +{ + unsigned int idx =3D ope_queue_idx_from_file(file); + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + struct v4l2_fh *fh =3D file_to_v4l2_fh(file); + struct vb2_queue *vq; + unsigned long flags; + __poll_t rc =3D 0; + + if (idx >=3D OPE_QUEUE_COUNT) + return EPOLLERR; + + vq =3D &ctx->queues[idx].vq; + + poll_wait(file, &vq->done_wq, wait); + poll_wait(file, &fh->wait, wait); + + spin_lock_irqsave(&vq->done_lock, flags); + if (!list_empty(&vq->done_list)) { + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + rc |=3D EPOLLOUT | EPOLLWRNORM; + else + rc |=3D EPOLLIN | EPOLLRDNORM; + } + spin_unlock_irqrestore(&vq->done_lock, flags); + + if (v4l2_event_pending(fh)) + rc |=3D EPOLLPRI; + + return rc; +} + +static int ope_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned int idx =3D ope_queue_idx_from_file(file); + struct ope_ctx *ctx =3D ope_ctx_from_file(file); + + if (idx >=3D OPE_QUEUE_COUNT) + return -EINVAL; + + return vb2_mmap(&ctx->queues[idx].vq, vma); +} + +static const struct v4l2_file_operations ope_fops =3D { + .owner =3D THIS_MODULE, + .open =3D ope_open, + .release =3D ope_release, + .poll =3D ope_poll, + .unlocked_ioctl =3D video_ioctl2, + .mmap =3D ope_mmap, +}; + +/* Pipeline descriptor */ +static const struct camss_isp_entity_desc ope_entity_descs[] =3D { + [OPE_ENTITY_FRAME_IN] =3D { + .name =3D "ope_input", + .obj_type =3D MEDIA_ENTITY_TYPE_VIDEO_DEVICE, + .function =3D MEDIA_ENT_F_IO_V4L, + .vdev.caps =3D V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING, + .vdev.drvdata =3D (void *)(uintptr_t)OPE_QUEUE_FRAME_IN, + .vdev.fops =3D &ope_fops, + .vdev.ioctl_ops =3D &ope_video_ioctl_ops, + .vdev.entity_ops =3D &ope_input_entity_ops, + .pads =3D (const struct camss_isp_pad_desc[]) { + { MEDIA_PAD_FL_SOURCE, OPE_ENTITY_PROC, OPE_PROC_PAD_SINK_IN, 0 }, + { } + }, + }, + [OPE_ENTITY_DISP_OUT] =3D { + .name =3D "ope_disp_output", + .obj_type =3D MEDIA_ENTITY_TYPE_VIDEO_DEVICE, + .function =3D MEDIA_ENT_F_IO_V4L, + .vdev.caps =3D V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING, + .vdev.drvdata =3D (void *)(uintptr_t)OPE_QUEUE_DISP_OUT, + .vdev.fops =3D &ope_fops, + .vdev.ioctl_ops =3D &ope_video_ioctl_ops, + .vdev.entity_ops =3D &ope_disp_output_entity_ops, + .pads =3D (const struct camss_isp_pad_desc[]) { + { MEDIA_PAD_FL_SINK, OPE_ENTITY_DISP, OPE_DISP_PAD_SOURCE, 0 }, + { } + }, + }, + [OPE_ENTITY_PARAMS] =3D { + .name =3D "ope_params", + .obj_type =3D MEDIA_ENTITY_TYPE_VIDEO_DEVICE, + .function =3D MEDIA_ENT_F_IO_V4L, + .vdev.caps =3D V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING, + .vdev.drvdata =3D (void *)(uintptr_t)OPE_QUEUE_PARAMS, + .vdev.fops =3D &ope_fops, + .vdev.ioctl_ops =3D &ope_meta_ioctl_ops, + .pads =3D (const struct camss_isp_pad_desc[]) { + { MEDIA_PAD_FL_SOURCE, OPE_ENTITY_PROC, OPE_PROC_PAD_SINK_PAR, 0 }, + { } + }, + }, + [OPE_ENTITY_PROC] =3D { + .name =3D "ope_proc", + .obj_type =3D MEDIA_ENTITY_TYPE_V4L2_SUBDEV, + .function =3D MEDIA_ENT_F_PROC_VIDEO_ISP, + .subdev.ops =3D &ope_proc_ops, + .subdev.internal_ops =3D &ope_proc_internal_ops, + .pads =3D (const struct camss_isp_pad_desc[]) { + { MEDIA_PAD_FL_SINK, OPE_ENTITY_FRAME_IN, 0, 0 }, + { MEDIA_PAD_FL_SINK, OPE_ENTITY_PARAMS, 0, 0 }, + { MEDIA_PAD_FL_SOURCE, OPE_ENTITY_DISP, OPE_DISP_PAD_SINK, + MEDIA_LNK_FL_ENABLED }, + { } + }, + }, + [OPE_ENTITY_DISP] =3D { + .name =3D "ope_disp", + .obj_type =3D MEDIA_ENTITY_TYPE_V4L2_SUBDEV, + .function =3D MEDIA_ENT_F_PROC_VIDEO_SCALER, + .subdev.ops =3D &ope_disp_ops, + .subdev.internal_ops =3D &ope_disp_internal_ops, + .subdev.entity_ops =3D &ope_disp_entity_ops, + .pads =3D (const struct camss_isp_pad_desc[]) { + { MEDIA_PAD_FL_SINK, OPE_ENTITY_PROC, OPE_PROC_PAD_SOURCE, 0 }, + { MEDIA_PAD_FL_SOURCE, OPE_ENTITY_DISP_OUT, 0, 0 }, + { } + }, + }, +}; + +static int ope_v4l2_init(struct ope_dev *ope) +{ + int ret; + + mutex_init(&ope->mutex); + INIT_LIST_HEAD(&ope->ctx_list); + spin_lock_init(&ope->sched_lock); + INIT_WORK(&ope->run_work, ope_run_work); + + ope->mdev.dev =3D ope->dev; + strscpy(ope->mdev.model, OPE_NAME, sizeof(ope->mdev.model)); + media_device_init(&ope->mdev); + ope->v4l2_dev.mdev =3D &ope->mdev; + + ret =3D v4l2_device_register(ope->dev, &ope->v4l2_dev); + if (ret) + goto err_mdev_cleanup; + + ret =3D media_device_register(&ope->mdev); + if (ret) + goto err_v4l2; + + ope->pipeline =3D camss_isp_pipeline_alloc(OPE_ENTITY_COUNT); + if (IS_ERR(ope->pipeline)) { + ret =3D PTR_ERR(ope->pipeline); + goto err_media; + } + + ret =3D camss_isp_pipeline_register(ope->pipeline, &ope->v4l2_dev, + ope_entity_descs, ARRAY_SIZE(ope_entity_descs)); + if (ret) { + camss_isp_pipeline_free(ope->pipeline); + ope->pipeline =3D NULL; + goto err_media; + } + + ope->pipeline->drv_priv =3D ope; + return 0; + +err_media: + media_device_unregister(&ope->mdev); +err_v4l2: + v4l2_device_unregister(&ope->v4l2_dev); +err_mdev_cleanup: + media_device_cleanup(&ope->mdev); + return ret; +} + +static void ope_v4l2_cleanup(struct ope_dev *ope) +{ + if (ope->pipeline) { + camss_isp_pipeline_unregister(ope->pipeline); + camss_isp_pipeline_free(ope->pipeline); + ope->pipeline =3D NULL; + } + media_device_unregister(&ope->mdev); + v4l2_device_unregister(&ope->v4l2_dev); + media_device_cleanup(&ope->mdev); + cancel_work_sync(&ope->run_work); +} + +static int ope_soft_reset(struct ope_dev *ope) +{ + u32 version; + int ret; + + ret =3D pm_runtime_resume_and_get(ope->dev); + if (ret) + return dev_err_probe(ope->dev, ret, "resume failed\n"); + + version =3D ope_read(ope, OPE_TOP_HW_VERSION); + dev_dbg(ope->dev, "HW version %u.%u.%u\n", + (u32)FIELD_GET(OPE_TOP_HW_VERSION_GEN, version), + (u32)FIELD_GET(OPE_TOP_HW_VERSION_REV, version), + (u32)FIELD_GET(OPE_TOP_HW_VERSION_STEP, version)); + + reinit_completion(&ope->reset_complete); + ope_write(ope, OPE_TOP_RESET_CMD, OPE_TOP_RESET_CMD_SW); + + if (!wait_for_completion_timeout(&ope->reset_complete, + msecs_to_jiffies(OPE_RESET_TIMEOUT_MS))) { + dev_err(ope->dev, "Reset timeout\n"); + pm_runtime_put(ope->dev); + return -ETIMEDOUT; + } + + pm_runtime_put(ope->dev); + + return 0; +} + +static int ope_init_power(struct ope_dev *ope) +{ + struct dev_pm_domain_list *pmdomains; + struct device *dev =3D ope->dev; + struct clk *axi_clk; + int ret; + + ope->icc_data =3D devm_of_icc_get(dev, "data"); + if (IS_ERR(ope->icc_data)) + return dev_err_probe(dev, PTR_ERR(ope->icc_data), + "failed to get interconnect data path\n"); + + ope->icc_config =3D devm_of_icc_get(dev, "config"); + if (IS_ERR(ope->icc_config)) + return dev_err_probe(dev, PTR_ERR(ope->icc_config), + "failed to get interconnect config path\n"); + + devm_pm_domain_attach_list(dev, NULL, &pmdomains); + + /* + * The AXI clock is shared with other CAMSS blocks and isn't + * dynamically scaled by this driver, just pick a rate that is + * high enough to sustain the maximum OPE throughput. + */ + axi_clk =3D devm_clk_get_optional(dev, "axi"); + if (IS_ERR(axi_clk)) + return dev_err_probe(dev, PTR_ERR(axi_clk), "failed to get AXI clock\n"); + + ret =3D clk_set_rate(axi_clk, OPE_AXI_CLK_RATE); + if (ret) + return dev_err_probe(dev, ret, "failed to set AXI clock rate\n"); + + ret =3D devm_pm_opp_set_clkname(dev, "core"); + if (ret) + return ret; + + ret =3D devm_pm_opp_of_add_table(dev); + if (ret && ret !=3D -ENODEV) + return dev_err_probe(dev, ret, "invalid OPP table\n"); + + ret =3D devm_pm_runtime_enable(dev); + if (ret) + return ret; + + ret =3D devm_pm_clk_create(dev); + if (ret) + return ret; + + ret =3D of_pm_clk_add_clks(dev); + if (ret < 0) + return ret; + + return 0; +} + +static int ope_init_mmio(struct ope_dev *ope) +{ + struct platform_device *pdev =3D to_platform_device(ope->dev); + + ope->base =3D devm_platform_ioremap_resource_byname(pdev, "top"); + if (IS_ERR(ope->base)) + return PTR_ERR(ope->base); + + ope->base_rd =3D devm_platform_ioremap_resource_byname(pdev, "bus_read"); + if (IS_ERR(ope->base_rd)) + return PTR_ERR(ope->base_rd); + + ope->base_wr =3D devm_platform_ioremap_resource_byname(pdev, "bus_write"); + if (IS_ERR(ope->base_wr)) + return PTR_ERR(ope->base_wr); + + ope->base_pp =3D devm_platform_ioremap_resource_byname(pdev, "pipeline"); + if (IS_ERR(ope->base_pp)) + return PTR_ERR(ope->base_pp); + + return 0; +} + +static int ope_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct ope_dev *ope; + int ret, irq; + + ope =3D devm_kzalloc(dev, sizeof(*ope), GFP_KERNEL); + if (!ope) + return -ENOMEM; + + ope->dev =3D dev; + init_completion(&ope->reset_complete); + + ret =3D dma_set_mask(dev, DMA_BIT_MASK(32)); + if (ret) + return dev_err_probe(dev, ret, "Failed to set DMA mask\n"); + + ret =3D ope_init_power(ope); + if (ret) + return dev_err_probe(dev, ret, "Power init failed\n"); + + ret =3D ope_init_mmio(ope); + if (ret) + return dev_err_probe(dev, ret, "MMIO init failed\n"); + + irq =3D platform_get_irq(pdev, 0); + if (irq < 0) + return dev_err_probe(dev, irq, "Unable to get IRQ\n"); + + ret =3D devm_request_irq(dev, irq, ope_irq, 0, "camss-ope", ope); + if (ret < 0) + return dev_err_probe(dev, ret, "Requesting IRQ failed\n"); + + ret =3D ope_soft_reset(ope); + if (ret) + return ret; + + ret =3D ope_v4l2_init(ope); + if (ret) + return dev_err_probe(dev, ret, "V4L2 init failed\n"); + + platform_set_drvdata(pdev, ope); + + return 0; +} + +static void ope_remove(struct platform_device *pdev) +{ + struct ope_dev *ope =3D platform_get_drvdata(pdev); + + ope_v4l2_cleanup(ope); +} + +static const struct of_device_id ope_dt_ids[] =3D { + { .compatible =3D "qcom,qcm2290-camss-ope" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ope_dt_ids); + +static const struct dev_pm_ops ope_pm_ops =3D { + SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + +static struct platform_driver ope_driver =3D { + .probe =3D ope_probe, + .remove =3D ope_remove, + .driver =3D { + .name =3D OPE_NAME, + .of_match_table =3D ope_dt_ids, + .pm =3D &ope_pm_ops, + }, +}; + +module_platform_driver(ope_driver); + +MODULE_DESCRIPTION("CAMSS Offline Processing Engine"); +MODULE_AUTHOR("Loic Poulain "); +MODULE_LICENSE("GPL"); --=20 2.34.1 From nobody Sun Jul 26 01:48:11 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8B0253F1AD9 for ; Fri, 10 Jul 2026 09:05:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674310; cv=none; b=Ff3VGIGqtIsl2QdSfoFeDK1Bp8kIIMiluNMJxuFRYjz6cXOoyLRp8ldGs+SG/XBAcqlqacU/WNr6tI9gJL0tbKtBH41ydvwNJJsCWhkmD0/sUunigFR4uwAX0EnQLsShwNuJlB0LFDQ4Uv0V8JCphHyEGCsigF9lQqP7M2Bc7EQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783674310; c=relaxed/simple; bh=SzvTslI/0rJw+ojS6p2oVMofrR8mq52q6PRHLns1hqM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=CSX74d8XYGb4oYrO8jglDJgQddDnkB3AySt+hU/8N/QZLnbvbJH2u7EH4ZBYht3lMQPXLSbnV6QbAo6HXCrXk1/KWJjxMmHopwfrq8MTWKkByOL+CFol8uRvhoReAActA2upBTZpVq+KhORjvRwT2pGOku5LGKMBpd1rdyu+jiA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=b6g0Ju2w; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=kW+em/Ni; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="b6g0Ju2w"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="kW+em/Ni" Received: from pps.filterd (m0279870.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66A7daG7140865 for ; Fri, 10 Jul 2026 09:05:07 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= C4yczvZPnvjUavtM9iyfNq5RJVHkn2SJJHAhN5SbVGM=; b=b6g0Ju2wuJ4rnKTx F7t9LsGn0cpBhoJgBYJQTCK+gDqmIjHboPgtuR0/Uqx3DP2rxw1Jd6qLXxIiVZ+r EO7v7SouPiOtBXF7B4kuqcLScjY7o9pz+pUhwe6zyuwImHg7LEVeKc9jtcI/IBW9 aF3FvuKg38GE/eb1NCT4nefkHM8YuWPOxbsuHl0UBBekQOGpXMoOiEQvJyATnFGd iVk3eVTWDZAZFu6bpNbdbhHzA/0BBFn0O3/9VO16YZ4Ty5Sau8TqA0SmAoURGdo8 mj5NqNH3huYpzuZ899ngpQIO7dKkQCAB5p+f5IIO69V2Jj2wL+kGfxIHO1v7HCUy 1KTgVg== Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4facqpm8wf-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 10 Jul 2026 09:05:07 +0000 (GMT) Received: by mail-qt1-f197.google.com with SMTP id d75a77b69052e-51a87ef9713so19075411cf.1 for ; Fri, 10 Jul 2026 02:05:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1783674307; x=1784279107; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=C4yczvZPnvjUavtM9iyfNq5RJVHkn2SJJHAhN5SbVGM=; b=kW+em/Nia1uxmyJ6BhN4NkF5Ha4vQum7wwruzfH+yKkiYnVbW5Nm1Ipc8QsPEwzQjb hDVUaYUCujF3DlHlsePCoIPr3ueToNdcDh8ZGR4pNL8e/EqnfFence7u5UW8KigCqld9 NFwuEWONfy/heb2RYPf6j6QyMZp/PZPK0WgnfjL7qoiiSRVZZshJVfUAipdA94Mt3Ful 5K62BeKf1rDVWVo6cexYU2E1GrfZ9n6U2s2UC5uQFAaqBLrtFJnqmERGZA6ToV9hZf4k NgmYyRwptybW+KX8/d+0TouIi8x+s+FNM01o92VdyYr2PCTnD5Ral4d1gx48kE9LNh5W yyFg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783674307; x=1784279107; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=C4yczvZPnvjUavtM9iyfNq5RJVHkn2SJJHAhN5SbVGM=; b=OrqVE7dJYOjiyGOmLB1YWl7kfxyNcjjDztWEmlxrWoawKw54vymOM/kYhUoFE26PTG ETm2df8dvtjjT/PLEr35qHOpN0/DZhnc4Z4/yV9RN/0ERI4vbufTg/jBF9K24/ZY7Nom DSf27InDu4i3KSNdUnvoxFL0qC9uCYElBkpCbDOECAoA9gp/j8oTbFGeAj9KFfPxQZZv OzlWOCIkF/SRE6oiLnLdwrMvTeea1u20PyAyMbmZCFxUdKNovzyXkFKQQCugjB9T5Nsg m/Z2F82sJYyXCkSNS3ugfs2sbxy+uK1wdNDya9euBvsnITQkPB5qwzs3NNh2KCg+cpsH j6ug== X-Gm-Message-State: AOJu0YxGerWyZIRBa03rpoqLZqIz0YHX2XUlf0aQmtayirn0IYCetO2j gTHYa5UBwB3o9YCZxH3FQ4Qnv41tbYXxkykMi54M7LsO+GvTaGhbVtS54POjdcvwMilgYISnglJ vhmanaD3FFhtSerLLjOlxO/MP7RscjrAYZT+0N8GOSO5D8yWvrDoC5+IH57hgsYaPZjGqTk1sdq D4Cw== X-Gm-Gg: AfdE7cl9JKzOc61L9baq+FjehQ4eydCnlYFo/uVridTiepMorfZhO86+X/3rw215YYq 5Sfi/6Bu8vlIE1z+8YfWI5aCIhfdCBdjoE1RETXcz1+jdHoJTsZI7Bc1/FaDFcHJb36FXcbk3mO DglIqbn6Mduo/mO3lmrzxJnKxOBidoK9YjkDTICy3p+viAdyJWhGXxmzzr/CoizmKzgvg12cTP1 4Dm1EeaDAEuPc/w9W9Z6eFa33osyIopFv1ZmCZ9rZxqKxMAO1aJKaLUF6zdwOk9XM6as5X3Z+xl WU0Ac7sAtb1BD6MxODUG8XtTuODFIsdbX7reRnSUSmgRPKwxYRF7QCIkApQ66tOECwlnIkOp54E cwvPm3PQV0scd0uKBpTnIB8Ee6bm/DXA7e2ml6eEf9aw6PD7pORsrVhZYdlts1srCuYqaLqZUMc FgQ5h6V345H4Ug575uAxpS4DJfvEhdk+oIMrxV/m+Y5F2sBo0pnJTIN560snBKVA== X-Received: by 2002:a05:622a:11:b0:516:dc75:1aca with SMTP id d75a77b69052e-51caa1286d1mr26301291cf.26.1783674306947; Fri, 10 Jul 2026 02:05:06 -0700 (PDT) X-Received: by 2002:a05:622a:11:b0:516:dc75:1aca with SMTP id d75a77b69052e-51caa1286d1mr26300891cf.26.1783674306432; Fri, 10 Jul 2026 02:05:06 -0700 (PDT) Received: from QCOM-eG0v1AUPpu.na.qualcomm.com ([82.64.236.198]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c15dfda815dsm259932266b.36.2026.07.10.02.05.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 02:05:05 -0700 (PDT) From: Loic Poulain Date: Fri, 10 Jul 2026 11:04:53 +0200 Subject: [PATCH v4 7/7] arm64: dts: qcom: agatti: Add OPE node Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260710-camss-isp-ope-v4-7-51207a0319d8@oss.qualcomm.com> References: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> In-Reply-To: <20260710-camss-isp-ope-v4-0-51207a0319d8@oss.qualcomm.com> To: Bryan O'Donoghue , Vladimir Zapolskiy , Loic Poulain , Mauro Carvalho Chehab , Kees Cook , "Gustavo A. R. Silva" , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bjorn Andersson , Konrad Dybcio Cc: linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-hardening@vger.kernel.org, devicetree@vger.kernel.org X-Mailer: b4 0.14.2 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfXwFWNKsn5BXRu NamoITX76z5pVnSJ7DzOIKG7QHsBqjV3JhLN92pXNitKI6S1LetPqvWOSZf3ze/NEZ6t4kp9Isl eTrBXmut+FM5cCj17BNS17bepRUbm79wF29fzFq67RtAf6OaJgPDUmCZqghRlcyqPJIG5M77WTQ THYdc+nj0T6v+uS9nxkqgf/dhDFswNBQB8PFDgiQ6voojrJotQw5RzFciMKVEYWcUkvcnpzL3pI Gepnoq0cuqVXBTIEeNJ433zB35xgp9I5JvfLtcd/Z1ByNyeUK8x09uWKLgtXfi7CA1Oq4pKxlek +8IrX3o498H1Bs4saE7HCVxKtOgosajQ1VQ+9nT/f9x696hEs0egHpTR1hLMfGo7gsF9Z9+RV0C 6ziJkhuVOJEu6AZhcR8Xp3VMZBqcC0jgu+TFEVLs5Y8l8c+r6VuThqJ/1XvEg6On6q0erPb9W6F 6g70hKpX/yp5xJvu6Nw== X-Proofpoint-Spam-Info: AW1haW4tMjYwNzEwMDA4OCBTYWx0ZWRfX5xepvYnai2n3 doRu/YA7MOAYUe0VS85xZlrASDrLYXy72teqbD03gFsLEBhHAY3c61lfXrLmj2kHuACgSYJ2Gjt +4LBdWSNVqDJRZ1PViPPzQ/MiOJBq2g= X-Proofpoint-GUID: oVZ3B_KdkNOGTHXDDwQPwXb-LVq-lP7P X-Proofpoint-ORIG-GUID: oVZ3B_KdkNOGTHXDDwQPwXb-LVq-lP7P X-Authority-Analysis: v=2.4 cv=GJ441ONK c=1 sm=1 tr=0 ts=6a50b5c3 cx=c_pps a=EVbN6Ke/fEF3bsl7X48z0g==:117 a=MDeckJw97qnk8wCBExTehA==:17 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=gowsoOTTUOVcmtlkKump:22 a=EUspDBNiAAAA:8 a=1BEw5LmG3YzgyHxDt7YA:9 a=iG3BWBHXWT8vAauT:21 a=QEXdDO2ut3YA:10 a=a_PwQJl-kcHnX1M80qC6:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-10_02,2026-07-09_04,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 lowpriorityscore=0 bulkscore=0 priorityscore=1501 clxscore=1015 suspectscore=0 spamscore=0 phishscore=0 adultscore=0 impostorscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607100088 Add the Offline Processing Engine (OPE) device tree node for the Agatti platform (QCM2290). The node describes the five register regions (top, bus_read, bus_write, pipeline, qos), clocks, interrupt, interconnects, IOMMU mappings, and OPP table. Signed-off-by: Loic Poulain --- arch/arm64/boot/dts/qcom/agatti.dtsi | 74 ++++++++++++++++++++++++++++++++= ++++ 1 file changed, 74 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/agatti.dtsi b/arch/arm64/boot/dts/qco= m/agatti.dtsi index f9b46cf1c6462a89784429565e1636ce2ba68d73..87bdf9df4b16f7f1b9a5129cf8c= d81da1ca9da97 100644 --- a/arch/arm64/boot/dts/qcom/agatti.dtsi +++ b/arch/arm64/boot/dts/qcom/agatti.dtsi @@ -1935,6 +1935,80 @@ port@1 { }; }; =20 + isp_ope: isp@5c42400 { + compatible =3D "qcom,qcm2290-camss-ope"; + + reg =3D <0x0 0x5c42400 0x0 0x200>, + <0x0 0x5c42600 0x0 0x200>, + <0x0 0x5c42800 0x0 0x4400>, + <0x0 0x5c46c00 0x0 0x190>, + <0x0 0x5c46d90 0x0 0xa00>; + reg-names =3D "top", + "qos", + "pipeline", + "bus_read", + "bus_write"; + + clocks =3D <&gcc GCC_CAMSS_AXI_CLK>, + <&gcc GCC_CAMSS_OPE_CLK>, + <&gcc GCC_CAMSS_OPE_AHB_CLK>, + <&gcc GCC_CAMSS_NRT_AXI_CLK>, + <&gcc GCC_CAMSS_TOP_AHB_CLK>; + clock-names =3D "axi", + "core", + "iface", + "nrt", + "top"; + + interrupts =3D ; + + interconnects =3D <&bimc MASTER_APPSS_PROC RPM_ACTIVE_TAG + &config_noc SLAVE_CAMERA_CFG RPM_ACTIVE_TAG>, + <&mmnrt_virt MASTER_CAMNOC_SF RPM_ALWAYS_TAG + &bimc SLAVE_EBI1 RPM_ALWAYS_TAG>; + interconnect-names =3D "config", + "data"; + + iommus =3D <&apps_smmu 0x820 0x0>, + <&apps_smmu 0x840 0x0>; + + operating-points-v2 =3D <&ope_opp_table>; + power-domains =3D <&gcc GCC_CAMSS_TOP_GDSC>, + <&rpmpd QCM2290_VDDCX>; + power-domain-names =3D "camss", + "cx"; + + ope_opp_table: opp-table { + compatible =3D "operating-points-v2"; + + opp-19200000 { + opp-hz =3D /bits/ 64 <19200000>; + required-opps =3D <&rpmpd_opp_min_svs>; + }; + + opp-200000000 { + opp-hz =3D /bits/ 64 <200000000>; + required-opps =3D <&rpmpd_opp_svs>; + }; + + opp-266600000 { + opp-hz =3D /bits/ 64 <266600000>; + required-opps =3D <&rpmpd_opp_svs_plus>; + }; + + opp-465000000 { + opp-hz =3D /bits/ 64 <465000000>; + required-opps =3D <&rpmpd_opp_nom>; + }; + + opp-580000000 { + opp-hz =3D /bits/ 64 <580000000>; + required-opps =3D <&rpmpd_opp_turbo>; + turbo-mode; + }; + }; + }; + mdss: display-subsystem@5e00000 { compatible =3D "qcom,qcm2290-mdss"; reg =3D <0x0 0x05e00000 0x0 0x1000>; --=20 2.34.1