From patchwork Mon Nov 25 09:21:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuji Ishikawa X-Patchwork-Id: 845473 Received: from mo-csw-fb.securemx.jp (mo-csw-fb1802.securemx.jp [210.130.202.161]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3547AA35; Mon, 25 Nov 2024 11:35:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.130.202.161 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732534516; cv=none; b=jme9rGNx5Ye6ode0oOhGv58wJf150rb3vq7RBSjnXZb7fENbeJ/z6SeUdFlvaeFLckDrtVLm1mupvnOzsuDrxjsdJdudiURCNexVhR++NgqmIx1Z4gAgBG48wLKx6rXMJyJ2DgN402dwdFvFKQh51S/crT47Ic2x7trIXRn0AsU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732534516; c=relaxed/simple; bh=s2QBTYvjALWbK/9t34XSfK/DRgxA5dhcbwCLcFUYzbo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=t1Lg6glUQrQEq5GAIl8I3MeqGr1WePP5lXG9BvG+Z9f/r8hs1Ps9NTkiWAoMEw2ZjxQ7xpRJsGBnu77bIlbXhP7VBzpRbJkhIvwstQUys7ZgUR4eEzxpBomMmvJbQzFpbfI0rt9T7+BnBsTM2a2E4twORXRIyQ7uBjGY/3Yu83A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp; spf=pass smtp.mailfrom=toshiba.co.jp; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b=ptyofG6g; arc=none smtp.client-ip=210.130.202.161 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b="ptyofG6g" Received: by mo-csw-fb.securemx.jp (mx-mo-csw-fb1802) id 4AP9RmHF2534971; Mon, 25 Nov 2024 18:27:48 +0900 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=toshiba.co.jp; h=From:To:Cc :Subject:Date:Message-Id:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding;i=yuji2.ishikawa@toshiba.co.jp;s=key2.smx;t= 1732526832; x=1733736432; bh=s2QBTYvjALWbK/9t34XSfK/DRgxA5dhcbwCLcFUYzbo=; b=pty ofG6gvZXuTXmZGloaGRHFDN5vnjmOEroyjENtiofn8xO4VTjp1TLme5RC2i6D8x3XF0t0m8T9w7Ax eObOVKuKAH7pi/SsJ7ANV+MV6BWFBqbouVlZj4HaG8jW+vh1YfDx9t1jImAXui5x6rX/G28eqTtRy 5ez1eumDiY5rFvYCj/es1wcgnRSm5/M6LTcL00EIdz1d+wSL2X9qcV4TGJzIAntKwhmHyrsf6Gsx+ 9v2kYeJIQDRaI4j+xqsjjoWCbhpo3v0VKpvpXsC+SmUdq1Mr/0VCfB2Xd/96ZHRXWL1/6z7D0dLY2 gWYDXlRF9EAjG51TwXKBuivXBa19y3A==; Received: by mo-csw.securemx.jp (mx-mo-csw1801) id 4AP9RCWA2549162; Mon, 25 Nov 2024 18:27:12 +0900 X-Iguazu-Qid: 2yAb1JZRvDQIMBFaAK X-Iguazu-QSIG: v=2; s=0; t=1732526831; q=2yAb1JZRvDQIMBFaAK; m=N3BxzmwQ7zs0tp7sqbG9wCdMkvuwdICtP93CLzazhJo= Received: from imx12-a.toshiba.co.jp ([38.106.60.135]) by relay.securemx.jp (mx-mr1802) id 4AP9R9Gm4129038 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 25 Nov 2024 18:27:10 +0900 X-SA-MID: 32872668 From: Yuji Ishikawa To: Laurent Pinchart , Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sakari Ailus , Hans Verkuil , Nobuhiro Iwamatsu , Yuji Ishikawa Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Subject: [PATCH v12 1/8] dt-bindings: media: platform: visconti: Add Toshiba Visconti MIPI CSI-2 Receiver Date: Mon, 25 Nov 2024 18:21:39 +0900 X-TSB-HOP2: ON Message-Id: <20241125092146.1561901-2-yuji2.ishikawa@toshiba.co.jp> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> References: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Adds the Device Tree binding documentation that allows to describe the MIPI CSI-2 Receiver found in Toshiba Visconti SoCs. Signed-off-by: Yuji Ishikawa Reviewed-by: Nobuhiro Iwamatsu --- Changelog v12: - Newly add bindings for CSI2RX driver .../media/toshiba,visconti5-csi2rx.yaml | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/toshiba,visconti5-csi2rx.yaml diff --git a/Documentation/devicetree/bindings/media/toshiba,visconti5-csi2rx.yaml b/Documentation/devicetree/bindings/media/toshiba,visconti5-csi2rx.yaml new file mode 100644 index 000000000000..5488072bc82a --- /dev/null +++ b/Documentation/devicetree/bindings/media/toshiba,visconti5-csi2rx.yaml @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/toshiba,visconti5-csi2rx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Toshiba Visconti5 SoC MIPI CSI-2 receiver + +maintainers: + - Nobuhiro Iwamatsu + +description: |- + Toshiba Visconti5 SoC MIPI CSI-2 receiver device receives MIPI CSI-2 video + stream. Use with VIIF device. T.B.D + +properties: + compatible: + const: toshiba,visconti5-csi2rx + + reg: + items: + - description: Registers for CSI2 receiver control + + interrupts: + items: + - description: CSI2 Receiver Interrupt + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Input port node, single endpoint describing the CSI-2 transmitter. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + description: CSI2 receiver supports 1, 2, 3 or 4 data lanes + minItems: 1 + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + required: + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + Output port node, single endpoint describing the Visconti VIIF. + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - interrupts + - ports + +additionalProperties: false + +examples: + - | + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + csi2rx@1c008000 { + compatible = "toshiba,visconti5-csi2rx"; + reg = <0 0x1c008000 0 0x400>; + interrupts = ; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + csi2rx_in0: endpoint { + data-lanes = <1 2>; + remote-endpoint = <&imx219_out0>; + }; + }; + port@1 { + reg = <1>; + csi2rx_out0: endpoint { + remote-endpoint = <&csi_in0>; + }; + }; + }; + }; + }; From patchwork Mon Nov 25 09:21:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuji Ishikawa X-Patchwork-Id: 845476 Received: from mo-csw-fb.securemx.jp (mo-csw-fb1801.securemx.jp [210.130.202.160]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 39C48192B90; Mon, 25 Nov 2024 10:49:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.130.202.160 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732531753; cv=none; b=Y6fqfRnn8ktX30CBamx2Mi8vVWuhXAM8b1xB3Tv5Cf4o5Kz5jDWRle+BcU9BIar+mWWOafQgQmXgpufp4UOF10xzrYUDKO+kVxUto4d2cCRf0DByh9SQXH1RwzRmBM/bjddyCzkrZm74g9AglrkBVjI3ROi34o3iunKWv/NpQk0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732531753; c=relaxed/simple; bh=Qr2pR5iOFRSZ7+g1nq5Xf9e/SUtdQos9mUpQSfloGDA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=FUVjVsCjBxxAgtwf2amQIuZvRyj/dO2IASejRBlUlfJvndQ3rU2370OPZBSWlUWbvv1h6insymmqJikbT+/ad7YXkXV8+QHHRKUTPDLYawuMQ2LjkdWhHotRiSWljRLuqohullCADYcuV/6HgykghMr0GYOXnxIrXxaKzt0AkgU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp; spf=pass smtp.mailfrom=toshiba.co.jp; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b=lvIL7ATL; arc=none smtp.client-ip=210.130.202.160 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b="lvIL7ATL" Received: by mo-csw-fb.securemx.jp (mx-mo-csw-fb1801) id 4AP9RkbI2159794; Mon, 25 Nov 2024 18:27:47 +0900 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=toshiba.co.jp; h=From:To:Cc :Subject:Date:Message-Id:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding;i=yuji2.ishikawa@toshiba.co.jp;s=key2.smx;t= 1732526832; x=1733736432; bh=Qr2pR5iOFRSZ7+g1nq5Xf9e/SUtdQos9mUpQSfloGDA=; b=lvI L7ATLHp6dSGjnKwSK8Da4e0uIogyTReWWuXo5KI0bM59tSckzXRoE2qzlW1hThceXYDpLFTjsDmqd LtC8ko4GmAltxivM3q3uq9FmXDdd7LWTOXT4gLxpx9GFblKkxyf04tIm38QWEn01CybXqDWBglxJy wfKH2It+dBRWwOwBXQCtrkDIv5+jtHfeDWFCOcOlTizfBXtaKZ4jK9sRMFmlr/Y86Ty2kBH1UFxZy UscyqlwwHknh3T67GJFs2mN7qWSO0z5JTMxQSRCdYtLXxZeDXQvrN9KNlQ8HRgGBAV48mKYyaZBKL EO+mcM50BA3FDWM03YI+cgEbqa92/cg==; Received: by mo-csw.securemx.jp (mx-mo-csw1800) id 4AP9RBDL023848; Mon, 25 Nov 2024 18:27:11 +0900 X-Iguazu-Qid: 2yAb1JZRvDQIMBFaAE X-Iguazu-QSIG: v=2; s=0; t=1732526831; q=2yAb1JZRvDQIMBFaAE; m=/swN041lxFAH+9+RIDcVzDvhh2V5tx9Y49KeY+kjvec= Received: from imx12-a.toshiba.co.jp ([38.106.60.135]) by relay.securemx.jp (mx-mr1801) id 4AP9R9Wd512264 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 25 Nov 2024 18:27:10 +0900 X-SA-MID: 32872672 From: Yuji Ishikawa To: Laurent Pinchart , Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sakari Ailus , Hans Verkuil , Nobuhiro Iwamatsu , Yuji Ishikawa Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Subject: [PATCH v12 2/8] dt-bindings: media: platform: visconti: Add Toshiba Visconti Video Input Interface Date: Mon, 25 Nov 2024 18:21:40 +0900 X-TSB-HOP2: ON Message-Id: <20241125092146.1561901-3-yuji2.ishikawa@toshiba.co.jp> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> References: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Adds the Device Tree binding documentation that allows to describe the Video Input Interface found in Toshiba Visconti SoCs. Signed-off-by: Yuji Ishikawa Reviewed-by: Nobuhiro Iwamatsu --- Changelog v2: - no change Changelog v3: - no change Changelog v4: - fix style problems at the v3 patch - remove "index" member - update example Changelog v5: - no change Changelog v6: - add register definition of BUS-IF and MPU Changelog v7: - remove trailing "bindings" from commit header message - remove trailing "Device Tree Bindings" from title - fix text wrapping of description - change compatible to visconti5-viif - explicitly define allowed properties for port::endpoint Changelog v8: - Suggestion from Krzysztof Kozlowski - rename bindings description file - use block style array instead of inline style - remove clock-lane (as it is fixed at position 0) - update sample node's name - use lowercase hex for literals - Suggestion from Laurent Pinchart - update description message port::description - remove port::endpoint::bus-type as it is fixed to <4> - remove port::endpoint::clock-lanes from example - add port::endpoint::data-lanes to required parameters list - fix sequence of data-lanes: <1 2 3 4> because current driver does not support data reordering - update port::endpoint::data-lanes::description - remove redundant type definition for port::endpoint::data-lanes Changelog v9: - place "required" after "properties" - dictionary ordering of properties Changelog v10: - no change Changelog v11: - no change Changelog v12: - remove property "clock-noncontinuous" as VIIF switches both modes automatically - remove property "link-frequencies" as VIIF does not use the information - remove reg[2] and interrupts[3] which are used for CSI2RX driver - update example to refer csi2rx for remote-endpoint .../media/toshiba,visconti5-viif.yaml | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml diff --git a/Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml b/Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml new file mode 100644 index 000000000000..ef0452a47e98 --- /dev/null +++ b/Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/toshiba,visconti5-viif.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Toshiba Visconti5 SoC Video Input Interface + +maintainers: + - Nobuhiro Iwamatsu + +description: |- + Toshiba Visconti5 SoC Video Input Interface (VIIF) receives videostream + from MIPI CSI-2 receiver device, processes the stream with image signal + processors (L1ISP, L2ISP), then stores pictures to main memory. + +properties: + compatible: + const: toshiba,visconti5-viif + + reg: + items: + - description: Registers for capture control + - description: Registers for bus interface unit control + - description: Registers for Memory Protection Unit + + interrupts: + items: + - description: Sync Interrupt + - description: Status (Error) Interrupt + - description: L1ISP Interrupt + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI-2 input port, with a single endpoint connected to the CSI-2 transmitter. + + properties: + endpoint: + $ref: video-interfaces.yaml# + additionalProperties: false + + properties: + data-lanes: + description: VIIF supports 1, 2, 3 or 4 data lanes + minItems: 1 + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + remote-endpoint: true + + required: + - data-lanes + - remote-endpoint + +required: + - compatible + - reg + - interrupts + - port + +additionalProperties: false + +examples: + - | + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + video@1c000000 { + compatible = "toshiba,visconti5-viif"; + reg = <0 0x1c000000 0 0x6000>, + <0 0x1c00e000 0 0x1000>, + <0 0x2417a000 0 0x1000>; + interrupts = , + , + ; + + port { + #address-cells = <1>; + #size-cells = <0>; + + csi_in0: endpoint { + data-lanes = <1 2>; + remote-endpoint = <&csi2rx_out0>; + }; + }; + }; + }; From patchwork Mon Nov 25 09:21:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuji Ishikawa X-Patchwork-Id: 845474 Received: from mo-csw-fb.securemx.jp (mo-csw-fb1800.securemx.jp [210.130.202.159]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 16D94199924; Mon, 25 Nov 2024 11:27:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.130.202.159 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732534082; cv=none; b=aueUtsfXFKnJVUorJYe8NPDMCXGUwqxkVM70GWQlKV9jNmSS65kTkabymAvLCbS9L9hVgsYf2yndbmeRopPpcXf3xHMgwBXYb8gA+nXqrzsOCYhBS5W+QGw4TKNQX+FPNHWcCzJViVM3gKElcQEU+rNISxLiiMNXlZnj0ObreFo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732534082; c=relaxed/simple; bh=J0mvAuwvV6YMmHv11HyZs8PDqoQuS1HOBJbGPaB7TSA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=L5z/8/TP8uH1qfkUEnlMhAG5F2M3LsFLTy4cajZNEH5y2a7CeG7nFy/Xa2wVf0X58M4PIn5fpUGjCvtEGi2l9gDVtAIpUSzhTbuRLCTxz4ZXv06nIOacS5uJVKMjydyZFT7gYKMf5aeX5Z13AIDH+bzmoNpLCubQE0/mwt7FIg0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp; spf=pass smtp.mailfrom=toshiba.co.jp; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b=PZeHxaCb; arc=none smtp.client-ip=210.130.202.159 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b="PZeHxaCb" Received: by mo-csw-fb.securemx.jp (mx-mo-csw-fb1800) id 4AP9RlYD2993029; Mon, 25 Nov 2024 18:27:47 +0900 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=toshiba.co.jp; h=From:To:Cc :Subject:Date:Message-Id:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding;i=yuji2.ishikawa@toshiba.co.jp;s=key2.smx;t= 1732526834; x=1733736434; bh=J0mvAuwvV6YMmHv11HyZs8PDqoQuS1HOBJbGPaB7TSA=; b=PZe HxaCbq2+aeXygF6D8+UCUIvv4geY7nKngbrTSugMHeKlonV68X246PBwATrTKI63UidHGsw0DjmO3 Fg41hpsIvxUik02rwer7UiIR6MmCEARv5WtDbAdbkJbcV/TbZEY1f6/dTtrhs2gM3pjq4XAsHImuJ ODhgNYcX1mXxJkEcwT8WlDykmlo6u5xQZdHq85ZklRGNuuf5HBdESMqU5tIq3c1W6mE08i5fd9GjM EX+ckdbynk1htQAqI5Q8RB4eVuSWM2qQAlul5wee/yxrXO239O0ut96gZ+Jqfco3K4Tf72aYejqvX jhJKrBr+s+JiH2U7I1mg6y1Yp3VD/bA==; Received: by mo-csw.securemx.jp (mx-mo-csw1800) id 4AP9RDcG023988; Mon, 25 Nov 2024 18:27:14 +0900 X-Iguazu-Qid: 2yAa5GSxJ6lY5CrZGV X-Iguazu-QSIG: v=2; s=0; t=1732526833; q=2yAa5GSxJ6lY5CrZGV; m=zfO2799Q2c8IvxS+F5k0v2J48l4RBfpfn/qT1hIeQLg= Received: from imx2-a.toshiba.co.jp (imx2-a.toshiba.co.jp [106.186.93.35]) by relay.securemx.jp (mx-mr1802) id 4AP9RCIP4129100 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 25 Nov 2024 18:27:12 +0900 X-SA-MID: 35004178 From: Yuji Ishikawa To: Laurent Pinchart , Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sakari Ailus , Hans Verkuil , Nobuhiro Iwamatsu , Yuji Ishikawa Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Subject: [PATCH v12 3/8] media: uapi: add visconti viif meta buffer format Date: Mon, 25 Nov 2024 18:21:41 +0900 X-TSB-HOP2: ON Message-Id: <20241125092146.1561901-4-yuji2.ishikawa@toshiba.co.jp> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> References: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Adds the Toshiba Visconti VIIF specific metadata format - V4L2_META_FMT_VISCONTI_VIIF_PARAMS for ISP parameters - V4L2_META_FMT_VISCONTI_VIIF_STATS for ISP statistics Signed-off-by: Yuji Ishikawa --- Changelog v10: - add entry for V4L2_META_FMT_VISCONTI_VIIF_PARAMS - add entry for V4L2_META_FMT_VISCONTI_VIIF_STATS Changelog v11: - no change Changelog v12: - add description for meta formats at v4l2-ioctl.c drivers/media/v4l2-core/v4l2-ioctl.c | 2 ++ include/uapi/linux/videodev2.h | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 0304daa8471d..f7facb63b8ea 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1470,6 +1470,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_META_FMT_RPI_BE_CFG: descr = "RPi PiSP BE Config format"; break; case V4L2_META_FMT_RPI_FE_CFG: descr = "RPi PiSP FE Config format"; break; case V4L2_META_FMT_RPI_FE_STATS: descr = "RPi PiSP FE Statistics format"; break; + case V4L2_META_FMT_VISCONTI_VIIF_PARAMS: descr = "Visconti ISP Parameters"; break; + case V4L2_META_FMT_VISCONTI_VIIF_STATS: descr = "Visconti ISP Statistics"; break; case V4L2_META_FMT_GENERIC_8: descr = "8-bit Generic Metadata"; break; case V4L2_META_FMT_GENERIC_CSI2_10: descr = "8-bit Generic Meta, 10b CSI-2"; break; case V4L2_META_FMT_GENERIC_CSI2_12: descr = "8-bit Generic Meta, 12b CSI-2"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index a5418759e2ba..9e1f66fdf038 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -863,6 +863,10 @@ struct v4l2_pix_format { #define V4L2_META_FMT_RPI_FE_CFG v4l2_fourcc('R', 'P', 'F', 'C') /* PiSP FE configuration */ #define V4L2_META_FMT_RPI_FE_STATS v4l2_fourcc('R', 'P', 'F', 'S') /* PiSP FE stats */ +/* Vendor specific - used for Visconti VIIF sub-system */ +#define V4L2_META_FMT_VISCONTI_VIIF_PARAMS v4l2_fourcc('V', 'I', 'F', 'P') /* ISP Params */ +#define V4L2_META_FMT_VISCONTI_VIIF_STATS v4l2_fourcc('V', 'I', 'F', 'S') /* ISP Stats */ + #ifdef __KERNEL__ /* * Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when From patchwork Mon Nov 25 09:21:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuji Ishikawa X-Patchwork-Id: 845475 Received: from mo-csw-fb.securemx.jp (mo-csw-fb1122.securemx.jp [210.130.202.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E127F195985; Mon, 25 Nov 2024 11:19:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.130.202.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732533560; cv=none; b=UsG1wcR7KEnu8mwKKS76HLIiVDeH8zcoymVKUxAKFsILlPOdfy7StzBW9kyTPNgEcpfm2/Um64IOyLiQb+uw7wrzmsY133UQ3+WhsAJ9tpaTfs0Q4VN96m4W+1vSA/usfWBQBFcINOWvsONlZ/kzK0t+Of+7jpGXqzJ/8uVPHQw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732533560; c=relaxed/simple; bh=uZL+oCJiQQb+B0av6XL1UsHZSXfCpI5C0+fq7+c/Cd4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ssk008OcVt9ZYkZkWjMFpogNS/DFAYvpqRCSpCwP001R9A46kLHTV8YVgmHv7Gva/tyL72lxUvyWn/nkVCO7uufyKaTYutk9el48u0pWFDbyl/aKnregFqg7QLsTCeKntw/8vtjxnWy+KhXEnN8vyxfsGhvRlYKQ67toRO+1D+w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp; spf=pass smtp.mailfrom=toshiba.co.jp; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b=hOoI40JO; arc=none smtp.client-ip=210.130.202.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b="hOoI40JO" Received: by mo-csw-fb.securemx.jp (mx-mo-csw-fb1122) id 4AP9Rp9A262724; Mon, 25 Nov 2024 18:27:51 +0900 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=toshiba.co.jp; h=From:To:Cc :Subject:Date:Message-Id:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding;i=yuji2.ishikawa@toshiba.co.jp;s=key2.smx;t= 1732526839; x=1733736439; bh=uZL+oCJiQQb+B0av6XL1UsHZSXfCpI5C0+fq7+c/Cd4=; b=hOo I40JOjmNHvy1ck871ImK9+MJr43G0d17imXZY9FraHtYGpTsrNd+EwZ9kTl4UQ4OA+hnx4n7yAbBm OpACrxaSDdFPJVeL6DnvmoYQkZD2UWaC5QXHe3f6RrTJsqug1FT/1cilkKiadV8geu+FQTJioBeCr vA5/6UczKjTvHI42ye4uosy7mPHQWyo7Pin/TlO2yWFURuir5Oh/EvQCaIliiK4dyNrAI2g1xVpHU MbBQ8UG8VMJr+e4NnetO12Wo+AHh1b7wqDzcRWzYPQ5iP1UCNeeagoeqMNywwlrPjgyBAJdn99CkP ikWBiab0u+E5yOc9rVuzLssaGr2ElCQ==; Received: by mo-csw.securemx.jp (mx-mo-csw1121) id 4AP9RHpW099343; Mon, 25 Nov 2024 18:27:18 +0900 X-Iguazu-Qid: 2rWgFNitjKYKO6hMyT X-Iguazu-QSIG: v=2; s=0; t=1732526837; q=2rWgFNitjKYKO6hMyT; m=TZXSZwOc5QcMMvGbsQzManldV9UpbFjv5/MHVnIvXaU= Received: from imx12-a.toshiba.co.jp ([38.106.60.135]) by relay.securemx.jp (mx-mr1123) id 4AP9RE95108739 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 25 Nov 2024 18:27:15 +0900 X-SA-MID: 32872675 From: Yuji Ishikawa To: Laurent Pinchart , Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sakari Ailus , Hans Verkuil , Nobuhiro Iwamatsu , Yuji Ishikawa Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Subject: [PATCH v12 4/8] media: platform: visconti: Add Toshiba Visconti CSI-2 Receiver driver Date: Mon, 25 Nov 2024 18:21:42 +0900 X-TSB-HOP2: ON Message-Id: <20241125092146.1561901-5-yuji2.ishikawa@toshiba.co.jp> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> References: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add support to MIPI CSI-2 Receiver on Toshiba Visconti ARM SoCs. This driver is used with Visconti Video Input Interface driver. Signed-off-by: Yuji Ishikawa --- Changelog v12: - Separate CSI2RX driver and made it independent driver - viif_csi2rx subdevice driver (in v11 patch) was removed. - dictionary order at Kconfig and Makefile drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 1 + drivers/media/platform/toshiba/Kconfig | 6 + drivers/media/platform/toshiba/Makefile | 2 + .../media/platform/toshiba/visconti/Kconfig | 16 + .../media/platform/toshiba/visconti/Makefile | 8 + .../platform/toshiba/visconti/csi2rx_drv.c | 791 ++++++++++++++++++ 7 files changed, 825 insertions(+) create mode 100644 drivers/media/platform/toshiba/Kconfig create mode 100644 drivers/media/platform/toshiba/Makefile create mode 100644 drivers/media/platform/toshiba/visconti/Kconfig create mode 100644 drivers/media/platform/toshiba/visconti/Makefile create mode 100644 drivers/media/platform/toshiba/visconti/csi2rx_drv.c diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 85d2627776b6..761b15b07b90 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -86,6 +86,7 @@ source "drivers/media/platform/samsung/Kconfig" source "drivers/media/platform/st/Kconfig" source "drivers/media/platform/sunxi/Kconfig" source "drivers/media/platform/ti/Kconfig" +source "drivers/media/platform/toshiba/Kconfig" source "drivers/media/platform/verisilicon/Kconfig" source "drivers/media/platform/via/Kconfig" source "drivers/media/platform/xilinx/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index ace4e34483dd..917145fe5171 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -29,6 +29,7 @@ obj-y += samsung/ obj-y += st/ obj-y += sunxi/ obj-y += ti/ +obj-y += toshiba/ obj-y += verisilicon/ obj-y += via/ obj-y += xilinx/ diff --git a/drivers/media/platform/toshiba/Kconfig b/drivers/media/platform/toshiba/Kconfig new file mode 100644 index 000000000000..f02983f4fc97 --- /dev/null +++ b/drivers/media/platform/toshiba/Kconfig @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Toshiba media platform drivers" + +source "drivers/media/platform/toshiba/visconti/Kconfig" + diff --git a/drivers/media/platform/toshiba/Makefile b/drivers/media/platform/toshiba/Makefile new file mode 100644 index 000000000000..2bce85ef3b48 --- /dev/null +++ b/drivers/media/platform/toshiba/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += visconti/ diff --git a/drivers/media/platform/toshiba/visconti/Kconfig b/drivers/media/platform/toshiba/visconti/Kconfig new file mode 100644 index 000000000000..e5c92d598f8b --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_VISCONTI_CSI2RX + tristate "Visconti MIPI CSI-2 Receiver driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + depends on ARCH_VISCONTI || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + Support for Toshiba Visconti MIPI CSI-2 receiver, + which is used with Visconti Camera Interface driver. + + This driver yields 1 subdevice node for a hardware instance. + To compile this driver as a module, choose M here: the + module will be called visconti-csi2rx. diff --git a/drivers/media/platform/toshiba/visconti/Makefile b/drivers/media/platform/toshiba/visconti/Makefile new file mode 100644 index 000000000000..62a029376134 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Visconti video input device driver +# + +visconti-csi2rx-objs = csi2rx_drv.o + +obj-$(CONFIG_VIDEO_VISCONTI_CSI2RX) += visconti-csi2rx.o diff --git a/drivers/media/platform/toshiba/visconti/csi2rx_drv.c b/drivers/media/platform/toshiba/visconti/csi2rx_drv.c new file mode 100644 index 000000000000..94567963872a --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/csi2rx_drv.c @@ -0,0 +1,791 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* CSI2HOST register space */ +#define REG_CSI2RX_NLANES 0x4 +#define REG_CSI2RX_RESETN 0x8 +#define REG_CSI2RX_INT_ST_MAIN 0xc +#define REG_CSI2RX_DATA_IDS_1 0x10 +#define REG_CSI2RX_DATA_IDS_2 0x14 +#define REG_CSI2RX_PHY_SHUTDOWNZ 0x40 +#define REG_CSI2RX_PHY_RSTZ 0x44 + +/* access to dphy external registers */ +#define REG_CSI2RX_PHY_TESTCTRL0 0x50 +#define BIT_TESTCTRL0_CLK_0 0 +#define BIT_TESTCTRL0_CLK_1 BIT(1) + +#define REG_CSI2RX_PHY_TESTCTRL1 0x54 +#define BIT_TESTCTRL1_ADDR BIT(16) +#define MASK_TESTCTRL1_DIN 0xff +#define MASK_TESTCTRL1_DOUT 0xff00 + +#define REG_CSI2RX_INT_ST_PHY_FATAL 0xe0 +#define REG_CSI2RX_INT_MSK_PHY_FATAL 0xe4 +#define MASK_PHY_FATAL_ALL 0x0000000f + +#define REG_CSI2RX_INT_ST_PKT_FATAL 0xf0 +#define REG_CSI2RX_INT_MSK_PKT_FATAL 0xf4 +#define MASK_PKT_FATAL_ALL 0x0001000f + +#define REG_CSI2RX_INT_ST_FRAME_FATAL 0x100 +#define REG_CSI2RX_INT_MSK_FRAME_FATAL 0x104 +#define MASK_FRAME_FATAL_ALL 0x000f0f0f + +#define REG_CSI2RX_INT_ST_PHY 0x110 +#define REG_CSI2RX_INT_MSK_PHY 0x114 +#define MASK_PHY_ERROR_ALL 0x000f000f + +#define REG_CSI2RX_INT_ST_PKT 0x120 +#define REG_CSI2RX_INT_MSK_PKT 0x124 +#define MASK_PKT_ERROR_ALL 0x000f000f + +#define REG_CSI2RX_INT_ST_LINE 0x130 +#define REG_CSI2RX_INT_MSK_LINE 0x134 +#define MASK_LINE_ERROR_ALL 0x00ff00ff + +/* DPHY register space */ +enum dphy_testcode { + DIG_TESTCODE_EXT = 0, + DIG_SYS_0 = 0x001, + DIG_SYS_1 = 0x002, + DIG_SYS_3 = 0x004, + DIG_SYS_7 = 0x008, + DIG_RX_STARTUP_OVR_2 = 0x0e2, + DIG_RX_STARTUP_OVR_3 = 0x0e3, + DIG_RX_STARTUP_OVR_4 = 0x0e4, + DIG_RX_STARTUP_OVR_5 = 0x0e5, + DIG_CB_2 = 0x1ac, + DIG_TERM_CAL_0 = 0x220, + DIG_TERM_CAL_1 = 0x221, + DIG_TERM_CAL_2 = 0x222, + DIG_CLKLANE_LANE_6 = 0x307, + DIG_CLKLANE_OFFSET_CAL_0 = 0x39d, + DIG_LANE0_OFFSET_CAL_0 = 0x59f, + DIG_LANE0_DDL_0 = 0x5e0, + DIG_LANE1_OFFSET_CAL_0 = 0x79f, + DIG_LANE1_DDL_0 = 0x7e0, + DIG_LANE2_OFFSET_CAL_0 = 0x99f, + DIG_LANE2_DDL_0 = 0x9e0, + DIG_LANE3_OFFSET_CAL_0 = 0xb9f, + DIG_LANE3_DDL_0 = 0xbe0, +}; + +#define SYS_0_HSFREQRANGE_OVR BIT(5) +#define SYS_3_NO_REXT BIT(4) +#define SYS_7_RESERVED FIELD_PREP(0x1f, 0x0c) +#define SYS_7_DESKEW_POL BIT(5) +#define STARTUP_OVR_4_CNTVAL FIELD_PREP(0x70, 0x01) +#define STARTUP_OVR_4_DDL_EN BIT(0) +#define STARTUP_OVR_5_BYPASS BIT(0) +#define CB_2_LPRX_BIAS BIT(6) +#define CB_2_RESERVED FIELD_PREP(0x3f, 0x0b) +#define CLKLANE_RXHS_PULL_LONG BIT(7) + +/* bit mask for calibration result registers */ +#define MASK_TERM_CAL_ERR 0 +#define MASK_TERM_CAL_DONE BIT(7) +#define MASK_CLK_CAL_ERR BIT(4) +#define MASK_CLK_CAL_DONE BIT(0) +#define MASK_CAL_ERR BIT(2) +#define MASK_CAL_DONE BIT(1) +#define MASK_DDL_ERR BIT(1) +#define MASK_DDL_DONE BIT(2) + +#define VISCONTI_CSI2RX_ERROR_MONITORS_NUM 8 + +/** + * struct visconti_csi2rx_line_err_target + * + * Virtual Channel and Data Type pair for CSI2RX line error monitor + * + * When 0 is set to dt, line error detection is disabled. + * + * @vc: Virtual Channel to monitor; Range 0..3 + * @dt: Data Type to monitor; Range 0, 0x10..0x3f + */ +struct visconti_csi2rx_line_err_target { + u32 vc[VISCONTI_CSI2RX_ERROR_MONITORS_NUM]; + u32 dt[VISCONTI_CSI2RX_ERROR_MONITORS_NUM]; +}; + +#define CSI2RX_MIN_DATA_RATE 80U +#define CSI2RX_MAX_DATA_RATE 1500U + +#define VISCONTI_CSI2RX_PAD_SINK 0 +#define VISCONTI_CSI2RX_PAD_SRC 1 +#define VISCONTI_CSI2RX_PAD_NUM 2 + +#define VISCONTI_CSI2RX_DEF_WIDTH 1920 +#define VISCONTI_CSI2RX_DEF_HEIGHT 1080 +#define VISCONTI_CSI2RX_MIN_WIDTH 640 +#define VISCONTI_CSI2RX_MAX_WIDTH 3840 +#define VISCONTI_CSI2RX_MIN_HEIGHT 480 +#define VISCONTI_CSI2RX_MAX_HEIGHT 2160 + +struct visconti_csi2rx { + struct device *dev; + void __iomem *base; + + struct v4l2_subdev subdev; + struct media_pad pads[VISCONTI_CSI2RX_PAD_NUM]; + struct v4l2_async_notifier notifier; + struct v4l2_subdev *remote; + unsigned int remote_pad; + + unsigned int lanes; + + unsigned int irq; +}; + +static inline struct visconti_csi2rx *notifier_to_csi2(struct v4l2_async_notifier *n) +{ + return container_of(n, struct visconti_csi2rx, notifier); +} + +static inline struct visconti_csi2rx *sd_to_csi2(struct v4l2_subdev *sd) +{ + return container_of(sd, struct visconti_csi2rx, subdev); +} + +static inline void visconti_csi2rx_write(struct visconti_csi2rx *priv, u32 regid, u32 val) +{ + writel(val, priv->base + regid); +} + +static inline u32 visconti_csi2rx_read(struct visconti_csi2rx *priv, u32 regid) +{ + return readl(priv->base + regid); +} + +static inline void tick_testclk(struct visconti_csi2rx *priv) +{ + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_TESTCTRL0, BIT_TESTCTRL0_CLK_1); + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_TESTCTRL0, BIT_TESTCTRL0_CLK_0); +} + +static inline void set_dphy_addr(struct visconti_csi2rx *priv, u32 test_mode) +{ + /* select testcode Ex space with top 4bits of test_mode */ + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_TESTCTRL1, + BIT_TESTCTRL1_ADDR | DIG_TESTCODE_EXT); + tick_testclk(priv); + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_TESTCTRL1, FIELD_GET(0xf00, test_mode)); + tick_testclk(priv); + + /* set bottom 8bit of test_mode */ + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_TESTCTRL1, + BIT_TESTCTRL1_ADDR | FIELD_GET(0xff, test_mode)); + tick_testclk(priv); +} + +static void write_dphy_param(struct visconti_csi2rx *priv, u32 test_mode, u8 test_in) +{ + set_dphy_addr(priv, test_mode); + + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_TESTCTRL1, (u32)test_in); + tick_testclk(priv); +} + +struct csi2rx_dphy_hs_info { + u32 rate; + u32 hsfreqrange; + u32 osc_freq_target; +}; + +static const struct csi2rx_dphy_hs_info dphy_hs_info[] = { + { 80, 0x0, 0x1cc }, { 85, 0x10, 0x1cc }, { 95, 0x20, 0x1cc }, { 105, 0x30, 0x1cc }, + { 115, 0x1, 0x1cc }, { 125, 0x11, 0x1cc }, { 135, 0x21, 0x1cc }, { 145, 0x31, 0x1cc }, + { 155, 0x2, 0x1cc }, { 165, 0x12, 0x1cc }, { 175, 0x22, 0x1cc }, { 185, 0x32, 0x1cc }, + { 198, 0x3, 0x1cc }, { 213, 0x13, 0x1cc }, { 228, 0x23, 0x1cc }, { 243, 0x33, 0x1cc }, + { 263, 0x4, 0x1cc }, { 288, 0x14, 0x1cc }, { 313, 0x25, 0x1cc }, { 338, 0x35, 0x1cc }, + { 375, 0x5, 0x1cc }, { 425, 0x16, 0x1cc }, { 475, 0x26, 0x1cc }, { 525, 0x37, 0x1cc }, + { 575, 0x7, 0x1cc }, { 625, 0x18, 0x1cc }, { 675, 0x28, 0x1cc }, { 725, 0x39, 0x1cc }, + { 775, 0x9, 0x1cc }, { 825, 0x19, 0x1cc }, { 875, 0x29, 0x1cc }, { 925, 0x3a, 0x1cc }, + { 975, 0xa, 0x1cc }, { 1025, 0x1a, 0x1cc }, { 1075, 0x2a, 0x1cc }, { 1125, 0x3b, 0x1cc }, + { 1175, 0xb, 0x1cc }, { 1225, 0x1b, 0x1cc }, { 1275, 0x2b, 0x1cc }, { 1325, 0x3c, 0x1cc }, + { 1375, 0xc, 0x1cc }, { 1425, 0x1c, 0x1cc }, { 1475, 0x2c, 0x1cc } +}; + +static void get_dphy_hs_transfer_info(u32 dphy_rate, u32 *hsfreqrange, u32 *osc_freq_target) +{ + unsigned int i; + + for (i = 1; i < ARRAY_SIZE(dphy_hs_info); i++) { + if (dphy_rate < dphy_hs_info[i].rate) { + *hsfreqrange = dphy_hs_info[i - 1].hsfreqrange; + *osc_freq_target = dphy_hs_info[i - 1].osc_freq_target; + return; + } + } + + /* not found; return the largest entry */ + *hsfreqrange = dphy_hs_info[ARRAY_SIZE(dphy_hs_info) - 1].hsfreqrange; + *osc_freq_target = dphy_hs_info[ARRAY_SIZE(dphy_hs_info) - 1].osc_freq_target; +} + +static void visconti_csi2rx_set_dphy_rate(struct visconti_csi2rx *priv, u32 dphy_rate) +{ + u32 hsfreqrange, osc_freq_target; + + get_dphy_hs_transfer_info(dphy_rate, &hsfreqrange, &osc_freq_target); + + write_dphy_param(priv, DIG_SYS_1, (u8)hsfreqrange); + write_dphy_param(priv, DIG_SYS_0, SYS_0_HSFREQRANGE_OVR); + write_dphy_param(priv, DIG_RX_STARTUP_OVR_5, STARTUP_OVR_5_BYPASS); + write_dphy_param(priv, DIG_RX_STARTUP_OVR_4, STARTUP_OVR_4_CNTVAL); + write_dphy_param(priv, DIG_CB_2, CB_2_LPRX_BIAS | CB_2_RESERVED); + write_dphy_param(priv, DIG_SYS_7, SYS_7_DESKEW_POL | SYS_7_RESERVED); + write_dphy_param(priv, DIG_CLKLANE_LANE_6, CLKLANE_RXHS_PULL_LONG); + write_dphy_param(priv, DIG_RX_STARTUP_OVR_2, FIELD_GET(0xff, osc_freq_target)); + write_dphy_param(priv, DIG_RX_STARTUP_OVR_3, FIELD_GET(0xf00, osc_freq_target)); + write_dphy_param(priv, DIG_RX_STARTUP_OVR_4, STARTUP_OVR_4_CNTVAL | STARTUP_OVR_4_DDL_EN); +} + +static int visconti_csi2rx_initialize(struct visconti_csi2rx *priv, u32 num_lane, u32 dphy_rate, + const struct visconti_csi2rx_line_err_target *err_target) +{ + u32 val; + + if (dphy_rate < CSI2RX_MIN_DATA_RATE || dphy_rate > CSI2RX_MAX_DATA_RATE) { + dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", dphy_rate); + return -ERANGE; + } + + /* 1st phase of initialization */ + visconti_csi2rx_write(priv, REG_CSI2RX_RESETN, 1); + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_RSTZ, 0); + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_SHUTDOWNZ, 0); + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_TESTCTRL0, 1); + ndelay(15U); + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_TESTCTRL0, 0); + + /* Configure D-PHY frequency range */ + visconti_csi2rx_set_dphy_rate(priv, dphy_rate); + + /* 2nd phase of initialization */ + visconti_csi2rx_write(priv, REG_CSI2RX_NLANES, (num_lane - 1U)); + ndelay(5U); + + /* Release D-PHY from Reset */ + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_SHUTDOWNZ, 1); + ndelay(5U); + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_RSTZ, 1); + + /* configuration of line error target */ + val = (err_target->vc[3] << 30U) | (err_target->dt[3] << 24U) | (err_target->vc[2] << 22U) | + (err_target->dt[2] << 16U) | (err_target->vc[1] << 14U) | (err_target->dt[1] << 8U) | + (err_target->vc[0] << 6U) | (err_target->dt[0]); + visconti_csi2rx_write(priv, REG_CSI2RX_DATA_IDS_1, val); + val = (err_target->vc[7] << 30U) | (err_target->dt[7] << 24U) | (err_target->vc[6] << 22U) | + (err_target->dt[6] << 16U) | (err_target->vc[5] << 14U) | (err_target->dt[5] << 8U) | + (err_target->vc[4] << 6U) | (err_target->dt[4]); + visconti_csi2rx_write(priv, REG_CSI2RX_DATA_IDS_2, val); + + /* configuration of mask */ + visconti_csi2rx_write(priv, REG_CSI2RX_INT_MSK_PHY_FATAL, MASK_PHY_FATAL_ALL); + visconti_csi2rx_write(priv, REG_CSI2RX_INT_MSK_PKT_FATAL, MASK_PKT_FATAL_ALL); + visconti_csi2rx_write(priv, REG_CSI2RX_INT_MSK_FRAME_FATAL, MASK_FRAME_FATAL_ALL); + visconti_csi2rx_write(priv, REG_CSI2RX_INT_MSK_PHY, MASK_PHY_ERROR_ALL); + visconti_csi2rx_write(priv, REG_CSI2RX_INT_MSK_PKT, MASK_PKT_ERROR_ALL); + visconti_csi2rx_write(priv, REG_CSI2RX_INT_MSK_LINE, MASK_LINE_ERROR_ALL); + + return 0; +} + +struct visconti_csi2rx_format { + u32 code; + unsigned int bpp; +}; + +static const struct visconti_csi2rx_format visconti_csi2rx_formats[] = { + { .code = MEDIA_BUS_FMT_RGB888_1X24, .bpp = 24 }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16 }, + { .code = MEDIA_BUS_FMT_UYVY10_1X20, .bpp = 20 }, + { .code = MEDIA_BUS_FMT_RGB565_1X16, .bpp = 16 }, + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8 }, + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8 }, + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8 }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8 }, + { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10 }, + { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10 }, + { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10 }, + { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10 }, + { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12 }, + { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12 }, + { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12 }, + { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12 }, + { .code = MEDIA_BUS_FMT_SBGGR14_1X14, .bpp = 14 }, + { .code = MEDIA_BUS_FMT_SGBRG14_1X14, .bpp = 14 }, + { .code = MEDIA_BUS_FMT_SGRBG14_1X14, .bpp = 14 }, + { .code = MEDIA_BUS_FMT_SRGGB14_1X14, .bpp = 14 }, +}; + +static const struct visconti_csi2rx_format *fmt_for_mbus_code(unsigned int mbus_code) +{ + int i; + + for (i = 0; ARRAY_SIZE(visconti_csi2rx_formats); i++) + if (visconti_csi2rx_formats[i].code == mbus_code) + return &visconti_csi2rx_formats[i]; +} + +static unsigned int bpp_for_mbus_code(unsigned int mbus_code) +{ + const struct visconti_csi2rx_format *fmt = fmt_for_mbus_code(mbus_code); + + return fmt ? fmt->bpp : 0; +} + +static int64_t get_pixelclock(struct v4l2_subdev *sd) +{ + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) + return -EINVAL; + + return v4l2_ctrl_g_ctrl_int64(ctrl); +} + +static const struct visconti_csi2rx_line_err_target err_target_vc0_alldt = { + /* select VC=0 */ + /* select all supported DataTypes */ + .dt = { + MIPI_CSI2_DT_RGB565, + MIPI_CSI2_DT_YUV422_8B, + MIPI_CSI2_DT_YUV422_10B, + MIPI_CSI2_DT_RGB888, + MIPI_CSI2_DT_RAW8, + MIPI_CSI2_DT_RAW10, + MIPI_CSI2_DT_RAW12, + MIPI_CSI2_DT_RAW14, + } +}; + +static int visconti_csi2rx_start(struct visconti_csi2rx *priv, struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *sink_fmt; + int cur_bpp, dphy_rate; + s64 pixelclock; + + /* get bpp for current format */ + sink_fmt = v4l2_subdev_state_get_format(state, VISCONTI_CSI2RX_PAD_SINK); + cur_bpp = bpp_for_mbus_code(sink_fmt->code); + + /* get pixel clock */ + pixelclock = get_pixelclock(priv->remote); + if (pixelclock < 0) + return -EINVAL; + + dphy_rate = div64_u64((u64)pixelclock * (u32)cur_bpp, priv->lanes * 1000000); + + ndelay(15U); + + return visconti_csi2rx_initialize(priv, priv->lanes, dphy_rate, &err_target_vc0_alldt); +} + +static void visconti_csi2rx_stop(struct visconti_csi2rx *priv) +{ + /* disable interrupt -> make sure registers cleared -> wait for current handlers finish */ + visconti_csi2rx_write(priv, REG_CSI2RX_INT_MSK_PHY_FATAL, 0); + visconti_csi2rx_write(priv, REG_CSI2RX_INT_MSK_PKT_FATAL, 0); + visconti_csi2rx_write(priv, REG_CSI2RX_INT_MSK_FRAME_FATAL, 0); + visconti_csi2rx_write(priv, REG_CSI2RX_INT_MSK_PHY, 0); + visconti_csi2rx_write(priv, REG_CSI2RX_INT_MSK_PKT, 0); + visconti_csi2rx_write(priv, REG_CSI2RX_INT_MSK_LINE, 0); + visconti_csi2rx_read(priv, REG_CSI2RX_INT_MSK_PHY_FATAL); + visconti_csi2rx_read(priv, REG_CSI2RX_INT_MSK_PKT_FATAL); + visconti_csi2rx_read(priv, REG_CSI2RX_INT_MSK_FRAME_FATAL); + visconti_csi2rx_read(priv, REG_CSI2RX_INT_MSK_PHY); + visconti_csi2rx_read(priv, REG_CSI2RX_INT_MSK_PKT); + visconti_csi2rx_read(priv, REG_CSI2RX_INT_MSK_LINE); + synchronize_irq(priv->irq); + + /* shutdown hardware */ + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_SHUTDOWNZ, 0); + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_RSTZ, 0); + visconti_csi2rx_write(priv, REG_CSI2RX_PHY_TESTCTRL0, 1); + visconti_csi2rx_write(priv, REG_CSI2RX_RESETN, 0); +} + +static int visconti_csi2rx_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct visconti_csi2rx *priv = sd_to_csi2(sd); + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + int ret; + + remote_pad = media_pad_remote_pad_first(&sd->entity.pads[VISCONTI_CSI2RX_PAD_SINK]); + if (!remote_pad) + return -ENODEV; + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + /* enabling: turn on CSI2RX -> turn on sensor */ + ret = visconti_csi2rx_start(priv, state); + if (ret) + return ret; + + /* currently CSI2RX supports only stream0 in source pad */ + ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, BIT(0)); + if (ret) { + visconti_csi2rx_stop(priv); + return ret; + } + + return 0; +} + +static int visconti_csi2rx_disable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct visconti_csi2rx *priv = sd_to_csi2(sd); + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + + remote_pad = media_pad_remote_pad_first(&sd->entity.pads[VISCONTI_CSI2RX_PAD_SINK]); + if (!remote_pad) + return -ENODEV; + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + /* disabling: turn off sensor -> turn off CSI2RX */ + v4l2_subdev_disable_streams(remote_sd, remote_pad->index, BIT(0)); + visconti_csi2rx_stop(priv); + + return 0; +} + +static int visconti_csi2rx_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad == VISCONTI_CSI2RX_PAD_SRC) { + const struct v4l2_mbus_framefmt *sink_fmt; + + /* SRC pad supports exactly the same format as SINK pad */ + if (code->index) + return -EINVAL; + sink_fmt = v4l2_subdev_state_get_format(sd_state, VISCONTI_CSI2RX_PAD_SINK); + code->code = sink_fmt->code; + return 0; + } + + if (code->index >= ARRAY_SIZE(visconti_csi2rx_formats)) + return -EINVAL; + code->code = visconti_csi2rx_formats[code->index].code; + + return 0; +} + +static int visconti_csi2rx_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VISCONTI_CSI2RX_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, VISCONTI_CSI2RX_PAD_SRC); + + sink_fmt->width = VISCONTI_CSI2RX_DEF_WIDTH; + sink_fmt->height = VISCONTI_CSI2RX_DEF_HEIGHT; + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->code = visconti_csi2rx_formats[0].code; + + *src_fmt = *sink_fmt; + + return 0; +} + +static int visconti_csi2rx_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + + /* SRC PAD has the same format as SINK PAD */ + if (fmt->pad == 1) + return v4l2_subdev_get_fmt(sd, sd_state, fmt); + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VISCONTI_CSI2RX_PAD_SINK); + + *sink_fmt = fmt->format; + sink_fmt->width = clamp_t(u32, fmt->format.width, VISCONTI_CSI2RX_MIN_WIDTH, + VISCONTI_CSI2RX_MAX_WIDTH); + sink_fmt->height = clamp_t(u32, fmt->format.height, VISCONTI_CSI2RX_MIN_HEIGHT, + VISCONTI_CSI2RX_MAX_HEIGHT); + if (!fmt_for_mbus_code(sink_fmt->code)) + sink_fmt->code = visconti_csi2rx_formats[0].code; + fmt->format = *sink_fmt; + + /* source pad should have the same format */ + src_fmt = v4l2_subdev_state_get_format(sd_state, VISCONTI_CSI2RX_PAD_SRC); + *src_fmt = *sink_fmt; + + return 0; +} + +static const struct media_entity_operations visconti_csi2rx_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_video_ops visconti_csi2rx_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops visconti_csi2rx_pad_ops = { + .enum_mbus_code = visconti_csi2rx_enum_mbus_code, + .disable_streams = visconti_csi2rx_disable_streams, + .enable_streams = visconti_csi2rx_enable_streams, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = visconti_csi2rx_set_pad_format, +}; + +static const struct v4l2_subdev_ops visconti_csi2rx_subdev_ops = { + .video = &visconti_csi2rx_video_ops, + .pad = &visconti_csi2rx_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops visconti_csi2rx_internal_ops = { + .init_state = visconti_csi2rx_init_state, +}; + +static int visconti_csi2rx_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asc) +{ + struct visconti_csi2rx *priv = notifier_to_csi2(notifier); + int pad; + + pad = media_entity_get_fwnode_pad(&subdev->entity, asc->match.fwnode, MEDIA_PAD_FL_SOURCE); + if (pad < 0) { + dev_err(priv->dev, "Failed to find pad for %s\n", subdev->name); + return pad; + } + + priv->remote = subdev; + priv->remote_pad = pad; + + return media_create_pad_link(&subdev->entity, pad, &priv->subdev.entity, 0, + MEDIA_LNK_FL_ENABLED); +} + +static void visconti_csi2rx_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asc) +{ + struct visconti_csi2rx *priv = notifier_to_csi2(notifier); + + priv->remote = NULL; +} + +static const struct v4l2_async_notifier_operations visconti_csi2rx_notify_ops = { + .bound = visconti_csi2rx_notify_bound, + .unbind = visconti_csi2rx_notify_unbind, +}; + +static int visconti_csi2rx_parse_v4l2(struct visconti_csi2rx *priv, + struct v4l2_fwnode_endpoint *vep) +{ + /* Only port 0 endpoint 0 is valid. */ + if (vep->base.port || vep->base.id) + return -ENOTCONN; + + priv->lanes = vep->bus.mipi_csi2.num_data_lanes; + + /* got trouble */ + if (vep->bus_type != V4L2_MBUS_CSI2_DPHY) { + dev_err(priv->dev, "Specified bus type is not supported\n"); + return -EINVAL; + } + + if (priv->lanes != 1 && priv->lanes != 2 && priv->lanes != 4) { + dev_err(priv->dev, "Unsupported number of data-lanes for D-PHY: %u\n", priv->lanes); + return -EINVAL; + } + + return 0; +} + +static int visconti_csi2rx_parse_dt(struct visconti_csi2rx *priv) +{ + struct v4l2_async_connection *asc; + struct fwnode_handle *fwnode; + struct fwnode_handle *ep; + struct v4l2_fwnode_endpoint v4l2_ep = { + .bus_type = V4L2_MBUS_UNKNOWN, + }; + int ret; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(priv->dev), 0, 0, 0); + if (!ep) { + dev_err(priv->dev, "Not connected to subdevice\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep); + if (ret) { + dev_err(priv->dev, "Could not parse v4l2 endpoint\n"); + fwnode_handle_put(ep); + return -EINVAL; + } + + ret = visconti_csi2rx_parse_v4l2(priv, &v4l2_ep); + if (ret) { + fwnode_handle_put(ep); + return ret; + } + + fwnode = fwnode_graph_get_remote_endpoint(ep); + fwnode_handle_put(ep); + + v4l2_async_subdev_nf_init(&priv->notifier, &priv->subdev); + priv->notifier.ops = &visconti_csi2rx_notify_ops; + + asc = v4l2_async_nf_add_fwnode(&priv->notifier, fwnode, struct v4l2_async_connection); + fwnode_handle_put(fwnode); + if (IS_ERR(asc)) + return PTR_ERR(asc); + + ret = v4l2_async_nf_register(&priv->notifier); + if (ret) + v4l2_async_nf_cleanup(&priv->notifier); + + return ret; +} + +static irqreturn_t visconti_csi2rx_irq(int irq, void *dev_id) +{ + struct visconti_csi2rx *priv = dev_id; + u32 event; + + event = visconti_csi2rx_read(priv, REG_CSI2RX_INT_ST_MAIN); + dev_err(priv->dev, "CSI2RX error 0x%x.\n", event); + + return IRQ_HANDLED; +} + +static const struct of_device_id visconti_csi2rx_of_table[] = { + { + .compatible = "toshiba,visconti5-csi2rx", + }, + {}, +}; + +static int visconti_csi2rx_probe(struct platform_device *pdev) +{ + struct visconti_csi2rx *priv; + int irq, ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) { + dev_err(priv->dev, "Failed to get registers\n"); + return PTR_ERR(priv->base); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + ret = devm_request_irq(&pdev->dev, irq, visconti_csi2rx_irq, 0, KBUILD_MODNAME, priv); + priv->irq = irq; + if (ret) { + dev_err(priv->dev, "request irq failed: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, priv); + + ret = visconti_csi2rx_parse_dt(priv); /*this function does v4l2_async_nf_register */ + if (ret) + return ret; + + priv->subdev.owner = THIS_MODULE; + priv->subdev.dev = &pdev->dev; + v4l2_subdev_init(&priv->subdev, &visconti_csi2rx_subdev_ops); + v4l2_set_subdevdata(&priv->subdev, &pdev->dev); + snprintf(priv->subdev.name, sizeof(priv->subdev.name), "%s %s", KBUILD_MODNAME, + dev_name(&pdev->dev)); + + priv->subdev.internal_ops = &visconti_csi2rx_internal_ops; + priv->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + priv->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + priv->subdev.entity.ops = &visconti_csi2rx_entity_ops; + + priv->pads[VISCONTI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + priv->pads[VISCONTI_CSI2RX_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&priv->subdev.entity, VISCONTI_CSI2RX_PAD_NUM, priv->pads); + if (ret) + goto err_cleanup_async; + + ret = v4l2_subdev_init_finalize(&priv->subdev); + if (ret) + goto err_cleanup_media_entity; + + ret = v4l2_async_register_subdev(&priv->subdev); + if (ret < 0) + goto err_cleanup_subdev_state; + + return 0; + +err_cleanup_subdev_state: + v4l2_subdev_cleanup(&priv->subdev); + +err_cleanup_media_entity: + media_entity_cleanup(&priv->subdev.entity); + +err_cleanup_async: + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); + + return ret; +} + +static void visconti_csi2rx_remove(struct platform_device *pdev) +{ + struct visconti_csi2rx *priv = platform_get_drvdata(pdev); + + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); + v4l2_async_unregister_subdev(&priv->subdev); + + v4l2_subdev_cleanup(&priv->subdev); + media_entity_cleanup(&priv->subdev.entity); +} + +static struct platform_driver visconti_csi2rx_driver = { + .probe = visconti_csi2rx_probe, + .remove = visconti_csi2rx_remove, + .driver = { + .name = "visconti_csi2rx_dev", + .of_match_table = visconti_csi2rx_of_table, + }, +}; + +module_platform_driver(visconti_csi2rx_driver); + +MODULE_AUTHOR("Yuji Ishikawa "); +MODULE_DESCRIPTION("Toshiba Visconti CSI-2 receiver driver"); +MODULE_LICENSE("Dual BSD/GPL"); From patchwork Mon Nov 25 09:21:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuji Ishikawa X-Patchwork-Id: 845871 Received: from mo-csw.securemx.jp (mo-csw1801.securemx.jp [210.130.202.135]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6C923185935; Mon, 25 Nov 2024 09:27:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.130.202.135 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732526881; cv=none; b=osKqiZpNyggi8CcYTej3eQHjupuTU4pYMY8GaYhYEAqT768krVFf/D8OOibpOU9DgmcPmNGq9TMac0Z2u/yYl9iUjBfa4/pVJBxvRs69L4uNZr6B4YuC9wX8IZMkEYsbb8hS8PDBzu5THRr73hJybRbsL0o8WL+1AgGGBguHEYs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732526881; c=relaxed/simple; bh=YcC7KxUu6r2b9IlaRyMRidAN+XoqTy53IE3nNoaUON4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=t8xFxtJjob+Do9G7MYohZLx06ymmwiGv1HzEGyfqx7+Hri8C3UBtCSWT6I6Jiyz70knoHTBuJexkv9J+IRwahBKhTCMRoOOSXAA79qysSoFgc90QKmFLavZp92D7xMITm57qVYXYloP9gxFbnCXWzem6Muylh1KN6hkzuc/hcNE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp; spf=pass smtp.mailfrom=toshiba.co.jp; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b=omsh42h0; arc=none smtp.client-ip=210.130.202.135 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b="omsh42h0" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=toshiba.co.jp; h=From:To:Cc :Subject:Date:Message-Id:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding;i=yuji2.ishikawa@toshiba.co.jp;s=key2.smx;t= 1732526839; x=1733736439; bh=YcC7KxUu6r2b9IlaRyMRidAN+XoqTy53IE3nNoaUON4=; b=oms h42h0ucuOOMjB/kdaTCVPJZF++AwCHT+vWE6FYDlm6zF/eT1nqJKStD+UE/jD8ConxAEOV+IIaXgL 2cy2YJ7MkUlhfC6g2pulXSRNTksSliLSIgRSLzFIlu/xlW/M4b6tirNDeGLDAAJ4J7hXNDQpOC5W6 yXOdqXxAVQkB0LXKrArof9OL6jsc2r14lwG8S54IxqdKSkDVqDxjrtzg/tjXB7wUASAXPuLWHrFlc +ytdHTgHtFivZZbMH+OuVBPwvuznRAzlXJd875sKBl0VWA0E/s2siv1QxBeM1ybDY3ILGTUoiLcLn AarDdzARfjTYSa7daQ6kZMScSG4VotQ==; Received: by mo-csw.securemx.jp (mx-mo-csw1801) id 4AP9RI1g2549470; Mon, 25 Nov 2024 18:27:19 +0900 X-Iguazu-Qid: 2yAafc7cGiz43BXZuU X-Iguazu-QSIG: v=2; s=0; t=1732526838; q=2yAafc7cGiz43BXZuU; m=efPC2DrfJffyG8r+NUm/lW2GEXdQiEwqjY7pbof/CIQ= Received: from imx2-a.toshiba.co.jp (imx2-a.toshiba.co.jp [106.186.93.35]) by relay.securemx.jp (mx-mr1800) id 4AP9RHxL314942 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 25 Nov 2024 18:27:17 +0900 X-SA-MID: 35004182 From: Yuji Ishikawa To: Laurent Pinchart , Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sakari Ailus , Hans Verkuil , Nobuhiro Iwamatsu , Yuji Ishikawa Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Subject: [PATCH v12 5/8] media: platform: visconti: Add Toshiba Visconti Video Input Interface driver Date: Mon, 25 Nov 2024 18:21:43 +0900 X-TSB-HOP2: ON Message-Id: <20241125092146.1561901-6-yuji2.ishikawa@toshiba.co.jp> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> References: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add support to Video Input Interface on Toshiba Visconti ARM SoCs. The interface device includes frame grabber, video DMAC and image signal processor. A driver instance provides three /dev/videoX device files; one for RGB image capture, another one for optional RGB capture with different parameters and the last one for RAW capture. Through the device files, the driver provides streaming interface. Both DMABUF and MMAP operations are supported. The buffer provided by a userland application should be DMA-contiguous. The driver is based on media controller framework. Its operations are roughly mapped to three subdrivers; CSI2 receiver subdevice, ISP subdevice and capture devices. The Video DMACs have 32bit address space and currently corresponding IOMMU driver is not provided. Therefore, memory-block address for captured image is 32bit IOVA which is equal to 32bit-truncated physical address. When the Visconti IOMMU driver (currently under development) is accepted, the hardware layer will use 32bit IOVA mapped by the attached IOMMU. Signed-off-by: Yuji Ishikawa --- Changelog v2: - Resend v1 because a patch exceeds size limit. Changelog v3: - Adapted to media control framework - Introduced ISP subdevice, capture device - Remove private IOCTLs and add vendor specific V4L2 controls - Change function name avoiding camelcase and uppercase letters Changelog v4: - fix style problems at the v3 patch - remove "index" member - update example - Split patches because the v3 patch exceeds size limit - Stop using ID number to identify driver instance: - Use dynamically allocated structure to hold driver's context, instead of static one indexed by ID number. - internal functions accept context structure instead of ID number. - Use pm_runtime to trigger initialization of HW along with open/close of device files. Changelog v5: - Fix coding style problems in viif.c Changelog v6: - update dependency description of Kconfig - bugfix: usage of buffer pointed with dma_active - remove unused macros - add viif_common.c for commonly used register buffer control routine - add initialization of Bus Controller (HWAIF) and Memory Protection Unit - removed hwd_ and HWD_ prefix - update source code documentation - Suggestion from Hans Verkuil - pointer to userland memory is removed from uAPI arguments - style of structure is now "nested" instead of "chained by pointer"; - use div64_u64 for 64bit division - define Visconti specific control IDs in v4l2-controls.h - set proper initial size to v4l2_ctrl_handler_init() - set all buffers to QUEUED state on an error at start_streaming - use vb2_is_busy() instead of vb2_is_streaming() - add parameter check for s->type and s->target in get_selection() - remove ioctls related to DV format and EDID - release v4l2 fh instance on and error at opening device file - support VB2_MMAP mode for streaming operation - add initial value to each vendor specific control - GET_LAST_CAPTURE_STATUS control is updated asynchronously from workqueue - applied v4l2-compliance - Suggestion from Sakari Ailus - use div64_u64 for 64bit division - update copyright's year - use common definition of MIPI CSI2 DataTypes - remove redundant cast - use bool instead of HWD_VIIF_ENABLE/DISABLE - simplify comparison to 0 - simplify statements with trigram operator - remove redundant local variables - simplify timeout loop - use general integer types instead of u32/s32 - Suggestion from Laurent Pinchart - moved VIIF driver to driver/platform/toshiba/visconti - add CSI2RX subdevice - change register access: struct-style to macro-style - use common definition of MIPI CSI2 DataTypes - Kconfig: add SPDX header, add V4L2_ASYNC - remove unused type definitions - define enums instead of successive macro constants - remove redundant parenthesis of macro constant - embed struct hwd_res into struct viif_device - turn switch-case into table lookup - use xxx_dma instead of xxx_paddr for variable names of IOVA - literal value: just 0 instead of 0x0 - use literal 1 or 0 instead of HWD_VIIF_ENABLE, DISABLE for register access - use true or false instead of HWD_VIIF_ENABLE, DISABLE for function calls - remove ioctl request handlers which refers subdevices Changelog v7: - change compatible string to visconti5-viif - remove unused variables - set static to internal functions - Suggestion from kernel test robot - update references to headers Changelog v8: - bugfix: handling return value of visconti_viif_parse_dt() - add visconti_viif_subdev_notifier_register() to gather all operations around v4l2_async_notifier - update for v6.6-rc2 - use v4l2_async_connection instead of v4l2_async_subdev - aid for devices using subdev active state - add __maybe_unused for runtime_pm callbacks - Suggestion from Krzysztof Kozlowski - use static initialization of local variable - use dev_err_probe() - remove error message for DMA memory allocation failure - remove unused comment messages - add error handling at fail of workqueue_create() - remove redundant mutex for pm_runtime callback routines - Suggestion from Hans Verkuil - remove pr_info() calls - build check with media_stage.git - some lacks for kerneldoc description Changelog v9: - applied sparse checker - add static qualifier to a file scoped local variable - expand functions for acquiring/releasing locks - bugfix: use NULL (instead of 0) for pad::get_fmt subdevice API - fix warnings for cast between ptr and dma_addr_t - call div64_u64 for 64bit division - rebase to media_staging tree; update Visconti specific control IDs Changelog v10: - remove vendor specific compound controls - remove "rawpack mode" flag - RAW16, RAW18, RAW20 (to be implemented and tested) should be used instead - catch up to v6.9-rc4 Changelog v11: - stop merging sensor's controls to capture device's - remove redundant parameter checkings - update routines handling crop/compose rects of the ISP subdevice - update kerneldoc comments - update copyright year Changelog v12: - expand functions used only once - separate IRQ handlers one for VSync and the other for error. - remove redundant NULL check at visconti_viif_create_sensor_link() - return error at "missing CSI-2 properties in endpoint" - detailed error message at return of platform_get_irq() - improve cast operations for viif_dev->tables_dma - use RUNTIME_PM_OPS instead of SET_RUNTIME_PM_OPS - improve identifiers for include guards - remove unused v4l2-m2m.h - add comment to VIIF_SYS_CLK macro - use struct v4l2_rect instead of viif_img_area to hold crop rectangle - name capture devices cap_{post0, post1, sub} for better understanding - remove viif_csi2rx subdevice and made it independent driver - limit vbp size; there might be a sensor with too large vbp; tested with IMX335 - newly add viif_resizer subdevice between ISP and Capture devices. - they are available on the capture paths for cap_dev_post0 and cap_dev_post1 - Resizer offer resize and crop feature; ISP's feature is dropped - use v4l2_subdev_enable_streams() to start streaming - implement callback enable_streams and disable_streams, instead of s_steram - applied rules for setting parameters of downstream pads - use lowercase hexadecimal literals - hardware limitation: cap_sub can capture only RAW8 or RAW16 - for RAW10, 12, 14 input, pixel values are shifted to MSB - add spinlocks to status_err, reported_err_main, reported_err_sub - call pm_runtime API at start/stop streaming, instead of file handle callback - use guard(spinlock)(locked_variable) macros - also use custom guard macros for viif_isp_guard - migration to v6.12: signature of platform_driver::remove is changed .../media/platform/toshiba/visconti/Kconfig | 18 + .../media/platform/toshiba/visconti/Makefile | 2 + .../media/platform/toshiba/visconti/viif.c | 576 +++++ .../media/platform/toshiba/visconti/viif.h | 379 ++++ .../platform/toshiba/visconti/viif_capture.c | 1285 +++++++++++ .../platform/toshiba/visconti/viif_capture.h | 21 + .../platform/toshiba/visconti/viif_common.c | 239 ++ .../platform/toshiba/visconti/viif_common.h | 45 + .../platform/toshiba/visconti/viif_isp.c | 909 ++++++++ .../platform/toshiba/visconti/viif_isp.h | 19 + .../platform/toshiba/visconti/viif_regs.h | 717 ++++++ .../platform/toshiba/visconti/viif_resizer.c | 461 ++++ .../platform/toshiba/visconti/viif_resizer.h | 18 + include/uapi/linux/visconti_viif.h | 1921 +++++++++++++++++ 14 files changed, 6610 insertions(+) create mode 100644 drivers/media/platform/toshiba/visconti/viif.c create mode 100644 drivers/media/platform/toshiba/visconti/viif.h create mode 100644 drivers/media/platform/toshiba/visconti/viif_capture.c create mode 100644 drivers/media/platform/toshiba/visconti/viif_capture.h create mode 100644 drivers/media/platform/toshiba/visconti/viif_common.c create mode 100644 drivers/media/platform/toshiba/visconti/viif_common.h create mode 100644 drivers/media/platform/toshiba/visconti/viif_isp.c create mode 100644 drivers/media/platform/toshiba/visconti/viif_isp.h create mode 100644 drivers/media/platform/toshiba/visconti/viif_regs.h create mode 100644 drivers/media/platform/toshiba/visconti/viif_resizer.c create mode 100644 drivers/media/platform/toshiba/visconti/viif_resizer.h create mode 100644 include/uapi/linux/visconti_viif.h diff --git a/drivers/media/platform/toshiba/visconti/Kconfig b/drivers/media/platform/toshiba/visconti/Kconfig index e5c92d598f8b..8fcab00a0c29 100644 --- a/drivers/media/platform/toshiba/visconti/Kconfig +++ b/drivers/media/platform/toshiba/visconti/Kconfig @@ -14,3 +14,21 @@ config VIDEO_VISCONTI_CSI2RX This driver yields 1 subdevice node for a hardware instance. To compile this driver as a module, choose M here: the module will be called visconti-csi2rx. + +config VIDEO_VISCONTI_VIIF + tristate "Visconti Camera Interface driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + depends on ARCH_VISCONTI || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_FWNODE + help + This is V4L2 driver for Toshiba Visconti Camera Interface hardware + + This driver yields 3 video device nodes + and 1 media device node for a hardware instance. + To compile this driver as a module, choose M here: the + module will be called visconti-viif. diff --git a/drivers/media/platform/toshiba/visconti/Makefile b/drivers/media/platform/toshiba/visconti/Makefile index 62a029376134..4c7433744b64 100644 --- a/drivers/media/platform/toshiba/visconti/Makefile +++ b/drivers/media/platform/toshiba/visconti/Makefile @@ -4,5 +4,7 @@ # visconti-csi2rx-objs = csi2rx_drv.o +visconti-viif-objs = viif.o viif_capture.o viif_common.o viif_isp.o viif_resizer.o obj-$(CONFIG_VIDEO_VISCONTI_CSI2RX) += visconti-csi2rx.o +obj-$(CONFIG_VIDEO_VISCONTI_VIIF) += visconti-viif.o diff --git a/drivers/media/platform/toshiba/visconti/viif.c b/drivers/media/platform/toshiba/visconti/viif.c new file mode 100644 index 000000000000..d2521718abe5 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "viif.h" +#include "viif_capture.h" +#include "viif_common.h" +#include "viif_isp.h" +#include "viif_regs.h" +#include "viif_resizer.h" + +/*=============================================*/ +/* Register Access */ +/*=============================================*/ +static inline void viif_hwaif_write(struct viif_device *viif_dev, unsigned int regid, u32 val) +{ + writel(val, viif_dev->hwaif_reg + regid); +} + +static inline void viif_mpu_write(struct viif_device *viif_dev, unsigned int regid, u32 val) +{ + writel(val, viif_dev->mpu_reg + regid); +} + +/*=============================================*/ +/* Low Layer hardware setup */ +/*=============================================*/ +static void viif_mpu_disable(struct viif_device *viif_dev) +{ + viif_mpu_write(viif_dev, REG_MPU_MP_EN, 0); + viif_mpu_write(viif_dev, REG_MPU_MF_EN, 1); +} + +static void viif_hwaif_enable(struct viif_device *viif_dev) +{ + /* pass through; disable all entries */ + viif_hwaif_write(viif_dev, REG_HWAIF_REGION_ENTRY_EN, 0); + + /* no limit for outstanding requests */ + viif_hwaif_write(viif_dev, REG_HWAIF_OSTD_RLEN, 0); + viif_hwaif_write(viif_dev, REG_HWAIF_OSTD_WREQ, 0); + + /* no data-pack/outstanding */ + viif_hwaif_write(viif_dev, REG_HWAIF_HWAIF_CONF, 0); + + /* enable bus access */ + viif_hwaif_write(viif_dev, REG_HWAIF_HWAIF_EN, 1); +} + +/*=============================================*/ +/* handling V4L2 framework */ +/*=============================================*/ +static irqreturn_t viif_vsync_irq_handler(int irq, void *dev_id) +{ + u32 event_main = 0, event_sub = 0, status_err, l2_transfer_status, mask, val; + struct viif_device *viif_dev = dev_id; + u64 ts; + + if (!viif_dev->irq_enabled) + return IRQ_HANDLED; + + ts = ktime_get_ns(); + + /* Delayed Vsync of MAIN unit */ + mask = viif_capture_read(viif_dev, REG_INT_M_SYNC_MASK); + event_main = viif_capture_read(viif_dev, REG_INT_M_SYNC) & ~mask; + if (event_main) + viif_capture_write(viif_dev, REG_INT_M_SYNC, event_main); + + if (event_main & MASK_INT_M_SYNC_LINES_DELAY_INT2) { + /* unmask timeout error of gamma table */ + viif_capture_write(viif_dev, REG_INT_M_MASK, MASK_INT_M_DELAY_INT_ERROR); + viif_dev->masked_gamma_path = 0; + + /* Get abort status of L2ISP */ + { + guard(spinlock)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + val = viif_capture_read(viif_dev, REG_L2_CRGBF_ISP_INT); + viif_capture_write(viif_dev, REG_L2_CRGBF_ISP_INT, val); + l2_transfer_status = val & MASK_L2_STATUS_ERR_ALL; + } + { + guard(spinlock)(&viif_dev->errflag_lock); + + status_err = viif_dev->status_err; + viif_dev->status_err = 0; + } + + visconti_viif_capture_switch_buffer(&viif_dev->cap_post0, status_err, + l2_transfer_status, ts); + visconti_viif_capture_switch_buffer(&viif_dev->cap_post1, status_err, + l2_transfer_status, ts); + } + + /* Delayed Vsync of SUB unit */ + mask = viif_capture_read(viif_dev, REG_INT_S_SYNC_MASK); + event_sub = viif_capture_read(viif_dev, REG_INT_S_SYNC) & ~mask; + if (event_sub) + viif_capture_write(viif_dev, REG_INT_S_SYNC, event_sub); + + if (event_sub & MASK_INT_S_SYNC_LINES_DELAY_INT1) + visconti_viif_capture_switch_buffer(&viif_dev->cap_sub, 0, 0, ts); + + return IRQ_HANDLED; +} + +static irqreturn_t viif_status_err_irq_handler(int irq, void *dev_id) +{ + u32 event_main = 0, event_sub = 0, val, mask; + struct viif_device *viif_dev = dev_id; + + if (!viif_dev->irq_enabled) + return IRQ_HANDLED; + + mask = viif_capture_read(viif_dev, REG_INT_M_MASK); + event_main = viif_capture_read(viif_dev, REG_INT_M_STATUS) & ~mask; + if (event_main) { + viif_capture_write(viif_dev, REG_INT_M_STATUS, event_main); + + /* mask for gamma table time out error which will be unmasked in the next Vsync */ + val = FIELD_GET(MASK_INT_M_L2ISP_GAMMA_TABLE_TIMEOUT, event_main); + if (val) { + viif_dev->masked_gamma_path |= val; + mask = MASK_INT_M_DELAY_INT_ERROR | + FIELD_PREP(MASK_INT_M_L2ISP_GAMMA_TABLE_TIMEOUT, + viif_dev->masked_gamma_path); + viif_capture_write(viif_dev, REG_INT_M_MASK, mask); + } + { + guard(spinlock)(&viif_dev->errflag_lock); + viif_dev->status_err = event_main; + } + } + + mask = viif_capture_read(viif_dev, REG_INT_S_MASK); + event_sub = viif_capture_read(viif_dev, REG_INT_S_STATUS) & ~mask; + if (event_sub) + viif_capture_write(viif_dev, REG_INT_S_STATUS, event_sub); + + { + guard(spinlock)(&viif_dev->repflag_lock); + + viif_dev->reported_err_main |= event_main; + viif_dev->reported_err_sub |= event_sub; + } + dev_err(viif_dev->dev, "MAIN/SUB error 0x%x 0x%x.\n", event_main, event_sub); + + return IRQ_HANDLED; +} + +/* ----- Async Notifier Operations----- */ +static int visconti_viif_create_sensor_link(struct viif_device *viif_dev) +{ + struct v4l2_subdev *remote_sd = viif_dev->remote_sd; + int source_pad; + int ret; + + /* camera subdev pad0 -> isp suddev pad0 */ + source_pad = media_entity_get_fwnode_pad(&remote_sd->entity, remote_sd->fwnode, + MEDIA_PAD_FL_SOURCE); + if (source_pad < 0) { + dev_err(viif_dev->dev, "failed to find source pad\n"); + return source_pad; + } + + ret = media_create_pad_link(&remote_sd->entity, source_pad, &viif_dev->isp_subdev.sd.entity, + VIIF_ISP_PAD_SINK_VIDEO, MEDIA_LNK_FL_ENABLED); + if (ret) + dev_err(viif_dev->dev, "failed create_pad_link (sensor:src -> isp:sink)\n"); + + return ret; +} + +/** + * struct viif_remote_async - remote subdevice information handled by v4l2_async APIs + * @asc: async_connection + * @v4l2_sd: v4l2_subdev for the subdevice + * @num_lane: number of lanes for the connection + * @index: index of the subdevice + */ +struct viif_remote_async { + struct v4l2_async_connection asc; + struct v4l2_subdev *v4l2_sd; + unsigned int num_lane; + unsigned int index; +}; + +static int visconti_viif_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *v4l2_sd, + struct v4l2_async_connection *asc) +{ + struct viif_device *viif_dev = container_of(notifier, struct viif_device, notifier); + struct viif_remote_async *s_as = container_of(asc, struct viif_remote_async, asc); + + s_as->v4l2_sd = v4l2_sd; + if (!s_as->index) { + viif_dev->remote_sd = v4l2_sd; + viif_dev->remote_num_lane = s_as->num_lane; + return visconti_viif_create_sensor_link(viif_dev); + } + + return 0; +} + +static void visconti_viif_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asc) +{ + struct viif_device *viif_dev = container_of(notifier, struct viif_device, notifier); + + if (viif_dev->remote_sd == subdev) + viif_dev->remote_sd = NULL; +} + +static int visconti_viif_notify_complete(struct v4l2_async_notifier *notifier) +{ + return v4l2_device_register_subdev_nodes(notifier->v4l2_dev); +} + +static const struct v4l2_async_notifier_operations viif_notify_ops = { + .bound = visconti_viif_notify_bound, + .unbind = visconti_viif_notify_unbind, + .complete = visconti_viif_notify_complete, +}; + +/* ----- Probe and Remove ----- */ +static int visconti_viif_subdev_notifier_register(struct viif_device *viif_dev) +{ + struct fwnode_handle *fwnode = dev_fwnode(viif_dev->dev); + struct v4l2_async_notifier *ntf = &viif_dev->notifier; + struct fwnode_handle *ep; + unsigned int index = 0; + int ret = 0; + + v4l2_async_nf_init(ntf, &viif_dev->v4l2_dev); + ntf->ops = &viif_notify_ops; + + fwnode_graph_for_each_endpoint(fwnode, ep) { + struct v4l2_fwnode_endpoint vep = {}; + struct viif_remote_async *viif_asd; + + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) { + dev_err(viif_dev->dev, "failed to parse endpoint %pfw\n", ep); + break; + } + + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY || vep.bus.mipi_csi2.num_data_lanes == 0) { + dev_err(viif_dev->dev, "missing CSI-2 properties in endpoint %pfw\n", ep); + ret = -EINVAL; + break; + } + + viif_asd = v4l2_async_nf_add_fwnode_remote(ntf, ep, struct viif_remote_async); + viif_asd->index = index++; + viif_asd->num_lane = vep.bus.mipi_csi2.num_data_lanes; + } + + if (ret) { + fwnode_handle_put(ep); + v4l2_async_nf_cleanup(ntf); + return ret; + } + + if (!index) + dev_dbg(viif_dev->dev, "No remote subdevice found\n"); + + ret = v4l2_async_nf_register(ntf); + if (ret) { + v4l2_async_nf_cleanup(ntf); + return ret; + } + + return 0; +} + +static int visconti_viif_create_links(struct viif_device *viif_dev) +{ + int ret; + + ret = media_create_pad_link(&viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SRC_PATH0, + &viif_dev->resizer_post0.sd.entity, VIIF_RESIZER_PAD_SINK, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(viif_dev->dev, "failed create_pad_link (isp:path0 -> resizer0:sink)\n"); + return ret; + } + + ret = media_create_pad_link(&viif_dev->resizer_post0.sd.entity, VIIF_RESIZER_PAD_SRC, + &viif_dev->cap_post0.vdev.entity, VIIF_CAPTURE_PAD_SINK, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(viif_dev->dev, "failed create_pad_link (resizer0:src -> capture0:sink)\n"); + return ret; + } + + ret = media_create_pad_link(&viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SRC_PATH1, + &viif_dev->resizer_post1.sd.entity, VIIF_RESIZER_PAD_SINK, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(viif_dev->dev, "failed create_pad_link (isp:path1 -> resizer1:sink)\n"); + return ret; + } + + ret = media_create_pad_link(&viif_dev->resizer_post1.sd.entity, VIIF_RESIZER_PAD_SRC, + &viif_dev->cap_post1.vdev.entity, VIIF_CAPTURE_PAD_SINK, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(viif_dev->dev, "failed create_pad_link (resizer1:src -> capture1:sink)\n"); + return ret; + } + + ret = media_create_pad_link(&viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SRC_PATH2, + &viif_dev->cap_sub.vdev.entity, VIIF_CAPTURE_PAD_SINK, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(viif_dev->dev, "failed create_pad_link (isp:path2 -> capture2:sink)\n"); + return ret; + } + + ret = media_create_pad_link(&viif_dev->params_dev.vdev.entity, VIIF_PARAMS_PAD_SRC, + &viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SINK_PARAMS, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(viif_dev->dev, "failed create_pad_link (params:src -> isp:params)\n"); + return ret; + } + + ret = media_create_pad_link(&viif_dev->isp_subdev.sd.entity, VIIF_ISP_PAD_SRC_STATS, + &viif_dev->stats_dev.vdev.entity, VIIF_STATS_PAD_SINK, + MEDIA_LNK_FL_ENABLED); + if (ret) + dev_err(viif_dev->dev, "failed create_pad_link (isp:stats -> stat:sink)\n"); + + return ret; +} + +static const struct of_device_id visconti_viif_of_table[] = { + { + .compatible = "toshiba,visconti5-viif", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, visconti_viif_of_table); + +static irqreturn_t (*viif_irq_handlers[VIIF_NUM_IRQS])(int, void *) = { + viif_vsync_irq_handler, + viif_status_err_irq_handler, +}; + +static int visconti_viif_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct viif_device *viif_dev; + dma_addr_t tables_dma; + int ret, i; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); + if (ret) + return ret; + + viif_dev = devm_kzalloc(dev, sizeof(*viif_dev), GFP_KERNEL); + if (!viif_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, viif_dev); + viif_dev->dev = dev; + + spin_lock_init(&viif_dev->regbuf_lock); + spin_lock_init(&viif_dev->errflag_lock); + spin_lock_init(&viif_dev->repflag_lock); + mutex_init(&viif_dev->stream_lock); + + viif_dev->capture_reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(viif_dev->capture_reg)) + return PTR_ERR(viif_dev->capture_reg); + + viif_dev->hwaif_reg = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(viif_dev->hwaif_reg)) + return PTR_ERR(viif_dev->hwaif_reg); + + viif_dev->mpu_reg = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(viif_dev->mpu_reg)) + return PTR_ERR(viif_dev->mpu_reg); + + viif_dev->run_flag_main = false; + + for (i = 0; i < ARRAY_SIZE(viif_irq_handlers); i++) { + int irq; + + irq = platform_get_irq(pdev, i); + if (irq < 0) + return dev_err_probe(dev, irq, "failed to acquire irq resource %d\n", i); + ret = devm_request_irq(dev, irq, viif_irq_handlers[i], 0, "viif", viif_dev); + if (ret) + return dev_err_probe(dev, ret, "irq request failed: resource %d\n", i); + viif_dev->irq[i] = irq; + } + + viif_dev->tables = + dma_alloc_wc(dev, sizeof(struct viif_table_area), &tables_dma, GFP_KERNEL); + if (!viif_dev->tables) + return -ENOMEM; + viif_dev->tables_dma = tables_dma; + + pm_runtime_enable(dev); + + /* build media_dev */ + viif_dev->media_dev.hw_revision = 0; + strscpy(viif_dev->media_dev.model, VIIF_DRIVER_NAME, sizeof(viif_dev->media_dev.model)); + viif_dev->media_dev.dev = dev; + media_device_init(&viif_dev->media_dev); + + /* build v4l2_dev */ + viif_dev->v4l2_dev.mdev = &viif_dev->media_dev; + ret = v4l2_device_register(dev, &viif_dev->v4l2_dev); + if (ret) + goto error_dma_free; + + ret = media_device_register(&viif_dev->media_dev); + if (ret) { + dev_err_probe(dev, ret, "Failed to register media device\n"); + goto error_v4l2_unregister; + } + + ret = visconti_viif_isp_register(viif_dev); + if (ret) { + dev_err_probe(dev, ret, "failed to register isp sub node\n"); + goto error_media_unregister; + } + + ret = visconti_viif_resizer_register(viif_dev); + if (ret) { + dev_err_probe(dev, ret, "failed to register resizer sub node\n"); + goto error_isp_unregister; + } + + ret = visconti_viif_capture_register(viif_dev); + if (ret) { + dev_err_probe(dev, ret, "failed to register capture node\n"); + goto error_resizer_unregister; + } + + ret = visconti_viif_create_links(viif_dev); + if (ret) + goto error_capture_unregister; + + visconti_viif_subdev_notifier_register(viif_dev); + if (ret) + goto error_capture_unregister; + + return 0; + +error_capture_unregister: + visconti_viif_capture_unregister(viif_dev); +error_resizer_unregister: + visconti_viif_resizer_unregister(viif_dev); +error_isp_unregister: + visconti_viif_isp_unregister(viif_dev); +error_media_unregister: + media_device_unregister(&viif_dev->media_dev); +error_v4l2_unregister: + v4l2_device_unregister(&viif_dev->v4l2_dev); +error_dma_free: + pm_runtime_disable(dev); + dma_free_wc(&pdev->dev, sizeof(struct viif_table_area), viif_dev->tables, + (dma_addr_t)(uintptr_t)viif_dev->tables_dma); + return ret; +} + +static void visconti_viif_remove(struct platform_device *pdev) +{ + struct viif_device *viif_dev = platform_get_drvdata(pdev); + + v4l2_async_nf_unregister(&viif_dev->notifier); + v4l2_async_nf_cleanup(&viif_dev->notifier); + visconti_viif_capture_unregister(viif_dev); + visconti_viif_resizer_unregister(viif_dev); + visconti_viif_isp_unregister(viif_dev); + media_device_unregister(&viif_dev->media_dev); + v4l2_device_unregister(&viif_dev->v4l2_dev); + + pm_runtime_disable(&pdev->dev); + dma_free_wc(&pdev->dev, sizeof(struct viif_table_area), viif_dev->tables, + (dma_addr_t)(uintptr_t)viif_dev->tables_dma); +} + +static int visconti_viif_runtime_suspend(struct device *dev) +{ + struct viif_device *viif_dev = dev_get_drvdata(dev); + int i; + + viif_dev->irq_enabled = false; + /* Make sure the IRQ handler will see the flag change */ + mb(); + + /* mask all IRQs */ + viif_capture_write(viif_dev, REG_INT_M_SYNC_MASK, 0); + viif_capture_write(viif_dev, REG_INT_M_MASK, 0); + viif_capture_write(viif_dev, REG_INT_S_SYNC_MASK, 0); + viif_capture_write(viif_dev, REG_INT_S_MASK, 0); + viif_capture_read(viif_dev, REG_INT_M_SYNC_MASK); + viif_capture_read(viif_dev, REG_INT_M_MASK); + viif_capture_read(viif_dev, REG_INT_S_SYNC_MASK); + viif_capture_read(viif_dev, REG_INT_S_MASK); + for (i = 0; i < VIIF_NUM_IRQS; i++) + synchronize_irq(viif_dev->irq[i]); + + return 0; +} + +static int visconti_viif_runtime_resume(struct device *dev) +{ + struct viif_device *viif_dev = dev_get_drvdata(dev); + + /* Disable MPU */ + viif_mpu_disable(viif_dev); + /* Enable HWAIF */ + viif_hwaif_enable(viif_dev); + + viif_dev->irq_enabled = true; + /* Make sure the IRQ handler will see the flag change */ + mb(); + + /* VSYNC mask setting of MAIN unit */ + viif_capture_write(viif_dev, REG_INT_M_SYNC_MASK, MASK_INT_M_SYNC_MASK_SET); + + /* STATUS error mask setting of MAIN unit */ + viif_capture_write(viif_dev, REG_INT_M_MASK, MASK_INT_M_DELAY_INT_ERROR); + + /* VSYNC mask settings of SUB unit */ + viif_capture_write(viif_dev, REG_INT_S_SYNC_MASK, MASK_INT_S_SYNC_MASK_SET); + + /* STATUS error mask setting(unmask) of SUB unit */ + viif_capture_write(viif_dev, REG_INT_S_MASK, + MASK_INT_S_RESERVED_SET | MASK_INT_S_DELAY_INT_ERROR); + + return 0; +} + +static const struct dev_pm_ops visconti_viif_pm_ops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(visconti_viif_runtime_suspend, visconti_viif_runtime_resume, NULL) +}; + +static struct platform_driver visconti_viif_driver = { + .probe = visconti_viif_probe, + .remove = visconti_viif_remove, + .driver = { + .name = "visconti_viif", + .of_match_table = visconti_viif_of_table, + .pm = pm_ptr(&visconti_viif_pm_ops), + }, +}; + +module_platform_driver(visconti_viif_driver); + +MODULE_AUTHOR("Yuji Ishikawa "); +MODULE_DESCRIPTION("Toshiba Visconti Video Input driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/media/platform/toshiba/visconti/viif.h b/drivers/media/platform/toshiba/visconti/viif.h new file mode 100644 index 000000000000..d720ea8bd8d9 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif.h @@ -0,0 +1,379 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#ifndef __VIIF_H__ +#define __VIIF_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VIIF_DRIVER_NAME "visconti-viif" + +#define VIIF_ISP_REGBUF_0 0 +#define VIIF_L2ISP_POST_0 0 +#define VIIF_L2ISP_POST_1 1 +#define VIIF_MAX_POST_NUM 2U + +#define VIIF_CAPTURE_PAD_SINK 0 + +#define VIIF_RESIZER_PAD_SINK 0 +#define VIIF_RESIZER_PAD_SRC 1 +#define VIIF_RESIZER_PAD_NUM 2 + +#define VIIF_ISP_PAD_SINK_VIDEO 0 +#define VIIF_ISP_PAD_SRC_PATH0 1 +#define VIIF_ISP_PAD_SRC_PATH1 2 +#define VIIF_ISP_PAD_SRC_PATH2 3 +#define VIIF_ISP_PAD_SINK_PARAMS 4 +#define VIIF_ISP_PAD_SRC_STATS 5 +#define VIIF_ISP_PAD_NUM 6 + +#define VIIF_PARAMS_PAD_SRC 0 +#define VIIF_PARAMS_PAD_NUM 1 + +#define VIIF_STATS_PAD_SINK 0 +#define VIIF_STATS_PAD_NUM 1 + +#define CAPTURE_PATH_MAIN_POST0 0 +#define CAPTURE_PATH_MAIN_POST1 1 +#define CAPTURE_PATH_SUB 2 + +#define RESIZER_PATH_MAIN_POST0 0 +#define RESIZER_PATH_MAIN_POST1 1 + +#define VIIF_DPC_TABLE_BYTES 8192 +#define VIIF_LSC_TABLE_BYTES 1536 +#define VIIF_UNDIST_TABLE_BYTES 8192 +#define VIIF_L2_GAMMA_TABLE_BYTES 512 +#define VIIF_L2_GAMMA_TABLE_CH_NUM 6 + +#define VIIF_NUM_IRQS 2 + +/* The system clock (500MHz fixed) */ +/* this should be retrieved dynamically when the clock driver is implemented */ +#define VIIF_SYS_CLK 500000UL + +enum viif_output_color_mode { + VIIF_COLOR_Y_G = 0, + VIIF_COLOR_U_B = 1U, + VIIF_COLOR_V_R = 2U, + VIIF_COLOR_YUV_RGB = 4U +}; + +/** + * struct viif_out_process - configuration of output process of MAIN unit and L2ISP + * @half_scale: true to enable half scaling + * @select_color: viif_output_color_mode "select output color" + * @alpha: alpha value used in case of ARGB8888 output. Range: [0..255] + */ +struct viif_out_process { + bool half_scale; + enum viif_output_color_mode select_color; + u8 alpha; +}; + +/** + * struct viif_fmt - description of supported output image format + * @fourcc: V4L2 fourcc format ID + * @bpp: bits per pixel for each plane + * @num_planes: number of planes in a image + * @colorspace: colorspace ID + * @pitch_align: alignment constraint of pitch + */ +struct viif_fmt { + u32 fourcc; + u8 bpp[3]; + u8 num_planes; + u32 colorspace; + u32 pitch_align; +}; + +/* + * struct viif_table_area - table for ISP features. + * + * The memory block for this structure must be allocated with dma_alloc_wc() + * so that the allocated block will be physically continuous. + */ +struct viif_table_area { + /* L1ISP DPC */ + u32 dpc_table_h[VIIF_DPC_TABLE_BYTES / sizeof(u32)]; + u32 dpc_table_m[VIIF_DPC_TABLE_BYTES / sizeof(u32)]; + u32 dpc_table_l[VIIF_DPC_TABLE_BYTES / sizeof(u32)]; + /* L1ISP LSC */ + u16 lsc_table_gr[VIIF_LSC_TABLE_BYTES / sizeof(u16)]; + u16 lsc_table_r[VIIF_LSC_TABLE_BYTES / sizeof(u16)]; + u16 lsc_table_b[VIIF_LSC_TABLE_BYTES / sizeof(u16)]; + u16 lsc_table_gb[VIIF_LSC_TABLE_BYTES / sizeof(u16)]; + /* L2ISP UNDIST */ + u32 undist_write_g[VIIF_UNDIST_TABLE_BYTES / sizeof(u32)]; + u32 undist_read_b[VIIF_UNDIST_TABLE_BYTES / sizeof(u32)]; + u32 undist_read_g[VIIF_UNDIST_TABLE_BYTES / sizeof(u32)]; + u32 undist_read_r[VIIF_UNDIST_TABLE_BYTES / sizeof(u32)]; + /* L2ISP GAMMA */ + u16 l2_gamma_table[VIIF_MAX_POST_NUM][VIIF_L2_GAMMA_TABLE_CH_NUM] + [VIIF_L2_GAMMA_TABLE_BYTES / sizeof(u16)]; +}; + +/** + * struct cap_dev - device node for capture device + * @pathid: 0 for MAIN POST0, 1 for MAIN POST1, 2 for SUB + * @vdev: video node + * @capture_pad: media pad + * @vlock: serialize ioctl to vb2_queue and video_device + * @vb2_vq: queue of buffers + * @buf_queue: list of available buffers + * @active: VDMAC will start writing to this buffer at the next VSYNC + * @dma_active: VDMAC will complete writing to this buffer at the next VSYNC + * @buf_cnt: number of queued buffers + * @sequence: total count of processed frames + * @buf_lock: serialize queue access (including ISR's) + * @v4l2_pix: current picture format (set by S_FMT) + * @out_format: output format for VDMAC + * @img_area: crop of output picture + * @out_process: output configuration + * @fmts: format supported by this capture device + * @fmt_size: sizeof fmts + * @viif_dev: reference to viif device + */ +struct cap_dev { + u32 pathid; + struct video_device vdev; + struct media_pad capture_pad; + struct mutex vlock; /*serialize ioctl to vb2_queue and video_device*/ + + /* vb2 queue, capture buffer list and active buffer pointer */ + struct vb2_queue vb2_vq; + struct list_head buf_queue; + struct vb2_v4l2_buffer *active; + struct vb2_v4l2_buffer *dma_active; + int buf_cnt; + unsigned int sequence; + spinlock_t buf_lock; /* serialize queue access (including ISR's) */ + + /* current configuration of frame and pixel format */ + struct v4l2_pix_format_mplane v4l2_pix; + unsigned int out_format; + struct v4l2_rect img_area; + struct viif_out_process out_process; + + /* format supported by this cap device */ + const struct viif_fmt *fmts; + int fmt_size; + + struct viif_device *viif_dev; +}; + +/** + * struct params_dev - device node for ISP parameters + * @vdev: video node + * @params_pad: media pad + * @vlock: serialize ioctl to vb2_queue and video_device + * @vb2_vq: queue of buffers + * @params_queue: list of available buffers + * @params_lock: serialize params_queue + */ +struct params_dev { + struct video_device vdev; + struct media_pad params_pad; + struct mutex vlock; /*serialize ioctl to vb2_queue and video_device*/ + + struct vb2_queue vb2_vq; + struct list_head params_queue; + spinlock_t params_lock; /* serialize params_queue */ +}; + +/** + * struct stats_dev - device node for ISP status + * @vdev: video node + * @stats_pad: media pad + * @vlock: serialize ioctl to vb2_queue and video_device + * @vb2_vq: queue of buffers + * @stats_queue: list of available buffers + * @stats_lock: serialize stats_queue + */ +struct stats_dev { + struct video_device vdev; + struct media_pad stats_pad; + struct mutex vlock; /*serialize ioctl to vb2_queue and video_device*/ + + struct vb2_queue vb2_vq; + struct list_head stats_queue; + spinlock_t stats_lock; /* serialize stats_queue */ +}; + +/** + * struct resizer_subdev - device node for Resizer subdevice + * @pathid: 0 for MAIN POST0, 1 for MAIN POST1 + * @sd: v4l2 subdevice + * @pads: media pad + * @viif_dev: reference to viif device + */ +struct resizer_subdev { + struct v4l2_subdev sd; + struct media_pad pads[VIIF_RESIZER_PAD_NUM]; + struct viif_device *viif_dev; + u32 pathid; +}; + +/** + * struct isp_subdev - device node for ISP subdevice + * @sd: v4l2 subdevice + * @pads: media pad + * @viif_dev: reference to viif device + */ +struct isp_subdev { + struct v4l2_subdev sd; + struct media_pad pads[VIIF_ISP_PAD_NUM]; + struct viif_device *viif_dev; +}; + +/** + * struct viif_l2_roi_path_info - crop information of main paths + * @roi_num: + * + * - 1: crops of MAIN POST0 and POST1 share the same ROI + * - 2: crops of MAIN POST0 and POST1 have independent ROIs + * + * @post_enable_flag: flag to enable corresponding main path + * @post_crop_x: left of crop rect for a POST + * @post_crop_y: top of crop rect for a POST + * @post_crop_w: width of crop rect for a POST + * @post_crop_h: height of crop rect for a POST + */ +struct viif_l2_roi_path_info { + u32 roi_num; + bool post_enable_flag[VIIF_MAX_POST_NUM]; + u32 post_crop_x[VIIF_MAX_POST_NUM]; + u32 post_crop_y[VIIF_MAX_POST_NUM]; + u32 post_crop_w[VIIF_MAX_POST_NUM]; + u32 post_crop_h[VIIF_MAX_POST_NUM]; +}; + +/** + * struct viif_img_clk - relation between realtime duration and number of lines + * @pixel_clock: picture transfer clock frequency + * @htotal_size: width of picture including blanking period + * + * These values are used to convert realtime duration (such as HW specific setup time) + * into number of lines in a picture. + * See sysclk_to_numlines() called at the reconfiguration of L1ISP HDRC feature. + */ +struct viif_img_clk { + unsigned int pixel_clock; + unsigned int htotal_size; +}; + +/** + * struct viif_device - driver information of Visconti VIIF + * @dev: device + * @v4l2_dev: v4l2 device + * @media_dev: media device + * @pipe: media pipeline + * @masked_gamma_path: flag to ignore L2_GAMMA error just after capture error + * @notifier: async subdev notification helper + * @cap_post0: capture device for MAIN PATH 0 + * @cap_post1: capture device for MAIN PATH 1 + * @cap_sub: capture device for MAIN PATH 2 + * @resizer_post0: resizer subdevice for PATH 0 + * @resizer_post1: resizer subdevice for PATH 1 + * @isp_subdev: ISP subdevice + * @params_dev: streaming device for ISP parameter + * @stats_dev: streaming device for ISP status + * @remote_sd: currently active remote (possibly sensor) subdevice + * @remote_num_lane: number of lanes for the currently active remote subdevice + * @stream_lock: serialize stream ON/OFF sequence + * @regbuf_lock: Serialize VIIF Register Buffer Access + * @l2_roi_path_info: crop information of main paths + * @img_clk: relation between realtime duration and number of lines + * @run_flag_main: flag to check if the stream is ON + * @capture_reg: HW capture registers + * @hwaif_reg: HW bus interface registers + * @mpu_reg: HW memory protection unit registers + * @irq: IRQ number + * @irq_enabled: true when interrupts can be handled correctly + * @tables: table for ISP features (virtual address) + * @tables_dma: table for ISP features (IOVA) + * @errflag_lock: serialize access to status_err + * @repflag_lock: serialize access to reported_err_main, reported_err_sub + * @status_err: error of Main path in a frame + * @reported_err_main: accumulated error flags for MAIN path + * @reported_err_sub: accumulated error flags for SUB path + */ +struct viif_device { + struct device *dev; + struct v4l2_device v4l2_dev; + struct media_device media_dev; + struct media_pipeline pipe; + u32 masked_gamma_path; + + struct v4l2_async_notifier notifier; + + struct cap_dev cap_post0; + struct cap_dev cap_post1; + struct cap_dev cap_sub; + struct resizer_subdev resizer_post0; + struct resizer_subdev resizer_post1; + struct isp_subdev isp_subdev; + struct params_dev params_dev; + struct stats_dev stats_dev; + struct v4l2_subdev *remote_sd; + unsigned int remote_num_lane; + + /* stream_lock - Serialize stream ON/OFF sequence */ + struct mutex stream_lock; + + /* regbuf_lock - Serialize VIIF Register Buffer Access */ + spinlock_t regbuf_lock; + + struct viif_l2_roi_path_info l2_roi_path_info; + struct viif_img_clk img_clk; + bool run_flag_main; + + void __iomem *capture_reg; + void __iomem *hwaif_reg; + void __iomem *mpu_reg; + unsigned int irq[VIIF_NUM_IRQS]; + + bool irq_enabled; + + /* Memory region for tables referred by the HW */ + struct viif_table_area *tables; + dma_addr_t tables_dma; + + /* errflag_lock - serialize access to status_err */ + spinlock_t errflag_lock; + + /* repflag_lock - serialize access to reported_err_main, reported_err_sub */ + spinlock_t repflag_lock; + + /* Error flag checked at delayed vsync handler */ + u32 status_err; + + /* Error flag checked at stats streaming interface */ + u32 reported_err_main; + u32 reported_err_sub; +}; + +static inline void viif_capture_write(struct viif_device *viif_dev, unsigned int regid, u32 val) +{ + writel(val, viif_dev->capture_reg + regid); +} + +static inline u32 viif_capture_read(struct viif_device *viif_dev, unsigned int regid) +{ + return readl(viif_dev->capture_reg + regid); +} + +#endif /* __VIIF_H__ */ diff --git a/drivers/media/platform/toshiba/visconti/viif_capture.c b/drivers/media/platform/toshiba/visconti/viif_capture.c new file mode 100644 index 000000000000..a7e51214f653 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_capture.c @@ -0,0 +1,1285 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include +#include +#include + +#include "viif.h" +#include "viif_capture.h" +#include "viif_common.h" +#include "viif_isp.h" +#include "viif_regs.h" +#include "viif_resizer.h" + +/* single plane for RGB/Grayscale types, 3 planes for YUV types */ +#define VIIF_MAX_PLANE_NUM 3 + +/* maximum horizontal/vertical position/dimension of CROP with ISP */ +#define VIIF_CROP_MAX_X_ISP 8062U +#define VIIF_CROP_MAX_Y_ISP 3966U +#define VIIF_CROP_MAX_W_ISP 8190U +#define VIIF_CROP_MAX_H_ISP 4094U + +/* minimum horizontal/vertical dimension of CROP */ +#define VIIF_CROP_MIN_W 128U +#define VIIF_CROP_MIN_H 128U + +/* maximum output size with ISP */ +#define VIIF_MAX_OUTPUT_IMG_WIDTH_ISP 5760U +#define VIIF_MAX_OUTPUT_IMG_HEIGHT_ISP 3240U +#define VIIF_MAX_PITCH_ISP 32704U + +/* minimum output size */ +#define VIIF_MIN_OUTPUT_IMG_WIDTH 128U +#define VIIF_MIN_OUTPUT_IMG_HEIGHT 128U + +/* DMA settings for SUB path */ +#define VDMAC_SRAM_BASE_ADDR_W03 0x440U +#define SRAM_SIZE_W_PORT 0x200 + +enum viif_color_format { + VIIF_YCBCR422_8_PACKED = 0, + VIIF_RGB888_PACKED = 1U, + VIIF_ARGB8888_PACKED = 3U, + VIIF_YCBCR422_8_PLANAR = 8U, + VIIF_RGB888_YCBCR444_8_PLANAR = 9U, + VIIF_ONE_COLOR_8 = 11U, + VIIF_YCBCR422_16_PLANAR = 12U, + VIIF_RGB161616_YCBCR444_16_PLANAR = 13U, + VIIF_ONE_COLOR_16 = 15U +}; + +/** + * struct viif_csc_param - color conversion information + * @r_cr_in_offset: input offset of R/Cr + * @g_y_in_offset: input offset of G/Y + * @b_cb_in_offset: input offset of B/Cb + * @coef: coefficient of matrix. + * @r_cr_out_offset: output offset of R/Cr + * @g_y_out_offset: output offset of G/Y + * @b_cb_out_offset: output offset of B/Cb + * + * Range of parameters is: + * + * - {r_cr,g_y,b_cb}_{in,out}_offset + * + * - Range: [0x0..0x1ffff] + * + * - coef + * + * - Range: [0x0..0xffff] + * - [0] : c00(YG_YG), [1] : c01(UB_YG), [2] : c02(VR_YG), + * - [3] : c10(YG_UB), [4] : c11(UB_UB), [5] : c12(VR_UB), + * - [6] : c20(YG_VR), [7] : c21(UB_VR), [8] : c22(VR_VR) + */ +struct viif_csc_param { + u32 r_cr_in_offset; + u32 g_y_in_offset; + u32 b_cb_in_offset; + u32 coef[9]; + u32 r_cr_out_offset; + u32 g_y_out_offset; + u32 b_cb_out_offset; +}; + +/** + * struct viif_pixelmap - pixelmap information + * @pmap_dma: start address of pixel data(DMA address). 4byte alignment. + * @pitch: pitch size of pixel map [unit: byte] + * + * Condition of pitch in case of L2ISP output is as below. + * + * * max: 32704 + * * min: max (active width of image * k / r, 128) + * * alignment: 64 + * + * Condition of pitch in the other cases is as below. + * + * * max: 65536 + * * min: active width of image * k / r + * * alignment: 4 + * + * k is the size of 1 pixel and the value is as below. + * + * * VIIF_YCBCR422_8_PACKED: 2 + * * VIIF_RGB888_PACKED: 3 + * * VIIF_ARGB8888_PACKED: 4 + * * VIIF_YCBCR422_8_PLANAR: 1 + * * VIIF_RGB888_YCBCR444_8_PLANAR: 1 + * * VIIF_ONE_COLOR_8: 1 + * * VIIF_YCBCR422_16_PLANAR: 2 + * * VIIF_RGB161616_YCBCR444_16_PLANAR: 2 + * * VIIF_ONE_COLOR_16: 2 + * + * r is the correction factor for Cb or Cr of YCbCr422 planar and the value is as below. + * + * * YCbCr422 Cb-planar: 2 + * * YCbCr422 Cr-planar: 2 + * * others: 1 + */ +struct viif_pixelmap { + dma_addr_t pmap_dma; + u32 pitch; +}; + +/** + * struct viif_img - image information + * @width: active width of image [unit: pixel] + * * Range: [128..5760](output from L2ISP) + * * Range: [128..4096](output from SUB unit) + * * The value should be even. + * + * @height: active height of image[line] + * * Range: [128..3240](output from L2ISP) + * * Range: [128..2160](output from SUB unit) + * * The value should be even. + * + * @num_planes: number of image planes for this image + * + * @format: viif_color_format "color format" + * * Below color formats are supported for input and output of MAIN unit + * * VIIF_YCBCR422_8_PACKED + * * VIIF_RGB888_PACKED + * * VIIF_ARGB8888_PACKED + * * VIIF_YCBCR422_8_PLANAR + * * VIIF_RGB888_YCBCR444_8_PLANAR + * * VIIF_ONE_COLOR_8 + * * VIIF_YCBCR422_16_PLANAR + * * VIIF_RGB161616_YCBCR444_16_PLANAR + * * VIIF_ONE_COLOR_16 + * * Below color formats are supported for output of SUB unit + * * VIIF_ONE_COLOR_8 + * * VIIF_ONE_COLOR_16 + * + * @pixelmap: pixelmap information + * * [0]: Y/G-planar, packed/Y/RAW + * * [1]: Cb/B-planar + * * [2]: Cr/R-planar + */ +struct viif_img { + u32 width; + u32 height; + u32 num_planes; + enum viif_color_format format; + struct viif_pixelmap pixelmap[VIIF_MAX_PLANE_NUM]; +}; + +/*=============================================*/ +/* Low Layer Implementation */ +/*=============================================*/ +/** + * viif_l2_set_output_csc() - Set output color space conversion parameters of L2ISP + * + * @viif_dev: the VIIF device + * @post_id: POST ID. Range: [0..1] + * @param: Pointer to output color space conversion parameters of L2ISP + */ +static void viif_l2_set_output_csc(struct viif_device *viif_dev, u32 post_id, + const struct viif_csc_param *param) +{ + /* disable csc matrix when param is NULL */ + if (!param) { + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB(post_id), 0); + return; + } + + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB_YG_OFFSETI(post_id), + param->g_y_in_offset); + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB_YG1(post_id), + FIELD_CSC_MTB_LOWER(param->coef[0])); + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB_YG2(post_id), + FIELD_CSC_MTB_UPPER(param->coef[1]) | + FIELD_CSC_MTB_LOWER(param->coef[2])); + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB_YG_OFFSETO(post_id), + param->g_y_out_offset); + + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB_CB_OFFSETI(post_id), + param->b_cb_in_offset); + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB_CB1(post_id), + FIELD_CSC_MTB_LOWER(param->coef[3])); + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB_CB2(post_id), + FIELD_CSC_MTB_UPPER(param->coef[4]) | + FIELD_CSC_MTB_LOWER(param->coef[5])); + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB_CB_OFFSETO(post_id), + param->b_cb_out_offset); + + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB_CR_OFFSETI(post_id), + param->r_cr_in_offset); + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB_CR1(post_id), + FIELD_CSC_MTB_LOWER(param->coef[6])); + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB_CR2(post_id), + FIELD_CSC_MTB_UPPER(param->coef[7]) | + FIELD_CSC_MTB_LOWER(param->coef[8])); + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB_CR_OFFSETO(post_id), + param->r_cr_out_offset); + + viif_capture_write(viif_dev, REG_L2_POST_X_CSC_MTB(post_id), 1); +} + +/** + * viif_l2_set_img_transmission() - Set image transfer condition of L2ISP + * + * @viif_dev: the VIIF device + * @post_id: POST ID. Range: [0..1] + * @enable: set True to enable image transfer of MAIN unit. + * @src: Pointer to crop area information + * @out_process: Pointer to output process information + * @img: Pointer to output image information + * + * see also: #viif_l2_set_roi_path + */ +static void viif_l2_set_img_transmission(struct viif_device *viif_dev, u32 post_id, bool enable, + const struct v4l2_rect *src, + const struct viif_out_process *out_process, + const struct viif_img *img) +{ + const struct viif_pixelmap *pm; + + /* DISABLE: no DMA transmission setup, set minimum crop rectangle */ + if (!enable) { + viif_dev->l2_roi_path_info.post_enable_flag[post_id] = false; + viif_dev->l2_roi_path_info.post_crop_x[post_id] = 0U; + viif_dev->l2_roi_path_info.post_crop_y[post_id] = 0U; + viif_dev->l2_roi_path_info.post_crop_w[post_id] = VIIF_CROP_MIN_W; + viif_dev->l2_roi_path_info.post_crop_h[post_id] = VIIF_CROP_MIN_H; + visconti_viif_l2_set_roi_path(viif_dev); + + return; + } + + pm = &img->pixelmap[0]; + viif_capture_write(viif_dev, REG_L2_POST_X_OUT_STADR_G(post_id), (u32)pm->pmap_dma); + viif_capture_write(viif_dev, REG_L2_POST_X_OUT_PITCH_G(post_id), pm->pitch); + if (img->num_planes == 3) { + pm = &img->pixelmap[1]; + viif_capture_write(viif_dev, REG_L2_POST_X_OUT_STADR_B(post_id), (u32)pm->pmap_dma); + viif_capture_write(viif_dev, REG_L2_POST_X_OUT_PITCH_B(post_id), pm->pitch); + pm = &img->pixelmap[2]; + viif_capture_write(viif_dev, REG_L2_POST_X_OUT_STADR_R(post_id), (u32)pm->pmap_dma); + viif_capture_write(viif_dev, REG_L2_POST_X_OUT_PITCH_R(post_id), pm->pitch); + } + + /* Set CROP */ + viif_capture_write(viif_dev, REG_L2_POST_X_CAP_OFFSET(post_id), + (src->top << 16U) | src->left); + viif_capture_write(viif_dev, REG_L2_POST_X_CAP_SIZE(post_id), + (src->height << 16U) | src->width); + + /* Set output process */ + viif_capture_write(viif_dev, REG_L2_POST_X_HALF_SCALE_EN(post_id), + out_process->half_scale ? 1 : 0); + viif_capture_write(viif_dev, REG_L2_POST_X_C_SELECT(post_id), out_process->select_color); + viif_capture_write(viif_dev, REG_L2_POST_X_OPORTALP(post_id), (u32)out_process->alpha); + viif_capture_write(viif_dev, REG_L2_POST_X_OPORTFMT(post_id), img->format); + + /* Update ROI area and input to each POST */ + viif_dev->l2_roi_path_info.post_enable_flag[post_id] = true; + viif_dev->l2_roi_path_info.post_crop_x[post_id] = src->left; + viif_dev->l2_roi_path_info.post_crop_y[post_id] = src->top; + viif_dev->l2_roi_path_info.post_crop_w[post_id] = src->width; + viif_dev->l2_roi_path_info.post_crop_h[post_id] = src->height; + visconti_viif_l2_set_roi_path(viif_dev); +} + +/** + * viif_sub_set_img_transmission() - Set image transfer condition of SUB unit + * + * @viif_dev: the VIIF device + * @img: Pointer to output image information + */ +static void viif_sub_set_img_transmission(struct viif_device *viif_dev, const struct viif_img *img) +{ + dma_addr_t img_start_addr, img_end_addr; + u32 data_width, pitch, height; + u32 port_control; + + /* disable VDMAC when img is NULL */ + if (!img) { + viif_capture_write(viif_dev, REG_IPORTS_IMGEN, 0); + port_control = ~((u32)1U << 3U) & viif_capture_read(viif_dev, REG_VDM_W_ENABLE); + viif_capture_write(viif_dev, REG_VDM_W_ENABLE, port_control); + return; + } + + img_start_addr = (u32)img->pixelmap[0].pmap_dma; + pitch = img->pixelmap[0].pitch; + height = img->height; + + if (img->format == VIIF_ONE_COLOR_8) { + data_width = 0U; + img_end_addr = img_start_addr + img->width - 1U; + } else { + /* VIIF_ONE_COLOR_16 */ + data_width = 1U; + img_end_addr = img_start_addr + (img->width * 2U) - 1U; + } + + viif_capture_write(viif_dev, REG_VDM_WPORT_X_W_SRAM_BASE(IDX_WPORT_SUB_IMG), + VDMAC_SRAM_BASE_ADDR_W03); + viif_capture_write(viif_dev, REG_VDM_WPORT_X_W_SRAM_SIZE(IDX_WPORT_SUB_IMG), + SRAM_SIZE_W_PORT); + viif_capture_write(viif_dev, REG_VDM_WPORT_X_W_STADR(IDX_WPORT_SUB_IMG), + (u32)img_start_addr); + viif_capture_write(viif_dev, REG_VDM_WPORT_X_W_ENDADR(IDX_WPORT_SUB_IMG), + (u32)img_end_addr); + viif_capture_write(viif_dev, REG_VDM_WPORT_X_W_HEIGHT(IDX_WPORT_SUB_IMG), height); + viif_capture_write(viif_dev, REG_VDM_WPORT_X_W_PITCH(IDX_WPORT_SUB_IMG), pitch); + viif_capture_write(viif_dev, REG_VDM_WPORT_X_W_CFG0(IDX_WPORT_SUB_IMG), data_width << 8U); + port_control = BIT(3) | viif_capture_read(viif_dev, REG_VDM_W_ENABLE); + viif_capture_write(viif_dev, REG_VDM_W_ENABLE, port_control); + viif_capture_write(viif_dev, REG_IPORTS_IMGEN, 1); +} + +/*=============================================*/ +/* handling V4L2 framework */ +/*=============================================*/ +struct viif_buffer { + struct vb2_v4l2_buffer vb; + struct list_head queue; +}; + +static inline struct viif_buffer *vb2_to_viif(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct viif_buffer, vb); +} + +static inline struct cap_dev *video_drvdata_to_capdev(struct file *file) +{ + return (struct cap_dev *)video_drvdata(file); +} + +static inline struct cap_dev *vb2queue_to_capdev(struct vb2_queue *vq) +{ + return (struct cap_dev *)vb2_get_drv_priv(vq); +} + +/* ----- ISRs and VB2 Operations ----- */ +static void viif_set_img(struct cap_dev *cap_dev, struct vb2_buffer *vb) +{ + struct v4l2_pix_format_mplane *pix = &cap_dev->v4l2_pix; + struct viif_device *viif_dev = cap_dev->viif_dev; + struct viif_img next_out_img; + int i; + + next_out_img.width = pix->width; + next_out_img.height = pix->height; + next_out_img.format = cap_dev->out_format; + next_out_img.num_planes = pix->num_planes; + + for (i = 0; i < pix->num_planes; i++) { + next_out_img.pixelmap[i].pitch = pix->plane_fmt[i].bytesperline; + next_out_img.pixelmap[i].pmap_dma = vb2_dma_contig_plane_dma_addr(vb, i); + } + + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) { + guard(spinlock)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + viif_l2_set_img_transmission(viif_dev, VIIF_L2ISP_POST_0, true, &cap_dev->img_area, + &cap_dev->out_process, &next_out_img); + } else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1) { + guard(spinlock)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + viif_l2_set_img_transmission(viif_dev, VIIF_L2ISP_POST_1, true, &cap_dev->img_area, + &cap_dev->out_process, &next_out_img); + } else if (cap_dev->pathid == CAPTURE_PATH_SUB) { + guard(spinlock)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + viif_sub_set_img_transmission(viif_dev, &next_out_img); + } +} + +/* + * viif_capture_switch_buffer() is called from interrupt service routine + * triggered by VSync with some fixed delay. + * The function may switch DMA target buffer by calling viif_set_img(). + * The VIIF DMA HW captures the destination address at next VSync + * and completes transfer at one more after. + * Therefore, filled buffer is available at the one after next ISR. + * + * To avoid DMA HW getting stuck, we always need to set valid destination address. + * If a prepared buffer is not available, we reuse the buffer currently being transferred to. + * + * The cap_dev structure has two pointers and a queue to handle video buffers; + + Description of each item at the entry of this function: + * * buf_queue: holds prepared buffers, set by vb2_queue() + * * active: pointing at address captured (and to be filled) by DMA HW + * * dma_active: pointing at buffer filled by DMA HW + * + * Rules to update items: + * * when buf_queue is not empty, "active" buffer goes "dma_active" + * * when buf_queue is empty: + * * "active" buffer stays the same (DMA HW fills the same buffer for coming two frames) + * * "dma_active" gets NULL (filled buffer will be reused; should not go "DONE" at next ISR) + * + * Simulation: + * | buf_queue | active | dma_active | note | + * | X | NULL | NULL | | + * + * | X | BUF0 | NULL | BUF0 stays | + * | X | BUF0 | NULL | BUF0 stays | + * + * + * | BUF2 BUF1 | BUF0 | NULL | | + * | BUF2 | BUF1 | BUF0 | BUF0 goes DONE | + * | X | BUF2 | BUF1 | BUF1 goes DONE, BUF2 stays | + * | X | BUF2 | NULL | BUF2 stays | + */ +void visconti_viif_capture_switch_buffer(struct cap_dev *cap_dev, u32 status_err, + u32 l2_transfer_status, u64 timestamp) +{ + guard(spinlock)(&cap_dev->buf_lock); + + if (cap_dev->dma_active) { + /* DMA has completed and another framebuffer instance is set */ + struct vb2_v4l2_buffer *vbuf = cap_dev->dma_active; + enum vb2_buffer_state state; + + cap_dev->buf_cnt--; + vbuf->vb2_buf.timestamp = timestamp; + vbuf->sequence = cap_dev->sequence++; + vbuf->field = V4L2_FIELD_NONE; + if (status_err || l2_transfer_status) + state = VB2_BUF_STATE_ERROR; + else + state = VB2_BUF_STATE_DONE; + + vb2_buffer_done(&vbuf->vb2_buf, state); + } + + /* QUEUE pop to register an instance as next DMA target; if empty, reuse current instance */ + if (!list_empty(&cap_dev->buf_queue)) { + struct viif_buffer *buf = + list_entry(cap_dev->buf_queue.next, struct viif_buffer, queue); + list_del_init(&buf->queue); + viif_set_img(cap_dev, &buf->vb.vb2_buf); + cap_dev->dma_active = cap_dev->active; + cap_dev->active = &buf->vb; + } else { + cap_dev->dma_active = NULL; + } +} + +/* --- Capture buffer control --- */ +static int viif_vb2_setup(struct vb2_queue *vq, unsigned int *count, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct cap_dev *cap_dev = vb2queue_to_capdev(vq); + struct v4l2_pix_format_mplane *pix = &cap_dev->v4l2_pix; + unsigned int i; + + /* num_planes is set: just check plane sizes. */ + if (*num_planes) { + for (i = 0; i < pix->num_planes; i++) + if (sizes[i] < pix->plane_fmt[i].sizeimage) + return -EINVAL; + + return 0; + } + + /* num_planes not set: called from REQBUFS, just set plane sizes. */ + *num_planes = pix->num_planes; + for (i = 0; i < pix->num_planes; i++) + sizes[i] = pix->plane_fmt[i].sizeimage; + + cap_dev->buf_cnt = 0; + + return 0; +} + +static void viif_vb2_queue(struct vb2_buffer *vb) +{ + struct cap_dev *cap_dev = vb2queue_to_capdev(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct viif_buffer *buf = vb2_to_viif(vbuf); + + guard(spinlock_irqsave)(&cap_dev->buf_lock); + + list_add_tail(&buf->queue, &cap_dev->buf_queue); + cap_dev->buf_cnt++; +} + +static int viif_vb2_prepare(struct vb2_buffer *vb) +{ + struct cap_dev *cap_dev = vb2queue_to_capdev(vb->vb2_queue); + struct v4l2_pix_format_mplane *pix = &cap_dev->v4l2_pix; + struct viif_device *viif_dev = cap_dev->viif_dev; + unsigned int i; + + for (i = 0; i < pix->num_planes; i++) { + if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) { + dev_info(viif_dev->dev, "Plane size too small (%lu < %u)\n", + vb2_plane_size(vb, i), pix->plane_fmt[i].sizeimage); + return -EINVAL; + } + + vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage); + } + return 0; +} + +static void viif_return_all_buffers(struct cap_dev *cap_dev, enum vb2_buffer_state state) +{ + struct viif_device *viif_dev = cap_dev->viif_dev; + struct viif_buffer *buf; + + guard(spinlock_irqsave)(&cap_dev->buf_lock); + + /* buffer control */ + if (cap_dev->active) { + vb2_buffer_done(&cap_dev->active->vb2_buf, state); + cap_dev->buf_cnt--; + cap_dev->active = NULL; + } + if (cap_dev->dma_active) { + vb2_buffer_done(&cap_dev->dma_active->vb2_buf, state); + cap_dev->buf_cnt--; + cap_dev->dma_active = NULL; + } + + /* Release all queued buffers. */ + list_for_each_entry(buf, &cap_dev->buf_queue, queue) { + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + cap_dev->buf_cnt--; + } + INIT_LIST_HEAD(&cap_dev->buf_queue); + if (cap_dev->buf_cnt) + dev_err(viif_dev->dev, "Buffer count error %d\n", cap_dev->buf_cnt); +} + +static int viif_l2_set_format(struct cap_dev *cap_dev); +static const struct viif_fmt *get_viif_fmt_from_fourcc(struct cap_dev *cap_dev, + unsigned int fourcc); + +static int viif_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct cap_dev *cap_dev = vb2queue_to_capdev(vq); + struct viif_device *viif_dev = cap_dev->viif_dev; + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + int ret = 0; + + guard(mutex)(&viif_dev->stream_lock); + + /* note that pipe is shared among paths; see pipe.streaming_count member variable */ + ret = video_device_pipeline_start(&cap_dev->vdev, &viif_dev->pipe); + if (ret) { + dev_err(viif_dev->dev, "start pipeline failed %d\n", ret); + return ret; + } + + ret = pm_runtime_resume_and_get(viif_dev->dev); + if (ret) { + dev_err(viif_dev->dev, "failed to power on %d\n", ret); + goto release_pipe; + } + + /* buffer control */ + cap_dev->sequence = 0; + + /* Currently, we assume that path0 (MAIN POST0) is enabled first and disabled last */ + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) { + ret = visconti_viif_isp_main_set_unit(viif_dev); + if (ret) { + dev_err(viif_dev->dev, "Setting up main path0 L1ISP failed %d\n", ret); + goto config_path_end; + } + ret = viif_l2_set_format(cap_dev); + if (ret) { + dev_err(viif_dev->dev, "Setting up main path0 L2VDM failed %d\n", ret); + goto config_path_end; + } + } else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1) { + ret = viif_l2_set_format(cap_dev); + if (ret) { + dev_err(viif_dev->dev, "Setting up main path1 L2VDM failed %d\n", ret); + goto config_path_end; + } + } else { + cap_dev->out_format = + get_viif_fmt_from_fourcc(cap_dev, cap_dev->v4l2_pix.pixelformat)->bpp[0] > + 8 ? + VIIF_ONE_COLOR_16 : + VIIF_ONE_COLOR_8; + ret = visconti_viif_isp_sub_set_unit(viif_dev); + if (ret) { + dev_err(viif_dev->dev, "Setting up sub path failed %d\n", ret); + goto config_path_end; + } + } + + remote_pad = media_pad_remote_pad_first(&cap_dev->vdev.entity.pads[0]); + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, BIT(0)); + if (ret) + dev_err(viif_dev->dev, "Start resizer/isp subdevice stream failed. %d\n", ret); + +config_path_end: + if (ret) { + viif_return_all_buffers(cap_dev, VB2_BUF_STATE_QUEUED); + pm_runtime_put(viif_dev->dev); + ret = -EPIPE; + } +release_pipe: + if (ret) + video_device_pipeline_stop(&cap_dev->vdev); + return ret; +} + +static void viif_stop_streaming(struct vb2_queue *vq) +{ + struct cap_dev *cap_dev = vb2queue_to_capdev(vq); + struct viif_device *viif_dev = cap_dev->viif_dev; + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + int ret; + + guard(mutex)(&viif_dev->stream_lock); + + remote_pad = media_pad_remote_pad_first(&cap_dev->vdev.entity.pads[0]); + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, BIT(0)); + if (ret) + dev_err(viif_dev->dev, "Stop isp subdevice stream failed %d\n", ret); + + ret = pm_runtime_put(viif_dev->dev); + if (ret) + dev_err(viif_dev->dev, "power down failed %d\n", ret); + + viif_return_all_buffers(cap_dev, VB2_BUF_STATE_ERROR); + video_device_pipeline_stop(&cap_dev->vdev); +} + +static const struct vb2_ops viif_vb2_ops = { + .queue_setup = viif_vb2_setup, + .buf_queue = viif_vb2_queue, + .buf_prepare = viif_vb2_prepare, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = viif_start_streaming, + .stop_streaming = viif_stop_streaming, +}; + +/* --- VIIF hardware settings --- */ +/* L2ISP output csc setting for YUV to RGB(ITU-R BT.709) */ +static const struct viif_csc_param viif_csc_yuv2rgb = { + .r_cr_in_offset = 0x18000, + .g_y_in_offset = 0x1f000, + .b_cb_in_offset = 0x18000, + .coef = { + [0] = 0x1000, + [1] = 0xfd12, + [2] = 0xf8ad, + [3] = 0x1000, + [4] = 0x1d07, + [5] = 0x0000, + [6] = 0x1000, + [7] = 0x0000, + [8] = 0x18a2, + }, + .r_cr_out_offset = 0x1000, + .g_y_out_offset = 0x1000, + .b_cb_out_offset = 0x1000, +}; + +/* L2ISP output csc setting for RGB to YUV(ITU-R BT.709) */ +static const struct viif_csc_param viif_csc_rgb2yuv = { + .r_cr_in_offset = 0x1f000, + .g_y_in_offset = 0x1f000, + .b_cb_in_offset = 0x1f000, + .coef = { + [0] = 0x0b71, + [1] = 0x0128, + [2] = 0x0367, + [3] = 0xf9b1, + [4] = 0x082f, + [5] = 0xfe20, + [6] = 0xf891, + [7] = 0xff40, + [8] = 0x082f, + }, + .r_cr_out_offset = 0x8000, + .g_y_out_offset = 0x1000, + .b_cb_out_offset = 0x8000, +}; + +static int viif_l2_set_format(struct cap_dev *cap_dev) +{ + struct v4l2_pix_format_mplane *pix = &cap_dev->v4l2_pix; + struct viif_device *viif_dev = cap_dev->viif_dev; + const struct viif_csc_param *csc_param = NULL; + struct v4l2_subdev_selection sel = { + .target = V4L2_SEL_TGT_CROP, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = VIIF_RESIZER_PAD_SRC, + }; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = VIIF_RESIZER_PAD_SRC, + }; + bool inp_is_rgb = false; + bool out_is_rgb = false; + struct v4l2_subdev *sd; + u32 postid; + int ret; + + /* check path id */ + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) { + sd = &viif_dev->resizer_post0.sd; + postid = VIIF_L2ISP_POST_0; + } else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1) { + sd = &viif_dev->resizer_post1.sd; + postid = VIIF_L2ISP_POST_1; + } else { + return -EINVAL; + } + + cap_dev->out_process.half_scale = false; + cap_dev->out_process.select_color = VIIF_COLOR_YUV_RGB; + cap_dev->out_process.alpha = 0; + + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sel); + if (ret) { + cap_dev->img_area.left = 0; + cap_dev->img_area.top = 0; + cap_dev->img_area.width = pix->width; + cap_dev->img_area.height = pix->height; + } else { + cap_dev->img_area.left = sel.r.left; + cap_dev->img_area.top = sel.r.top; + cap_dev->img_area.width = sel.r.width; + cap_dev->img_area.height = sel.r.height; + } + + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (!ret) + inp_is_rgb = (fmt.format.code == MEDIA_BUS_FMT_RGB888_1X24); + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_RGB24: + cap_dev->out_format = VIIF_RGB888_PACKED; + out_is_rgb = true; + break; + case V4L2_PIX_FMT_ABGR32: + cap_dev->out_format = VIIF_ARGB8888_PACKED; + cap_dev->out_process.alpha = 0xff; + out_is_rgb = true; + break; + case V4L2_PIX_FMT_YUV422M: + cap_dev->out_format = VIIF_YCBCR422_8_PLANAR; + break; + case V4L2_PIX_FMT_YUV444M: + cap_dev->out_format = VIIF_RGB888_YCBCR444_8_PLANAR; + break; + case V4L2_PIX_FMT_Y16: + cap_dev->out_format = VIIF_ONE_COLOR_16; + cap_dev->out_process.select_color = VIIF_COLOR_Y_G; + break; + } + + if (!inp_is_rgb && out_is_rgb) + csc_param = &viif_csc_yuv2rgb; /* YUV -> RGB */ + else if (inp_is_rgb && !out_is_rgb) + csc_param = &viif_csc_rgb2yuv; /* RGB -> YUV */ + + viif_l2_set_output_csc(viif_dev, postid, csc_param); + + return 0; +} + +/* --- IOCTL Operations --- */ +static const struct viif_fmt viif_capture_fmt_list_mainpath[] = { + { + .fourcc = V4L2_PIX_FMT_RGB24, + .bpp = { 24, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 384, + }, + { + .fourcc = V4L2_PIX_FMT_ABGR32, + .bpp = { 32, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 512, + }, + { + .fourcc = V4L2_PIX_FMT_YUV422M, + .bpp = { 8, 4, 4 }, + .num_planes = 3, + .colorspace = V4L2_COLORSPACE_REC709, + .pitch_align = 128, + }, + { + .fourcc = V4L2_PIX_FMT_YUV444M, + .bpp = { 8, 8, 8 }, + .num_planes = 3, + .colorspace = V4L2_COLORSPACE_REC709, + .pitch_align = 128, + }, + { + .fourcc = V4L2_PIX_FMT_Y16, + .bpp = { 16, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_REC709, + .pitch_align = 128, + }, +}; + +static const struct viif_fmt viif_capture_fmt_list_subpath[] = { + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .bpp = { 8, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 256, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .bpp = { 8, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 256, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .bpp = { 8, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 256, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .bpp = { 8, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 256, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB16, + .bpp = { 16, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 256, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG16, + .bpp = { 16, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 256, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG16, + .bpp = { 16, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 256, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .bpp = { 16, 0, 0 }, + .num_planes = 1, + .colorspace = V4L2_COLORSPACE_SRGB, + .pitch_align = 256, + }, +}; + +static const struct viif_fmt *get_viif_fmt_from_fourcc(struct cap_dev *cap_dev, unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < cap_dev->fmt_size; i++) { + const struct viif_fmt *fmt = &cap_dev->fmts[i]; + + if (fmt->fourcc == fourcc) + return fmt; + } + return NULL; +} + +static u32 get_pixelformat_from_fourcc(struct cap_dev *cap_dev, unsigned int fourcc) +{ + const struct viif_fmt *fmt = get_viif_fmt_from_fourcc(cap_dev, fourcc); + + return fmt ? fmt->fourcc : cap_dev->fmts[0].fourcc; +} + +static u32 get_pixelformat_from_mbus_code(struct cap_dev *cap_dev, unsigned int mbus_code) +{ + unsigned int fourcc; + + switch (mbus_code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + fourcc = V4L2_PIX_FMT_SRGGB8; + break; + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SRGGB14_1X14: + fourcc = V4L2_PIX_FMT_SRGGB16; + break; + case MEDIA_BUS_FMT_SGRBG8_1X8: + fourcc = V4L2_PIX_FMT_SGRBG8; + break; + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGRBG14_1X14: + fourcc = V4L2_PIX_FMT_SGRBG16; + break; + case MEDIA_BUS_FMT_SGBRG8_1X8: + fourcc = V4L2_PIX_FMT_SGBRG8; + break; + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGBRG14_1X14: + fourcc = V4L2_PIX_FMT_SGBRG16; + break; + case MEDIA_BUS_FMT_SBGGR8_1X8: + fourcc = V4L2_PIX_FMT_SBGGR8; + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SBGGR14_1X14: + fourcc = V4L2_PIX_FMT_SBGGR16; + break; + default: + return cap_dev->fmts[0].fourcc; + } + + return get_pixelformat_from_fourcc(cap_dev, fourcc); +} + +static void viif_calc_plane_sizes(struct cap_dev *cap_dev, struct v4l2_pix_format_mplane *pix) +{ + const struct viif_fmt *viif_fmt = get_viif_fmt_from_fourcc(cap_dev, pix->pixelformat); + unsigned int i; + + for (i = 0; i < viif_fmt->num_planes; i++) { + struct v4l2_plane_pix_format *plane_i = &pix->plane_fmt[i]; + unsigned int bpl; + + memset(plane_i, 0, sizeof(*plane_i)); + bpl = roundup(pix->width * viif_fmt->bpp[i] / 8, viif_fmt->pitch_align); + + plane_i->bytesperline = bpl; + plane_i->sizeimage = pix->height * bpl; + } + pix->num_planes = viif_fmt->num_planes; +} + +static int viif_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + + strscpy(cap->driver, VIIF_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, cap_dev->vdev.name, sizeof(cap->card)); + + return 0; +} + +static int viif_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + + if (f->index >= cap_dev->fmt_size) + return -EINVAL; + + f->pixelformat = cap_dev->fmts[f->index].fourcc; + return 0; +} + +static void viif_try_fmt(struct cap_dev *cap_dev, struct v4l2_pix_format_mplane *pix) +{ + struct viif_device *viif_dev = cap_dev->viif_dev; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_subdev *sd; + int ret; + + /* check path id */ + if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST0) { + sd = &viif_dev->resizer_post0.sd; + format.pad = VIIF_RESIZER_PAD_SRC; + } else if (cap_dev->pathid == CAPTURE_PATH_MAIN_POST1) { + sd = &viif_dev->resizer_post1.sd; + format.pad = VIIF_RESIZER_PAD_SRC; + } else { + sd = &viif_dev->isp_subdev.sd; + format.pad = VIIF_ISP_PAD_SRC_PATH2; + } + + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_DEFAULT; + pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix->quantization = V4L2_QUANTIZATION_DEFAULT; + + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &format); + if (ret) { + /* minimal default format */ + pix->width = VIIF_MIN_OUTPUT_IMG_WIDTH; + pix->height = VIIF_MIN_OUTPUT_IMG_HEIGHT; + pix->pixelformat = (cap_dev->pathid == CAPTURE_PATH_SUB) ? V4L2_PIX_FMT_SRGGB8 : + V4L2_PIX_FMT_RGB24; + viif_calc_plane_sizes(cap_dev, pix); + return; + } + + pix->width = format.format.width; + pix->height = format.format.height; + + /* check output format */ + if (cap_dev->pathid == CAPTURE_PATH_SUB) + pix->pixelformat = get_pixelformat_from_mbus_code(cap_dev, format.format.code); + else + pix->pixelformat = get_pixelformat_from_fourcc(cap_dev, pix->pixelformat); + + /* update derived parameters, such as bpp */ + viif_calc_plane_sizes(cap_dev, pix); +} + +static int viif_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + + viif_try_fmt(cap_dev, &f->fmt.pix_mp); + return 0; +} + +static int viif_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + + if (vb2_is_busy(&cap_dev->vb2_vq)) + return -EBUSY; + + viif_try_fmt(cap_dev, &f->fmt.pix_mp); + cap_dev->v4l2_pix = f->fmt.pix_mp; + + return 0; +} + +static int viif_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + + f->fmt.pix_mp = cap_dev->v4l2_pix; + + return 0; +} + +static int viif_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) +{ + struct cap_dev *cap_dev = video_drvdata_to_capdev(file); + + if (fsize->index) + return -EINVAL; + + if (!get_viif_fmt_from_fourcc(cap_dev, fsize->pixel_format)) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = VIIF_MIN_OUTPUT_IMG_WIDTH; + fsize->stepwise.max_width = VIIF_MAX_OUTPUT_IMG_WIDTH_ISP; + fsize->stepwise.min_height = VIIF_MIN_OUTPUT_IMG_HEIGHT; + fsize->stepwise.max_height = VIIF_MAX_OUTPUT_IMG_HEIGHT_ISP; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + + return 0; +} + +static const struct v4l2_ioctl_ops viif_ioctl_ops = { + .vidioc_querycap = viif_querycap, + + .vidioc_enum_fmt_vid_cap = viif_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap_mplane = viif_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap_mplane = viif_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap_mplane = viif_g_fmt_vid_cap, + + .vidioc_enum_framesizes = viif_enum_framesizes, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* --- File Operations --- */ +static const struct v4l2_pix_format_mplane pixm_default[3] = { + { + .pixelformat = V4L2_PIX_FMT_RGB24, + .width = 1920, + .height = 1080, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + }, + { + .pixelformat = V4L2_PIX_FMT_RGB24, + .width = 1920, + .height = 1080, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + }, + { + .pixelformat = V4L2_PIX_FMT_SRGGB8, + .width = 1920, + .height = 1080, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + } +}; + +static const struct v4l2_file_operations viif_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +/* ----- media control callbacks ----- */ +static int viif_capture_link_validate(struct media_link *link) +{ + /* link validation at start-stream */ + return 0; +} + +static const struct media_entity_operations viif_media_ops = { + .link_validate = viif_capture_link_validate, +}; + +/* ----- register/remove capture device node ----- */ +static int visconti_viif_capture_register_node(struct cap_dev *cap_dev) +{ + struct viif_device *viif_dev = cap_dev->viif_dev; + struct v4l2_device *v4l2_dev = &viif_dev->v4l2_dev; + struct video_device *vdev = &cap_dev->vdev; + struct vb2_queue *q = &cap_dev->vb2_vq; + static const char *const node_name[] = { + "viif_capture_post0", + "viif_capture_post1", + "viif_capture_sub", + }; + struct v4l2_pix_format_mplane pixm; + int ret; + + INIT_LIST_HEAD(&cap_dev->buf_queue); + + mutex_init(&cap_dev->vlock); + spin_lock_init(&cap_dev->buf_lock); + + /* Initialize image format */ + pixm = pixm_default[cap_dev->pathid]; + viif_try_fmt(cap_dev, &pixm); + cap_dev->v4l2_pix = pixm; + + /* Initialize vb2 queue. */ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->min_queued_buffers = 3; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->ops = &viif_vb2_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->drv_priv = cap_dev; + q->buf_struct_size = sizeof(struct viif_buffer); + q->lock = &cap_dev->vlock; + q->dev = viif_dev->v4l2_dev.dev; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + /* Register the video device. */ + strscpy(vdev->name, node_name[cap_dev->pathid], sizeof(vdev->name)); + vdev->v4l2_dev = v4l2_dev; + vdev->lock = &cap_dev->vlock; + vdev->queue = &cap_dev->vb2_vq; + vdev->fops = &viif_fops; + vdev->ioctl_ops = &viif_ioctl_ops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; + vdev->device_caps |= V4L2_CAP_IO_MC; + vdev->entity.ops = &viif_media_ops; + vdev->release = video_device_release_empty; + video_set_drvdata(vdev, cap_dev); + vdev->vfl_dir = VFL_DIR_RX; + cap_dev->capture_pad.flags = MEDIA_PAD_FL_SINK; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + dev_err(v4l2_dev->dev, "video_register_device failed: %d\n", ret); + return ret; + } + + ret = media_entity_pads_init(&vdev->entity, 1, &cap_dev->capture_pad); + if (ret) { + video_unregister_device(vdev); + return ret; + } + + return 0; +} + +int visconti_viif_capture_register(struct viif_device *viif_dev) +{ + int ret; + + /* register MAIN POST0 (primary RGB)*/ + viif_dev->cap_post0.pathid = CAPTURE_PATH_MAIN_POST0; + viif_dev->cap_post0.viif_dev = viif_dev; + viif_dev->cap_post0.fmts = viif_capture_fmt_list_mainpath; + viif_dev->cap_post0.fmt_size = ARRAY_SIZE(viif_capture_fmt_list_mainpath); + ret = visconti_viif_capture_register_node(&viif_dev->cap_post0); + if (ret) + return ret; + + /* register MAIN POST1 (additional RGB)*/ + viif_dev->cap_post1.pathid = CAPTURE_PATH_MAIN_POST1; + viif_dev->cap_post1.viif_dev = viif_dev; + viif_dev->cap_post1.fmts = viif_capture_fmt_list_mainpath; + viif_dev->cap_post1.fmt_size = ARRAY_SIZE(viif_capture_fmt_list_mainpath); + ret = visconti_viif_capture_register_node(&viif_dev->cap_post1); + if (ret) + return ret; + + /* register SUB (RAW) */ + viif_dev->cap_sub.pathid = CAPTURE_PATH_SUB; + viif_dev->cap_sub.viif_dev = viif_dev; + viif_dev->cap_sub.fmts = viif_capture_fmt_list_subpath; + viif_dev->cap_sub.fmt_size = ARRAY_SIZE(viif_capture_fmt_list_subpath); + ret = visconti_viif_capture_register_node(&viif_dev->cap_sub); + if (ret) + return ret; + + return 0; +} + +static void visconti_viif_capture_unregister_node(struct cap_dev *cap_dev) +{ + media_entity_cleanup(&cap_dev->vdev.entity); + vb2_video_unregister_device(&cap_dev->vdev); + mutex_destroy(&cap_dev->vlock); +} + +void visconti_viif_capture_unregister(struct viif_device *viif_dev) +{ + visconti_viif_capture_unregister_node(&viif_dev->cap_post0); + visconti_viif_capture_unregister_node(&viif_dev->cap_post1); + visconti_viif_capture_unregister_node(&viif_dev->cap_sub); +} diff --git a/drivers/media/platform/toshiba/visconti/viif_capture.h b/drivers/media/platform/toshiba/visconti/viif_capture.h new file mode 100644 index 000000000000..8587b1575278 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_capture.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#ifndef __VIIF_CAPTURE_H__ +#define __VIIF_CAPTURE_H__ + +struct cap_dev; +struct viif_device; +struct viif_l2_roi_config; + +void visconti_viif_capture_switch_buffer(struct cap_dev *cap_dev, u32 status_err, + u32 l2_transfer_status, u64 timestamp); + +int visconti_viif_capture_register(struct viif_device *viif_dev); +void visconti_viif_capture_unregister(struct viif_device *viif_dev); + +#endif /* __VIIF_CAPTURE_H__ */ diff --git a/drivers/media/platform/toshiba/visconti/viif_common.c b/drivers/media/platform/toshiba/visconti/viif_common.c new file mode 100644 index 000000000000..efee40e15cd4 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_common.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include +#include + +#include "viif.h" +#include "viif_common.h" +#include "viif_regs.h" + +/*=============================================*/ +/* Low level guards for registers */ +/*=============================================*/ + +#define VIIF_L1_CRGBF_R_START_ADDR_LIMIT 0x0200U +#define VIIF_L1_CRGBF_R_END_ADDR_LIMIT 0x10bfU +#define VIIF_L2_CRGBF_R_START_ADDR_LIMIT 0x1cU +#define VIIF_L2_CRGBF_R_END_ADDR_LIMIT 0x1fU + +/** + * hwd_viif_main_mask_vlatch() - Control Vlatch mask of MAIN unit + * + * @viif_dev: the VIIF device + * @enable: true to enable Vlatch mask of MAIN unit, false to disable + */ +static void hwd_viif_main_mask_vlatch(struct viif_device *viif_dev, bool enable) +{ + u32 val = enable ? MASK_IPORTM_VLATCH : 0; + + viif_capture_write(viif_dev, REG_IPORTM0_LD, val); + viif_capture_write(viif_dev, REG_IPORTM1_LD, val); +} + +/** + * hwd_viif_isp_set_regbuf_auto_transmission() - Set register buffer auto transmission + * + * @viif_dev: the VIIF device + */ +void hwd_viif_isp_set_regbuf_auto_transmission(struct viif_device *viif_dev) +{ + /* Set parameters for auto read transmission of register buffer */ + viif_capture_write(viif_dev, REG_L1_CRGBF_TRN_A_CONF, 0); + viif_capture_write(viif_dev, REG_L2_CRGBF_TRN_A_CONF, 0); + viif_capture_write(viif_dev, REG_L1_CRGBF_TRN_RBADDR, VIIF_L1_CRGBF_R_START_ADDR_LIMIT); + viif_capture_write(viif_dev, REG_L1_CRGBF_TRN_READDR, VIIF_L1_CRGBF_R_END_ADDR_LIMIT); + viif_capture_write(viif_dev, REG_L2_CRGBF_TRN_RBADDR, VIIF_L2_CRGBF_R_START_ADDR_LIMIT); + viif_capture_write(viif_dev, REG_L2_CRGBF_TRN_READDR, VIIF_L2_CRGBF_R_END_ADDR_LIMIT); + viif_capture_write(viif_dev, REG_L2_CRGBF_TRN_A_CONF, VAL_L2_CRGBF_TRN_AUTO_READ_BANK0); + viif_capture_write(viif_dev, REG_L1_CRGBF_TRN_A_CONF, VAL_L1_CRGBF_TRN_AUTO_READ_BANK0); +} + +/** + * hwd_viif_isp_disable_regbuf_auto_transmission() - Disable register buffer auto transmission + * + * @viif_dev: the VIIF device + */ +void hwd_viif_isp_disable_regbuf_auto_transmission(struct viif_device *viif_dev) +{ + viif_capture_write(viif_dev, REG_L1_CRGBF_TRN_A_CONF, 0); + viif_capture_write(viif_dev, REG_L2_CRGBF_TRN_A_CONF, 0); +} + +/** + * hwd_viif_isp_guard_start() - stop register auto update + * + * @viif_dev: the VIIF device + * + * This function call stops update of some hardware registers + * while the manual setup of VIIF, L1ISP registers is in progress. + * + * * regbuf control: load/store HW register (settings, status) values to backup SRAM. + * * vlatch control: copy timer-counter register value to status register. + */ +void hwd_viif_isp_guard_start(struct viif_device *viif_dev) +{ + hwd_viif_isp_disable_regbuf_auto_transmission(viif_dev); + ndelay(500); + hwd_viif_main_mask_vlatch(viif_dev, true); +} + +/** + * hwd_viif_isp_guard_end() - restart register auto update + * + * @viif_dev: the VIIF device + * + * see also hwd_viif_isp_guard_start(). + */ +void hwd_viif_isp_guard_end(struct viif_device *viif_dev) +{ + hwd_viif_main_mask_vlatch(viif_dev, false); + hwd_viif_isp_set_regbuf_auto_transmission(viif_dev); +} + +/*=============================================*/ +/* supported Visual formats */ +/*=============================================*/ +static const struct viif_mbus_format mbus_formats[] = { + { .code = MEDIA_BUS_FMT_RGB888_1X24, + .bpp = 24, + .is_bayer = false, + .rgb_out = true, + .mipi_dt = MIPI_CSI2_DT_RGB888 }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, + .bpp = 16, + .is_bayer = false, + .rgb_out = false, + .mipi_dt = MIPI_CSI2_DT_YUV422_8B }, + { .code = MEDIA_BUS_FMT_UYVY10_1X20, + .bpp = 20, + .is_bayer = false, + .rgb_out = false, + .mipi_dt = MIPI_CSI2_DT_YUV422_10B }, + { .code = MEDIA_BUS_FMT_RGB565_1X16, + .bpp = 16, + .is_bayer = false, + .rgb_out = true, + .mipi_dt = MIPI_CSI2_DT_RGB565 }, + + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .bpp = 8, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_BGGR, + .mipi_dt = MIPI_CSI2_DT_RAW8 }, + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .bpp = 8, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_GBRG, + .mipi_dt = MIPI_CSI2_DT_RAW8 }, + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .bpp = 8, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_GRBG, + .mipi_dt = MIPI_CSI2_DT_RAW8 }, + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .bpp = 8, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_RGGB, + .mipi_dt = MIPI_CSI2_DT_RAW8 }, + + { .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .bpp = 10, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_BGGR, + .mipi_dt = MIPI_CSI2_DT_RAW10 }, + { .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .bpp = 10, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_GBRG, + .mipi_dt = MIPI_CSI2_DT_RAW10 }, + { .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .bpp = 10, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_GRBG, + .mipi_dt = MIPI_CSI2_DT_RAW10 }, + { .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .bpp = 10, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_RGGB, + .mipi_dt = MIPI_CSI2_DT_RAW10 }, + + { .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .bpp = 12, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_BGGR, + .mipi_dt = MIPI_CSI2_DT_RAW12 }, + { .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .bpp = 12, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_GBRG, + .mipi_dt = MIPI_CSI2_DT_RAW12 }, + { .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .bpp = 12, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_GRBG, + .mipi_dt = MIPI_CSI2_DT_RAW12 }, + { .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .bpp = 12, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_RGGB, + .mipi_dt = MIPI_CSI2_DT_RAW12 }, + + { .code = MEDIA_BUS_FMT_SBGGR14_1X14, + .bpp = 14, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_BGGR, + .mipi_dt = MIPI_CSI2_DT_RAW14 }, + { .code = MEDIA_BUS_FMT_SGBRG14_1X14, + .bpp = 14, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_GBRG, + .mipi_dt = MIPI_CSI2_DT_RAW14 }, + { .code = MEDIA_BUS_FMT_SGRBG14_1X14, + .bpp = 14, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_GRBG, + .mipi_dt = MIPI_CSI2_DT_RAW14 }, + { .code = MEDIA_BUS_FMT_SRGGB14_1X14, + .bpp = 14, + .is_bayer = true, + .rgb_out = false, + .bayer_pattern = VAL_L1_SYSM_START_COLOR_RGGB, + .mipi_dt = MIPI_CSI2_DT_RAW14 }, +}; + +const struct viif_mbus_format *viif_mbus_format_from_code(unsigned int mbus_code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mbus_formats); i++) + if (mbus_formats[i].code == mbus_code) + return &mbus_formats[i]; + + return NULL; +} + +const struct viif_mbus_format *viif_mbus_format_nth(unsigned int n) +{ + return (n < ARRAY_SIZE(mbus_formats)) ? &mbus_formats[n] : NULL; +} diff --git a/drivers/media/platform/toshiba/visconti/viif_common.h b/drivers/media/platform/toshiba/visconti/viif_common.h new file mode 100644 index 000000000000..075361085011 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_common.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#ifndef __VIIF_COMMON_H__ +#define __VIIF_COMMON_H__ + +#include "viif.h" + +/** + * struct viif_mbus_format - description of supported input format + * + * @code: V4L2 media bus format (coming from image sensor) + * @bpp: bits per pixel + * @mipi_dt: MIPI Datatype corresponding to code + * @bayer_pattern: ordering of bayer color filter + * @is_bayer: true when the format is bayer, false otherwise + * @rgb_out: + * * True: L1ISP output is RGB format + * * False: L1ISP output is YUV format + */ +struct viif_mbus_format { + unsigned int code; + unsigned int bpp; + unsigned int mipi_dt; + unsigned int bayer_pattern; + bool is_bayer; + bool rgb_out; +}; + +void hwd_viif_isp_set_regbuf_auto_transmission(struct viif_device *viif_dev); +void hwd_viif_isp_disable_regbuf_auto_transmission(struct viif_device *viif_dev); +void hwd_viif_isp_guard_start(struct viif_device *viif_dev); +void hwd_viif_isp_guard_end(struct viif_device *viif_dev); + +DEFINE_GUARD(viif_isp, struct viif_device *, hwd_viif_isp_guard_start(_T), + hwd_viif_isp_guard_end(_T)) + +const struct viif_mbus_format *viif_mbus_format_from_code(unsigned int mbus_code); +const struct viif_mbus_format *viif_mbus_format_nth(unsigned int n); + +#endif /* __VIIF_COMMON_H__ */ diff --git a/drivers/media/platform/toshiba/visconti/viif_isp.c b/drivers/media/platform/toshiba/visconti/viif_isp.c new file mode 100644 index 000000000000..0bcc6bba9bf1 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_isp.c @@ -0,0 +1,909 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include +#include +#include +#include + +#include "viif.h" +#include "viif_common.h" +#include "viif_isp.h" +#include "viif_regs.h" + +/* disable CSI2 capture at viif_mux_start() */ +#define VIIF_CSI2_NOT_CAPTURE -1 + +/* validation at main_set_unit() and sub_set_unit() */ +/* picture size: [unit: pixel] */ +#define VIIF_MIN_HTOTAL_PIXEL 143U +#define VIIF_MAX_HTOTAL_PIXEL 65535U + +/* pixel clock: [unit: kHz] */ +#define VIIF_MIN_PIXEL_CLOCK 3375U +#define VIIF_MAX_PIXEL_CLOCK 600000U + +/* horizontal back porch size: [unit: system clock ticks] */ +#define VIIF_HBP_SYSCLK 10U + +/* num of pictures accepted by the ISP */ +#define VIIF_L1_INPUT_NUM_MIN 1U +#define VIIF_L1_INPUT_NUM_MAX 3U + +/* active picture size: [unit: pixel] */ +#define VIIF_MIN_HACTIVE_PIXEL_W_L1ISP 640U +#define VIIF_MAX_HACTIVE_PIXEL_W_L1ISP 3840U +#define VIIF_MIN_HACTIVE_PIXEL_WO_L1ISP 128U +#define VIIF_MAX_HACTIVE_PIXEL_WO_L1ISP 4096U + +/* picture vertical size: [unit: line] */ +#define VIIF_MIN_VTOTAL_LINE 144U +#define VIIF_MAX_VTOTAL_LINE 16383U +#define VIIF_MIN_VBP_LINE 5U +#define VIIF_MAX_VBP_LINE 4095U +#define VIIF_MIN_VACTIVE_LINE_WO_L1ISP 128U +#define VIIF_MAX_VACTIVE_LINE_WO_L1ISP 2160U +#define VIIF_MIN_VACTIVE_LINE_W_L1ISP 480U +#define VIIF_MAX_VACTIVE_LINE_W_L1ISP 2160U + +/* internal operation latencies: [unit: system clock ticks]*/ +#define VIIF_TABLE_LOAD_TIME 24000UL +#define VIIF_REGBUF_ACCESS_TIME 15360UL + +/* offset of Vsync delay: [unit: line] */ +#define VIIF_L1_DELAY_W_HDRC 31U +#define VIIF_L1_DELAY_WO_HDRC 11U + +/* timeout definitions for viif_stop_mux() */ +/* + * wait time for force abort to complete (max 1line time = 1228.8 us) + * when width = 4096, RAW24, 80Mbps + */ +#define VIIF_WAIT_ABORT_COMPLETE_TIME 1229U + +/* + * complete time of register buffer transfer. + * actual time is about 30us in case of L1ISP + */ +#define VIIF_WAIT_ISP_REGBF_TRNS_COMPLETE_TIME 39U + +/* default parameters for V4L2 subdevice node */ +#define VISCONTI_VIIF_ISP_DEFAULT_WIDTH 1920 +#define VISCONTI_VIIF_ISP_DEFAULT_HEIGHT 1080 +#define VISCONTI_VIIF_MAX_COMPOSED_WIDTH 8190 +#define VISCONTI_VIIF_MAX_COMPOSED_HEIGHT 4094 + +/* CSI2RX DPHY settings: corresponding registers reside in ISP register region */ +enum viif_csi2rx_dphy_lane { + VIIF_CSI2RX_DPHY_L0L1L2L3 = 0U, + VIIF_CSI2RX_DPHY_L0L3L1L2 = 1U, + VIIF_CSI2RX_DPHY_L0L2L3L1 = 2U, + VIIF_CSI2RX_DPHY_L0L1L3L2 = 4U, + VIIF_CSI2RX_DPHY_L0L3L2L1 = 5U, + VIIF_CSI2RX_DPHY_L0L2L1L3 = 6U +}; + +#define VIIF_DPHY_CFG_CLK_25M 32U + +/** + * struct viif_input_img - input image information + * @pixel_clock: pixel clock [unit: kHz]. Range: [3375..600000] + * @htotal_size: horizontal total size [unit: pixel]. Range: [143..65535] + * @hactive_size: horizontal active size [unit: pixel] + * * Range (w/o L1ISP): [128..4096] (multiple of 2) + * * Range (with L1ISP): [640..3840] (multiple of 8) + * * Range (SUB path): 0 + * @vtotal_size: vertical total size [unit: line]. + * * Range: [144..16383] + * @vbp_size: vertical back porch size. + * * Range: [5..4095] + * @vactive_size: vertical active size [unit: line]. + * * Range (w/o L1ISP) [128..2160] (multiple of 2) + * * Range (with L1ISP) [480..2160] (multiple of 2) + * @hobc_width: the number of horizontal optical black pixels. + * * Range (w/o L1ISP): 0 + * * Range (with L1ISP): [0,16,32,64 or 128] + * * should be 0 when hobc_margin = 0 + * * Range (SUB path): 0 + * @hobc_margin: the number of horizontal optical black margin. + * * Range (w/o L1ISP): 0 + * * Range (with L1ISP): [0..30] (even number) + + * should be 0 when hobc_width = 0 + * * Range (SUB path): 0 + * + * Constraints between parameters: + * + * * (htotal_size > (hactive_size + hobc_width + hobc_margin)) + * * (vtotal_size > (vbp_size + vactive_size)) + * * w/o L1ISP: + * * vbp_size >= (39360[cycle] / 500000[kHz]) * (pixel_clock / htotal_size) + 16 + ISST time + * * with L1ISP: + * * vbp_size >= (54720[cycle] / 500000[kHz]) * (pixel_clock / htotal_size) + 38 + ISST time + * + * Note: L1ISP is used when RAW data is input to MAIN unit + */ +struct viif_input_img { + u32 pixel_clock; + u32 htotal_size; + u32 hactive_size; + u32 vtotal_size; + u32 vbp_size; + u32 vactive_size; + u32 hobc_width; + u32 hobc_margin; +}; + +/*=============================================*/ +/* Low Layer Implementation */ +/*=============================================*/ +/* Convert the unit of time-period (from sysclk, to num lines in the image) */ +static u32 sysclk_to_numlines(u32 time_in_sysclk, const struct viif_input_img *img) +{ + u64 v1 = (u64)time_in_sysclk * img->pixel_clock; + u64 v2 = (u64)img->htotal_size * VIIF_SYS_CLK; + + return div64_u64(v1, v2); +} + +static u32 lineperiod_in_sysclk(u32 hsize, u32 pixel_clock) +{ + return div64_u64((u64)hsize * VIIF_SYS_CLK, pixel_clock); +} + +/** + * viif_main_set_unit() - Set static configuration of MAIN unit(CH0 or CH1) + * + * @viif_dev: the VIIF device + * @data_type: DT of image; either of + * YUV422_8B, YUV422_10B, RGB565, RGB888, RAW8, RAW10, RAW12, RAW14 + * @in_img: Pointer to input image information + * @yuv_interp: true to use interpolation for YUV422 to YUV444 conversion. + */ +static void viif_main_set_unit(struct viif_device *viif_dev, u32 data_type, + const struct viif_input_img *in_img, bool yuv_interp) +{ + u32 total_hact_size; + u32 sw_delay0, sw_delay1, hw_delay; + u32 val, color, sysclk_num; + + if (data_type == MIPI_CSI2_DT_RAW8 || data_type == MIPI_CSI2_DT_RAW10 || + data_type == MIPI_CSI2_DT_RAW12 || data_type == MIPI_CSI2_DT_RAW14) { + total_hact_size = in_img->hactive_size + in_img->hobc_width + in_img->hobc_margin; + } else { + total_hact_size = in_img->hactive_size; + } + + /* Set DT and color type of image data */ + viif_capture_write(viif_dev, REG_IPORTM_MAIN_DT, (data_type << 8U) | data_type); + viif_capture_write(viif_dev, REG_IPORTM_OTHER, 0x00); + + /* Set back porch*/ + viif_capture_write(viif_dev, REG_BACK_PORCH_M, (in_img->vbp_size << 16U) | VIIF_HBP_SYSCLK); + + /* single pulse of vsync is input to DPGM */ + viif_capture_write(viif_dev, REG_DPGM_VSYNC_SOURCE, VAL_DPGM_VSYNC_PULSE); + + /* set preprocess type before L2ISP based on color_type. */ + if (data_type == MIPI_CSI2_DT_YUV422_8B || data_type == MIPI_CSI2_DT_YUV422_10B) { + color = VAL_PREPROCESS_FMT_YUV422; + } else if (data_type == MIPI_CSI2_DT_RGB565 || data_type == MIPI_CSI2_DT_RGB888) { + color = VAL_PREPROCESS_FMT_RGB; + } else { + /* RGB or YUV444 from L1ISP */ + color = VAL_PREPROCESS_FMT_YUV444; + } + viif_capture_write(viif_dev, REG_PREPROCESS_FMTM, color); + + /* set Total size and valid size information of image data */ + sysclk_num = lineperiod_in_sysclk(in_img->htotal_size, in_img->pixel_clock); + sysclk_num &= GENMASK(15, 0); + viif_capture_write(viif_dev, REG_TOTALSIZE_M, (in_img->vtotal_size << 16U) | sysclk_num); + viif_capture_write(viif_dev, REG_VALSIZE_M, + (in_img->vactive_size << 16U) | total_hact_size); + + /* set image size information to L2ISP */ + viif_capture_write(viif_dev, REG_L2_SENSOR_CROP_VSIZE, in_img->vactive_size); + viif_capture_write(viif_dev, REG_L2_SENSOR_CROP_HSIZE, in_img->hactive_size); + + /* RAW input case */ + if (data_type >= MIPI_CSI2_DT_RAW8) { + /* interpolation mode = by LINE */ + viif_capture_write(viif_dev, REG_L1_IBUF_INPUT_ORDER, 1U); + viif_capture_write(viif_dev, REG_L1_SYSM_HEIGHT, in_img->vactive_size); + viif_capture_write(viif_dev, REG_L1_SYSM_WIDTH, in_img->hactive_size); + val = (in_img->hobc_margin << 8U) | in_img->hobc_width; + viif_capture_write(viif_dev, REG_L1_HOBC_MARGIN, val); + } + + /* disable rawpack */ + viif_capture_write(viif_dev, REG_IPORTM_MAIN_RAW, 0); + + /* Set yuv_conv; only for VAL_PREPROCESS_FMT_YUV422 */ + viif_capture_write(viif_dev, REG_PREPROCESS_C24M, yuv_interp ? 1 : 0); + + /* Set vsync delay */ + hw_delay = in_img->vbp_size - sysclk_to_numlines(VIIF_TABLE_LOAD_TIME, in_img) + 4U; + hw_delay = min(hw_delay, 255U); + + sw_delay0 = hw_delay - sysclk_to_numlines(VIIF_REGBUF_ACCESS_TIME, in_img) + 2U; + + if (data_type == MIPI_CSI2_DT_RAW8 || data_type == MIPI_CSI2_DT_RAW10 || + data_type == MIPI_CSI2_DT_RAW12 || data_type == MIPI_CSI2_DT_RAW14) { + sw_delay1 = sysclk_to_numlines(VIIF_REGBUF_ACCESS_TIME, in_img) + + VIIF_L1_DELAY_WO_HDRC + 1U; + } else { + sw_delay1 = 10U; + } + viif_capture_write(viif_dev, REG_INT_M0_LINE, sw_delay0 << 16U); + viif_capture_write(viif_dev, REG_INT_M1_LINE, (sw_delay1 << 16U) | hw_delay); + + /* M2_LINE is the same condition as M1_LINE */ + viif_capture_write(viif_dev, REG_INT_M2_LINE, (sw_delay1 << 16U) | hw_delay); + + /* hold pixel_clock, htotal_size for future use */ + viif_dev->img_clk.pixel_clock = in_img->pixel_clock; + viif_dev->img_clk.htotal_size = in_img->htotal_size; +} + +/** + * viif_sub_set_unit() - Set static configuration of SUB unit + * + * @viif_dev: the VIIF device + * @dt_image: DT of image. Range: [0x1e, 0x1f, 0x22, 0x24, 0x2a-0x2d] + * @in_img: Pointer to input image information + */ +static void viif_sub_set_unit(struct viif_device *viif_dev, u32 dt_image, + const struct viif_input_img *in_img) +{ + u32 sysclk_num, temp_delay; + + viif_capture_write(viif_dev, REG_IPORTS_MAIN_DT, dt_image); + viif_capture_write(viif_dev, REG_IPORTS_OTHER, 0x00); + + /* Set line size and delay value of delayed Vsync */ + sysclk_num = lineperiod_in_sysclk(in_img->htotal_size, in_img->pixel_clock); + viif_capture_write(viif_dev, REG_INT_SA0_LINE, sysclk_num & GENMASK(15, 0)); + temp_delay = in_img->vbp_size - 4U; + if (temp_delay > 255U) { + /* Replace the value with HW max spec */ + temp_delay = 255U; + } + viif_capture_write(viif_dev, REG_INT_SA1_LINE, temp_delay); +} + +/** + * viif_mux_start() - Setup CSI-2 input path + * + * @viif_dev: the VIIF device + * @vc_main: VCID (0, 1, 2, 3) to capture with Main unit; VIIF_CSI2_NOT_CAPTURE to disable. + * @vc_sub: VCID (0, 1, 2, 3) to capture with Sub unit; VIIF_CSI2_NOT_CAPTURE to disable. + */ +static void viif_mux_start(struct viif_device *viif_dev, s32 vc_main, s32 vc_sub) +{ + bool en_vc0 = false, en_vc1 = false; + + viif_capture_write(viif_dev, REG_IPORTM, VAL_IPORTM_INPUT_CSI2); + + if (vc_main != VIIF_CSI2_NOT_CAPTURE) { + viif_capture_write(viif_dev, REG_VCID0SELECT, (u32)vc_main); + en_vc0 = true; + } + if (vc_sub != VIIF_CSI2_NOT_CAPTURE) { + viif_capture_write(viif_dev, REG_VCID1SELECT, (u32)vc_sub); + en_vc1 = true; + } + + /* Control VC port enable */ + viif_capture_write(viif_dev, REG_VCPORTEN, + (en_vc0 ? MASK_VCPORTEN_EN_VC0 : 0) | + (en_vc1 ? MASK_VCPORTEN_EN_VC1 : 0)); + + if (en_vc0) { + /* Update flag information for run status of MAIN unit */ + viif_dev->run_flag_main = true; + } +} + +/** + * viif_mux_stop() - Teardown CSI-2 input path + * + * @viif_dev: the VIIF device + * Return: 0 for success, -ETIMEDOUT for timeout error + */ +static int viif_mux_stop(struct viif_device *viif_dev) +{ + u64 timeout_ns, cur_ns; + + /* Disable auto transmission of register buffer */ + viif_capture_write(viif_dev, REG_L1_CRGBF_TRN_A_CONF, 0); + viif_capture_write(viif_dev, REG_L2_CRGBF_TRN_A_CONF, 0); + + /* Wait for completion of register buffer transmission */ + udelay(VIIF_WAIT_ISP_REGBF_TRNS_COMPLETE_TIME); + + /* Stop all VCs, long packet input and emb data input of MAIN unit */ + viif_capture_write(viif_dev, REG_VCPORTEN, 0); + viif_capture_write(viif_dev, REG_IPORTM_OTHEREN, 0); + viif_capture_write(viif_dev, REG_IPORTM_EMBEN, 0); + + /* Stop image data input, long packet input and emb data input of SUB unit */ + viif_capture_write(viif_dev, REG_IPORTS_OTHEREN, 0); + viif_capture_write(viif_dev, REG_IPORTS_EMBEN, 0); + viif_capture_write(viif_dev, REG_IPORTS_IMGEN, 0); + + /* Stop VDMAC for all table ports, input ports and write ports */ + viif_capture_write(viif_dev, REG_VDM_T_ENABLE, 0); + viif_capture_write(viif_dev, REG_VDM_R_ENABLE, 0); + viif_capture_write(viif_dev, REG_VDM_W_ENABLE, 0); + + /* Stop all groups(g00, g01 and g02) of VDMAC */ + viif_capture_write(viif_dev, REG_VDM_ABORTSET, 0x7); + + timeout_ns = ktime_get_ns() + VIIF_WAIT_ABORT_COMPLETE_TIME * 1000; + + do { + u32 status_r, status_w, status_t, l2_status; + + /* Get VDMAC transfer status */ + status_r = viif_capture_read(viif_dev, REG_VDM_R_RUN); + status_w = viif_capture_read(viif_dev, REG_VDM_W_RUN); + status_t = viif_capture_read(viif_dev, REG_VDM_T_RUN); + l2_status = viif_capture_read(viif_dev, REG_L2_BUS_L2_STATUS); + + if (!status_r && !status_w && !status_t && !l2_status) { + viif_dev->run_flag_main = false; + return 0; + } + + cur_ns = ktime_get_ns(); + } while (timeout_ns > cur_ns); + + return -ETIMEDOUT; +} + +/*=============================================*/ +/* handling V4L2 framework */ +/*=============================================*/ +/* ----- supported MBUS formats ----- */ +static bool viif_get_mbus_rgb_out(unsigned int mbus_code) +{ + const struct viif_mbus_format *fmt; + + fmt = viif_mbus_format_from_code(mbus_code); + + return fmt ? fmt->rgb_out : false; /* YUV as default */ +} + +/* ----- handling main processing path ----- */ +/* find a linked media entity which represents a sensor */ +static struct media_entity *viif_find_sensor(struct media_entity *entity) +{ + struct media_pad *pad; + + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + return NULL; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return NULL; + + entity = pad->entity; + if (entity->function == MEDIA_ENT_F_CAM_SENSOR) + return entity; + } +} + +static int viif_get_dv_timings(struct viif_device *viif_dev, struct v4l2_dv_timings *timings, + unsigned int *mbus_code) +{ + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = 0, + }; + struct media_entity *sensor_entity; + struct v4l2_subdev_state *state; + struct v4l2_subdev *sensor_sd; + struct v4l2_ctrl *ctrl; + int ret; + + sensor_entity = viif_find_sensor(&viif_dev->isp_subdev.sd.entity); + if (!sensor_entity) + return -EINVAL; + + sensor_sd = media_entity_to_v4l2_subdev(sensor_entity); + if (!sensor_sd) + return -EINVAL; + + state = v4l2_subdev_lock_and_get_active_state(sensor_sd); + if (state) { + ret = v4l2_subdev_call(sensor_sd, pad, get_fmt, state, &format); + v4l2_subdev_unlock_state(state); + } else { + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_state pad_state = { .pads = &pad_cfg }; + + ret = v4l2_subdev_call(sensor_sd, pad, get_fmt, &pad_state, &format); + } + if (ret) + return ret; + + /* some video I/F support dv_timings query */ + ret = v4l2_subdev_call(sensor_sd, pad, g_dv_timings, 0, timings); + if (!ret) { + *mbus_code = format.format.code; + return 0; + } + + /* others: call some discrete APIs */ + timings->bt.width = format.format.width; + timings->bt.height = format.format.height; + *mbus_code = format.format.code; + + ctrl = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_HBLANK); + if (!ctrl) { + dev_err(viif_dev->dev, "subdev: V4L2_CID_VBLANK error.\n"); + return -EINVAL; + } + timings->bt.hsync = v4l2_ctrl_g_ctrl(ctrl); + + ctrl = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_VBLANK); + if (!ctrl) { + dev_err(viif_dev->dev, "subdev: V4L2_CID_VBLANK error.\n"); + return -EINVAL; + } + timings->bt.vsync = v4l2_ctrl_g_ctrl(ctrl); + + ctrl = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + dev_err(viif_dev->dev, "subdev: V4L2_CID_PIXEL_RATE error.\n"); + return -EINVAL; + } + timings->bt.pixelclock = v4l2_ctrl_g_ctrl_int64(ctrl); + + return 0; +} + +static unsigned int dt_image_from_mbus_code(unsigned int mbus_code) +{ + const struct viif_mbus_format *fmt; + + fmt = viif_mbus_format_from_code(mbus_code); + + return fmt ? fmt->mipi_dt : MIPI_CSI2_DT_RGB888; +} + +static void set_isp_input_depth_and_bayer_pattern(struct viif_device *viif_dev, + unsigned int mbus_code) +{ + const struct viif_mbus_format *format; + + format = viif_mbus_format_from_code(mbus_code); + if (format && format->is_bayer) { + viif_capture_write(viif_dev, REG_L1_IBUF_DEPTH, format->bpp); + viif_capture_write(viif_dev, REG_L1_SYSM_START_COLOR, format->bayer_pattern); + } +} + +int visconti_viif_isp_main_set_unit(struct viif_device *viif_dev) +{ + struct viif_input_img in_img_main = {}; + struct v4l2_dv_timings timings = {}; + bool yuv_interp = false; + unsigned int data_type; + unsigned int mbus_code; + int ret = 0; + + ret = viif_get_dv_timings(viif_dev, &timings, &mbus_code); + if (ret) { + dev_err(viif_dev->dev, "could not get timing information of subdev"); + return -EINVAL; + } + + data_type = dt_image_from_mbus_code(mbus_code); + + set_isp_input_depth_and_bayer_pattern(viif_dev, mbus_code); + + yuv_interp = (data_type == MIPI_CSI2_DT_YUV422_8B || data_type == MIPI_CSI2_DT_YUV422_10B); + + in_img_main.hactive_size = timings.bt.width; + in_img_main.vactive_size = timings.bt.height; + in_img_main.htotal_size = timings.bt.width + timings.bt.hsync; + in_img_main.vtotal_size = timings.bt.height + timings.bt.vsync; + in_img_main.pixel_clock = div64_u64(timings.bt.pixelclock, 1000); + /* ISP can issue a frame error when too large VBP value is set */ + in_img_main.vbp_size = min(100, timings.bt.vsync - 5); + in_img_main.hobc_width = 0; + in_img_main.hobc_margin = 0; + + /* TODO: might need to check Sink pad's {width, height} matches to active_size */ + + /* configuration of MAIN unit */ + viif_main_set_unit(viif_dev, data_type, &in_img_main, yuv_interp); + + /* Enable regbuf */ + hwd_viif_isp_set_regbuf_auto_transmission(viif_dev); + + return 0; +} + +int visconti_viif_isp_sub_set_unit(struct viif_device *viif_dev) +{ + struct viif_input_img in_img_sub; + struct v4l2_dv_timings timings; + unsigned int mbus_code; + unsigned int dt_image; + int ret; + + ret = viif_get_dv_timings(viif_dev, &timings, &mbus_code); + if (ret) + return -EINVAL; + + dt_image = dt_image_from_mbus_code(mbus_code); + + in_img_sub.hactive_size = 0; + in_img_sub.vactive_size = timings.bt.height; + in_img_sub.htotal_size = timings.bt.width + timings.bt.hsync; + in_img_sub.vtotal_size = timings.bt.height + timings.bt.vsync; + in_img_sub.pixel_clock = div64_u64(timings.bt.pixelclock, 1000); + in_img_sub.vbp_size = timings.bt.vsync - 5; + in_img_sub.hobc_width = 0; + in_img_sub.hobc_margin = 0; + + /* TODO: might need to check Sink pad's {width, height} matches to active_size */ + + viif_sub_set_unit(viif_dev, dt_image, &in_img_sub); + + return 0; +}; + +/* ----- subdevice video operations ----- */ +static int visconti_viif_isp_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev; + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + int ret; + + /* Currently, we assume PATH0 is enabled first */ + /* Currently, further configuration is only for PATH0 */ + if (pad != VIIF_ISP_PAD_SRC_PATH0) + return 0; + + remote_pad = media_pad_remote_pad_first(&sd->entity.pads[VIIF_ISP_PAD_SINK_VIDEO]); + if (!remote_pad) + return -ENODEV; + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + /* enabling: start ISP, MUX -> start CSI2RX, sensor */ + viif_dev->masked_gamma_path = 0; + viif_mux_start(viif_dev, 0, 0); + + /* CSI2RX control registers which reside in the ISP register region */ + /* FIXED 25MHz clock for CSI2RX DPHY configuration clock */ + viif_capture_write(viif_dev, REG_DPHY_FREQRANGE, VIIF_DPHY_CFG_CLK_25M); + /* CSI2 data lane swapping; controlled by a vendor specific register */ + viif_capture_write(viif_dev, REG_DPHY_LANE, VIIF_CSI2RX_DPHY_L0L1L2L3); + + /* currently ISP.sink_video supports only stream0 */ + ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, BIT(0)); + if (ret) + viif_mux_stop(viif_dev); + + return ret; +} + +static int visconti_viif_isp_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct viif_device *viif_dev = ((struct isp_subdev *)sd)->viif_dev; + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + + /* Currently, we assume PATH0 is disabled last */ + /* Currently, further configuration is only for PATH0 */ + if (pad != VIIF_ISP_PAD_SRC_PATH0) + return 0; + + remote_pad = media_pad_remote_pad_first(&sd->entity.pads[VIIF_ISP_PAD_SINK_VIDEO]); + if (!remote_pad) + return -ENODEV; + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + /* disabling: stop sensor, CSI2RX -> stop MUX, ISP */ + v4l2_subdev_disable_streams(remote_sd, remote_pad->index, BIT(0)); + viif_mux_stop(viif_dev); + + return 0; +} + +/* ----- subdevice pad operations ----- */ +static int visconti_viif_isp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad == VIIF_ISP_PAD_SINK_VIDEO) { + const struct viif_mbus_format *fmt; + + fmt = viif_mbus_format_nth(code->index); + if (!fmt) + return -EINVAL; + + code->code = fmt->code; + return 0; + } else if (code->pad == VIIF_ISP_PAD_SRC_PATH0 || code->pad == VIIF_ISP_PAD_SRC_PATH1) { + struct v4l2_mbus_framefmt *sink_fmt; + + if (code->index > 0) + return -EINVAL; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SINK_VIDEO); + if (viif_get_mbus_rgb_out(sink_fmt->code)) + code->code = MEDIA_BUS_FMT_RGB888_1X24; + else + code->code = MEDIA_BUS_FMT_YUV8_1X24; + return 0; + } else if (code->pad == VIIF_ISP_PAD_SRC_PATH2) { + struct v4l2_mbus_framefmt *sink_fmt; + + if (code->index > 0) + return -EINVAL; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SINK_VIDEO); + code->code = sink_fmt->code; + return 0; + } else if (code->pad == VIIF_ISP_PAD_SINK_PARAMS || code->pad == VIIF_ISP_PAD_SRC_STATS) { + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_METADATA_FIXED; + return 0; + } + + return -EINVAL; +} + +static void visconti_viif_isp_set_sink_fmt(struct v4l2_subdev_state *sd_state, + struct v4l2_mbus_framefmt *format) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src0_fmt, *src1_fmt, *src2_fmt; + const struct viif_mbus_format *mb_fmt; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SINK_VIDEO); + src0_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SRC_PATH0); + src1_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SRC_PATH1); + src2_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SRC_PATH2); + + /* sink format */ + mb_fmt = viif_mbus_format_from_code(format->code); + if (!mb_fmt) + mb_fmt = viif_mbus_format_nth(0); + + sink_fmt->code = mb_fmt->code; + + if (mb_fmt->is_bayer) { + u32 tmp_width = round_down(format->width, 8); + u32 tmp_height = round_down(format->height, 2); + + sink_fmt->width = clamp(tmp_width, VIIF_MIN_HACTIVE_PIXEL_W_L1ISP, + VIIF_MAX_HACTIVE_PIXEL_W_L1ISP); + sink_fmt->height = clamp(tmp_height, VIIF_MIN_VACTIVE_LINE_W_L1ISP, + VIIF_MAX_VACTIVE_LINE_W_L1ISP); + } else { + u32 tmp_width = round_down(format->width, 2); + u32 tmp_height = round_down(format->height, 2); + + sink_fmt->width = clamp(tmp_width, VIIF_MIN_HACTIVE_PIXEL_WO_L1ISP, + VIIF_MAX_HACTIVE_PIXEL_WO_L1ISP); + sink_fmt->height = clamp(tmp_height, VIIF_MIN_VACTIVE_LINE_WO_L1ISP, + VIIF_MAX_VACTIVE_LINE_WO_L1ISP); + } + + /* source format */ + if (viif_get_mbus_rgb_out(sink_fmt->code)) { + src0_fmt->code = MEDIA_BUS_FMT_RGB888_1X24; + src1_fmt->code = MEDIA_BUS_FMT_RGB888_1X24; + } else { + src0_fmt->code = MEDIA_BUS_FMT_YUV8_1X24; + src1_fmt->code = MEDIA_BUS_FMT_YUV8_1X24; + } + src0_fmt->width = sink_fmt->width; + src0_fmt->height = sink_fmt->height; + src1_fmt->width = sink_fmt->width; + src1_fmt->height = sink_fmt->height; + + /* SRC2 (RAW output) follows SINK format */ + src2_fmt->code = mb_fmt->is_bayer ? sink_fmt->code : MEDIA_BUS_FMT_SRGGB10_1X10; + src2_fmt->width = sink_fmt->width; + src2_fmt->height = sink_fmt->height; + + *format = *sink_fmt; +} + +static void visconti_viif_isp_set_src_fmt(struct v4l2_subdev_state *sd_state, + struct v4l2_mbus_framefmt *format, unsigned int pad) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SINK_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, pad); + + if (viif_get_mbus_rgb_out(sink_fmt->code)) + src_fmt->code = MEDIA_BUS_FMT_RGB888_1X24; + else + src_fmt->code = MEDIA_BUS_FMT_YUV8_1X24; + + src_fmt->width = sink_fmt->width; + src_fmt->height = sink_fmt->height; + + *format = *src_fmt; +} + +static void visconti_viif_isp_set_src_fmt_rawpath(struct v4l2_subdev_state *sd_state, + struct v4l2_mbus_framefmt *format, + unsigned int pad) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + const struct viif_mbus_format *mb_fmt; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SINK_VIDEO); + src_fmt = v4l2_subdev_state_get_format(sd_state, pad); + mb_fmt = viif_mbus_format_from_code(sink_fmt->code); + + src_fmt->code = mb_fmt->is_bayer ? sink_fmt->code : MEDIA_BUS_FMT_SRGGB10_1X10; + src_fmt->width = sink_fmt->width; + src_fmt->height = sink_fmt->height; + + *format = *src_fmt; +} + +static int visconti_viif_isp_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + if (fmt->pad == VIIF_ISP_PAD_SINK_VIDEO) + visconti_viif_isp_set_sink_fmt(sd_state, &fmt->format); + else if (fmt->pad == VIIF_ISP_PAD_SRC_PATH2) + visconti_viif_isp_set_src_fmt_rawpath(sd_state, &fmt->format, fmt->pad); + else if (fmt->pad == VIIF_ISP_PAD_SRC_PATH0 || fmt->pad == VIIF_ISP_PAD_SRC_PATH1) + visconti_viif_isp_set_src_fmt(sd_state, &fmt->format, fmt->pad); + else + fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); + + return 0; +} + +static int visconti_viif_isp_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SINK_VIDEO); + sink_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + sink_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + + src_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SRC_PATH0); + src_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + src_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->code = MEDIA_BUS_FMT_YUV8_1X24; + + src_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SRC_PATH1); + src_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + src_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->code = MEDIA_BUS_FMT_YUV8_1X24; + + src_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SRC_PATH2); + src_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + src_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SINK_PARAMS); + src_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_ISP_PAD_SRC_STATS); + sink_fmt->width = 0; + sink_fmt->height = 0; + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->code = MEDIA_BUS_FMT_METADATA_FIXED; + *src_fmt = *sink_fmt; + + return 0; +} + +static int visconti_viif_isp_subdev_link_validate(struct media_link *link) +{ + if (link->sink->index == VIIF_ISP_PAD_SINK_PARAMS) + return 0; + + return v4l2_subdev_link_validate(link); +} + +static const struct media_entity_operations visconti_viif_isp_media_ops = { + .link_validate = visconti_viif_isp_subdev_link_validate, +}; + +static const struct v4l2_subdev_pad_ops visconti_viif_isp_pad_ops = { + .disable_streams = visconti_viif_isp_disable_streams, + .enable_streams = visconti_viif_isp_enable_streams, + .enum_mbus_code = visconti_viif_isp_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = visconti_viif_isp_set_fmt, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_video_ops visconti_viif_isp_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_ops visconti_viif_isp_ops = { + .video = &visconti_viif_isp_video_ops, + .pad = &visconti_viif_isp_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops visconti_viif_isp_internal_ops = { + .init_state = visconti_viif_isp_init_state, +}; + +/* ----- register/remove isp subdevice node ----- */ +int visconti_viif_isp_register(struct viif_device *viif_dev) +{ + struct media_pad *pads = viif_dev->isp_subdev.pads; + struct v4l2_subdev *sd = &viif_dev->isp_subdev.sd; + int ret; + + viif_dev->isp_subdev.viif_dev = viif_dev; + + v4l2_subdev_init(sd, &visconti_viif_isp_ops); + sd->internal_ops = &visconti_viif_isp_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->entity.ops = &visconti_viif_isp_media_ops; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; + sd->owner = THIS_MODULE; + strscpy(sd->name, "visconti-viif:isp", sizeof(sd->name)); + + pads[VIIF_ISP_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; + pads[VIIF_ISP_PAD_SRC_PATH0].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; + pads[VIIF_ISP_PAD_SRC_PATH1].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; + pads[VIIF_ISP_PAD_SRC_PATH2].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; + pads[VIIF_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK; + pads[VIIF_ISP_PAD_SRC_STATS].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, VIIF_ISP_PAD_NUM, pads); + if (ret) { + dev_err(viif_dev->dev, "Failed on media_entity_pads_init\n"); + return ret; + } + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_cleanup_media_entity; + + ret = v4l2_device_register_subdev(&viif_dev->v4l2_dev, sd); + if (ret) { + dev_err(viif_dev->dev, "Failed to register ISP subdev\n"); + goto err_cleanup_subdev; + } + + return 0; + +err_cleanup_subdev: + v4l2_subdev_cleanup(sd); +err_cleanup_media_entity: + media_entity_cleanup(&sd->entity); + return ret; +} + +void visconti_viif_isp_unregister(struct viif_device *viif_dev) +{ + v4l2_device_unregister_subdev(&viif_dev->isp_subdev.sd); + media_entity_cleanup(&viif_dev->isp_subdev.sd.entity); +} diff --git a/drivers/media/platform/toshiba/visconti/viif_isp.h b/drivers/media/platform/toshiba/visconti/viif_isp.h new file mode 100644 index 000000000000..633d53acc84a --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_isp.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#ifndef __VIIF_ISP_H__ +#define __VIIF_ISP_H__ + +struct viif_device; + +int visconti_viif_isp_main_set_unit(struct viif_device *viif_dev); +int visconti_viif_isp_sub_set_unit(struct viif_device *viif_dev); + +int visconti_viif_isp_register(struct viif_device *viif_dev); +void visconti_viif_isp_unregister(struct viif_device *viif_dev); + +#endif /* __VIIF_ISP_H__ */ diff --git a/drivers/media/platform/toshiba/visconti/viif_regs.h b/drivers/media/platform/toshiba/visconti/viif_regs.h new file mode 100644 index 000000000000..a6518364227b --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_regs.h @@ -0,0 +1,717 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* Toshiba Visconti Video Capture register definitions + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#ifndef __VIIF_REGS_H__ +#define __VIIF_REGS_H__ + +#include + +/*=============================================*/ +/* Capture registers */ +/*=============================================*/ +#define REG_IPORTM0_LD 0 +#define REG_IPORTM1_LD 0x4 +#define MASK_IPORTM_VLATCH 0x3 + +#define REG_VCID0SELECT 0x30 +#define REG_VCID1SELECT 0x34 +#define REG_VCPORTEN 0x3c +#define MASK_VCPORTEN_EN_VC0 BIT(0) +#define MASK_VCPORTEN_EN_VC1 BIT(4) +#define REG_IPORTM 0x54 +#define VAL_IPORTM_INPUT_CSI2 0 + +#define REG_IPORTM_MAIN_DT 0x58 +#define REG_IPORTM_MAIN_RAW 0x5c +#define REG_IPORTM_OTHER 0x60 +#define REG_IPORTM_OTHEREN 0x64 +#define REG_IPORTM_EMBEN 0x68 +#define REG_IPORTS_MAIN_DT 0x78 +#define REG_IPORTS_OTHER 0x80 +#define REG_IPORTS_OTHEREN 0x84 +#define REG_IPORTS_EMBEN 0x88 +#define REG_IPORTS_IMGEN 0x8c +#define REG_TOTALSIZE_M 0xc0 +#define REG_VALSIZE_M 0xc4 +#define REG_BACK_PORCH_M 0xc8 +#define REG_INT_M_SYNC 0x130 +#define REG_INT_M_SYNC_MASK 0x134 +#define MASK_INT_M_SYNC_VSYNC_INT BIT(0) +#define MASK_INT_M_SYNC_LINES_DELAY_INT1 BIT(1) +#define MASK_INT_M_SYNC_LINES_DELAY_INT2 BIT(2) +#define MASK_INT_M_SYNC_SW_DELAY_INT0 BIT(16) +#define MASK_INT_M_SYNC_SW_DELAY_INT1 BIT(17) +#define MASK_INT_M_SYNC_SW_DELAY_INT2 BIT(18) +/* we only care for LINES_DELAY_INT2, SW_DELAY_INT2 */ +#define MASK_INT_M_SYNC_MASK_SET \ + (MASK_INT_M_SYNC_VSYNC_INT | MASK_INT_M_SYNC_LINES_DELAY_INT1 | \ + MASK_INT_M_SYNC_SW_DELAY_INT0 | MASK_INT_M_SYNC_SW_DELAY_INT1) + +#define REG_INT_S_SYNC 0x138 +#define REG_INT_S_SYNC_MASK 0x13c +#define MASK_INT_S_SYNC_VSYNC_INT BIT(0) +#define MASK_INT_S_SYNC_LINES_DELAY_INT1 BIT(1) +#define MASK_INT_S_SYNC_SW_DELAY_INT0 BIT(16) +#define MASK_INT_S_SYNC_SW_DELAY_INT1 BIT(17) +/* we only care for LINES_DELAY_INT1 */ +#define MASK_INT_S_SYNC_MASK_SET \ + (MASK_INT_S_SYNC_VSYNC_INT | MASK_INT_S_SYNC_SW_DELAY_INT0 | MASK_INT_S_SYNC_SW_DELAY_INT1) + +#define REG_INT_M0_LINE 0x140 +#define REG_INT_M1_LINE 0x144 +#define REG_INT_M2_LINE 0x148 +#define REG_INT_SA0_LINE 0x160 +#define REG_INT_SA1_LINE 0x164 +#define REG_INT_M_STATUS 0x180 +#define REG_INT_M_MASK 0x184 +#define MASK_INT_M_L2ISP_SIZE_ERROR BIT(0) +#define MASK_INT_M_CRGBF_INTCRGERR_WRSTART BIT(1) +#define MASK_INT_M_CRGBF_INTCRGERR_RDSTART BIT(2) +#define MASK_INT_M_EMBED_ERROR BIT(3) +#define MASK_INT_M_USERDATA_ERROR BIT(4) +#define MASK_INT_M_L2ISP_GAMMA_TABLE_TIMEOUT (BIT(8) | BIT(9) | BIT(10)) +#define MASK_INT_M_L2ISP_GRID_TABLE_TIMEOUT BIT(11) +#define MASK_INT_M_L1ISP_SIZE_ERROR0 BIT(16) +#define MASK_INT_M_L1ISP_SIZE_ERROR1 BIT(17) +#define MASK_INT_M_L1ISP_SIZE_ERROR2 BIT(18) +#define MASK_INT_M_L1ISP_SIZE_ERROR3 BIT(19) +#define MASK_INT_M_L1ISP_SIZE_ERROR4 BIT(20) +#define MASK_INT_M_L1ISP_INT_ERR_CRGWRSTART BIT(21) +#define MASK_INT_M_L1ISP_INT_ERR_CRGRDSTART BIT(22) +#define MASK_INT_M_DELAY_INT_ERROR BIT(24) + +#define REG_INT_S_STATUS 0x188 +#define REG_INT_S_MASK 0x18c +#define MASK_INT_S_SIZE_ERROR BIT(0) +#define MASK_INT_S_EMBED_ERROR BIT(1) +#define MASK_INT_S_USERDATA_ERROR BIT(2) +#define MASK_INT_S_DELAY_INT_ERROR BIT(24) +#define MASK_INT_S_RESERVED_SET (BIT(16) | BIT(28)) + +#define REG_PREPROCESS_FMTM 0x210 +#define VAL_PREPROCESS_FMT_RGB (0x0 << 4) +#define VAL_PREPROCESS_FMT_YUV444 (0x1 << 4) +#define VAL_PREPROCESS_FMT_YUV422 (0x3 << 4) + +#define REG_PREPROCESS_C24M 0x214 +#define REG_L2ISP_INPUT_CSC_MTB 0x220 +#define REG_L2ISP_INPUT_CSC_MTB_YG_OFFSETI 0x230 +#define REG_L2ISP_INPUT_CSC_MTB_YG1 0x234 +#define REG_L2ISP_INPUT_CSC_MTB_YG2 0x238 +#define REG_L2ISP_INPUT_CSC_MTB_YG_OFFSETO 0x23c +#define REG_L2ISP_INPUT_CSC_MTB_CB_OFFSETI 0x240 +#define REG_L2ISP_INPUT_CSC_MTB_CB1 0x244 +#define REG_L2ISP_INPUT_CSC_MTB_CB2 0x248 +#define REG_L2ISP_INPUT_CSC_MTB_CB_OFFSETO 0x24c +#define REG_L2ISP_INPUT_CSC_MTB_CR_OFFSETI 0x250 +#define REG_L2ISP_INPUT_CSC_MTB_CR1 0x254 +#define REG_L2ISP_INPUT_CSC_MTB_CR2 0x258 +#define REG_L2ISP_INPUT_CSC_MTB_CR_OFFSETO 0x25c +#define REG_DPHY_FREQRANGE 0x700 +#define REG_DPHY_LANE 0x710 +#define REG_DPGM_VSYNC_SOURCE 0x804 +#define VAL_DPGM_VSYNC_PULSE 1 + +#define REG_VDM_R_ENABLE 0x1030 +#define REG_VDM_W_ENABLE 0x1034 +#define REG_VDM_T_ENABLE 0x1038 +#define MASK_VDM_T_ENABLE_L1_DPC_H BIT(0) +#define MASK_VDM_T_ENABLE_L1_DPC_M BIT(1) +#define MASK_VDM_T_ENABLE_L1_DPC_L BIT(2) +#define MASK_VDM_T_ENABLE_L1_DPC 0x07 +#define MASK_VDM_T_ENABLE_L1_LSSC_GR BIT(4) +#define MASK_VDM_T_ENABLE_L1_LSSC_R BIT(5) +#define MASK_VDM_T_ENABLE_L1_LSSC_B BIT(6) +#define MASK_VDM_T_ENABLE_L1_LSSC_GB BIT(7) +#define MASK_VDM_T_ENABLE_L1_LSSC 0x00f0 +#define MASK_VDM_T_ENABLE_L2_UNDIST_RD_B BIT(8) +#define MASK_VDM_T_ENABLE_L2_UNDIST_RD_G BIT(9) +#define MASK_VDM_T_ENABLE_L2_UNDIST_RD_R BIT(10) +#define MASK_VDM_T_ENABLE_L2_UNDIST_WR_G BIT(11) +#define MASK_VDM_T_ENABLE_L2_UNDIST 0x0f00 +/* val: 12-23; postid: 0-1, ch: 0-5 */ +#define MASK_VDM_T_ENABLE_L2_GAMMA(postid, ch) BIT(12 + ((postid) * 6) + (ch)) +#define MASK_VDM_T_ENABLE_L2_GAMMA_ALL(postid) (0x3fu << (12 + ((postid) * 6))) + +#define REG_VDM_ABORTSET 0x103c + +/* x: 0-3 */ +#define REG_VDM_TGROUP_X_BASE(x) (0x1040 + 16 * (x)) +#define REG_VDM_TGROUP_X_CFG(x) (REG_VDM_TGROUP_X_BASE(x) + 0) +#define REG_VDM_TGROUP_X_SRAM_BASE(x) (REG_VDM_TGROUP_X_BASE(x) + 4) +#define REG_VDM_TGROUP_X_SRAM_SIZE(x) (REG_VDM_TGROUP_X_BASE(x) + 8) +#define IDX_TGROUP_L1_ISP 0 +#define IDX_TGROUP_L2_UNDIST 1 +#define IDX_TGROUP_L2_GAMMA_LUT(post_id) (2 + (post_id)) +#define VAL_TGROUP_CFG_64BIT_RD 0x0310U +#define VAL_TGROUP_CFG_32BIT_RD 0x0210U + +/* x: 0-23*/ +#define REG_VDM_TPORT_X_BASE(x) (0x1100 + 8 * (x)) +#define REG_VDM_TPORT_X_SIZE(x) (REG_VDM_TPORT_X_BASE(x) + 4) +#define REG_VDM_TPORT_X_STADR(x) (REG_VDM_TPORT_X_BASE(x) + 0) +/* idx: 0-2; x: 0-2 */ +#define IDX_TPORT_L1_DPC(x) (x) +#define IDX_TPORT_L1_DPC_H 0 +#define IDX_TPORT_L1_DPC_M 1 +#define IDX_TPORT_L1_DPC_L 2 +#define IDX_TPORT_L1_LSSC_GR 4 +#define IDX_TPORT_L1_LSSC_R 5 +#define IDX_TPORT_L1_LSSC_B 6 +#define IDX_TPORT_L1_LSSC_GB 7 +#define IDX_TPORT_L2_UNDIST_RD_B 8 +#define IDX_TPORT_L2_UNDIST_RD_G 9 +#define IDX_TPORT_L2_UNDIST_RD_R 10 +#define IDX_TPORT_L2_UNDIST_WR_G 11 +/* idx: 12-23; post_id: 0-1, col: 0-5 */ +#define IDX_TPORT_L2_GAMMA_LUT(post_id, col) (12 + (col) + (post_id) * 6) + +/* x: 0-5 */ +#define REG_VDM_WPORT_X_BASE(x) (0x1300 + 64 * (x)) +#define REG_VDM_WPORT_X_W_CFG0(x) (REG_VDM_WPORT_X_BASE(x) + 16) +#define REG_VDM_WPORT_X_W_ENDADR(x) (REG_VDM_WPORT_X_BASE(x) + 4) +#define REG_VDM_WPORT_X_W_HEIGHT(x) (REG_VDM_WPORT_X_BASE(x) + 8) +#define REG_VDM_WPORT_X_W_PITCH(x) (REG_VDM_WPORT_X_BASE(x) + 12) +#define REG_VDM_WPORT_X_W_SRAM_BASE(x) (REG_VDM_WPORT_X_BASE(x) + 24) +#define REG_VDM_WPORT_X_W_SRAM_SIZE(x) (REG_VDM_WPORT_X_BASE(x) + 28) +#define REG_VDM_WPORT_X_W_STADR(x) (REG_VDM_WPORT_X_BASE(x) + 0) +#define IDX_WPORT_MAIN_LNG 0 +#define IDX_WPORT_MAIN_EMB 1 +#define IDX_WPORT_SUB_IMG 3 +#define IDX_WPORT_SUB_LNG 4 +#define IDX_WPORT_SUB_EMB 5 + +#define REG_VDM_R_RUN 0x1e18 +#define REG_VDM_W_RUN 0x1e1c +#define REG_VDM_T_RUN 0x1e20 +#define REG_L1_SYSM_WIDTH 0x2000 +#define REG_L1_SYSM_HEIGHT 0x2004 +#define REG_L1_SYSM_START_COLOR 0x2008 +#define VAL_L1_SYSM_START_COLOR_GRBG 0 +#define VAL_L1_SYSM_START_COLOR_RGGB 1 +#define VAL_L1_SYSM_START_COLOR_BGGR 2 +#define VAL_L1_SYSM_START_COLOR_GBRG 3 + +#define REG_L1_SYSM_INPUT_MODE 0x200c +#define REG_L1_SYSM_YCOEF_R 0x2014 +#define REG_L1_SYSM_YCOEF_G 0x2018 +#define REG_L1_SYSM_YCOEF_B 0x201c +#define REG_L1_SYSM_AG_H 0x2040 +#define REG_L1_SYSM_AG_M 0x2044 +#define REG_L1_SYSM_AG_L 0x2048 +#define REG_L1_SYSM_AG_PARAM_A 0x204c +#define REG_L1_SYSM_AG_PARAM_B 0x2050 +#define REG_L1_SYSM_AG_PARAM_C 0x2054 +#define REG_L1_SYSM_AG_PARAM_D 0x2058 +#define PACK_L1_SYSM_AG_PARAM(grad, ofst) \ + (FIELD_PREP(0x00ff0000, (grad)) | FIELD_PREP(0x00ffff, (ofst))) + +#define REG_L1_SYSM_AG_SEL_HOBC 0x205c +#define REG_L1_SYSM_AG_SEL_ABPC 0x2060 +#define REG_L1_SYSM_AG_SEL_RCNR 0x2064 +#define PACK_L1_SYSM_AG_SEL_HML(high, mid, low) \ + (FIELD_PREP(0x00c0, (high)) | FIELD_PREP(0x0030, (mid)) | FIELD_PREP(0x0c, (low))) + +#define REG_L1_SYSM_AG_SEL_LSSC 0x2068 +#define REG_L1_SYSM_AG_SEL_MPRO 0x206c +#define REG_L1_SYSM_AG_SEL_VPRO 0x2070 +#define PACK_L1_SYSM_AG_SEL_SP(ssel, psel) (FIELD_PREP(0x00c0, (ssel)) | (FIELD_PREP(0x03, (psel)))) + +#define REG_L1_SYSM_AG_CONT_HOBC01_EN 0x2074 +#define REG_L1_SYSM_AG_CONT_HOBC2_EN 0x2078 +#define REG_L1_SYSM_AG_CONT_ABPC01_EN 0x207c +#define REG_L1_SYSM_AG_CONT_ABPC2_EN 0x2080 +#define REG_L1_SYSM_AG_CONT_RCNR01_EN 0x2084 +#define REG_L1_SYSM_AG_CONT_RCNR2_EN 0x2088 +#define MASK_L1_SYSM_AG_CONT_M_EN BIT(24) +#define MASK_L1_SYSM_AG_CONT_H_EN BIT(8) +#define MASK_L1_SYSM_AG_CONT_M_VAL 0x00ff0000 +#define MASK_L1_SYSM_AG_CONT_H_VAL 0x00ff +#define MASK_L1_SYSM_AG_CONT_L_EN BIT(8) +#define MASK_L1_SYSM_AG_CONT_L_VAL 0x00ff + +#define REG_L1_SYSM_AG_CONT_LSSC_EN 0x208c +#define REG_L1_SYSM_AG_CONT_MPRO_EN 0x2090 +#define REG_L1_SYSM_AG_CONT_VPRO_EN 0x2094 +#define MASK_L1_SYSM_AG_CONT_EN BIT(8) +#define MASK_L1_SYSM_AG_CONT_VAL 0x00ff + +#define LEN_L1_HDRE_SRCPOINT 16 +#define REG_L1_HDRE_SRCPOINT(x) (0x20c4 + 4 * (x)) + +#define LEN_L1_HDRE_SRCBASE 17 +#define REG_L1_HDRE_SRCBASE(x) (0x2104 + 4 * (x)) + +#define LEN_L1_HDRE_RATIO 17 +#define REG_L1_HDRE_RATIO(x) (0x2148 + 4 * (x)) + +#define LEN_L1_HDRE_DSTBASE 17 +#define REG_L1_HDRE_DSTBASE(x) (0x218c + 4 * (x)) + +#define REG_L1_HDRE_DSTMAXVAL 0x21d0 +#define REG_L1_AEXP_ON 0x2200 +#define REG_L1_AEXP_RESULT_AVE 0x2204 +#define REG_L1_AEXP_START_X 0x2210 +#define REG_L1_AEXP_START_Y 0x2214 +#define REG_L1_AEXP_BLOCK_WIDTH 0x2218 +#define REG_L1_AEXP_BLOCK_HEIGHT 0x221c +#define REG_L1_AEXP_WEIGHT_0 0x2220 +#define REG_L1_AEXP_WEIGHT_1 0x2224 +#define REG_L1_AEXP_WEIGHT_2 0x2228 +#define REG_L1_AEXP_WEIGHT_3 0x222c +#define REG_L1_AEXP_WEIGHT_4 0x2230 +#define REG_L1_AEXP_WEIGHT_5 0x2234 +#define REG_L1_AEXP_WEIGHT_6 0x2238 +#define REG_L1_AEXP_WEIGHT_7 0x223c +#define REG_L1_AEXP_SATUR_RATIO 0x2240 +#define REG_L1_AEXP_BLACK_RATIO 0x2244 +#define REG_L1_AEXP_SATUR_LEVEL 0x2248 +/* 8 x 8 elements */ +#define REG_L1_AEXP_AVE(y, x) (0x2250 + 32 * (y) + 4 * (x)) +#define REG_L1_AEXP_SATUR_BLACK_PIXNUM 0x2350 +#define REG_L1_AEXP_AVE4LINESY0 0x2354 +#define REG_L1_AEXP_AVE4LINESY1 0x2358 +#define REG_L1_AEXP_AVE4LINESY2 0x235c +#define REG_L1_AEXP_AVE4LINESY3 0x2360 +#define REG_L1_AEXP_AVE4LINES0 0x2364 +#define REG_L1_AEXP_AVE4LINES1 0x2368 +#define REG_L1_AEXP_AVE4LINES2 0x236c +#define REG_L1_AEXP_AVE4LINES3 0x2370 +#define REG_L1_IBUF_DEPTH 0x2380 +#define REG_L1_IBUF_INPUT_ORDER 0x2384 +#define REG_L1_SLIC_SRCBLACKLEVEL_GR 0x2390 +#define REG_L1_SLIC_SRCBLACKLEVEL_R 0x2394 +#define REG_L1_SLIC_SRCBLACKLEVEL_B 0x2398 +#define REG_L1_SLIC_SRCBLACKLEVEL_GB 0x239c +#define REG_L1_ABPC012_STA_EN 0x240c +#define REG_L1_ABPC012_DYN_EN 0x2410 +#define MASK_L1_ABPC_ENABLE_H BIT(24) +#define MASK_L1_ABPC_ENABLE_M BIT(16) +#define MASK_L1_ABPC_ENABLE_L BIT(8) + +#define REG_L1_ABPC012_DYN_MODE 0x2414 +#define MASK_L1_ABPC_DYN_MODE_2PIXEL_H BIT(24) +#define MASK_L1_ABPC_DYN_MODE_2PIXEL_M BIT(16) +#define MASK_L1_ABPC_DYN_MODE_2PIXEL_L BIT(8) + +#define REG_L1_ABPC0_RATIO_LIMIT 0x2424 +#define REG_L1_ABPC0_DARK_LIMIT 0x242c +#define REG_L1_ABPC0_SN_COEF_W_AG_MIN 0x2430 +#define REG_L1_ABPC0_SN_COEF_W_AG_MID 0x2434 +#define REG_L1_ABPC0_SN_COEF_W_AG_MAX 0x2438 +#define REG_L1_ABPC0_SN_COEF_W_TH_MIN 0x243c +#define REG_L1_ABPC0_SN_COEF_W_TH_MAX 0x2440 +#define REG_L1_ABPC0_SN_COEF_B_AG_MIN 0x2444 +#define REG_L1_ABPC0_SN_COEF_B_AG_MID 0x2448 +#define REG_L1_ABPC0_SN_COEF_B_AG_MAX 0x244c +#define REG_L1_ABPC0_SN_COEF_B_TH_MIN 0x2450 +#define REG_L1_ABPC0_SN_COEF_B_TH_MAX 0x2454 +#define REG_L1_ABPC1_RATIO_LIMIT 0x2460 +#define REG_L1_ABPC1_DARK_LIMIT 0x2468 +#define REG_L1_ABPC1_SN_COEF_W_AG_MIN 0x246c +#define REG_L1_ABPC1_SN_COEF_W_AG_MID 0x2470 +#define REG_L1_ABPC1_SN_COEF_W_AG_MAX 0x2474 +#define REG_L1_ABPC1_SN_COEF_W_TH_MIN 0x2478 +#define REG_L1_ABPC1_SN_COEF_W_TH_MAX 0x247c +#define REG_L1_ABPC1_SN_COEF_B_AG_MIN 0x2480 +#define REG_L1_ABPC1_SN_COEF_B_AG_MID 0x2484 +#define REG_L1_ABPC1_SN_COEF_B_AG_MAX 0x2488 +#define REG_L1_ABPC1_SN_COEF_B_TH_MIN 0x248c +#define REG_L1_ABPC1_SN_COEF_B_TH_MAX 0x2490 +#define REG_L1_ABPC2_RATIO_LIMIT 0x249c +#define REG_L1_ABPC2_DARK_LIMIT 0x24a4 +#define REG_L1_ABPC2_SN_COEF_W_AG_MIN 0x24a8 +#define REG_L1_ABPC2_SN_COEF_W_AG_MID 0x24ac +#define REG_L1_ABPC2_SN_COEF_W_AG_MAX 0x24b0 +#define REG_L1_ABPC2_SN_COEF_W_TH_MIN 0x24b4 +#define REG_L1_ABPC2_SN_COEF_W_TH_MAX 0x24b8 +#define REG_L1_ABPC2_SN_COEF_B_AG_MIN 0x24bc +#define REG_L1_ABPC2_SN_COEF_B_AG_MID 0x24c0 +#define REG_L1_ABPC2_SN_COEF_B_AG_MAX 0x24c4 +#define REG_L1_ABPC2_SN_COEF_B_TH_MIN 0x24c8 +#define REG_L1_ABPC2_SN_COEF_B_TH_MAX 0x24cc +#define REG_L1_PWHB_H_GR 0x2584 +#define REG_L1_PWHB_HR 0x2588 +#define REG_L1_PWHB_HB 0x258c +#define REG_L1_PWHB_H_GB 0x2590 +#define REG_L1_PWHB_M_GR 0x2594 +#define REG_L1_PWHB_MR 0x2598 +#define REG_L1_PWHB_MB 0x259c +#define REG_L1_PWHB_M_GB 0x25a0 +#define REG_L1_PWHB_L_GR 0x25a4 +#define REG_L1_PWHB_LR 0x25a8 +#define REG_L1_PWHB_LB 0x25ac +#define REG_L1_PWHB_L_GB 0x25b0 +#define REG_L1_PWHB_DSTMAXVAL 0x25b4 + +/* 0 for high, 1 for middle, 2 for low sensitivity image */ +#define REG_L1_RCNR_X_SW(x) (0x2608 + 0x94 * (x)) +#define REG_L1_RCNR_X_CNF_DARK_AG0(x) (0x260c + 0x94 * (x)) +#define REG_L1_RCNR_X_CNF_DARK_AG1(x) (0x2610 + 0x94 * (x)) +#define REG_L1_RCNR_X_CNF_DARK_AG2(x) (0x2614 + 0x94 * (x)) +#define REG_L1_RCNR_X_CNF_RATIO_AG0(x) (0x2618 + 0x94 * (x)) +#define REG_L1_RCNR_X_CNF_RATIO_AG1(x) (0x261c + 0x94 * (x)) +#define REG_L1_RCNR_X_CNF_RATIO_AG2(x) (0x2620 + 0x94 * (x)) +#define REG_L1_RCNR_X_CNF_CLIP_GAIN_R(x) (0x2624 + 0x94 * (x)) +#define REG_L1_RCNR_X_CNF_CLIP_GAIN_G(x) (0x2628 + 0x94 * (x)) +#define REG_L1_RCNR_X_CNF_CLIP_GAIN_B(x) (0x262c + 0x94 * (x)) +#define REG_L1_RCNR_X_A1L_DARK_AG0(x) (0x2630 + 0x94 * (x)) +#define REG_L1_RCNR_X_A1L_DARK_AG1(x) (0x2634 + 0x94 * (x)) +#define REG_L1_RCNR_X_A1L_DARK_AG2(x) (0x2638 + 0x94 * (x)) +#define REG_L1_RCNR_X_A1L_RATIO_AG0(x) (0x263c + 0x94 * (x)) +#define REG_L1_RCNR_X_A1L_RATIO_AG1(x) (0x2640 + 0x94 * (x)) +#define REG_L1_RCNR_X_A1L_RATIO_AG2(x) (0x2644 + 0x94 * (x)) +#define REG_L1_RCNR_X_INF_ZERO_CLIP(x) (0x2648 + 0x94 * (x)) +#define REG_L1_RCNR_X_MERGE_D2BLEND_AG0(x) (0x2650 + 0x94 * (x)) +#define REG_L1_RCNR_X_MERGE_D2BLEND_AG1(x) (0x2654 + 0x94 * (x)) +#define REG_L1_RCNR_X_MERGE_D2BLEND_AG2(x) (0x2658 + 0x94 * (x)) +#define REG_L1_RCNR_X_MERGE_BLACK(x) (0x265c + 0x94 * (x)) +#define REG_L1_RCNR_X_MERGE_MINDIV(x) (0x2660 + 0x94 * (x)) +#define REG_L1_RCNR_X_HRY_TYPE(x) (0x2664 + 0x94 * (x)) +#define REG_L1_RCNR_X_ANF_BLEND_AG0(x) (0x2668 + 0x94 * (x)) +#define REG_L1_RCNR_X_ANF_BLEND_AG1(x) (0x266c + 0x94 * (x)) +#define REG_L1_RCNR_X_ANF_BLEND_AG2(x) (0x2670 + 0x94 * (x)) +#define REG_L1_RCNR_X_LPF_THRESHOLD(x) (0x2678 + 0x94 * (x)) +#define REG_L1_RCNR_X_MERGE_HLBLEND_AG0(x) (0x267c + 0x94 * (x)) +#define REG_L1_RCNR_X_MERGE_HLBLEND_AG1(x) (0x2680 + 0x94 * (x)) +#define REG_L1_RCNR_X_MERGE_HLBLEND_AG2(x) (0x2684 + 0x94 * (x)) +#define REG_L1_RCNR_X_GNR_SW(x) (0x2688 + 0x94 * (x)) +#define REG_L1_RCNR_X_GNR_RATIO(x) (0x268c + 0x94 * (x)) +#define REG_L1_RCNR_X_GNR_WIDE_EN(x) (0x2690 + 0x94 * (x)) + +#define REG_L1_HDRS_HDRRATIO_M 0x2884 +#define REG_L1_HDRS_HDRRATIO_L 0x2888 +#define REG_L1_HDRS_HDRRATIO_E 0x288c +#define REG_L1_HDRS_BLENDEND_H 0x2898 +#define REG_L1_HDRS_BLENDEND_M 0x289c +#define REG_L1_HDRS_BLENDEND_E 0x28a0 +#define REG_L1_HDRS_BLENDBEG_H 0x28a4 +#define REG_L1_HDRS_BLENDBEG_M 0x28a8 +#define REG_L1_HDRS_BLENDBEG_E 0x28ac +#define REG_L1_HDRS_DG_H 0x28c8 +#define REG_L1_HDRS_DG_M 0x28cc +#define REG_L1_HDRS_DG_L 0x28d0 +#define REG_L1_HDRS_DG_E 0x28d4 +#define REG_L1_HDRS_LEDMODE_ON 0x28d8 +#define REG_L1_HDRS_HDRMODE 0x28dc +#define REG_L1_HDRS_DSTMAXVAL 0x28ec +#define REG_L1_BLVC_SRCBLACKLEVEL_GR 0x2900 +#define REG_L1_BLVC_SRCBLACKLEVEL_R 0x2904 +#define REG_L1_BLVC_SRCBLACKLEVEL_B 0x2908 +#define REG_L1_BLVC_SRCBLACKLEVELGB 0x290c +#define REG_L1_BLVC_MULTVAL_GR 0x2910 +#define REG_L1_BLVC_MULTVAL_R 0x2914 +#define REG_L1_BLVC_MULTVAL_B 0x2918 +#define REG_L1_BLVC_MULTVAL_GB 0x291c +#define REG_L1_BLVC_DSTMAXVAL 0x2920 +#define REG_L1_LSSC_EN 0x2980 +#define REG_L1_LSSC_PWHB_R_GAIN 0x2990 +#define REG_L1_LSSC_PWHB_GR_GAIN 0x2994 +#define REG_L1_LSSC_PWHB_GB_GAIN 0x2998 +#define REG_L1_LSSC_PWHB_B_GAIN 0x299c +#define REG_L1_LSSC_PARA_EN 0x29a0 +#define REG_L1_LSSC_PARA_H_CENTER 0x29a4 +#define REG_L1_LSSC_PARA_V_CENTER 0x29a8 +#define REG_L1_LSSC_PARA_H_GAIN 0x29ac +#define REG_L1_LSSC_PARA_V_GAIN 0x29b0 +#define REG_L1_LSSC_PARA_MGSEL2 0x29b4 +#define REG_L1_LSSC_PARA_MGSEL4 0x29b8 + +/*0: R/2D, 1: R/4D, 2: GR/2D, 3: GR/4D, 4: GB/2D, 5: GB/4D, 6: B/2D, 7: B/4D*/ +#define REG_L1_LSSC_PARA_COEF_X_H_L(x) (0x29bc + 0x20 * (x)) +#define REG_L1_LSSC_PARA_COEF_X_H_R(x) (0x29c0 + 0x20 * (x)) +#define REG_L1_LSSC_PARA_COEF_X_V_U(x) (0x29c4 + 0x20 * (x)) +#define REG_L1_LSSC_PARA_COEF_X_V_D(x) (0x29c8 + 0x20 * (x)) +#define REG_L1_LSSC_PARA_COEF_X_HV_LU(x) (0x29cc + 0x20 * (x)) +#define REG_L1_LSSC_PARA_COEF_X_HV_RU(x) (0x29d0 + 0x20 * (x)) +#define REG_L1_LSSC_PARA_COEF_X_HV_LD(x) (0x29d4 + 0x20 * (x)) +#define REG_L1_LSSC_PARA_COEF_X_HV_RD(x) (0x29d8 + 0x20 * (x)) + +#define REG_L1_LSSC_GRID_EN 0x2abc +#define REG_L1_LSSC_GRID_H_CENTER 0x2ac0 +#define REG_L1_LSSC_GRID_V_CENTER 0x2ac4 +#define REG_L1_LSSC_GRID_H_SIZE 0x2ac8 +#define REG_L1_LSSC_GRID_V_SIZE 0x2acc +#define REG_L1_LSSC_GRID_MGSEL 0x2ad0 +#define REG_L1_MPRO_SW 0x2b00 +#define REG_L1_MPRO_CONF 0x2b04 +#define REG_L1_MPRO_DST_MINVAL 0x2b0c +#define REG_L1_MPRO_DST_MAXVAL 0x2b10 +#define REG_L1_MPRO_LM0_RMG_MIN 0x2b20 +#define REG_L1_MPRO_LM0_RMB_MIN 0x2b24 +#define REG_L1_MPRO_LM0_GMR_MIN 0x2b28 +#define REG_L1_MPRO_LM0_GMB_MIN 0x2b2c +#define REG_L1_MPRO_LM0_BMR_MIN 0x2b30 +#define REG_L1_MPRO_LM0_BMG_MIN 0x2b34 +#define REG_L1_MPRO_LM0_RMG_MAX 0x2b38 +#define REG_L1_MPRO_LM0_RMB_MAX 0x2b3c +#define REG_L1_MPRO_LM0_GMR_MAX 0x2b40 +#define REG_L1_MPRO_LM0_GMB_MAX 0x2b44 +#define REG_L1_MPRO_LM0_BMR_MAX 0x2b48 +#define REG_L1_MPRO_LM0_BMG_MAX 0x2b4c +#define REG_L1_MPRO_LCS_MODE 0x2bf0 +#define REG_L1_VPRO_PGC_SW 0x2d80 +#define REG_L1_VPRO_YUVC_SW 0x2d88 +#define REG_L1_VPRO_YNR_SW 0x2d8c +#define REG_L1_VPRO_ETE_SW 0x2d90 +#define REG_L1_VPRO_CSUP_UVSUP_SW 0x2d94 +#define REG_L1_VPRO_CSUP_CORING_SW 0x2d98 +#define REG_L1_VPRO_BRIGHT_SW 0x2d9c +#define REG_L1_VPRO_LCNT_SW 0x2da0 +#define REG_L1_VPRO_NLCNT_SW 0x2da4 +#define REG_L1_VPRO_EDGE_SUP_SW 0x2dac +#define REG_L1_VPRO_CNR_SW 0x2db0 +#define REG_L1_VPRO_BLKADJ 0x2db8 +/* (GAM00P is fixed 0) GAM01P-GAM44P: x:0-43 */ +#define REG_L1_VPRO_GAMxP(x) (0x2dbc + 4 * (x)) +#define REG_L1_VPRO_CB_MAT 0x2e6c +#define REG_L1_VPRO_CR_MAT 0x2e70 +#define REG_L1_VPRO_BRIGHT 0x2e74 +#define REG_L1_VPRO_LCONT_LEV 0x2e78 +#define REG_L1_VPRO_BLK_KNEE 0x2e7c +#define REG_L1_VPRO_WHT_KNEE 0x2e80 +#define REG_L1_VPRO_BLK_CONT0 0x2e84 +#define REG_L1_VPRO_BLK_CONT1 0x2e88 +#define REG_L1_VPRO_BLK_CONT2 0x2e8c +#define REG_L1_VPRO_WHT_CONT0 0x2e90 +#define REG_L1_VPRO_WHT_CONT1 0x2e94 +#define REG_L1_VPRO_WHT_CONT2 0x2e98 +#define REG_L1_VPRO_YNR_GAIN_MIN 0x2eb4 +#define REG_L1_VPRO_YNR_GAIN_MAX 0x2eb8 +#define REG_L1_VPRO_YNR_LIM_MIN 0x2ebc +#define REG_L1_VPRO_YNR_LIM_MAX 0x2ec0 +#define REG_L1_VPRO_ETE_GAIN_MIN 0x2ec4 +#define REG_L1_VPRO_ETE_GAIN_MAX 0x2ec8 +#define REG_L1_VPRO_ETE_LIM_MIN 0x2ecc +#define REG_L1_VPRO_ETE_LIM_MAX 0x2ed0 +#define REG_L1_VPRO_ETE_CORING_MIN 0x2ed4 +#define REG_L1_VPRO_ETE_CORING_MAX 0x2ed8 +#define REG_L1_VPRO_CB_GAIN 0x2edc +#define REG_L1_VPRO_CR_GAIN 0x2ee0 +#define REG_L1_VPRO_CBR_MGAIN_MIN 0x2ee4 +#define REG_L1_VPRO_CB_P_GAIN_MAX 0x2ee8 +#define REG_L1_VPRO_CB_M_GAIN_MAX 0x2eec +#define REG_L1_VPRO_CR_P_GAIN_MAX 0x2ef0 +#define REG_L1_VPRO_CR_M_GAIN_MAX 0x2ef4 +#define REG_L1_VPRO_CSUP_CORING_LV_MIN 0x2ef8 +#define REG_L1_VPRO_CSUP_CORING_LV_MAX 0x2efc +#define REG_L1_VPRO_CSUP_CORING_GAIN_MIN 0x2f00 +#define REG_L1_VPRO_CSUP_CORING_GAIN_MAX 0x2f04 +#define REG_L1_VPRO_CSUP_BK_SLV 0x2f08 +#define REG_L1_VPRO_CSUP_BK_MP 0x2f0c +#define REG_L1_VPRO_CSUP_BLACK 0x2f10 +#define REG_L1_VPRO_CSUP_WH_SLV 0x2f14 +#define REG_L1_VPRO_CSUP_WH_MP 0x2f18 +#define REG_L1_VPRO_CSUP_WHITE 0x2f1c +#define REG_L1_VPRO_EDGE_SUP_GAIN 0x2f20 +#define REG_L1_VPRO_EDGE_SUP_LIM 0x2f24 +#define REG_L1_AWHB_SW 0x2f80 +#define MASK_L1_AWHB_SW_EN BIT(7) +#define MASK_L1_AWHB_SW_LOCK BIT(5) + +#define REG_L1_AWHB_WBMRG 0x2f88 +#define REG_L1_AWHB_WBMGG 0x2f8c +#define REG_L1_AWHB_WBMBG 0x2f90 +#define REG_L1_AWHB_GATE_CONF0 0x2f94 +#define MASK_L1_AWHB_GATE_YGATE_SEL BIT(7) +#define MASK_L1_AWHB_GATE_YGATE_DATA 0x0060 +#define MASK_L1_AWHB_GATE_CGRANGE 0x0003 + +#define REG_L1_AWHB_GATE_CONF1 0x2f98 +#define MASK_L1_AWHB_GATE_YGATESW BIT(5) +#define MASK_L1_AWHB_GATE_HEXSW BIT(4) +#define MASK_L1_AWHB_GATE_AREAMODE 0x0003 + +#define REG_L1_AWHB_AREA_HSIZE 0x2f9c +#define REG_L1_AWHB_AREA_VSIZE 0x2fa0 +#define REG_L1_AWHB_AREA_HOFS 0x2fa4 +#define REG_L1_AWHB_AREA_VOFS 0x2fa8 +#define REG_L1_AWHB_AREA_MASKH 0x2fac +#define REG_L1_AWHB_AREA_MASKL 0x2fb0 +#define REG_L1_AWHB_SQ_CONF 0x2fb4 +#define MASK_L1_AWHB_SQ_CONF_SQ3POL BIT(2) +#define MASK_L1_AWHB_SQ_CONF_SQ3SW BIT(3) +#define MASK_L1_AWHB_SQ_CONF_SQ2POL BIT(4) +#define MASK_L1_AWHB_SQ_CONF_SQ2SW BIT(5) +#define MASK_L1_AWHB_SQ_CONF_SQ1POL BIT(6) +#define MASK_L1_AWHB_SQ_CONF_SQ1SW BIT(7) + +#define REG_L1_AWHB_YGATEH 0x2fb8 +#define REG_L1_AWHB_YGATEL 0x2fbc +#define REG_L1_AWHB_BYCUT0P 0x2fc8 +#define REG_L1_AWHB_BYCUT0N 0x2fcc +#define REG_L1_AWHB_RYCUT0P 0x2fd0 +#define REG_L1_AWHB_RYCUT0N 0x2fd4 +#define REG_L1_AWHB_RBCUT0H 0x2fd8 +#define REG_L1_AWHB_RBCUT0L 0x2fdc +#define REG_L1_AWHB_BYCUT1H 0x2ff8 +#define REG_L1_AWHB_BYCUT1L 0x2ffc +#define REG_L1_AWHB_RYCUT1H 0x3000 +#define REG_L1_AWHB_RYCUT1L 0x3004 +#define REG_L1_AWHB_BYCUT2H 0x3008 +#define REG_L1_AWHB_BYCUT2L 0x300c +#define REG_L1_AWHB_RYCUT2H 0x3010 +#define REG_L1_AWHB_RYCUT2L 0x3014 +#define REG_L1_AWHB_BYCUT3H 0x3018 +#define REG_L1_AWHB_BYCUT3L 0x301c +#define REG_L1_AWHB_RYCUT3H 0x3020 +#define REG_L1_AWHB_RYCUT3L 0x3024 +#define REG_L1_AWHB_AWBSFTU 0x3028 +#define REG_L1_AWHB_AWBSFTV 0x302c +#define REG_L1_AWHB_AWBSPD 0x3030 +#define MASK_L1_AWHB_AWBSPD_HUECOR BIT(4) +#define MASK_L1_AWHB_AWBSPD_SPD 0x0f + +#define REG_L1_AWHB_AWBULV 0x3034 +#define REG_L1_AWHB_AWBVLV 0x3038 +#define REG_L1_AWHB_AWBWAIT 0x303c +#define REG_L1_AWHB_AWBONDOT 0x3040 +#define REG_L1_AWHB_AWBFZTIM 0x3044 +#define REG_L1_AWHB_WBGRMAX 0x3048 +#define REG_L1_AWHB_WBGRMIN 0x304c +#define REG_L1_AWHB_WBGBMAX 0x3050 +#define REG_L1_AWHB_WBGBMIN 0x3054 +#define REG_L1_AWHB_AVE_USIG 0x308c +#define REG_L1_AWHB_AVE_VSIG 0x3090 +#define REG_L1_AWHB_NUM_UVON 0x3094 +#define REG_L1_AWHB_AWBGAINR 0x3098 +#define REG_L1_AWHB_AWBGAING 0x309c +#define REG_L1_AWHB_AWBGAINB 0x30a0 +#define REG_L1_AWHB_R_CTR_STOP 0x30b0 +#define REG_L1_HOBC_MARGIN 0x30c4 +#define REG_L1_HDRC_EN 0x3200 +#define REG_L1_HDRC_THR_SFT_AMT 0x3204 +#define REG_L1_HDRC_RATIO 0x320c +#define REG_L1_HDRC_PT_RATIO 0x321c +#define REG_L1_HDRC_PT_BLEND 0x3220 +#define REG_L1_HDRC_PT_BLEND2 0x3224 +#define REG_L1_HDRC_PT_SAT 0x3228 +#define REG_L1_HDRC_TN_TYPE 0x322c +#define REG_L1_HDRC_TNP_MAX 0x3230 +#define REG_L1_HDRC_TNP_MAG 0x3234 + +#define LEN_L1_HDRC_TNP_FIL 5 +#define REG_L1_HDRC_TNP_FIL(x) (0x3248 + 4 * (x)) + +#define LEN_L1_HDRC_UTN_TBL 20 +#define REG_L1_HDRC_UTN_TBL(x) (0x325c + 4 * (x)) + +#define REG_L1_HDRC_FLR_VAL 0x32ac +#define REG_L1_HDRC_FLR_ADP 0x32b0 +#define REG_L1_HDRC_YBR_OFF 0x32ec +#define REG_L1_HDRC_ORGY_BLEND 0x32f0 +#define REG_L1_HDRC_MAR_TOP 0x3300 +#define REG_L1_HDRC_MAR_LEFT 0x3304 +#define REG_L1_CRGBF_ACC_CONF 0x3700 +#define VAL_L1_CRGBF_ACC_CONF_MODE_BYPASS 0 +#define VAL_L1_CRGBF_ACC_CONF_MODE_BUFFER0 1 + +#define REG_L1_CRGBF_TRN_A_CONF 0x370c +#define VAL_L1_CRGBF_TRN_AUTO_READ_BANK0 BIT(16) + +#define REG_L1_CRGBF_INT_STAT 0x3718 +#define REG_L1_CRGBF_INT_MASK 0x371c +#define REG_L1_CRGBF_INT_MASKED_STAT 0x3720 +#define REG_L1_CRGBF_TRN_RBADDR 0x372c +#define REG_L1_CRGBF_TRN_READDR 0x3730 +#define REG_L1_CRGBF_ISP_INT_MASK 0x3738 +#define REG_L2_SENSOR_CROP_OFS_H 0x5000 +#define REG_L2_SENSOR_CROP_OFS_V 0x5004 +#define REG_L2_SENSOR_CROP_HSIZE 0x5008 +#define REG_L2_SENSOR_CROP_VSIZE 0x500c +#define REG_L2_BUS_L2_STATUS 0x5018 +#define REG_L2_ROI_NUM 0x5040 +/* x: 0-1 */ +#define REG_L2_ROI_TO_POST(x) (0x5044 + 4 * (x)) +#define REG_L2_ROI_X_BASE(x) (0x5050 + 32 * (x)) +#define REG_L2_ROI_X_SCALE(x) (REG_L2_ROI_X_BASE(x) + 0) +#define REG_L2_ROI_X_SCALE_INV(x) (REG_L2_ROI_X_BASE(x) + 4) +#define REG_L2_ROI_X_CORRECTED_HSIZE(x) (REG_L2_ROI_X_BASE(x) + 8) +#define REG_L2_ROI_X_CORRECTED_VSIZE(x) (REG_L2_ROI_X_BASE(x) + 12) +#define REG_L2_ROI_X_OUT_OFS_H(x) (REG_L2_ROI_X_BASE(x) + 16) +#define REG_L2_ROI_X_OUT_HSIZE(x) (REG_L2_ROI_X_BASE(x) + 24) +#define REG_L2_ROI_X_OUT_OFS_V(x) (REG_L2_ROI_X_BASE(x) + 20) +#define REG_L2_ROI_X_OUT_VSIZE(x) (REG_L2_ROI_X_BASE(x) + 28) +#define REG_L2_VALID_R_NORM2_POLY 0x50b0 +#define REG_L2_VALID_R_NORM2_GRID 0x50b4 +#define REG_L2_MODE 0x5100 +#define REG_L2_NORM_SCALE 0x5104 +#define REG_L2_ROI_WRITE_AREA_DELTA(x) (0x510c + 4 * (x)) +#define REG_L2_GRID_NODE_NUM_H 0x5118 +#define REG_L2_GRID_NODE_NUM_V 0x511c +#define REG_L2_GRID_PATCH_HSIZE_INV 0x5120 +#define REG_L2_GRID_PATCH_VSIZE_INV 0x5124 +/* x: 0-10 */ +#define REG_L2_POLY10_WRITE_G_COEF(x) (0x5128 + 4 * (x)) +#define REG_L2_POLY10_READ_B_COEF(x) (0x5154 + 4 * (x)) +#define REG_L2_POLY10_READ_G_COEF(x) (0x5180 + 4 * (x)) +#define REG_L2_POLY10_READ_R_COEF(x) (0x51ac + 4 * (x)) +/* x: 0-1 */ +#define REG_L2_POST_X_BASE(x) (0x5200 + 256 * (x)) +#define REG_L2_POST_X_CAP_OFFSET(x) (REG_L2_POST_X_BASE(x) + 0) +#define REG_L2_POST_X_CAP_SIZE(x) (REG_L2_POST_X_BASE(x) + 4) +#define REG_L2_POST_X_CSC_MTB_CB1(x) (REG_L2_POST_X_BASE(x) + 148) +#define REG_L2_POST_X_CSC_MTB_CB2(x) (REG_L2_POST_X_BASE(x) + 152) +#define REG_L2_POST_X_CSC_MTB_CB_OFFSETI(x) (REG_L2_POST_X_BASE(x) + 144) +#define REG_L2_POST_X_CSC_MTB_CB_OFFSETO(x) (REG_L2_POST_X_BASE(x) + 156) +#define REG_L2_POST_X_CSC_MTB_CR1(x) (REG_L2_POST_X_BASE(x) + 164) +#define REG_L2_POST_X_CSC_MTB_CR2(x) (REG_L2_POST_X_BASE(x) + 168) +#define REG_L2_POST_X_CSC_MTB_CR_OFFSETI(x) (REG_L2_POST_X_BASE(x) + 160) +#define REG_L2_POST_X_CSC_MTB_CR_OFFSETO(x) (REG_L2_POST_X_BASE(x) + 172) +#define REG_L2_POST_X_CSC_MTB(x) (REG_L2_POST_X_BASE(x) + 112) +#define REG_L2_POST_X_CSC_MTB_YG1(x) (REG_L2_POST_X_BASE(x) + 132) +#define REG_L2_POST_X_CSC_MTB_YG2(x) (REG_L2_POST_X_BASE(x) + 136) +#define REG_L2_POST_X_CSC_MTB_YG_OFFSETI(x) (REG_L2_POST_X_BASE(x) + 128) +#define REG_L2_POST_X_CSC_MTB_YG_OFFSETO(x) (REG_L2_POST_X_BASE(x) + 140) +#define FIELD_CSC_MTB_UPPER(x) (FIELD_PREP(0xffff0000, (x))) +#define FIELD_CSC_MTB_LOWER(x) (FIELD_PREP(0x0000ffff, (x))) + +#define REG_L2_POST_X_C_SELECT(x) (REG_L2_POST_X_BASE(x) + 96) +#define REG_L2_POST_X_GAMMA_M(x) (REG_L2_POST_X_BASE(x) + 80) +#define REG_L2_POST_X_HALF_SCALE_EN(x) (REG_L2_POST_X_BASE(x) + 8) +#define REG_L2_POST_X_OPORTALP(x) (REG_L2_POST_X_BASE(x) + 176) +#define REG_L2_POST_X_OPORTFMT(x) (REG_L2_POST_X_BASE(x) + 180) +#define REG_L2_POST_X_OUT_PITCH_B(x) (REG_L2_POST_X_BASE(x) + 196) +#define REG_L2_POST_X_OUT_PITCH_G(x) (REG_L2_POST_X_BASE(x) + 200) +#define REG_L2_POST_X_OUT_PITCH_R(x) (REG_L2_POST_X_BASE(x) + 204) +#define REG_L2_POST_X_OUT_STADR_B(x) (REG_L2_POST_X_BASE(x) + 184) +#define REG_L2_POST_X_OUT_STADR_G(x) (REG_L2_POST_X_BASE(x) + 188) +#define REG_L2_POST_X_OUT_STADR_R(x) (REG_L2_POST_X_BASE(x) + 192) +#define REG_L2_CRGBF_TRN_A_CONF 0x570c +#define VAL_L2_CRGBF_TRN_AUTO_READ_BANK0 BIT(16) + +#define REG_L2_CRGBF_INT_STAT 0x5718 +#define REG_L2_CRGBF_INT_MASK 0x571c +#define REG_L2_CRGBF_INT_MASKED_STAT 0x5720 +#define REG_L2_CRGBF_TRN_RBADDR 0x572c +#define REG_L2_CRGBF_TRN_READDR 0x5730 +#define REG_L2_CRGBF_ISP_INT 0x5734 +#define REG_L2_CRGBF_ISP_INT_MASK 0x5738 +#define MASK_L2_CRGBF_ISP_INT_DONE BIT(0) +#define MASK_L2_CRGBF_ISP_INT_ABORT_POST0 BIT(1) +#define MASK_L2_CRGBF_ISP_INT_ABORT_POST1 BIT(2) +#define MASK_L2_CRGBF_ISP_INT_ABORT_OTHER BIT(4) +#define MASK_L2_STATUS_ERR_ALL \ + (MASK_L2_CRGBF_ISP_INT_ABORT_POST0 | MASK_L2_CRGBF_ISP_INT_ABORT_POST1 | \ + MASK_L2_CRGBF_ISP_INT_ABORT_OTHER) + +/*=============================================*/ +/* HWA bus interface registers */ +/*=============================================*/ +#define REG_HWAIF_HWAIF_EN 0 +#define REG_HWAIF_HWAIF_CONF 0x10 +#define REG_HWAIF_OSTD_RLEN 0x14 +#define REG_HWAIF_OSTD_WREQ 0x18 +#define REG_HWAIF_REGION_ENTRY_EN 0xf0 + +/*=============================================*/ +/* Memory Protection Unit registers */ +/*=============================================*/ +#define REG_MPU_MP_EN 0 +#define REG_MPU_MF_EN 0x3a4 + +#endif /* __VIIF_REGS_H__ */ diff --git a/drivers/media/platform/toshiba/visconti/viif_resizer.c b/drivers/media/platform/toshiba/visconti/viif_resizer.c new file mode 100644 index 000000000000..ec056ff3adc4 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_resizer.c @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include +#include +#include +#include + +#include "viif.h" +#include "viif_common.h" +#include "viif_resizer.h" +#include "viif_regs.h" + +/* default parameters for V4L2 subdevice node */ +#define VISCONTI_VIIF_ISP_DEFAULT_WIDTH 1920 +#define VISCONTI_VIIF_ISP_DEFAULT_HEIGHT 1080 +#define VISCONTI_VIIF_MAX_COMPOSED_WIDTH 8190 +#define VISCONTI_VIIF_MAX_COMPOSED_HEIGHT 4094 + +/* up to 2 ROIs are available to be passed to POSTs */ +/* set ROI_NONE for a POST currently not running */ +#define VIIF_L2_ROI_MAX_NUM 2U +#define VIIF_L2_ROI_NONE 3U + +/* minimum crop width and height */ +#define VIIF_CROP_MIN_W 128U +#define VIIF_CROP_MIN_H 128U + +/* Set ROI path condition when ROI num is 2 */ +static void viif_l2_set_roi_num_2(struct viif_device *viif_dev) +{ + struct viif_l2_roi_path_info *info = &viif_dev->l2_roi_path_info; + u32 i; + + for (i = 0; i < VIIF_L2_ROI_MAX_NUM; i++) { + /* ROI-n is the same as CROP area of POST-n */ + if (info->post_enable_flag[i]) { + viif_capture_write(viif_dev, REG_L2_ROI_X_OUT_OFS_H(i), + info->post_crop_x[i]); + viif_capture_write(viif_dev, REG_L2_ROI_X_OUT_OFS_V(i), + info->post_crop_y[i]); + viif_capture_write(viif_dev, REG_L2_ROI_X_OUT_HSIZE(i), + info->post_crop_w[i]); + viif_capture_write(viif_dev, REG_L2_ROI_X_OUT_VSIZE(i), + info->post_crop_h[i]); + viif_capture_write(viif_dev, REG_L2_ROI_TO_POST(i), i); + } else { + viif_capture_write(viif_dev, REG_L2_ROI_X_OUT_OFS_H(i), 0); + viif_capture_write(viif_dev, REG_L2_ROI_X_OUT_OFS_V(i), 0); + viif_capture_write(viif_dev, REG_L2_ROI_X_OUT_HSIZE(i), VIIF_CROP_MIN_W); + viif_capture_write(viif_dev, REG_L2_ROI_X_OUT_VSIZE(i), VIIF_CROP_MIN_H); + viif_capture_write(viif_dev, REG_L2_ROI_TO_POST(i), VIIF_L2_ROI_NONE); + } + } +} + +/* Set ROI path condition when ROI num is 1 */ +static void viif_l2_set_roi_num_1(struct viif_device *viif_dev) +{ + struct viif_l2_roi_path_info *info = &viif_dev->l2_roi_path_info; + u32 val, x_min, x_max, y_min, y_max; + u32 i, x, y, w, h; + + /* ROI0 is input to POST0 and POST1 */ + if (info->post_enable_flag[0]) { + /* POST0 is enabled */ + x_min = info->post_crop_x[0]; + x_max = info->post_crop_x[0] + info->post_crop_w[0]; + y_min = info->post_crop_y[0]; + y_max = info->post_crop_y[0] + info->post_crop_h[0]; + if (info->post_enable_flag[1]) { + /* POST1 is enabled */ + x_min = min(x_min, info->post_crop_x[1]); + val = info->post_crop_x[1] + info->post_crop_w[1]; + x_max = max(x_max, val); + y_min = min(y_min, info->post_crop_y[1]); + val = info->post_crop_y[1] + info->post_crop_h[1]; + y_max = max(y_max, val); + } + x = x_min; + y = y_min; + w = x_max - x_min; + h = y_max - y_min; + } else if (info->post_enable_flag[1]) { + /* POST0 is disabled and POST1 is enabled */ + x = info->post_crop_x[1]; + w = info->post_crop_w[1]; + y = info->post_crop_y[1]; + h = info->post_crop_h[1]; + } else { + /* All POSTs are disabled */ + x = 0; + y = 0; + w = VIIF_CROP_MIN_W; + h = VIIF_CROP_MIN_H; + } + viif_capture_write(viif_dev, REG_L2_ROI_X_OUT_OFS_H(0), x); + viif_capture_write(viif_dev, REG_L2_ROI_X_OUT_OFS_V(0), y); + viif_capture_write(viif_dev, REG_L2_ROI_X_OUT_HSIZE(0), w); + viif_capture_write(viif_dev, REG_L2_ROI_X_OUT_VSIZE(0), h); + + for (i = 0; i < VIIF_MAX_POST_NUM; i++) + viif_capture_write(viif_dev, REG_L2_ROI_TO_POST(i), + info->post_enable_flag[i] ? 0 : VIIF_L2_ROI_NONE); +} + +/* Set ROI path condition */ +void visconti_viif_l2_set_roi_path(struct viif_device *viif_dev) +{ + if (viif_dev->l2_roi_path_info.roi_num == 1U) + viif_l2_set_roi_num_1(viif_dev); + else + viif_l2_set_roi_num_2(viif_dev); +} + +static int visconti_viif_resizer_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + + remote_pad = media_pad_remote_pad_first(&sd->entity.pads[VIIF_RESIZER_PAD_SINK]); + if (!remote_pad) + return -ENODEV; + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + /* currently RESIZER.sink supports only stream0 */ + return v4l2_subdev_enable_streams(remote_sd, remote_pad->index, BIT(0)); +} + +static int visconti_viif_resizer_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + + remote_pad = media_pad_remote_pad_first(&sd->entity.pads[VIIF_RESIZER_PAD_SINK]); + if (!remote_pad) + return -ENODEV; + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + /* currently RESIZER.sink supports only stream0 */ + return v4l2_subdev_disable_streams(remote_sd, remote_pad->index, BIT(0)); +} + +static int visconti_viif_resizer_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const unsigned int sink_codes[] = { MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_YUV8_1X24 }; + + if (code->pad == VIIF_RESIZER_PAD_SINK) { + if (code->index >= ARRAY_SIZE(sink_codes)) + return -EINVAL; + code->code = sink_codes[code->index]; + } else { + /* source pad: same as the sink pad */ + struct v4l2_mbus_framefmt *sink_fmt; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_RESIZER_PAD_SINK); + code->code = sink_fmt->code; + } + + return 0; +} + +static void visconti_viif_resizer_set_sink_fmt(struct v4l2_subdev_state *sd_state, + struct v4l2_mbus_framefmt *format) +{ + static const unsigned int sink_codes[] = { MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_YUV8_1X24 }; + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + struct v4l2_rect sink_rect; + unsigned int code; + int i; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_RESIZER_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_RESIZER_PAD_SRC); + + /* sink format */ + code = sink_codes[0]; + for (i = 0; i < ARRAY_SIZE(sink_codes); i++) + if (format->code == sink_codes[i]) + code = format->code; + sink_fmt->code = code; + sink_fmt->width = format->width; + sink_fmt->height = format->height; + + sink_rect.top = 0; + sink_rect.left = 0; + sink_rect.width = sink_fmt->width; + sink_rect.height = sink_fmt->height; + + /* sink compose */ + *v4l2_subdev_state_get_compose(sd_state, VIIF_RESIZER_PAD_SINK) = sink_rect; + + /* src crop */ + *v4l2_subdev_state_get_crop(sd_state, VIIF_RESIZER_PAD_SRC) = sink_rect; + + /* src format */ + src_fmt->code = sink_fmt->code; + src_fmt->width = sink_fmt->width; + src_fmt->height = sink_fmt->height; + + *format = *sink_fmt; +} + +static void visconti_viif_resizer_set_src_fmt(struct v4l2_subdev_state *sd_state, + struct v4l2_mbus_framefmt *format) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_RESIZER_PAD_SINK); + src_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_RESIZER_PAD_SRC); + + src_fmt->code = sink_fmt->code; + src_fmt->width = sink_fmt->width; + src_fmt->height = sink_fmt->height; + + *format = *src_fmt; +} + +static int visconti_viif_resizer_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + if (fmt->pad == VIIF_RESIZER_PAD_SINK) + visconti_viif_resizer_set_sink_fmt(sd_state, &fmt->format); + else + visconti_viif_resizer_set_src_fmt(sd_state, &fmt->format); + + return 0; +} + +static int visconti_viif_resizer_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; + struct v4l2_rect *src_crop, *sink_compose; + + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_RESIZER_PAD_SINK); + sink_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + sink_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + sink_fmt->field = V4L2_FIELD_NONE; + sink_fmt->code = MEDIA_BUS_FMT_RGB888_1X24; + + sink_compose = v4l2_subdev_state_get_compose(sd_state, VIIF_RESIZER_PAD_SINK); + sink_compose->top = 0; + sink_compose->left = 0; + sink_compose->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + sink_compose->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + + src_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_RESIZER_PAD_SRC); + src_fmt->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + src_fmt->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + src_fmt->field = V4L2_FIELD_NONE; + src_fmt->code = MEDIA_BUS_FMT_RGB888_1X24; + + src_crop = v4l2_subdev_state_get_crop(sd_state, VIIF_RESIZER_PAD_SRC); + src_crop->top = 0; + src_crop->left = 0; + src_crop->width = VISCONTI_VIIF_ISP_DEFAULT_WIDTH; + src_crop->height = VISCONTI_VIIF_ISP_DEFAULT_HEIGHT; + + return 0; +} + +static const struct v4l2_rect viif_resizer_sink_compose_bound = { + .top = 0, + .left = 0, + .width = VISCONTI_VIIF_MAX_COMPOSED_WIDTH, + .height = VISCONTI_VIIF_MAX_COMPOSED_HEIGHT, +}; + +static int visconti_viif_resizer_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + if (sel->pad == VIIF_RESIZER_PAD_SINK) { + struct v4l2_mbus_framefmt *sink_fmt; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_RESIZER_PAD_SINK); + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = sink_fmt->width; + sel->r.height = sink_fmt->height; + return 0; + case V4L2_SEL_TGT_COMPOSE: + sel->r = *v4l2_subdev_state_get_compose(sd_state, VIIF_RESIZER_PAD_SINK); + return 0; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r = viif_resizer_sink_compose_bound; + return 0; + } + } else { + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); + return 0; + } + } + return -EINVAL; +} + +static int visconti_viif_resizer_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_rect *rect_compose; + struct v4l2_mbus_framefmt *src_fmt; + + if (sel->pad == VIIF_RESIZER_PAD_SINK && sel->target == V4L2_SEL_TGT_COMPOSE) { + rect_compose = v4l2_subdev_state_get_compose(sd_state, VIIF_RESIZER_PAD_SINK); + v4l2_rect_set_max_size(&sel->r, &viif_resizer_sink_compose_bound); + v4l2_rect_map_inside(&sel->r, &viif_resizer_sink_compose_bound); + *rect_compose = sel->r; + + /* update SRC::CROP with SINK::COMPOSE */ + *v4l2_subdev_state_get_crop(sd_state, VIIF_RESIZER_PAD_SRC) = *rect_compose; + + /* update SRC::FMT with SINK_COMPOSE */ + src_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_RESIZER_PAD_SRC); + src_fmt->width = rect_compose->width; + src_fmt->height = rect_compose->height; + return 0; + } + if (sel->pad == VIIF_RESIZER_PAD_SRC && sel->target == V4L2_SEL_TGT_CROP) { + struct v4l2_rect *rect, rect_crop_bound; + + rect_compose = v4l2_subdev_state_get_compose(sd_state, VIIF_RESIZER_PAD_SINK); + rect_crop_bound.left = 1; + rect_crop_bound.top = 1; + rect_crop_bound.width = rect_compose->width - 2; + rect_crop_bound.height = rect_compose->height - 2; + v4l2_rect_set_max_size(&sel->r, &rect_crop_bound); + v4l2_rect_map_inside(&sel->r, &rect_crop_bound); + rect = v4l2_subdev_state_get_crop(sd_state, VIIF_RESIZER_PAD_SRC); + *rect = sel->r; + + /* update SRC::FMT with given SRC::CROP */ + src_fmt = v4l2_subdev_state_get_format(sd_state, VIIF_RESIZER_PAD_SRC); + src_fmt->width = sel->r.width; + src_fmt->height = sel->r.height; + return 0; + } + return -EINVAL; +} + +static const struct media_entity_operations visconti_viif_resizer_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_pad_ops visconti_viif_resizer_pad_ops = { + .disable_streams = visconti_viif_resizer_disable_streams, + .enable_streams = visconti_viif_resizer_enable_streams, + .enum_mbus_code = visconti_viif_resizer_enum_mbus_code, + .get_selection = visconti_viif_resizer_get_selection, + .set_selection = visconti_viif_resizer_set_selection, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = visconti_viif_resizer_set_fmt, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_video_ops visconti_viif_resizer_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_ops visconti_viif_resizer_ops = { + .video = &visconti_viif_resizer_video_ops, + .pad = &visconti_viif_resizer_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops visconti_viif_resizer_internal_ops = { + .init_state = visconti_viif_resizer_init_state, +}; + +static int viif_resizer_register_node(struct resizer_subdev *resizer_subdev) +{ + struct viif_device *viif_dev = resizer_subdev->viif_dev; + struct media_pad *pads = resizer_subdev->pads; + struct v4l2_subdev *sd = &resizer_subdev->sd; + static const char *const node_name[] = { + "visconti-viif:resizer0", + "visconti-viif:resizer1", + }; + int ret; + + v4l2_subdev_init(sd, &visconti_viif_resizer_ops); + sd->internal_ops = &visconti_viif_resizer_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->entity.ops = &visconti_viif_resizer_media_ops; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; + sd->owner = THIS_MODULE; + strscpy(sd->name, node_name[resizer_subdev->pathid], sizeof(sd->name)); + + pads[VIIF_RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; + pads[VIIF_RESIZER_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; + + ret = media_entity_pads_init(&sd->entity, VIIF_RESIZER_PAD_NUM, pads); + if (ret) { + dev_err(viif_dev->dev, "Failed on media_entity_pads_init\n"); + return ret; + } + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_cleanup_media_entity; + + ret = v4l2_device_register_subdev(&viif_dev->v4l2_dev, sd); + if (ret) { + dev_err(viif_dev->dev, "Failed to register Resizer subdev\n"); + goto err_cleanup_subdev; + } + + return 0; + +err_cleanup_subdev: + v4l2_subdev_cleanup(sd); +err_cleanup_media_entity: + media_entity_cleanup(&sd->entity); + return ret; +} + +int visconti_viif_resizer_register(struct viif_device *viif_dev) +{ + int ret; + + /* register MAIN POST0 (primary RGB) */ + viif_dev->resizer_post0.pathid = RESIZER_PATH_MAIN_POST0; + viif_dev->resizer_post0.viif_dev = viif_dev; + ret = viif_resizer_register_node(&viif_dev->resizer_post0); + if (ret) + return ret; + + /* register MAIN POST1 (additional RGB) */ + viif_dev->resizer_post1.pathid = RESIZER_PATH_MAIN_POST1; + viif_dev->resizer_post1.viif_dev = viif_dev; + ret = viif_resizer_register_node(&viif_dev->resizer_post1); + if (ret) + return ret; + + /* no resizer for SUB (RAW) */ + return 0; +} + +void visconti_viif_resizer_unregister(struct viif_device *viif_dev) +{ + v4l2_device_unregister_subdev(&viif_dev->resizer_post0.sd); + v4l2_device_unregister_subdev(&viif_dev->resizer_post1.sd); + media_entity_cleanup(&viif_dev->resizer_post0.sd.entity); + media_entity_cleanup(&viif_dev->resizer_post1.sd.entity); +} diff --git a/drivers/media/platform/toshiba/visconti/viif_resizer.h b/drivers/media/platform/toshiba/visconti/viif_resizer.h new file mode 100644 index 000000000000..2494be320494 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_resizer.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#ifndef __VIIF_RESIZER_H__ +#define __VIIF_RESIZER_H__ + +struct viif_device; + +void visconti_viif_l2_set_roi_path(struct viif_device *viif_dev); + +int visconti_viif_resizer_register(struct viif_device *viif_dev); +void visconti_viif_resizer_unregister(struct viif_device *viif_dev); + +#endif /* __VIIF_RESIZER_H__ */ diff --git a/include/uapi/linux/visconti_viif.h b/include/uapi/linux/visconti_viif.h new file mode 100644 index 000000000000..b44e8a73173c --- /dev/null +++ b/include/uapi/linux/visconti_viif.h @@ -0,0 +1,1921 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#ifndef __UAPI_VISCONTI_VIIF_H_ +#define __UAPI_VISCONTI_VIIF_H_ + +#include +#include + +/* Enable/Disable flag */ +#define VIIF_DISABLE (0U) +#define VIIF_ENABLE (1U) + +/** + * enum viif_l1_input - L1ISP preprocessing mode, + * + * @VIIF_L1_INPUT_HDR: accepts HDR input; bypass preprocessing + * @VIIF_L1_INPUT_PWL: accepts PWL input; runs HDRE + * @VIIF_L1_INPUT_HDR_IMG_CORRECT: accepts HDR input; runs SLIC, ABPC, PWHB, RCNR, HDRS + * @VIIF_L1_INPUT_PWL_IMG_CORRECT: accepts PWR input; runs HDRE, SLIC, ABPC, PWHB, RCNR, HDRS + */ +enum viif_l1_input { + VIIF_L1_INPUT_HDR = 0, + VIIF_L1_INPUT_PWL = 1, + VIIF_L1_INPUT_HDR_IMG_CORRECT = 3, + VIIF_L1_INPUT_PWL_IMG_CORRECT = 4, +}; + +/** + * struct viif_l1_input_mode_config - L1ISP parameter for input mode. + * + * @mode: &enum viif_l1_input value. + */ +struct viif_l1_input_mode_config { + __u32 mode; +}; + +/** + * struct viif_l1_rgb_to_y_coef_config - L1ISP parameter for calculating Y from RGB + * + * @coef_r: R co-efficient. Range: [256..65024], Accuracy: 1/65536. + * @coef_g: G co-efficient. Range: [256..65024], Accuracy: 1/65536. + * @coef_b: B co-efficient. Range: [256..65024], Accuracy: 1/65536. + * @reserved: padding field + */ +struct viif_l1_rgb_to_y_coef_config { + __u16 coef_r; + __u16 coef_g; + __u16 coef_b; + __u16 reserved; +}; + +/** + * enum viif_l1_img_sensitivity_mode - L1ISP image sensitivity + * + * @VIIF_L1_IMG_SENSITIVITY_HIGH: high sensitivity + * @VIIF_L1_IMG_SENSITIVITY_MIDDLE_LED: middle sensitivity or led + * @VIIF_L1_IMG_SENSITIVITY_LOW: low sensitivity + */ +enum viif_l1_img_sensitivity_mode { + VIIF_L1_IMG_SENSITIVITY_HIGH = 0, + VIIF_L1_IMG_SENSITIVITY_MIDDLE_LED = 1, + VIIF_L1_IMG_SENSITIVITY_LOW = 2, +}; + +/** + * struct viif_l1_ag_mode_config - L1ISP parameter for analog gain + * + * @sysm_ag_grad: Analog gain slope. Range: [0..255], Index corresponds to psel id. + * @sysm_ag_ofst: Analog gain offset. Range: [0..65535], Index corresponds to psel id. + * @sysm_ag_cont_hobc_en_high: set 1 to enable control analog gain + * for high sensitivity image of OBCC + * @sysm_ag_psel_hobc_high: Analog gain id for high sensitivity image of OBCC. Range: [0..3] + * @sysm_ag_cont_hobc_en_middle_led: set 1 to enable control analog gain + * for middle sensitivity or LED image of OBCC + * @sysm_ag_psel_hobc_middle_led: Analog gain id for middle sensitivity + * or LED image of OBCC. Range: [0..3] + * @sysm_ag_cont_hobc_en_low: set 1 to enable control analog gain + * for low sensitivity image of OBCC + * @sysm_ag_psel_hobc_low: Analog gain id for low sensitivity image of OBCC. Range:[0..3] + * @sysm_ag_cont_abpc_en_high: set 1 to enable control analog gain + * for high sensitivity image of ABPC + * @sysm_ag_psel_abpc_high: Analog gain id for high sensitivity image of ABPC. Range: [0..3] + * @sysm_ag_cont_abpc_en_middle_led: set 1 to enable control analog gain + * for middle sensitivity or LED image of ABPC + * @sysm_ag_psel_abpc_middle_led: Analog gain id for middle sensitivity + * or LED image of ABPC. Range: [0..3] + * @sysm_ag_cont_abpc_en_low: set 1 to enable control analog gain + * for low sensitivity image of ABPC + * @sysm_ag_psel_abpc_low: Analog gain id for low sensitivity image of ABPC. Range: [0..3] + * @sysm_ag_cont_rcnr_en_high: set 1 to enable control analog gain + * for high sensitivity image of RCNR + * @sysm_ag_psel_rcnr_high: Analog gain id for high sensitivity image of RCNR. Range: [0..3] + * @sysm_ag_cont_rcnr_en_middle_led: set 1 to enable control analog gain + * for middle sensitivity or LED image of RCNR + * @sysm_ag_psel_rcnr_middle_led: Analog gain id for middle sensitivity + * or LED image of RCNR. Range: [0..3] + * @sysm_ag_cont_rcnr_en_low: set 1 to enable control analog gain + * for low sensitivity image of RCNR + * @sysm_ag_psel_rcnr_low: Analog gain id for low sensitivity image of RCNR. Range: [0..3] + * @sysm_ag_cont_lssc_en: set 1 to enable control analog gain for LSC + * @sysm_ag_ssel_lssc: &enum viif_l1_img_sensitivity_mode value. Sensitive image used for LSC. + * @sysm_ag_psel_lssc: Analog gain id for LSC. Range: [0..3] + * @sysm_ag_cont_mpro_en: set 1 to enable control analog gain for color matrix + * @sysm_ag_ssel_mpro: &enum viif_l1_img_sensitivity_mode value. + * Sensitive image used for color matrix. + * @sysm_ag_psel_mpro: Analog gain id for color matrix. Range: [0..3] + * @sysm_ag_cont_vpro_en: set 1 to enable control analog gain for image adjustment + * @sysm_ag_ssel_vpro: &enum viif_l1_img_sensitivity_mode value. + * Sensitive image used for image adjustment. + * @sysm_ag_psel_vpro: Analog gain id for image adjustment. Range: [0..3] + * @sysm_ag_cont_hobc_test_high: Manual analog gain for high sensitivity image + * of OBCC. Range: [0..255] + * @sysm_ag_cont_hobc_test_middle_led: Manual analog gain for middle sensitivity + * or led image of OBCC. Range: [0..255] + * @sysm_ag_cont_hobc_test_low: Manual analog gain for low sensitivity image + * of OBCC. Range: [0..255] + * @sysm_ag_cont_abpc_test_high: Manual analog gain for high sensitivity image + * of ABPC. Range: [0..255] + * @sysm_ag_cont_abpc_test_middle_led: Manual analog gain for middle sensitivity + * or led image of ABPC. Range: [0..255] + * @sysm_ag_cont_abpc_test_low: Manual analog gain for low sensitivity image + * of ABPC. Range: [0..255] + * @sysm_ag_cont_rcnr_test_high: Manual analog gain for high sensitivity image + * of RCNR. Range: [0..255] + * @sysm_ag_cont_rcnr_test_middle_led: Manual analog gain for middle sensitivity + * or led image of RCNR. Range: [0..255] + * @sysm_ag_cont_rcnr_test_low: Manual analog gain for low sensitivity image + * of RCNR. Range: [0..255] + * @sysm_ag_cont_lssc_test: Manual analog gain for LSSC. Range: [0..255] + * @sysm_ag_cont_mpro_test: Manual analog gain for color matrix. Range: [0..255] + * @sysm_ag_cont_vpro_test: Manual analog gain for image adjustment. Range: [0..255] + * + * This parameter sets rules of generating analog gains for each feature in L1ISP. + * Related features are: + * + * - Optical Black Clamp Correction (OBCC) + * - Defect Pixel Correction (DPC) + * - RAW Color Noise Reduction (RCNR) + * - Lens Shading Correction (LSC) + * - Color matrix correction (MPRO) + * - Image quality adjustment (VPRO) + * + * The base gain is set with &struct viif_l1_ag_config. + * + * If sysm_ag_cont_xxxx_en = 1, analog_gain for each module is generated from + * sysm_ag_grad, sysm_ag_ofst and the value specified with &struct viif_l1_ag_config. + * If sysm_ag_cont_xxxx_en = 0, + * the value of sysm_ag_cont_xxxx_test is used for analog_gain for each module. + * + * Up to 4 sets of parameters (sysm_ag_grad[4] and sysm_ag_ofst[4]) can be used + * to generate analog gain. + * The parameter sysm_ag_psel_xxxx specifies the set to be used for module xxxx. + * For example, if sysm_ag_psel_hobc_high is set to 2, + * values in sysm_ag_grad[2] and sysm_ag_ofst[2] are used + * to generate analog gain for high sensitivity images in OBCC processing. + * + * Analog gain generation for each L1ISP module is disabled when + * "sysm_ag_cont_xxxx_en=0" and "sysm_ag_cont_xxxx_test=0". + * Be sure to disable the analog gain generation + * if VIIF_L1_INPUT_HDR or VIIF_L1_INPUT_PWL is set to + * &struct viif_l1_input_mode_config + * + */ +struct viif_l1_ag_mode_config { + __u8 sysm_ag_grad[4]; + __u16 sysm_ag_ofst[4]; + __u32 sysm_ag_cont_hobc_en_high; + __u32 sysm_ag_psel_hobc_high; + __u32 sysm_ag_cont_hobc_en_middle_led; + __u32 sysm_ag_psel_hobc_middle_led; + __u32 sysm_ag_cont_hobc_en_low; + __u32 sysm_ag_psel_hobc_low; + __u32 sysm_ag_cont_abpc_en_high; + __u32 sysm_ag_psel_abpc_high; + __u32 sysm_ag_cont_abpc_en_middle_led; + __u32 sysm_ag_psel_abpc_middle_led; + __u32 sysm_ag_cont_abpc_en_low; + __u32 sysm_ag_psel_abpc_low; + __u32 sysm_ag_cont_rcnr_en_high; + __u32 sysm_ag_psel_rcnr_high; + __u32 sysm_ag_cont_rcnr_en_middle_led; + __u32 sysm_ag_psel_rcnr_middle_led; + __u32 sysm_ag_cont_rcnr_en_low; + __u32 sysm_ag_psel_rcnr_low; + __u32 sysm_ag_cont_lssc_en; + __u32 sysm_ag_ssel_lssc; + __u32 sysm_ag_psel_lssc; + __u32 sysm_ag_cont_mpro_en; + __u32 sysm_ag_ssel_mpro; + __u32 sysm_ag_psel_mpro; + __u32 sysm_ag_cont_vpro_en; + __u32 sysm_ag_ssel_vpro; + __u32 sysm_ag_psel_vpro; + __u8 sysm_ag_cont_hobc_test_high; + __u8 sysm_ag_cont_hobc_test_middle_led; + __u8 sysm_ag_cont_hobc_test_low; + __u8 sysm_ag_cont_abpc_test_high; + __u8 sysm_ag_cont_abpc_test_middle_led; + __u8 sysm_ag_cont_abpc_test_low; + __u8 sysm_ag_cont_rcnr_test_high; + __u8 sysm_ag_cont_rcnr_test_middle_led; + __u8 sysm_ag_cont_rcnr_test_low; + __u8 sysm_ag_cont_lssc_test; + __u8 sysm_ag_cont_mpro_test; + __u8 sysm_ag_cont_vpro_test; +}; + +/** + * struct viif_l1_ag_config - L1ISP parameter for analog gain + * + * @gain_h: Analog gain for high sensitive image. Range: [0..65535]. + * @gain_m: Analog gain for middle sensitive image or LED image. Range: [0..65535]. + * @gain_l: Analog gain for low sensitive image. Range: [0..65535]. + * @reserved: padding field + * + * This parameter provides base analog gain commonly used in L1ISP features. + * Analog gain for each L1ISP feature is generated from this base analog gain + * and a configuration via &struct viif_l1_ag_mode_config. + */ +struct viif_l1_ag_config { + __u16 gain_h; + __u16 gain_m; + __u16 gain_l; + __u16 reserved; +}; + +/** + * struct viif_l1_hdre_config - L1ISP parameter for HDR expansion at preprocessing + * + * @hdre_src_point: Knee point N value of PWL compressed signal. Range: [0..0x3fff]. + * @hdre_dst_base: Offset value of HDR signal in Knee area M. Range: [0..0xffffff]. + * @hdre_ratio: Slope of output pixel value in Knee area M. + * Range: [0..0x3fffff], Accuracy: 1/64. + * @hdre_dst_max_val: Maximum value of output pixel. Range: [0..0xffffff]. + */ +struct viif_l1_hdre_config { + __u32 hdre_src_point[16]; + __u32 hdre_dst_base[17]; + __u32 hdre_ratio[17]; + __u32 hdre_dst_max_val; +}; + +/** + * struct viif_l1_img_extraction_config - L1ISP parameter for black level of input image + * + * @input_black_gr: Black level of input pixel (Gr). Range: [0..0xffffff]. + * @input_black_r: Black level of input pixel (R). Range: [0..0xffffff]. + * @input_black_b: Black level of input pixel (B). Range: [0..0xffffff]. + * @input_black_gb: Black level of input pixel (Gb). Range: [0..0xffffff]. + */ +struct viif_l1_img_extraction_config { + __u32 input_black_gr; + __u32 input_black_r; + __u32 input_black_b; + __u32 input_black_gb; +}; + +/** + * enum viif_l1_dpc_mode - L1ISP defect pixel correction mode + * @VIIF_L1_DPC_1PIXEL: 1 pixel correction mode + * @VIIF_L1_DPC_2PIXEL: 2 pixel correction mode + */ +enum viif_l1_dpc_mode { + VIIF_L1_DPC_1PIXEL = 0, + VIIF_L1_DPC_2PIXEL = 1, +}; + +/** + * struct viif_l1_dpc - L1ISP defect pixel correction parameters + * for &struct viif_l1_dpc_config + * @abpc_sta_en: 1:enable/0:disable setting of Static DPC + * @abpc_dyn_en: 1:enable/0:disable setting of Dynamic DPC + * @abpc_dyn_mode: &enum viif_l1_dpc_mode value. Sets dynamic DPC mode. + * @abpc_ratio_limit: Variation adjustment of dynamic DPC. Range: [0..1023]. + * @abpc_dark_limit: White defect judgment limit of dark area. Range: [0..1023]. + * @abpc_sn_coef_w_ag_min: Luminance difference adjustment of white DPC + * (undere lower threshold). + * @abpc_sn_coef_w_ag_mid: Luminance difference adjustment of white DPC + * (between lower and upper threshold). + * @abpc_sn_coef_w_ag_max: Luminance difference adjustment of white DPC + * (over upper threshold). + * @abpc_sn_coef_b_ag_min: Luminance difference adjustment of black DPC + * (undere lower threshold). + * @abpc_sn_coef_b_ag_mid: Luminance difference adjustment of black DPC + * (between lower and upper threshold). + * @abpc_sn_coef_b_ag_max: Luminance difference adjustment of black DPC + * (over upper threshold). + * @abpc_sn_coef_w_th_min: Luminance difference adjustment of white DPC + * analog gain lower threshold. + * @abpc_sn_coef_w_th_max: Luminance difference adjustment of white DPC + * analog gain upper threshold. + * @abpc_sn_coef_b_th_min: Luminance difference adjustment of black DPC + * analog gain lower threshold. + * @abpc_sn_coef_b_th_max: Luminance difference adjustment of black DPC + * analog gain upper threshold. + * + * Range of abpc_sn_coef_{w,b}_ag_{min,mid,max} is: + * + * - Range: [1..31] + * + * Range and constraints of sn_coef_{w,b}_th_{min,max} are: + * + * - Range: [0..255] + * - Constraint: abpc_sn_coef_w_th_min < abpc_sn_coef_w_th_max + * - Constraint: abpc_sn_coef_b_th_min < abpc_sn_coef_b_th_max + */ +struct viif_l1_dpc { + __u32 abpc_sta_en; + __u32 abpc_dyn_en; + __u32 abpc_dyn_mode; + __u32 abpc_ratio_limit; + __u32 abpc_dark_limit; + __u32 abpc_sn_coef_w_ag_min; + __u32 abpc_sn_coef_w_ag_mid; + __u32 abpc_sn_coef_w_ag_max; + __u32 abpc_sn_coef_b_ag_min; + __u32 abpc_sn_coef_b_ag_mid; + __u32 abpc_sn_coef_b_ag_max; + __u8 abpc_sn_coef_w_th_min; + __u8 abpc_sn_coef_w_th_max; + __u8 abpc_sn_coef_b_th_min; + __u8 abpc_sn_coef_b_th_max; +}; + +/** + * struct viif_l1_dpc_config - L1ISP parameter for defect pixel correction + * + * @param_h: DPC parameter for high sensitive image. Refer to &struct viif_l1_dpc + * @param_m: DPC parameter for middle sensitive image. Refer to &struct viif_l1_dpc + * @param_l: DPC parameter for low sensitive image. Refer to &struct viif_l1_dpc + * @table_h: DPC table for high sensitive image. + * The table is referred only when param_h.abpc_sta_en =1 + * @table_m: DPC table for middle sensitive image or LED image. + * The table is referred only when param_m.abpc_sta_en =1 + * @table_l: DPC table for low sensitive image. + * The table is referred only when param_l.abpc_sta_en =1 + * + * The size of each table is 8192 Bytes (u32 * 2048) + * Application should make sure that the table data is based on HW specification + * since this driver does not check the DPC table. + */ +struct viif_l1_dpc_config { + struct viif_l1_dpc param_h; + struct viif_l1_dpc param_m; + struct viif_l1_dpc param_l; + __u32 table_h[2048]; + __u32 table_m[2048]; + __u32 table_l[2048]; +}; + +/** + * struct viif_l1_preset_wb - L1ISP preset white balance parameters + * for &struct viif_l1_preset_white_balance_config + * @gain_gr: Gr gain. Range: [0..524287], Accuracy 1/16384 + * @gain_r: R gain. Range: [0..524287], Accuracy 1/16384 + * @gain_b: B gain. Range: [0..524287], Accuracy 1/16384 + * @gain_gb: Gb gain. Range: [0..524287], Accuracy 1/16384 + */ +struct viif_l1_preset_wb { + __u32 gain_gr; + __u32 gain_r; + __u32 gain_b; + __u32 gain_gb; +}; + +/** + * struct viif_l1_preset_white_balance_config - L1ISP parameter for preset white balance + * + * @dstmaxval: Maximum value of output pixel. Range: [0..4095] + * @param_h: Preset white balance parameter for high sensitive image. + * Refer to &struct viif_l1_preset_wb + * @param_m: Preset white balance parameters for middle sensitive image or LED image. + * Refer to &struct viif_l1_preset_wb + * @param_l: Preset white balance parameters for low sensitive image. + * Refer to &struct viif_l1_preset_wb + */ +struct viif_l1_preset_white_balance_config { + __u32 dstmaxval; + struct viif_l1_preset_wb param_h; + struct viif_l1_preset_wb param_m; + struct viif_l1_preset_wb param_l; +}; + +/** + * enum viif_l1_rcnr_type - L1ISP high resolution luminance filter type + * + * @VIIF_L1_RCNR_LOW_RESOLUTION: low resolution + * @VIIF_L1_RCNR_MIDDLE_RESOLUTION: middle resolution + * @VIIF_L1_RCNR_HIGH_RESOLUTION: high resolution + * @VIIF_L1_RCNR_ULTRA_HIGH_RESOLUTION: ultra high resolution + */ +enum viif_l1_rcnr_type { + VIIF_L1_RCNR_LOW_RESOLUTION = 0, + VIIF_L1_RCNR_MIDDLE_RESOLUTION = 1, + VIIF_L1_RCNR_HIGH_RESOLUTION = 2, + VIIF_L1_RCNR_ULTRA_HIGH_RESOLUTION = 3, +}; + +/** + * enum viif_l1_msf_blend_ratio - L1ISP MSF blend ratio + * + * @VIIF_L1_MSF_BLEND_RATIO_0_DIV_64: 0/64 + * @VIIF_L1_MSF_BLEND_RATIO_1_DIV_64: 1/64 + * @VIIF_L1_MSF_BLEND_RATIO_2_DIV_64: 2/64 + */ +enum viif_l1_msf_blend_ratio { + VIIF_L1_MSF_BLEND_RATIO_0_DIV_64 = 0, + VIIF_L1_MSF_BLEND_RATIO_1_DIV_64 = 1, + VIIF_L1_MSF_BLEND_RATIO_2_DIV_64 = 2, +}; + +/** + * struct viif_l1_raw_color_noise_reduction - L1ISP RCNR parameters + * for &struct viif_l1_raw_color_noise_reduction_config + * + * @rcnr_sw: set 1 to enable RAW color noise reduction, 0 to disable. + * @rcnr_cnf_dark_ag0: Maximum value of LSF dark noise adjustment. Range: [0..63]. + * @rcnr_cnf_dark_ag1: Middle value of LSF dark noise adjustment. Range: [0..63]. + * @rcnr_cnf_dark_ag2: Minimum value of LSF dark noise adjustment. Range: [0..63]. + * @rcnr_cnf_ratio_ag0: Maximum value of LSF luminance interlocking noise adjustment. + * Range: [0..31]. + * @rcnr_cnf_ratio_ag1: Middle value of LSF luminance interlocking noise adjustment: + * Range: [0..31]. + * @rcnr_cnf_ratio_ag2: Minimum value of LSF luminance interlocking noise adjustment: + * Range: [0..31]. + * @rcnr_cnf_clip_gain_r: LSF color correction limit adjustment gain R. Range: [0..3]. + * @rcnr_cnf_clip_gain_g: LSF color correction limit adjustment gain G. Range: [0..3]. + * @rcnr_cnf_clip_gain_b: LSF color correction limit adjustment gain B. Range: [0..3]. + * @rcnr_a1l_dark_ag0: Maximum value of MSF dark noise adjustment. Range: [0..63]. + * @rcnr_a1l_dark_ag1: Middle value of MSF dark noise adjustment. Range: [0..63]. + * @rcnr_a1l_dark_ag2: Minimum value of MSF dark noise adjustment. Range: [0..63]. + * @rcnr_a1l_ratio_ag0: Maximum value of MSF luminance interlocking noise adjustment. + * Range: [0..31]. + * @rcnr_a1l_ratio_ag1: Middle value of MSF luminance interlocking noise adjustment. + * Range: [0..31]. + * @rcnr_a1l_ratio_ag2: Minimum value of MSF luminance interlocking noise adjustment. + * Range: [0..31]. + * @rcnr_inf_zero_clip: Input stage zero clip setting. Range: [0..256]. + * @rcnr_merge_d2blend_ag0: Maximum value of filter results and input blend ratio. Range: [0..16]. + * @rcnr_merge_d2blend_ag1: Middle value of filter results and input blend ratio. Range: [0..16]. + * @rcnr_merge_d2blend_ag2: Minimum value of filter results and input blend ratio. Range: [0..16]. + * @rcnr_merge_black: Black level minimum value. Range: [0..64]. + * @rcnr_merge_mindiv: 0 div guard value of inverse arithmetic unit. Range: [4..16]. + * @rcnr_hry_type: &enum viif_l1_rcnr_type value. Filter type for HSF filter process. + * @rcnr_anf_blend_ag0: &enum viif_l1_msf_blend_ratio value. + * Maximum value of MSF result blend ratio in write back data to line memory. + * @rcnr_anf_blend_ag1: &enum viif_l1_msf_blend_ratio value. + * Middle value of MSF result blend ratio in write back data to line memory. + * @rcnr_anf_blend_ag2: &enum viif_l1_msf_blend_ratio value. + * Minimum value of MSF result blend ratio in write back data to line memory. + * @rcnr_lpf_threshold: Multiplier value for calculating dark noise / luminance + * interlock noise of MSF. Range: [0..31], Accuracy: 1/8. + * @rcnr_merge_hlblend_ag0: Maximum value of luminance signal generation blend. Range: [0..2]. + * @rcnr_merge_hlblend_ag1: Middle value of luminance signal generation blend. Range: [0..2]. + * @rcnr_merge_hlblend_ag2: Minimum value of luminance signal generation blend. Range: [0..2]. + * @rcnr_gnr_sw: set 1 to enable Gr/Gb sensitivity ratio correction function switching, + * 0 to disable. + * @rcnr_gnr_ratio: Upper limit of Gr/Gb sensitivity ratio correction factor. Range: [0..15]. + * @rcnr_gnr_wide_en: set 1 to double correction upper limit ratio of rcnr_gnr_ratio, + * 0 to just use the specified ratio. + */ +struct viif_l1_raw_color_noise_reduction { + __u32 rcnr_sw; + __u32 rcnr_cnf_dark_ag0; + __u32 rcnr_cnf_dark_ag1; + __u32 rcnr_cnf_dark_ag2; + __u32 rcnr_cnf_ratio_ag0; + __u32 rcnr_cnf_ratio_ag1; + __u32 rcnr_cnf_ratio_ag2; + __u32 rcnr_cnf_clip_gain_r; + __u32 rcnr_cnf_clip_gain_g; + __u32 rcnr_cnf_clip_gain_b; + __u32 rcnr_a1l_dark_ag0; + __u32 rcnr_a1l_dark_ag1; + __u32 rcnr_a1l_dark_ag2; + __u32 rcnr_a1l_ratio_ag0; + __u32 rcnr_a1l_ratio_ag1; + __u32 rcnr_a1l_ratio_ag2; + __u32 rcnr_inf_zero_clip; + __u32 rcnr_merge_d2blend_ag0; + __u32 rcnr_merge_d2blend_ag1; + __u32 rcnr_merge_d2blend_ag2; + __u32 rcnr_merge_black; + __u32 rcnr_merge_mindiv; + __u32 rcnr_hry_type; + __u32 rcnr_anf_blend_ag0; + __u32 rcnr_anf_blend_ag1; + __u32 rcnr_anf_blend_ag2; + __u32 rcnr_lpf_threshold; + __u32 rcnr_merge_hlblend_ag0; + __u32 rcnr_merge_hlblend_ag1; + __u32 rcnr_merge_hlblend_ag2; + __u32 rcnr_gnr_sw; + __u32 rcnr_gnr_ratio; + __u32 rcnr_gnr_wide_en; +}; + +/** + * struct viif_l1_raw_color_noise_reduction_config - L1ISP parameter for raw color noise reduction. + * + * @param_h: RAW color noise reduction parameter for high sensitive image. + * Refer to &struct viif_l1_raw_color_noise_reduction + * @param_m: RAW color noise reduction parameter for middle sensitive image or LED image. + * Refer to &struct viif_l1_raw_color_noise_reduction + * @param_l: RAW color noise reduction parameter for low sensitive image. + * Refer to &struct viif_l1_raw_color_noise_reduction + */ +struct viif_l1_raw_color_noise_reduction_config { + struct viif_l1_raw_color_noise_reduction param_h; + struct viif_l1_raw_color_noise_reduction param_m; + struct viif_l1_raw_color_noise_reduction param_l; +}; + +/** + * enum viif_l1_hdrs_middle_img_mode - L1ISP HDR setting + * + * @VIIF_L1_HDRS_NOT_USE_MIDDLE_SENS_IMAGE: not use middle image + * @VIIF_L1_HDRS_USE_MIDDLE_SENS_IMAGE: use middle image + */ +enum viif_l1_hdrs_middle_img_mode { + VIIF_L1_HDRS_NOT_USE_MIDDLE_SENS_IMAGE = 0, + VIIF_L1_HDRS_USE_MIDDLE_SENS_IMAGE = 1, +}; + +/** + * struct viif_l1_hdrs_config - L1ISP parameter for HDR synthesis + * + * @hdrs_hdr_mode: &enum viif_l1_hdrs_middle_img_mode value. + * Switch for use of middle sensitivity image in HDRS. + * @hdrs_hdr_ratio_m: Magnification ratio of middle sensitivity image for high + * sensitivity image. + * @hdrs_hdr_ratio_l: Magnification ratio of low sensitivity image for high + * sensitivity image. + * @hdrs_hdr_ratio_e: Magnification ratio of LED image for high sensitivity image. + * @hdrs_dg_h: High sensitivity image digital gain. + * @hdrs_dg_m: Middle sensitivity image digital gain. + * @hdrs_dg_l: Low sensitivity image digital gain. + * @hdrs_dg_e: LED image digital gain. + * @hdrs_blendend_h: Maximum luminance used for blend high sensitivity image. + * @hdrs_blendend_m: Maximum luminance used for blend middle sensitivity image. + * @hdrs_blendend_e: Maximum luminance used for blend LED image. + * @hdrs_blendbeg_h: Minimum luminance used for blend high sensitivity image. + * @hdrs_blendbeg_m: Minimum luminance used for blend middle sensitivity image. + * @hdrs_blendbeg_e: Minimum luminance used for blend LED image. + * @hdrs_led_mode_on: set 1 to enable LED mode, 0 to disable + * @hdrs_dst_max_val: Maximum value of output pixel. Range: [0..0xffffff]. + * + * Range and Accuracy of parameters are: + * + * - hdrs_hdr_ratio_{m,l,e} + * + * - Range: [0x400..0x400000] + * - Accuracy: 1/1024 + * + * - hdrs_dg_{h,m,l,e} + * + * - Range: [0..0x3fffff] + * - Accuracy: 1/1024 + * + * - hdrs_blend{end,beg}_{h,m,e} + * + * - Range [0..4095] + * + * Parameter error will be returned when: + * (hdrs_hdr_mode == VIIF_L1_HDRS_USE_MIDDLE_SENS_IMAGE) && (hdrs_led_mode_on == 1) + */ +struct viif_l1_hdrs_config { + __u32 hdrs_hdr_mode; + __u32 hdrs_hdr_ratio_m; + __u32 hdrs_hdr_ratio_l; + __u32 hdrs_hdr_ratio_e; + __u32 hdrs_dg_h; + __u32 hdrs_dg_m; + __u32 hdrs_dg_l; + __u32 hdrs_dg_e; + __u32 hdrs_blendend_h; + __u32 hdrs_blendend_m; + __u32 hdrs_blendend_e; + __u32 hdrs_blendbeg_h; + __u32 hdrs_blendbeg_m; + __u32 hdrs_blendbeg_e; + __u32 hdrs_led_mode_on; + __u32 hdrs_dst_max_val; +}; + +/** + * struct viif_l1_black_level_correction_config - L1ISP parameter for black level correction. + * + * @srcblacklevel_gr: Black level of Gr input pixel. Range: [0..0xffffff]. + * @srcblacklevel_r: Black level of R input pixel. Range: [0..0xffffff]. + * @srcblacklevel_b: Black level of B input pixel. Range: [0..0xffffff]. + * @srcblacklevel_gb: Black level of Gb input pixel. Range: [0..0xffffff]. + * @mulval_gr: Gr gain. Range: [0..0xfffff], Accuracy: 1/256. + * @mulval_r: R gain. Range: [0..0xfffff], Accuracy: 1/256. + * @mulval_b: B gain. Range: [0..0xfffff], Accuracy: 1/256. + * @mulval_gb: Gb gain. Range: [0..0xfffff], Accuracy: 1/256. + * @dstmaxval: Maximum value of output pixel. Range: [0..0xffffff]. + */ +struct viif_l1_black_level_correction_config { + __u32 srcblacklevel_gr; + __u32 srcblacklevel_r; + __u32 srcblacklevel_b; + __u32 srcblacklevel_gb; + __u32 mulval_gr; + __u32 mulval_r; + __u32 mulval_b; + __u32 mulval_gb; + __u32 dstmaxval; +}; + +/** + * enum viif_l1_para_coef_gain - L1ISP parabola shading correction coefficient ratio + * + * @VIIF_L1_PARA_COEF_GAIN_ONE_EIGHTH: 1/8 + * @VIIF_L1_PARA_COEF_GAIN_ONE_FOURTH: 1/4 + * @VIIF_L1_PARA_COEF_GAIN_ONE_SECOND: 1/2 + * @VIIF_L1_PARA_COEF_GAIN_ONE_FIRST: 1/1 + */ +enum viif_l1_para_coef_gain { + VIIF_L1_PARA_COEF_GAIN_ONE_EIGHTH = 0, /* 1/8 */ + VIIF_L1_PARA_COEF_GAIN_ONE_FOURTH = 1, /* 1/4 */ + VIIF_L1_PARA_COEF_GAIN_ONE_SECOND = 2, /* 1/2 */ + VIIF_L1_PARA_COEF_GAIN_ONE_FIRST = 3, /* 1/1 */ +}; + +/** + * enum viif_l1_grid_coef_gain - L1ISP grid shading correction coefficient ratio + * + * @VIIF_L1_GRID_COEF_GAIN_X1: x1 + * @VIIF_L1_GRID_COEF_GAIN_X2: x2 + */ +enum viif_l1_grid_coef_gain { + VIIF_L1_GRID_COEF_GAIN_X1 = 0, + VIIF_L1_GRID_COEF_GAIN_X2 = 1, +}; + +/** + * struct viif_l1_lsc_parabola_ag_param - L2ISP parabola shading parameters + * for &struct viif_l1_lsc_parabola_param + * @lssc_paracoef_h_l_max: Parabola coefficient left maximum gain value + * @lssc_paracoef_h_l_min: Parabola coefficient left minimum gain value + * @lssc_paracoef_h_r_max: Parabola coefficient right maximum gain value + * @lssc_paracoef_h_r_min: Parabola coefficient right minimum gain value + * @lssc_paracoef_v_u_max: Parabola coefficient upper maximum gain value + * @lssc_paracoef_v_u_min: Parabola coefficient upper minimum gain value + * @lssc_paracoef_v_d_max: Parabola coefficient lower maximum gain value + * @lssc_paracoef_v_d_min: Parabola coefficient lower minimum gain value + * @lssc_paracoef_hv_lu_max: Parabola coefficient upper left gain maximum value + * @lssc_paracoef_hv_lu_min: Parabola coefficient upper left gain minimum value + * @lssc_paracoef_hv_ru_max: Parabola coefficient upper right gain maximum value + * @lssc_paracoef_hv_ru_min: Parabola coefficient upper right minimum gain value + * @lssc_paracoef_hv_ld_max: Parabola coefficient lower left gain maximum value + * @lssc_paracoef_hv_ld_min: Parabola coefficient lower left gain minimum value + * @lssc_paracoef_hv_rd_max: Parabola coefficient lower right gain maximum value + * @lssc_paracoef_hv_rd_min: Parabola coefficient lower right minimum gain value + * + * The Range, Accuracy and Constraint of each coefficient are: + * + * - Range: [-4096..4095] + * - Accuracy: accuracy: 1/256 + * - Constraint: lssc_paracoef_xx_xx_min <= lssc_paracoef_xx_xx_max + */ +struct viif_l1_lsc_parabola_ag_param { + __s16 lssc_paracoef_h_l_max; + __s16 lssc_paracoef_h_l_min; + __s16 lssc_paracoef_h_r_max; + __s16 lssc_paracoef_h_r_min; + __s16 lssc_paracoef_v_u_max; + __s16 lssc_paracoef_v_u_min; + __s16 lssc_paracoef_v_d_max; + __s16 lssc_paracoef_v_d_min; + __s16 lssc_paracoef_hv_lu_max; + __s16 lssc_paracoef_hv_lu_min; + __s16 lssc_paracoef_hv_ru_max; + __s16 lssc_paracoef_hv_ru_min; + __s16 lssc_paracoef_hv_ld_max; + __s16 lssc_paracoef_hv_ld_min; + __s16 lssc_paracoef_hv_rd_max; + __s16 lssc_paracoef_hv_rd_min; +}; + +/** + * struct viif_l1_lsc_parabola_param - L2ISP parabola shading parameters + * for &struct viif_l1_lsc + * @lssc_para_h_center: Horizontal coordinate of central optical axis. + * Range: [0..(Input image width - 1)]. + * @lssc_para_v_center: Vertical coordinate of central optical axis. + * Range: [0..(Input image height - 1)]. + * @lssc_para_h_gain: Horizontal distance gain with the optical axis. + * Range: [0..4095], Accuracy: 1/256. + * @lssc_para_v_gain: Vertical distance gain with the optical axis. + * Range: [0..4095], Accuracy: 1/256. + * @lssc_para_mgsel2: &enum viif_l1_para_coef_gain value. + * Parabola 2D correction coefficient gain magnification ratio. + * @lssc_para_mgsel4: &enum viif_l1_para_coef_gain value. + * Parabola 4D correction coefficient gain magnification ratio. + * @r_2d: 2D parabola coefficient for R. + * Refer to &struct viif_l1_lsc_parabola_ag_param + * @r_4d: 4D parabola coefficient for R. + * Refer to &struct viif_l1_lsc_parabola_ag_param + * @gr_2d: 2D parabola coefficient for Gr + * Refer to &struct viif_l1_lsc_parabola_ag_param + * @gr_4d: 4D parabola coefficient for Gr + * Refer to &struct viif_l1_lsc_parabola_ag_param + * @gb_2d: 2D parabola coefficient for Gb + * Refer to &struct viif_l1_lsc_parabola_ag_param + * @gb_4d: 4D parabola coefficient for Gb + * Refer to &struct viif_l1_lsc_parabola_ag_param + * @b_2d: 2D parabola coefficient for B + * Refer to &struct viif_l1_lsc_parabola_ag_param + * @b_4d: 4D parabola coefficient for B + * Refer to &struct viif_l1_lsc_parabola_ag_param + */ +struct viif_l1_lsc_parabola_param { + __u32 lssc_para_h_center; + __u32 lssc_para_v_center; + __u32 lssc_para_h_gain; + __u32 lssc_para_v_gain; + __u32 lssc_para_mgsel2; + __u32 lssc_para_mgsel4; + struct viif_l1_lsc_parabola_ag_param r_2d; + struct viif_l1_lsc_parabola_ag_param r_4d; + struct viif_l1_lsc_parabola_ag_param gr_2d; + struct viif_l1_lsc_parabola_ag_param gr_4d; + struct viif_l1_lsc_parabola_ag_param gb_2d; + struct viif_l1_lsc_parabola_ag_param gb_4d; + struct viif_l1_lsc_parabola_ag_param b_2d; + struct viif_l1_lsc_parabola_ag_param b_4d; +}; + +/** + * struct viif_l1_lsc_grid_param - L2ISP grid shading parameters + * for &struct viif_l1_lsc + * @lssc_grid_h_size: Grid horizontal direction pixel count. + * Range: [32, 64, 128, 256, 512] + * @lssc_grid_v_size: Grid vertical direction pixel count. + * Range: [32, 64, 128, 256, 512] + * @lssc_grid_h_center: Horizontal coordinates of grid (1, 1) + * @lssc_grid_v_center: Vertical coordinates of grid (1, 1) + * @lssc_grid_mgsel: &enum viif_l1_grid_coef_gain value. + * Grid correction coefficient gain value magnification ratio. + * + * Range and constraint of parameters are: + * + * - lssc_grid_h_center: + * + * - Range: [1..lssc_grid_h_size] + * - Constraint: "Input image width <= lssc_grid_h_center + lssc_grid_h_size * 31" + * + * - lssc_grid_v_center: + * + * - Range: [1..lssc_grid_v_size] + * - Constraint: "Input image height <= lssc_grid_v_center + lssc_grid_v_size * 23" + */ +struct viif_l1_lsc_grid_param { + __u32 lssc_grid_h_size; + __u32 lssc_grid_v_size; + __u32 lssc_grid_h_center; + __u32 lssc_grid_v_center; + __u32 lssc_grid_mgsel; +}; + +/** + * struct viif_l1_lsc - L2ISP LSC parameters for &struct viif_l1_lsc_config + * @lssc_parabola_param: see &struct viif_l1_lsc_parabola_param + * @lssc_grid_param: see &struct viif_l1_lsc_grid_param + * @lssc_pwhb_r_gain_max: PWB R correction processing coefficient maximum value + * @lssc_pwhb_r_gain_min: PWB R correction processing coefficient minimum value + * @lssc_pwhb_gr_gain_max: PWB Gr correction processing coefficient maximum value + * @lssc_pwhb_gr_gain_min: PWB Gr correction processing coefficient minimum value + * @lssc_pwhb_gb_gain_max: PWB Gb correction processing coefficient maximum value + * @lssc_pwhb_gb_gain_min: PWB Gb correction processing coefficient minimum value + * @lssc_pwhb_b_gain_max: PWB B correction processing coefficient maximum value + * @lssc_pwhb_b_gain_min: PWB B correction processing coefficient minimum value + * + * The range, accuracy and restriction of lssc_pwhb_{r,gr,gb,b}_gain_{max,min} are: + * + * - Range: [0..2047] + * - Accuracy: 1/256 + * - Restriction: xxxx_gain_min <= xxxx_gain_max + */ +struct viif_l1_lsc { + struct viif_l1_lsc_parabola_param lssc_parabola_param; + struct viif_l1_lsc_grid_param lssc_grid_param; + __u32 lssc_pwhb_r_gain_max; + __u32 lssc_pwhb_r_gain_min; + __u32 lssc_pwhb_gr_gain_max; + __u32 lssc_pwhb_gr_gain_min; + __u32 lssc_pwhb_gb_gain_max; + __u32 lssc_pwhb_gb_gain_min; + __u32 lssc_pwhb_b_gain_max; + __u32 lssc_pwhb_b_gain_min; +}; + +/* MASKs for viif_l1_lsc_config::enable */ +#define VIIF_L1_LSC_PARABOLA_EN_MASK BIT(0) +#define VIIF_L1_LSC_GRID_EN_MASK BIT(1) + +/** + * struct viif_l1_lsc_config - L1ISP parameter for lens shading correction. + * + * @enable: set 0 to disable LSC operation, + * 1 to enable parabola shading, + * 2 to enable grid shading, + * 3 to enable both parabola and grid shadings. + * @param: see &struct viif_l1_lsc + * @table_gr: Grid table for LSC of Gr component. + * This table is referred only when grid shading is used + * @table_r: Grid table for LSC of R component. + * This table is referred only when grid shading is used + * @table_b: Grid table for LSC of B component. + * This table is referred only when grid shading is used + * @table_gb: Grid table for LSC of Gb component. + * This table is referred only when grid shading is used + * + * The size of each table is 1536 Bytes (u16 * 768). + * Application should make sure that the table data is based on HW specification + * since this driver does not check the grid table. + */ +struct viif_l1_lsc_config { + __u32 enable; + struct viif_l1_lsc param; + __u16 table_gr[768]; + __u16 table_r[768]; + __u16 table_b[768]; + __u16 table_gb[768]; +}; + +/** + * enum viif_l1_demosaic_mode - L1ISP demosaic modeenum viif_l1_demosaic_mode + * + * @VIIF_L1_DEMOSAIC_ACPI: Toshiba ACPI algorithm + * @VIIF_L1_DEMOSAIC_DMG: DMG algorithm + */ +enum viif_l1_demosaic_mode { + VIIF_L1_DEMOSAIC_ACPI = 0, + VIIF_L1_DEMOSAIC_DMG = 1, +}; + +/** + * struct viif_l1_color_matrix_correction - L1ISP color matrix correction + * parameters for &struct viif_l1_main_process_config + * @coef_rmg_min: (R-G) Minimum coefficient + * @coef_rmg_max: (R-G) Maximum coefficient + * @coef_rmb_min: (R-B) Minimum coefficient + * @coef_rmb_max: (R-B) Maximum coefficient + * @coef_gmr_min: (G-R) Minimum coefficient + * @coef_gmr_max: (G-R) Maximum coefficient + * @coef_gmb_min: (G-B) Minimum coefficient + * @coef_gmb_max: (G-B) Maximum coefficient + * @coef_bmr_min: (B-R) Minimum coefficient + * @coef_bmr_max: (B-R) Maximum coefficient + * @coef_bmg_min: (B-G) Minimum coefficient + * @coef_bmg_max: (B-G) Maximum coefficient + * @dst_minval: Minimum value of output pixel. Range: [0..0xffff]. + * @reserved: padding field + * + * The range, accuracy and restriction of each coefficient are: + * + * - Range: [-32768..32767] + * - Accuracy: 1/4096 + * - Restriction: coef_xxx_min <= coef_xxx_max + */ +struct viif_l1_color_matrix_correction { + __s16 coef_rmg_min; + __s16 coef_rmg_max; + __s16 coef_rmb_min; + __s16 coef_rmb_max; + __s16 coef_gmr_min; + __s16 coef_gmr_max; + __s16 coef_gmb_min; + __s16 coef_gmb_max; + __s16 coef_bmr_min; + __s16 coef_bmr_max; + __s16 coef_bmg_min; + __s16 coef_bmg_max; + __u16 dst_minval; + __u16 reserved; +}; + +/** + * struct viif_l1_main_process_config - L1ISP parameter for main process + * + * @demosaic_mode: &enum viif_l1_demosaic_mode value. Sets demosaic mode. + * @damp_lsbsel: Clipping range of output pixel value to AWB adjustment function. Range: [0..15]. + * @colormat_enable: set 1 to enable color matrix correction, 0 to disable. + * @dst_maxval: Maximum value of output pixel. Range: [0..0xffffff]. + * Applicable to output of each process (digital amplifier, + * demosaicing and color matrix correction) in L1ISP Main process. + * @colormat_param: see &struct viif_l1_color_matrix_correction + * + * L1ISP main process is composed of: + * - demosaicing + * - color matrix correction + */ +struct viif_l1_main_process_config { + __u32 demosaic_mode; + __u32 damp_lsbsel; + __u32 colormat_enable; + __u32 dst_maxval; + struct viif_l1_color_matrix_correction colormat_param; +}; + +/** + * enum viif_l1_awb_mag - L1ISP signal magnification before AWB adjustment + * + * @VIIF_L1_AWB_ONE_SECOND: x 1/2 + * @VIIF_L1_AWB_X1: 1 times + * @VIIF_L1_AWB_X2: 2 times + * @VIIF_L1_AWB_X4: 4 times + */ +enum viif_l1_awb_mag { + VIIF_L1_AWB_ONE_SECOND = 0, + VIIF_L1_AWB_X1 = 1, + VIIF_L1_AWB_X2 = 2, + VIIF_L1_AWB_X4 = 3, +}; + +/** + * enum viif_l1_awb_area_mode - L1ISP AWB detection target area + * + * @VIIF_L1_AWB_AREA_MODE0: only center area + * @VIIF_L1_AWB_AREA_MODE1: center area when uv is in square gate + * @VIIF_L1_AWB_AREA_MODE2: all area except center area + * @VIIF_L1_AWB_AREA_MODE3: all area + */ +enum viif_l1_awb_area_mode { + VIIF_L1_AWB_AREA_MODE0 = 0, + VIIF_L1_AWB_AREA_MODE1 = 1, + VIIF_L1_AWB_AREA_MODE2 = 2, + VIIF_L1_AWB_AREA_MODE3 = 3, +}; + +/** + * enum viif_l1_awb_restart_cond - L1ISP AWB adjustment restart conditions + * + * @VIIF_L1_AWB_RESTART_NO: no restart + * @VIIF_L1_AWB_RESTART_128FRAME: restart after 128 frame + * @VIIF_L1_AWB_RESTART_64FRAME: restart after 64 frame + * @VIIF_L1_AWB_RESTART_32FRAME: restart after 32 frame + * @VIIF_L1_AWB_RESTART_16FRAME: restart after 16 frame + * @VIIF_L1_AWB_RESTART_8FRAME: restart after 8 frame + * @VIIF_L1_AWB_RESTART_4FRAME: restart after 4 frame + * @VIIF_L1_AWB_RESTART_2FRAME: restart after 2 frame + */ +enum viif_l1_awb_restart_cond { + VIIF_L1_AWB_RESTART_NO = 0, + VIIF_L1_AWB_RESTART_128FRAME = 1, + VIIF_L1_AWB_RESTART_64FRAME = 2, + VIIF_L1_AWB_RESTART_32FRAME = 3, + VIIF_L1_AWB_RESTART_16FRAME = 4, + VIIF_L1_AWB_RESTART_8FRAME = 5, + VIIF_L1_AWB_RESTART_4FRAME = 6, + VIIF_L1_AWB_RESTART_2FRAME = 7, +}; + +/** + * struct viif_l1_awb - L1ISP AWB adjustment parameters + * for &struct viif_l1_awb_config + * @awhb_ygate_sel: 1:Enable/0:Disable to fix Y value at YUV conversion + * @awhb_ygate_data: Y value when Y value is fixed. Range: [64, 128, 256, 512]. + * @awhb_cgrange: &enum viif_l1_awb_mag value. + * Signal output magnification ratio before AWB adjustment. + * @awhb_ygatesw: 1:Enable/0:Disable settings of luminance gate + * @awhb_hexsw: 1:Enable/0:Disable settings of hexa-gate + * @awhb_areamode: &enum viif_l1_awb_area_mode value. + * Final selection of accumulation area for detection target area. + * @awhb_area_hsize: Horizontal size per block in central area. + * Range: [1..(Input image width -8)/8]. + * @awhb_area_vsize: Vertical size per block in central area. + * Range: [1..(Input image height -4)/8]. + * @awhb_area_hofs: Horizontal offset of block [0] in central area. + * Range: [0..(Input image width -9)]. + * @awhb_area_vofs: Vertical offset of block [0] in central area. + * Range: [0..(Input image height -5)]. + * @awhb_area_maskh: Setting 1:Enable/0:Disable of accumulated selection. + * Each bit corresponds to the following: + * [31:0] = { + * (7, 3),(6, 3),(5, 3),(4, 3),(3, 3),(2, 3),(1, 3),(0, 3), + * (7, 2),(6, 2),(5, 2),(4, 2),(3, 2),(2, 2),(1, 2),(0, 2), + * (7, 1),(6, 1),(5, 1),(4, 1),(3, 1),(2, 1),(1, 1),(0, 1), + * (7, 0),(6, 0),(5, 0),(4, 0),(3, 0),(2, 0),(1, 0),(0, 0)} + * @awhb_area_maskl: Setting 1:Enable/0:Disable of accumulated selection. + * Each bit corresponds to the following: + * [31:0] = { + * (7, 7),(6, 7),(5, 7),(4, 7),(3, 7),(2, 7),(1, 7),(0, 7), + * (7, 6),(6, 6),(5, 6),(4, 6),(3, 6),(2, 6),(1, 6),(0, 6), + * (7, 5),(6, 5),(5, 5),(4, 5),(3, 5),(2, 5),(1, 5),(0, 5), + * (7, 4),(6, 4),(5, 4),(4, 4),(3, 4),(2, 4),(1, 4),(0, 4)} + * @awhb_sq_sw: 1:Enable/0:Disable each square gate + * @awhb_sq_pol: 1:Enable/0:Disable to add accumulated gate for each square gate + * @awhb_bycut0p: U upper end value. Range: [0..127]. + * @awhb_bycut0n: U lower end value. Range: [0..127]. + * @awhb_rycut0p: V upper end value. Range: [0..127]. + * @awhb_rycut0n: V lower end value. Range: [0..127]. + * @awhb_rbcut0h: V-axis intercept upper end. Range: [-127..127]. + * @awhb_rbcut0l: V-axis intercept lower end. Range: [-127..127]. + * @awhb_bycut_h: U direction center value of each square gate. Range: [-127..127]. + * @awhb_bycut_l: U direction width of each square gate. Range: [0..127]. + * @awhb_rycut_h: V direction center value of each square gate. Range: [-127..127]. + * @awhb_rycut_l: V direction width of each square gate. Range: [0..127]. + * @awhb_awbsftu: U gain offset. Range: [-127..127]. + * @awhb_awbsftv: V gain offset. Range: [-127..127]. + * @awhb_awbhuecor: 1:Enable/0:Disable setting of color correlation retention function + * @awhb_awbspd: UV convergence speed multiplier. Range: [0..15] (0 means "stop"). + * @awhb_awbulv: U convergence point level. Range: [0..31]. + * @awhb_awbvlv: V convergence point level. Range: [0..31]. + * @awhb_awbondot: Accumulation operation stop pixel count threshold. Range: [0..1023]. + * @awhb_awbfztim: &enum viif_l1_awb_restart_cond value. Condition to restart AWB process. + * @awhb_wbgrmax: B gain adjustment range (Width from center to upper limit). + * Range: [0..255], Accuracy: 1/64. + * @awhb_wbgbmax: R gain adjustment range (Width from center to upper limit). + * Range: [0..255], Accuracy: 1/64. + * @awhb_wbgrmin: B gain adjustment range (Width from center to lower limit). + * Range: [0..255], Accuracy: 1/64. + * @awhb_wbgbmin: R gain adjustment range (Width from center to lower limit). + * Range: [0..255], Accuracy: 1/64. + * @awhb_ygateh: Luminance gate maximum value. Range: [0..255]. + * @awhb_ygatel: Luminance gate minimum value. Range: [0..255]. + * @awhb_awbwait: Number of restart frames after UV convergence freeze. Range: [0..255]. + * @reserved: padding field + */ +struct viif_l1_awb { + __u32 awhb_ygate_sel; + __u32 awhb_ygate_data; + __u32 awhb_cgrange; + __u32 awhb_ygatesw; + __u32 awhb_hexsw; + __u32 awhb_areamode; + __u32 awhb_area_hsize; + __u32 awhb_area_vsize; + __u32 awhb_area_hofs; + __u32 awhb_area_vofs; + __u32 awhb_area_maskh; + __u32 awhb_area_maskl; + __u32 awhb_sq_sw[3]; + __u32 awhb_sq_pol[3]; + __u32 awhb_bycut0p; + __u32 awhb_bycut0n; + __u32 awhb_rycut0p; + __u32 awhb_rycut0n; + __s32 awhb_rbcut0h; + __s32 awhb_rbcut0l; + __s32 awhb_bycut_h[3]; + __u32 awhb_bycut_l[3]; + __s32 awhb_rycut_h[3]; + __u32 awhb_rycut_l[3]; + __s32 awhb_awbsftu; + __s32 awhb_awbsftv; + __u32 awhb_awbhuecor; + __u32 awhb_awbspd; + __u32 awhb_awbulv; + __u32 awhb_awbvlv; + __u32 awhb_awbondot; + __u32 awhb_awbfztim; + __u8 awhb_wbgrmax; + __u8 awhb_wbgbmax; + __u8 awhb_wbgrmin; + __u8 awhb_wbgbmin; + __u8 awhb_ygateh; + __u8 awhb_ygatel; + __u8 awhb_awbwait; + __u8 reserved; +}; + +/** + * struct viif_l1_awb_config - L1ISP parameter for automatic white balance + * + * @enable: set 1 to enable AWB , 0 to disable + * @awhb_wbmrg: White balance adjustment R gain. Range: [64..1023], Accuracy: 1/256. + * @awhb_wbmgg: White balance adjustment G gain. Range: [64..1023], Accuracy: 1/256. + * @awhb_wbmbg: White balance adjustment B gain. Range: [64..1023], Accuracy: 1/256. + * @param: a &struct viif_l1_awb instance + */ +struct viif_l1_awb_config { + __u32 enable; + __u32 awhb_wbmrg; + __u32 awhb_wbmgg; + __u32 awhb_wbmbg; + struct viif_l1_awb param; +}; + +/** + * enum viif_l1_hdrc_tone_type - L1ISP HDRC tone type + * + * @VIIF_L1_HDRC_TONE_USER: User Tone + * @VIIF_L1_HDRC_TONE_PRESET: Preset Tone + */ +enum viif_l1_hdrc_tone_type { + VIIF_L1_HDRC_TONE_USER = 0, + VIIF_L1_HDRC_TONE_PRESET = 1, +}; + +/** + * struct viif_l1_hdrc - L1ISP HDRC parameters for &struct viif_l1_hdrc_config + * @hdrc_ratio: Data width of input image. Range: [10..24] bits. + * @hdrc_pt_ratio: Preset Tone curve slope. Range: [0..13]. + * @hdrc_pt_blend: Preset Tone0 curve blend ratio. Range: [0..256], Accuracy: 1/256. + * @hdrc_pt_blend2: Preset Tone2 curve blend ratio. Range: [0..256], Accuracy: 1/256. + * @hdrc_tn_type: &enum viif_l1_hdrc_tone_type value. L1ISP HDRC tone type. + * @hdrc_utn_tbl: HDRC value of User Tone curve. Range: [0..0xffff]. + * @hdrc_flr_val: Constant flare value. Range: [0..0xffffff]. + * @hdrc_flr_adp: set 1 to enable dynamic flare measurement, 0 to disable. + * @hdrc_ybr_off: set 1 to turn OFF bilateral luminance filter, 0 to turn ON. + * @hdrc_orgy_blend: Blend settings of luminance correction data after HDRC + * and data before luminance correction. Range: [0..16] + * (0:Luminance correction 100%, 8:Luminance correction 50%, + * 16:Luminance correction 0%). + * @hdrc_pt_sat: Preset Tone saturation value. Range: [0..0xffff]. + * @reserved: padding field + * + * Restrictions for parameters + * + * - hdrc_pt_blend + hdrc_pt_blend2 <= 256 + * - input_image_height % 64 != {18, 20, 22, 24, 26} + * + * - only when dynamic flare control is enabled + * - note that the driver will not return error if this condition is not satisfied. + * + * - hdrc_utn_tbl[N] <= hdrc_utn_tbl[N+1] + * + * - note that the driver will not return error if this condition is not satisfied. + */ +struct viif_l1_hdrc { + __u32 hdrc_ratio; + __u32 hdrc_pt_ratio; + __u32 hdrc_pt_blend; + __u32 hdrc_pt_blend2; + __u32 hdrc_tn_type; + __u16 hdrc_utn_tbl[20]; + __u32 hdrc_flr_val; + __u32 hdrc_flr_adp; + __u32 hdrc_ybr_off; + __u32 hdrc_orgy_blend; + __u16 hdrc_pt_sat; + __u16 reserved; +}; + +/** + * struct viif_l1_hdrc_config - L1ISP parameter for HDR compression + * + * @enable: set 1 to enable HDR compression, 0 to disable + * @hdrc_thr_sft_amt: Amount of right shift in through mode (HDRC disabled). Range: [0..8]. + * Should be 0 if HDRC is enabled + * @param: HDR compression parameter; see &struct viif_l1_hdrc + */ +struct viif_l1_hdrc_config { + __u32 enable; + __u32 hdrc_thr_sft_amt; + struct viif_l1_hdrc param; +}; + +/** + * struct viif_l1_hdrc_ltm_config - L1ISP parameter for HDR compression local tone mapping + * + * @tnp_max: Tone blend rate maximum value of LTM function. + * Range: [0..4194303], Accuracy: 1/64. Set 0 to turn off LTM function. + * @tnp_mag: Intensity adjustment of LTM function. Range: [0..16383], Accuracy: 1/64. + * @tnp_fil: Smoothing filter coefficient. Range: [0..255]. + * @reserved: padding field + * + * Restriction: (tmp_fil[1] + tnp_fil[2] + tnp_fil[3] + tnp_fil[4]) * 2 + tnp_fil[0] = 1024 + */ +struct viif_l1_hdrc_ltm_config { + __u32 tnp_max; + __u32 tnp_mag; + __u8 tnp_fil[5]; + __u8 reserved[3]; +}; + +/** + * struct viif_l1_gamma - L1ISP gamma correction parameters + * for &struct viif_l1_gamma_config + * @gam_p: Luminance value after gamma correction. Range: [0..8191]. + * @blkadj: Black level adjustment value after gamma correction. Range: [0..65535]. + * @reserved: padding field + */ +struct viif_l1_gamma { + __u16 gam_p[44]; + __u16 blkadj; + __u16 reserved; +}; + +/** + * struct viif_l1_gamma_config - L1ISP parameter for gamma correction + * @enable: set 1 to enable gamma correction at L1ISP, 0 to disable. + * @param: see &struct viif_l1_gamma. + */ +struct viif_l1_gamma_config { + __u32 enable; + struct viif_l1_gamma param; +}; + +/** + * struct viif_l1_nonlinear_contrast - L1ISP VPRO non-linear contrast parameters + * for &struct viif_l1_img_quality_adjustment_config + * @blk_knee: Black side peak luminance value. Range: [0..0xffff]. + * @wht_knee: White side peak luminance value. Range: [0..0xffff]. + * @blk_cont: Black side slope + * @wht_cont: White side slope + * @reserved: padding field + * + * Range, Accuracy and Index for {blk,wht}_cont is: + * + * - Range: [0..255] + * - Accuracy: 1/256 + * + * - Index + * + * - 0: the value at AG minimum + * - 1: the value at AG less than 128 + * - 2: the value at AG equal to or more than 128 + */ +struct viif_l1_nonlinear_contrast { + __u16 blk_knee; + __u16 wht_knee; + __u8 blk_cont[3]; + __u8 wht_cont[3]; + __u8 reserved[2]; +}; + +/** + * struct viif_l1_lum_noise_reduction - L1ISP VPRO luminance noise reduction parameters + * for &struct viif_l1_img_quality_adjustment_config + * @gain_min: Minimum value of extracted noise gain. Range: [0..0xffff], Accuracy: 1/256 + * @gain_max: Maximum value of extracted noise gain. Range: [0..0xffff], Accuracy: 1/256 + * @lim_min: Minimum value of extracted noise limit. Range: [0..0xffff] + * @lim_max: Maximum value of extracted noise limit. Range: [0..0xffff] + * + * Constraint: "gain_min <= gain_max" and "lim_min <= lim_max" + * + */ +struct viif_l1_lum_noise_reduction { + __u16 gain_min; + __u16 gain_max; + __u16 lim_min; + __u16 lim_max; +}; + +/** + * struct viif_l1_edge_enhancement - L1ISP VPRO edge enhancement parameters + * for &struct viif_l1_img_quality_adjustment_config + * @gain_min: Extracted edge gain minimum value. Range: [0..0xffff], Accuracy: 1/256 + * @gain_max: Extracted edge gain maximum value. Range: [0..0xffff], Accuracy: 1/256 + * @lim_min: Extracted edge limit minimum value. Range: [0..0xffff] + * @lim_max: Extracted edge limit maximum value. Range: [0..0xffff] + * @coring_min: Extracted edge coring threshold minimum value. Range: [0..0xffff] + * @coring_max: Extracted edge coring threshold maximum value. Range: [0..0xffff] + * + * Constraint: "gain_min <= gain_max" and "lim_min <= lim_max" and "coring_min <= coring_max" + */ +struct viif_l1_edge_enhancement { + __u16 gain_min; + __u16 gain_max; + __u16 lim_min; + __u16 lim_max; + __u16 coring_min; + __u16 coring_max; +}; + +/** + * struct viif_l1_uv_suppression - L1ISP VPRO UV suppression parameters + * for &struct viif_l1_img_quality_adjustment_config + * @bk_mp: Black side slope. Range: [0..0x3fff], Accuracy: 1/16384 + * @black: Minimum black side gain. Range: [0..0x3fff], Accuracy: 1/16384 + * @wh_mp: White side slope. Range: [0..0x3fff], Accuracy: 1/16384 + * @white: Minimum white side gain. Range: [0..0x3fff], Accuracy: 1/16384 + * @bk_slv: Black side intercept. Range: [0..0xffff] + * @wh_slv: White side intercept. Range: [0..0xffff] + * + * Constraint: bk_slb < wh_slv + */ +struct viif_l1_uv_suppression { + __u32 bk_mp; + __u32 black; + __u32 wh_mp; + __u32 white; + __u16 bk_slv; + __u16 wh_slv; +}; + +/** + * struct viif_l1_coring_suppression - L1ISP VPRO coring suppression parameters + * for &struct viif_l1_img_quality_adjustment_config + * @lv_min: Minimum coring threshold. Range: [0..0xffff] + * @lv_max: Maximum coring threshold. Range: [0..0xffff] + * @gain_min: Minimum gain. Range: [0..0xffff], Accuracy: 1/65536 + * @gain_max: Maximum gain. Range: [0..0xffff], Accuracy: 1/65536 + * + * Constraint: "lv_min <= lv_max" and "gain_min <= gain_max" + */ +struct viif_l1_coring_suppression { + __u16 lv_min; + __u16 lv_max; + __u16 gain_min; + __u16 gain_max; +}; + +/** + * struct viif_l1_edge_suppression - L1ISP VPRO edge suppression parameters + * for &struct viif_l1_img_quality_adjustment_config + * @gain: Gain of edge color suppression. Range: [0..0xffff], Accuracy: 1/256 + * @lim: Limiter threshold of edge color suppression. Range: [0..15] + */ +struct viif_l1_edge_suppression { + __u16 gain; + __u16 lim; +}; + +/** + * struct viif_l1_color_level - L1ISP VPRO color level parameters + * for &struct viif_l1_img_quality_adjustment_config + * @cb_gain: U component gain + * @cr_gain: V component gain + * @cbr_mgain_min: UV component gain + * @cbp_gain_max: Positive U component gain + * @cbm_gain_max: Negative V component gain + * @crp_gain_max: Positive U component gain + * @crm_gain_max: Negative V component gain + * + * Range and Accuracy of parameters are: + * + * - Range: [0..0xfff] + * - Accuracy: 1/2048 + */ +struct viif_l1_color_level { + __u32 cb_gain; + __u32 cr_gain; + __u32 cbr_mgain_min; + __u32 cbp_gain_max; + __u32 cbm_gain_max; + __u32 crp_gain_max; + __u32 crm_gain_max; +}; + +/* MASKs for viif_l1_img_quality_adjustment_config::enable */ +#define VIIF_L1_IQA_NONLINEAR_CONTRAST_EN_MASK BIT(0) +#define VIIF_L1_IQA_LUM_NOISE_REDUCTION_EN_MASK BIT(1) +#define VIIF_L1_IQA_EDGE_ENHANCEMENT_EN_MASK BIT(2) +#define VIIF_L1_IQA_UV_SUPPRESSION_EN_MASK BIT(3) +#define VIIF_L1_IQA_CORING_SUPPRESSION_EN_MASK BIT(4) +#define VIIF_L1_IQA_EDGE_SUPPRESSION_EN_MASK BIT(5) +#define VIIF_L1_IQA_COLOR_LEVEL_EN_MASK BIT(6) +#define VIIF_L1_IQA_COLOR_NOISE_REDUCTION_EN_MASK BIT(7) + +/** + * struct viif_l1_img_quality_adjustment_config - L1ISP parameter + * for image quality adjustment (VPRO) + * + * @enable: bit vector; set 1 to enable each function, 0 to disable. + * + * - bit[0]: nonlinear contrast + * - bit[1]: luminance noise reduction + * - bit[2]: edge enhancement + * - bit[3]: uv suppression + * - bit[4]: coring suppression + * - bit[5]: edge suppression + * - bit[6]: color level + * - bit[7]: color noise reduction + * + * @nonlinear_contrast: see &struct viif_l1_nonlinear_contrast; controlled by bit0 of enable. + * @lum_noise_reduction: see &struct viif_l1_lum_noise_reduction; controlled by bit1 of enable. + * @edge_enhancement: see &struct viif_l1_edge_enhancement; controlled by bit2 of enable. + * @uv_suppression: see &struct viif_l1_uv_suppression: controlled by bit3 of enable. + * @coring_suppression: see &struct viif_l1_coring_suppression; controlled by bit4 of enable. + * @edge_suppression: see &struct viif_l1_edge_suppression; controlled by bit5 of enable. + * @color_level: see &struct viif_l1_color_level; controlled by bit6 of enable. + * @coef_cb: Cb coefficient used in RGB to YUV conversion. + * Range: [0..0xffff], Accuracy: 1/65536 + * @coef_cr: Cr coefficient used in RGB to YUV conversion. + * Range: [0..0xffff], Accuracy: 1/65536 + * @brightness: Brightness adjustment value. Range: [-32768..32767] (0 to turn off) + * @linear_contrast: Linear contrast adjustment value. + * Range: [0..0xff], Accuracy: 1/128 (128 to turn off) + * @reserved: padding field + * + * The VPRO feature provides following functions: + * - luminance adjustment: + * - brightness adjustment + * - linear contrast adjusment + * - nonlinear contrast adjustment + * - luminance noise reduction + * - edge enhancement + * - chroma adjustment: + * - chroma suppression + * - color level adjustment + * - chroma noise reduction + * - coring suppression + * - edge chroma suppression + * - color noise reduction + */ +struct viif_l1_img_quality_adjustment_config { + __u32 enable; + struct viif_l1_nonlinear_contrast nonlinear_contrast; + struct viif_l1_lum_noise_reduction lum_noise_reduction; + struct viif_l1_edge_enhancement edge_enhancement; + struct viif_l1_uv_suppression uv_suppression; + struct viif_l1_coring_suppression coring_suppression; + struct viif_l1_edge_suppression edge_suppression; + struct viif_l1_color_level color_level; + __u16 coef_cb; + __u16 coef_cr; + __s16 brightness; + __u8 linear_contrast; + __u8 reserved; +}; + +/** + * struct viif_l1_avg_lum_generation_config - L1ISP parameter for calculating average luminance + * + * @enable: set 1 to enable aggregation of AVG LUM, 0 to disable + * @aexp_start_x: horizontal position of block 0. Range: [0.."width of input image - 1"] + * @aexp_start_y: vertical position of block 0. Range: [0.."height of input image - 1"] + * @aexp_block_width: width of one block. + * Range: [64.."width of input image"] (Should be multiple of 64) + * @aexp_block_height: height of one block. + * Range: [64.."height of input image"] (Should be multiple of 64) + * @aexp_weight: weight of each block. Range: [0..3]. + * Nested indices are: [y position][x position]. + * @aexp_satur_ratio: threshold to judge whether saturated block or not. Range: [0..256] + * @aexp_black_ratio: threshold to judge whether black block or not. Range: [0..256] + * @aexp_satur_level: threshold to judge whether saturated pixel or not. Range: [0x0..0xffffff] + * @aexp_ave4linesy: vertical position of the initial line + * for 4-lines average luminance. + * Range: [0.."height of input image - 4"] + */ +struct viif_l1_avg_lum_generation_config { + __u32 enable; + __u32 aexp_start_x; + __u32 aexp_start_y; + __u32 aexp_block_width; + __u32 aexp_block_height; + __u32 aexp_weight[8][8]; + __u32 aexp_satur_ratio; + __u32 aexp_black_ratio; + __u32 aexp_satur_level; + __u32 aexp_ave4linesy[4]; +}; + +/** + * enum viif_l2_undist_mode - L2ISP undistortion mode + * @VIIF_L2_UNDIST_POLY: polynomial mode + * @VIIF_L2_UNDIST_GRID: grid table mode + * @VIIF_L2_UNDIST_POLY_TO_GRID: polynomial, then grid table mode + * @VIIF_L2_UNDIST_GRID_TO_POLY: grid table, then polynomial mode + */ +enum viif_l2_undist_mode { + VIIF_L2_UNDIST_POLY = 0, + VIIF_L2_UNDIST_GRID = 1, + VIIF_L2_UNDIST_POLY_TO_GRID = 2, + VIIF_L2_UNDIST_GRID_TO_POLY = 3, +}; + +/** + * struct viif_l2_undist - L2ISP UNDIST parameters + * for &struct viif_l2_undist_config + * @through_mode: 1:enable or 0:disable through mode of undistortion + * @roi_mode: &enum viif_l2_undist_mode value. Sets L2ISP undistortion mode. + * @sensor_crop_ofs_h: Horizontal start position of sensor crop area. + * @sensor_crop_ofs_v: Vertical start position of sensor crop area. + * @norm_scale: Normalization coefficient for distance from center + * @valid_r_norm2_poly: Setting target area for polynomial correction + * @valid_r_norm2_grid: Setting target area for grid table correction + * @roi_write_area_delta: Error adjustment value of forward function and + * inverse function for pixel position calculation + * @poly_write_g_coef: 10th-order polynomial coefficient for G write pixel position calculation + * @poly_read_b_coef: 10th-order polynomial coefficient for B read pixel position calculation + * @poly_read_g_coef: 10th-order polynomial coefficient for G read pixel position calculation + * @poly_read_r_coef: 10th-order polynomial coefficient for R read pixel position calculation + * @grid_node_num_h: Number of horizontal grids + * @grid_node_num_v: Number of vertical grids + * @grid_patch_hsize_inv: Inverse pixel size between horizontal grids + * @grid_patch_vsize_inv: Inverse pixel size between vertical grids + * + * Range and Accuracy of parameters are: + * + * - sensor_crop_ofs_{h,v} + * + * - Range: [-4296..4296] + * - Accuracy: 1/2 + * + * - norm_scale + * + * - Range: [0..1677721] + * - Accuracy: 1/33554432 + * + * - valid_r_norm2_{poly,grid} + * + * - Range: [0..0x3ffffff] + * - Accuracy: 1/33554432 + * + * - roi_write_area_delta + * + * - Range: [0..0x7ff] + * - Accuracy: 1/1024 + * + * - poly_write_g_coef, poly_read_{b,g,r}_coef + * + * - Range: [-2147352576..2147352576] + * - Accuracy: 1/131072 + * + * - grid_node_num_{v.h} + * + * - Range: [16..64] + * + * - grid_patch_{hsize,vsize}_inv + * + * - Range: [0..0x7fffff] + * - Accuracy: 1/8388608 + */ +struct viif_l2_undist { + __u32 through_mode; + __u32 roi_mode[2]; + __s32 sensor_crop_ofs_h; + __s32 sensor_crop_ofs_v; + __u32 norm_scale; + __u32 valid_r_norm2_poly; + __u32 valid_r_norm2_grid; + __u32 roi_write_area_delta[2]; + __s32 poly_write_g_coef[11]; + __s32 poly_read_b_coef[11]; + __s32 poly_read_g_coef[11]; + __s32 poly_read_r_coef[11]; + __u32 grid_node_num_h; + __u32 grid_node_num_v; + __u32 grid_patch_hsize_inv; + __u32 grid_patch_vsize_inv; +}; + +/** + * struct viif_l2_undist_config - L2ISP parameter for undistortion + * + * @param: &struct viif_l2_undist + * @write_g: write-G grid table. + * @read_b: read-B grid table. + * @read_g: read-G grid table. + * @read_r: read-R grid table. + * @size: Table size in bytes. Range: [1024..8192] or 0. + * The value should be "grid_node_num_h * grid_node_num_v * 4". + * See also &struct viif_l2_undist. + * + * The tables are referred when param.roi_mode[] is + * either of VIIF_L2_UNDIST_GRID, VIIF_L2_UNDIST_POLY_TO_GRID, VIIF_L2_UNDIST_GRID_TO_POLY + * Application should make sure that the table data is based on HW specification + * since this driver does not check the contents of specified grid table. + */ +struct viif_l2_undist_config { + struct viif_l2_undist param; + __u8 write_g[8192]; + __u8 read_b[8192]; + __u8 read_g[8192]; + __u8 read_r[8192]; + __u32 size; +}; + +/** + * struct viif_l2_roi_config - L2ISP parameter for specifying ROI + * + * @roi_num: + * 1 when only capture path0 is activated, + * 2 when both capture path 0 and path 1 are activated. + * @roi_scale: Scale value for each ROI. Range: [32768..131072], Accuracy: 1/65536 + * @roi_scale_inv: Inverse scale value for each ROI. Range: [32768..131072], Accuracy: 1/65536 + * @corrected_wo_scale_hsize: Corrected image width for each ROI. Range: [128..8190] + * @corrected_wo_scale_vsize: Corrected image height for each ROI. Range: [128..4094] + * @corrected_hsize: Corrected and scaled image width for each ROI. Range: [128..8190] + * @corrected_vsize: Corrected and scaled image height for each ROI. Range: [128..4094] + */ +struct viif_l2_roi_config { + __u32 roi_num; + __u32 roi_scale[2]; + __u32 roi_scale_inv[2]; + __u32 corrected_wo_scale_hsize[2]; + __u32 corrected_wo_scale_vsize[2]; + __u32 corrected_hsize[2]; + __u32 corrected_vsize[2]; +}; + +/** enum viif_gamma_mode - Gamma correction mode + * + * @VIIF_GAMMA_COMPRESSED: compressed table mode + * @VIIF_GAMMA_LINEAR: linear table mode + */ +enum viif_gamma_mode { + VIIF_GAMMA_COMPRESSED = 0, + VIIF_GAMMA_LINEAR = 1, +}; + +/** + * struct viif_l2_gamma_config - L2ISP parameter for gamma correction + * + * @table_en: 6bit vector to enable gamma table; all 0 to disable gamma correction + * @vsplit: Line switching position of first table and second table. Range: [0..4094]. + * Should set 0 in case 0 is set to @enable + * @mode: &enum viif_gamma_mode value. + * Should set VIIF_GAMMA_COMPRESSED when 0 is set to @enable + * @table: gamma table for L2ISP gamma; 6 channels, each has __u16 typed 512 bytes. + * [0]: G/Y(1st table), [1]: G/Y(2nd table), [2]: B/U(1st table) + * [3]: B/U(2nd table), [4]: R/V(1st table), [5]: R/V(2nd table) + */ +struct viif_l2_gamma_config { + __u32 table_en; + __u32 vsplit; + __u32 mode; + __u16 table[6][256]; +}; + +/** + * struct viif_csi2rx_dphy_calibration_status - CSI2RX status of DPHY Calibration + * + * @term_cal_with_rext: Result of termination calibration with rext + * @clock_lane_offset_cal: Result of offset calibration of clock lane + * @data_lane0_offset_cal: Result of offset calibration of data lane0 + * @data_lane1_offset_cal: Result of offset calibration of data lane1 + * @data_lane2_offset_cal: Result of offset calibration of data lane2 + * @data_lane3_offset_cal: Result of offset calibration of data lane3 + * @data_lane0_ddl_tuning_cal: Result of digital delay line tuning calibration of data lane0 + * @data_lane1_ddl_tuning_cal: Result of digital delay line tuning calibration of data lane1 + * @data_lane2_ddl_tuning_cal: Result of digital delay line tuning calibration of data lane2 + * @data_lane3_ddl_tuning_cal: Result of digital delay line tuning calibration of data lane3 + * + * Possible returned values for each member are: + * + * - -EAGAIN: calibration is not done + * - -EIO: calibration was failed + * - 0; calibration is succeeded + */ +struct viif_csi2rx_dphy_calibration_status { + __s32 term_cal_with_rext; + __s32 clock_lane_offset_cal; + __s32 data_lane0_offset_cal; + __s32 data_lane1_offset_cal; + __s32 data_lane2_offset_cal; + __s32 data_lane3_offset_cal; + __s32 data_lane0_ddl_tuning_cal; + __s32 data_lane1_ddl_tuning_cal; + __s32 data_lane2_ddl_tuning_cal; + __s32 data_lane3_ddl_tuning_cal; +}; + +/** + * struct viif_csi2rx_err_status - CSI2RX status of errors + * + * @err_phy_fatal: D-PHY FATAL error. + * + * - bit[3]: Start of transmission error on DATA Lane3. + * - bit[2]: Start of transmission error on DATA Lane2. + * - bit[1]: Start of transmission error on DATA Lane1. + * - bit[0]: Start of transmission error on DATA Lane0. + * + * @err_pkt_fatal: Packet FATAL error. + * + * - bit[16]: Header ECC contains 2 errors, unrecoverable. + * - bit[3]: Checksum error detected on virtual channel 3. + * - bit[2]: Checksum error detected on virtual channel 2. + * - bit[1]: Checksum error detected on virtual channel 1. + * - bit[0]: Checksum error detected on virtual channel 0. + * + * @err_frame_fatal: Frame FATAL error. + * + * - bit[19]: Last received Frame, in virtual channel 3, has at least one CRC error. + * - bit[18]: Last received Frame, in virtual channel 2, has at least one CRC error. + * - bit[17]: Last received Frame, in virtual channel 1, has at least one CRC error. + * - bit[16]: Last received Frame, in virtual channel 0, has at least one CRC error. + * - bit[11]: Incorrect Frame Sequence detected in virtual channel 3. + * - bit[10]: Incorrect Frame Sequence detected in virtual channel 2. + * - bit[9]: Incorrect Frame Sequence detected in virtual channel 1. + * - bit[8]: Incorrect Frame Sequence detected in virtual channel 0. + * - bit[3]: Error matching Frame Start with Frame End for virtual channel 3. + * - bit[2]: Error matching Frame Start with Frame End for virtual channel 2. + * - bit[1]: Error matching Frame Start with Frame End for virtual channel 1. + * - bit[0]: Error matching Frame Start with Frame End for virtual channel 0. + * + * @err_phy: D-PHY error. + * + * - bit[19]: Escape Entry Error on Data Lane 3. + * - bit[18]: Escape Entry Error on Data Lane 2. + * - bit[17]: Escape Entry Error on Data Lane 1. + * - bit[16]: Escape Entry Error on Data Lane 0. + * - bit[3]: Start of Transmission Error on Data Lane 3 (synchronization can still be achieved). + * - bit[2]: Start of Transmission Error on Data Lane 2 (synchronization can still be achieved). + * - bit[1]: Start of Transmission Error on Data Lane 1 (synchronization can still be achieved). + * - bit[0]: Start of Transmission Error on Data Lane 0 (synchronization can still be achieved). + * + * @err_pkt: Packet error. + * + * - bit[19]: Header Error detected and corrected on virtual channel 3. + * - bit[18]: Header Error detected and corrected on virtual channel 2. + * - bit[17]: Header Error detected and corrected on virtual channel 1. + * - bit[16]: Header Error detected and corrected on virtual channel 0. + * - bit[3]: Unrecognized or unimplemented data type detected in virtual channel 3. + * - bit[2]: Unrecognized or unimplemented data type detected in virtual channel 2. + * - bit[1]: Unrecognized or unimplemented data type detected in virtual channel 1. + * - bit[0]: Unrecognized or unimplemented data type detected in virtual channel 0. + * + * @err_line: Line error. + * + * - bit[23]: Error in the sequence of lines for vc7 and dt7. + * - bit[22]: Error in the sequence of lines for vc6 and dt6. + * - bit[21]: Error in the sequence of lines for vc5 and dt5. + * - bit[20]: Error in the sequence of lines for vc4 and dt4. + * - bit[19]: Error in the sequence of lines for vc3 and dt3. + * - bit[18]: Error in the sequence of lines for vc2 and dt2. + * - bit[17]: Error in the sequence of lines for vc1 and dt1. + * - bit[16]: Error in the sequence of lines for vc0 and dt0. + * - bit[7]: Error matching Line Start with Line End for vc7 and dt7. + * - bit[6]: Error matching Line Start with Line End for vc6 and dt6. + * - bit[5]: Error matching Line Start with Line End for vc5 and dt5. + * - bit[4]: Error matching Line Start with Line End for vc4 and dt4. + * - bit[3]: Error matching Line Start with Line End for vc3 and dt3. + * - bit[2]: Error matching Line Start with Line End for vc2 and dt2. + * - bit[1]: Error matching Line Start with Line End for vc1 and dt1. + * - bit[0]: Error matching Line Start with Line End for vc0 and dt0. + */ +struct viif_csi2rx_err_status { + __u32 err_phy_fatal; + __u32 err_pkt_fatal; + __u32 err_frame_fatal; + __u32 err_phy; + __u32 err_pkt; + __u32 err_line; +}; + +/** + * struct viif_l1_info - L1ISP status of automatic white balance and average luminance + * + * @avg_lum_weight: weighted average luminance value at average luminance generation + * @avg_lum_block: average luminance of each block. + * Nested indices are: [y position][x position]. + * @avg_lum_four_line_lum: 4-lines average luminance. + * avg_lum_four_line_lum[n] corresponds to aexp_ave4linesy[n] + * @avg_satur_pixnum: the number of saturated pixel at average luminance generation + * @avg_black_pixnum: the number of black pixel at average luminance generation + * @awb_ave_u: U average value of AWB adjustment + * @awb_ave_v: V average value of AWB adjustment + * @awb_accumulated_pixel: Accumulated pixel count of AWB adjustment + * @awb_gain_r: R gain used in the next frame of AWB adjustment + * @awb_gain_g: G gain used in the next frame of AWB adjustment + * @awb_gain_b: B gain used in the next frame of AWB adjustment + * @awb_status_u: boolean value of U convergence state of AWB adjustment + * (0: not-converged, 1: converged) + * @awb_status_v: boolean value of V convergence state of AWB adjustment + * (0: not-converged, 1: converged) + * @reserved: padding field + */ +struct viif_l1_info { + __u32 avg_lum_weight; + __u32 avg_lum_block[8][8]; + __u32 avg_lum_four_line_lum[4]; + __u32 avg_satur_pixnum; + __u32 avg_black_pixnum; + __u32 awb_ave_u; + __u32 awb_ave_v; + __u32 awb_accumulated_pixel; + __u32 awb_gain_r; + __u32 awb_gain_g; + __u32 awb_gain_b; + __u8 awb_status_u; + __u8 awb_status_v; + __u8 reserved[2]; +}; + +/** + * struct viif_isp_capture_status - L1ISP status of automatic white balance and average luminance + * @l1_info: L1ISP AWB information. Refer to &struct viif_l1_info + */ +struct viif_isp_capture_status { + struct viif_l1_info l1_info; +}; + +/** + * struct viif_reported_errors - VIIF status of errors + * + * @main: error flag value for capture device 0 and 1 + * + * - bit[24]: VSync generator error + * - bit[20]: L1ISP input size inconsistency + * - bit[19]: L1ISP output size inconsistency + * - bit[18]: L1ISP ABPC table transfer error + * - bit[17]: L1ISP LSSC table transfer error + * - bit[11]: L2ISP grid table transfer error + * - bit[9]: L2ISP POST1 table transfer error + * - bit[8]: L2ISP POST0 table transfer error + * - bit[0]: L2ISP size error + * + * @sub: error flag value for capture device 2 + * + * - bit[24]: VSync generator error + * - bit[0]: data transfer error + * + * @csi2rx: error flag value for CSI2 receiver; + * see also &struct viif_csi2_err_status for detailed cause + * + * - bit[18]: Line error + * - bit[17]: Packet error + * - bit[16]: PHY error + * - bit[2]: Frame fatal error + * - bit[1]: Packet fatal error + * - bit[0]: PHY fatal error + * + */ +struct viif_reported_errors { + __u32 main; + __u32 sub; + __u32 csi2rx; +}; + +/*==========Definitions for Param-buffer based I/F ============*/ +#define VISCONTI_VIIF_CFG_ISP_L1_INPUT_MODE (1U << 0) +#define VISCONTI_VIIF_CFG_ISP_L1_RGB_TO_Y_COEF (1U << 1) +#define VISCONTI_VIIF_CFG_ISP_L1_AG_MODE (1U << 2) +#define VISCONTI_VIIF_CFG_ISP_L1_AG (1U << 3) +#define VISCONTI_VIIF_CFG_ISP_L1_HDRE (1U << 4) +#define VISCONTI_VIIF_CFG_ISP_L1_IMG_EXTRACTION (1U << 5) +#define VISCONTI_VIIF_CFG_ISP_L1_DPC (1U << 6) +#define VISCONTI_VIIF_CFG_ISP_L1_PRESET_WHITE_BALANCE (1U << 7) +#define VISCONTI_VIIF_CFG_ISP_L1_RAW_COLOR_NOISE_REDUCTION (1U << 8) +#define VISCONTI_VIIF_CFG_ISP_L1_HDRS (1U << 9) +#define VISCONTI_VIIF_CFG_ISP_L1_BLACK_LEVEL_CORRECTION (1U << 10) +#define VISCONTI_VIIF_CFG_ISP_L1_LSC (1U << 11) +#define VISCONTI_VIIF_CFG_ISP_L1_MAIN_PROCESS (1U << 12) +#define VISCONTI_VIIF_CFG_ISP_L1_AWB (1U << 13) +#define VISCONTI_VIIF_CFG_ISP_L1_LOCK_AWB_GAIN (1U << 14) +#define VISCONTI_VIIF_CFG_ISP_L1_HDRC (1U << 15) +#define VISCONTI_VIIF_CFG_ISP_L1_HDRC_LTM (1U << 16) +#define VISCONTI_VIIF_CFG_ISP_L1_GAMMA (1U << 17) +#define VISCONTI_VIIF_CFG_ISP_L1_IMG_QUALITY_ADJUSTMENT (1U << 18) +#define VISCONTI_VIIF_CFG_ISP_L1_AVG_LUM_GENERATION (1U << 19) +#define VISCONTI_VIIF_CFG_ISP_L2_UNDIST (1U << 20) +#define VISCONTI_VIIF_CFG_ISP_L2_ROI (1U << 21) +#define VISCONTI_VIIF_CFG_ISP_L2_GAMMA_POST0 (1U << 22) +#define VISCONTI_VIIF_CFG_ISP_L2_GAMMA_POST1 (1U << 23) + +#define VISCONTI_VIIF_STAT_CSI2RX_CALIBRATION (1U << 0) +#define VISCONTI_VIIF_STAT_CSI2RX_ERR_STATUS (1U << 1) +#define VISCONTI_VIIF_STAT_LAST_CAPTURE_STATUS (1U << 2) +#define VISCONTI_VIIF_STAT_L1_INFO (1U << 3) +#define VISCONTI_VIIF_STAT_ERRORS (1U << 4) + +/** + * struct visconti_viif_isp_config - Visconti VIIF ISP parameters metadata + * + * @update_cfg: each bit specifies whether a feature should be updated + * @l1_input_mode: input format and preprocessing mode + * @l1_rgb_to_y_coef: coefficient to yield Y from RGB + * @l1_ag_mode: rules to derive analog gains for each feature + * @l1_ag: base analog gain + * @l1_hdre: parameter for HDR expansion + * @l1_img_extraction: parameter for image extraction + * @l1_dpc: parameter for defect pixel correction + * @l1_preset_white_balance: parameter for preset white balance + * @l1_raw_color_noise_reduction: parameter for raw color noise reduction + * @l1_hdrs: parameter for HDR synthesis + * @l1_black_level_correction: parameter for black level correction + * @l1_lsc: parameter for lens shading correction + * @l1_main_process: parameter for demosaicing and color matrix + * @l1_awb: parameter for automatic white balance + * @lock_awb_gain: 0 to run AWB, 1 to lock AWB gain + * @l1_hdrc: parameter for HDR compression + * @l1_hdrc_ltm: parameter for HDR compression local tone mapping + * @l1_gamma: parameter for L1ISP gamma correction + * @l1_img_quality_adjustment: parameter for image quality adjustment (VPRO) + * @l1_avg_lum_generation: parameter for calculating average luminance + * @l2_undist: parameter for undistortion + * @l2_roi: parameter for l2 ROI and scaling + * @l2_gamma_post0: parameter for L2ISP POST0 gamma correction + * @l2_gamma_post1: parameter for L2ISP POST1 gamma correction + */ +struct visconti_viif_isp_config { + __u32 update_cfg; + + struct viif_l1_input_mode_config l1_input_mode; + struct viif_l1_rgb_to_y_coef_config l1_rgb_to_y_coef; + struct viif_l1_ag_mode_config l1_ag_mode; + struct viif_l1_ag_config l1_ag; + struct viif_l1_hdre_config l1_hdre; + struct viif_l1_img_extraction_config l1_img_extraction; + struct viif_l1_dpc_config l1_dpc; + struct viif_l1_preset_white_balance_config l1_preset_white_balance; + struct viif_l1_raw_color_noise_reduction_config l1_raw_color_noise_reduction; + struct viif_l1_hdrs_config l1_hdrs; + struct viif_l1_black_level_correction_config l1_black_level_correction; + struct viif_l1_lsc_config l1_lsc; + struct viif_l1_main_process_config l1_main_process; + struct viif_l1_awb_config l1_awb; + __u32 lock_awb_gain; + struct viif_l1_hdrc_config l1_hdrc; + struct viif_l1_hdrc_ltm_config l1_hdrc_ltm; + struct viif_l1_gamma_config l1_gamma; + struct viif_l1_img_quality_adjustment_config l1_img_quality_adjustment; + struct viif_l1_avg_lum_generation_config l1_avg_lum_generation; + struct viif_l2_undist_config l2_undist; + struct viif_l2_roi_config l2_roi; + struct viif_l2_gamma_config l2_gamma_post0; + struct viif_l2_gamma_config l2_gamma_post1; +}; + +/** + * struct visconti_viif_isp_stat - Visconti VIIF ISP status metadata + * + * @stat_type: each bit specifies whether information is available + * @csi2rx_dphy_calibration: CSI2RX calibration status + * @csi2rx_err: CSI2RX error status + * @isp_capture: L1ISP status of whitebalance and average luminance + * @errors: VIIF error status + */ +struct visconti_viif_isp_stat { + __u32 stat_type; + + struct viif_csi2rx_dphy_calibration_status csi2rx_dphy_calibration; + struct viif_csi2rx_err_status csi2rx_err; + struct viif_isp_capture_status isp_capture; + struct viif_reported_errors errors; +}; + +#endif /* __UAPI_VISCONTI_VIIF_H_ */ From patchwork Mon Nov 25 09:21:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuji Ishikawa X-Patchwork-Id: 845869 Received: from mo-csw-fb.securemx.jp (mo-csw-fb1800.securemx.jp [210.130.202.159]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 67DD718FC72; Mon, 25 Nov 2024 10:36:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.130.202.159 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732531006; cv=none; b=YBpCO8K7VCIxUi0uLdrJbJ270khxBuWFp2Oa83QqVvbdQPxJbLK+cPrk/LuEXOV/DG/DXm5sHCMbeT9KWN0a5cBVWtga3jgw/9C0Pb6C84b1P9EplQlerosjbfNSnXityog1U4Wgfk15T47XR/yivljuU7L+4g1PUtRSKXr9LQM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732531006; c=relaxed/simple; bh=ScleyibcVOg/OUKMHFwooz3TaLJSJQMM0yH8uFfYE44=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=M05fTniUrJbItEdPdIG/NocB1YZOHpeTnjG8kN1ME7kQNkKR3nH1obYpl/l/8ZjWmYwpayB5zRU1IuLmlXqQUnP71pvF8qzwfCDby4k0elre8Ly7CgEWmsCQVXp5+mR8LQ6SJiSOSUX5+Ak2gHdmbi/qoBu+Vd3FngFA62IRrnk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp; spf=pass smtp.mailfrom=toshiba.co.jp; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b=qMWZu8IN; arc=none smtp.client-ip=210.130.202.159 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b="qMWZu8IN" Received: by mo-csw-fb.securemx.jp (mx-mo-csw-fb1800) id 4AP9Rmgi2993030; Mon, 25 Nov 2024 18:27:48 +0900 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=toshiba.co.jp; h=From:To:Cc :Subject:Date:Message-Id:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding;i=yuji2.ishikawa@toshiba.co.jp;s=key2.smx;t= 1732526840; x=1733736440; bh=ScleyibcVOg/OUKMHFwooz3TaLJSJQMM0yH8uFfYE44=; b=qMW Zu8INgQp3mRLZqbDO/yuazQrMoUPVmN596woptSTBJA+EdcmpPwCnoLiUoaZNzOb4sp9ar00Bo0AS CG/D1AmqZXiDvqRMxEQcYNh6zoWpwYDsYPqS4PNmpykjIblEsHtleUrdbhKbC3vbiWE+4YSiTIR6z Hs15xFCGstKIQWgtn5dnu+4ndjLhxkOCNILl7sGvkTTBoGpDem/8PjanYjzpXJgPFVLPcawl3o/BQ ZkcZIIBbp3OJX+FQkzMP1dMnT8/CTG9ZV3VHjmj6S9UJyFtwUTbc22gioJlv1IhG/HowR+jEFvJBw GSnkD2FOSaGplD7eO0He84qmchVQMLQ==; Received: by mo-csw.securemx.jp (mx-mo-csw1801) id 4AP9RJEP2549517; Mon, 25 Nov 2024 18:27:19 +0900 X-Iguazu-Qid: 2yAamusiv1EgGB3a5d X-Iguazu-QSIG: v=2; s=0; t=1732526838; q=2yAamusiv1EgGB3a5d; m=U5Ie+OlG3A8nDo1dfe5/6Kjz9LsJFeD2rX+r5xvZyVo= Received: from imx2-a.toshiba.co.jp (imx2-a.toshiba.co.jp [106.186.93.35]) by relay.securemx.jp (mx-mr1802) id 4AP9RHPI4129257 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 25 Nov 2024 18:27:17 +0900 X-SA-MID: 35004185 From: Yuji Ishikawa To: Laurent Pinchart , Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sakari Ailus , Hans Verkuil , Nobuhiro Iwamatsu , Yuji Ishikawa Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Subject: [PATCH v12 6/8] media: platform: visconti: Add streaming interface for ISP parameters and status Date: Mon, 25 Nov 2024 18:21:44 +0900 X-TSB-HOP2: ON Message-Id: <20241125092146.1561901-7-yuji2.ishikawa@toshiba.co.jp> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> References: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add support to Image Signal Processors of Visconti's Video Input Interface. This patch adds two streaming interfaces; one for passing parameters to the signal processor, the other for receiving status. Signed-off-by: Yuji Ishikawa --- Changelog v2: - Resend v1 because a patch exceeds size limit. Changelog v3: - Adapted to media control framework - Introduced ISP subdevice, capture device - Remove private IOCTLs and add vendor specific V4L2 controls - Change function name avoiding camelcase and uppercase letters Changelog v4: - Split patches because the v3 patch exceeds size limit - Stop using ID number to identify driver instance: - Use dynamically allocated structure to hold HW specific context, instead of static one. - Call HW layer functions with the context structure instead of ID number Changelog v5: - no change Changelog v6: - remove unused macros - removed hwd_ and HWD_ prefix - update source code documentation - Suggestion from Hans Verkuil - pointer to userland memory is removed from uAPI arguments - style of structure is now "nested" instead of "chained by pointer"; - use div64_u64 for 64bit division - vendor specific controls support TRY_EXT_CTRLS - add READ_ONLY flag to GET_CALIBRATION_STATUS control and similar ones - human friendry control names for vendor specific controls - add initial value to each vendor specific control - GET_LAST_CAPTURE_STATUS control is updated asyncnously from workqueue - remove EXECUTE_ON_WRITE flag of vendor specific control - uAPI: return value of GET_CALIBRATION_STATUS follows common rules of error codes - applied v4l2-compliance - Suggestion from Sakari Ailus - use div64_u64 for 64bit division - update copyright's year - remove redandunt cast - use bool instead of HWD_VIIF_ENABLE/DISABLE - simplify comparison to 0 - simplify statements with trigram operator - remove redundant local variables - use general integer types instead of u32/s32 - Suggestion from Laurent Pinchart - moved VIIF driver to driver/platform/toshiba/visconti - change register access: struct-style to macro-style - remove unused type definitions - define enums instead of successive macro constants - remove redundant parenthesis of macro constant - embed struct hwd_res into struct viif_device - use xxx_dma instead of xxx_paddr for variable names of IOVA - literal value: just 0 instead of 0x0 - use literal 1 or 0 instead of HWD_VIIF_ENABLE, DISABLE for register access - use true or false instead of HWD_VIIF_ENABLE, DISABLE for function calls - uAPI: return value of GET_CALIBRATION_STATUS follows common rules of error codes Changelog v7: - remove unused variables - split long statements which have multiple logical-OR and trigram operators Changelog v8: - define constant V4L2_CTRL_TYPE_VISCONTI_ISP for datatype of Visconti specific controls - Suggestion from Hans Verkuil - remove pr_info() - use pm_runtime_get_if_in_use() to get power status Changelog v9: - fix warning for cast between ptr and dma_addr_t Changelog v10: - use parameter buffer instead of vendor specific compound controls - add viif_params interface for passing ISP parameters - add viif_stats interface for passing ISP status - remove parameter validation routine; moved to userland library Changelog v11: - remove feature VB2_USERPTR from viif_params and viif_stats - fix strange indents at initializations - remove a redundant default setting of the ISP: L2_ROI - update copyright year Changelog v12: - use guard(spinlock)(locked_variable) macros - also use custom guard macros for viif_isp_guard - improve cast operations for viif_dev->tables_dma - add default parameter for undistortion (identical transformation) - add function to calculate the default scaling according to the request from the resizer subdevice .../media/platform/toshiba/visconti/Makefile | 2 +- .../media/platform/toshiba/visconti/viif.c | 26 +- .../platform/toshiba/visconti/viif_isp.c | 2 + .../platform/toshiba/visconti/viif_params.c | 2034 +++++++++++++++++ .../platform/toshiba/visconti/viif_params.h | 24 + .../platform/toshiba/visconti/viif_resizer.c | 30 + .../platform/toshiba/visconti/viif_stats.c | 301 +++ .../platform/toshiba/visconti/viif_stats.h | 14 + 8 files changed, 2430 insertions(+), 3 deletions(-) create mode 100644 drivers/media/platform/toshiba/visconti/viif_params.c create mode 100644 drivers/media/platform/toshiba/visconti/viif_params.h create mode 100644 drivers/media/platform/toshiba/visconti/viif_stats.c create mode 100644 drivers/media/platform/toshiba/visconti/viif_stats.h diff --git a/drivers/media/platform/toshiba/visconti/Makefile b/drivers/media/platform/toshiba/visconti/Makefile index 4c7433744b64..e5bbfcf3f9d0 100644 --- a/drivers/media/platform/toshiba/visconti/Makefile +++ b/drivers/media/platform/toshiba/visconti/Makefile @@ -4,7 +4,7 @@ # visconti-csi2rx-objs = csi2rx_drv.o -visconti-viif-objs = viif.o viif_capture.o viif_common.o viif_isp.o viif_resizer.o +visconti-viif-objs = viif.o viif_capture.o viif_common.o viif_isp.o viif_resizer.o viif_params.o viif_stats.o obj-$(CONFIG_VIDEO_VISCONTI_CSI2RX) += visconti-csi2rx.o obj-$(CONFIG_VIDEO_VISCONTI_VIIF) += visconti-viif.o diff --git a/drivers/media/platform/toshiba/visconti/viif.c b/drivers/media/platform/toshiba/visconti/viif.c index d2521718abe5..65e483b4b6aa 100644 --- a/drivers/media/platform/toshiba/visconti/viif.c +++ b/drivers/media/platform/toshiba/visconti/viif.c @@ -20,8 +20,10 @@ #include "viif_capture.h" #include "viif_common.h" #include "viif_isp.h" +#include "viif_params.h" #include "viif_regs.h" #include "viif_resizer.h" +#include "viif_stats.h" /*=============================================*/ /* Register Access */ @@ -106,6 +108,8 @@ static irqreturn_t viif_vsync_irq_handler(int irq, void *dev_id) l2_transfer_status, ts); visconti_viif_capture_switch_buffer(&viif_dev->cap_post1, status_err, l2_transfer_status, ts); + visconti_viif_stats_isr(viif_dev, viif_dev->cap_post0.sequence, ts); + visconti_viif_params_isr(viif_dev); } /* Delayed Vsync of SUB unit */ @@ -457,16 +461,32 @@ static int visconti_viif_probe(struct platform_device *pdev) goto error_resizer_unregister; } + ret = visconti_viif_params_register(viif_dev); + if (ret) { + dev_err_probe(dev, ret, "failed to register parameter node\n"); + goto error_capture_unregister; + } + + ret = visconti_viif_stats_register(viif_dev); + if (ret) { + dev_err_probe(dev, ret, "failed to register stat node\n"); + goto error_params_unregister; + } + ret = visconti_viif_create_links(viif_dev); if (ret) - goto error_capture_unregister; + goto error_stats_unregister; visconti_viif_subdev_notifier_register(viif_dev); if (ret) - goto error_capture_unregister; + goto error_stats_unregister; return 0; +error_stats_unregister: + visconti_viif_stats_unregister(viif_dev); +error_params_unregister: + visconti_viif_params_unregister(viif_dev); error_capture_unregister: visconti_viif_capture_unregister(viif_dev); error_resizer_unregister: @@ -490,6 +510,8 @@ static void visconti_viif_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&viif_dev->notifier); v4l2_async_nf_cleanup(&viif_dev->notifier); + visconti_viif_stats_unregister(viif_dev); + visconti_viif_params_unregister(viif_dev); visconti_viif_capture_unregister(viif_dev); visconti_viif_resizer_unregister(viif_dev); visconti_viif_isp_unregister(viif_dev); diff --git a/drivers/media/platform/toshiba/visconti/viif_isp.c b/drivers/media/platform/toshiba/visconti/viif_isp.c index 0bcc6bba9bf1..1bb7b9122ede 100644 --- a/drivers/media/platform/toshiba/visconti/viif_isp.c +++ b/drivers/media/platform/toshiba/visconti/viif_isp.c @@ -14,6 +14,7 @@ #include "viif.h" #include "viif_common.h" #include "viif_isp.h" +#include "viif_params.h" #include "viif_regs.h" /* disable CSI2 capture at viif_mux_start() */ @@ -583,6 +584,7 @@ static int visconti_viif_isp_enable_streams(struct v4l2_subdev *sd, struct v4l2_ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); /* enabling: start ISP, MUX -> start CSI2RX, sensor */ + visconti_viif_params_eval_queue(viif_dev); viif_dev->masked_gamma_path = 0; viif_mux_start(viif_dev, 0, 0); diff --git a/drivers/media/platform/toshiba/visconti/viif_params.c b/drivers/media/platform/toshiba/visconti/viif_params.c new file mode 100644 index 000000000000..af5a95217b83 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_params.c @@ -0,0 +1,2034 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include +#include +#include +#include + +#include "viif.h" +#include "viif_common.h" +#include "viif_isp.h" +#include "viif_params.h" +#include "viif_regs.h" +#include "viif_resizer.h" + +/* ISP_L1_SET_HDRC */ +#define VIIF_L1_HDRC_RATIO_OFFSET 10U +#define VIIF_REGBUF_ACCESS_TIME 15360UL +#define VIIF_L1_DELAY_W_HDRC 31U + +/* V4L2_CID_VISCONTI_VIIF_ISP_L2_SET_UNDIST */ +#define VIIF_L2_UNDIST_POLY_NUM 11U + +/*=======================================================================*/ +/* ISP parameter configuration */ +/*=======================================================================*/ +static void viif_l1_set_input_mode(struct viif_device *viif_dev, + const struct viif_l1_input_mode_config *arg) +{ + /* values are already tested in _try*/ + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + viif_capture_write(viif_dev, REG_L1_SYSM_INPUT_MODE, (u32)arg->mode); +} + +static void viif_l1_set_rgb_to_y_coef(struct viif_device *viif_dev, + const struct viif_l1_rgb_to_y_coef_config *arg) +{ + /* value is already tested in _try*/ + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + viif_capture_write(viif_dev, REG_L1_SYSM_YCOEF_R, (u32)arg->coef_r); + viif_capture_write(viif_dev, REG_L1_SYSM_YCOEF_G, (u32)arg->coef_g); + viif_capture_write(viif_dev, REG_L1_SYSM_YCOEF_B, (u32)arg->coef_b); +} + +static void viif_l1_set_ag_mode(struct viif_device *viif_dev, + const struct viif_l1_ag_mode_config *arg) +{ + u32 val; + + /* value is already tested in _try*/ + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + /* SYSM_AG_PARAM */ + viif_capture_write(viif_dev, REG_L1_SYSM_AG_PARAM_A, + PACK_L1_SYSM_AG_PARAM(arg->sysm_ag_grad[0], arg->sysm_ag_ofst[0])); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_PARAM_B, + PACK_L1_SYSM_AG_PARAM(arg->sysm_ag_grad[1], arg->sysm_ag_ofst[1])); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_PARAM_C, + PACK_L1_SYSM_AG_PARAM(arg->sysm_ag_grad[2], arg->sysm_ag_ofst[2])); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_PARAM_D, + PACK_L1_SYSM_AG_PARAM(arg->sysm_ag_grad[3], arg->sysm_ag_ofst[3])); + + /* SYSM_AG_SEL */ + viif_capture_write(viif_dev, REG_L1_SYSM_AG_SEL_HOBC, + PACK_L1_SYSM_AG_SEL_HML(arg->sysm_ag_psel_hobc_high, + arg->sysm_ag_psel_hobc_middle_led, + arg->sysm_ag_psel_hobc_low)); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_SEL_ABPC, + PACK_L1_SYSM_AG_SEL_HML(arg->sysm_ag_psel_abpc_high, + arg->sysm_ag_psel_abpc_middle_led, + arg->sysm_ag_psel_abpc_low)); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_SEL_RCNR, + PACK_L1_SYSM_AG_SEL_HML(arg->sysm_ag_psel_rcnr_high, + arg->sysm_ag_psel_rcnr_middle_led, + arg->sysm_ag_psel_rcnr_low)); + + viif_capture_write(viif_dev, REG_L1_SYSM_AG_SEL_LSSC, + PACK_L1_SYSM_AG_SEL_SP(arg->sysm_ag_ssel_lssc, arg->sysm_ag_psel_lssc)); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_SEL_MPRO, + PACK_L1_SYSM_AG_SEL_SP(arg->sysm_ag_ssel_mpro, arg->sysm_ag_psel_mpro)); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_SEL_VPRO, + PACK_L1_SYSM_AG_SEL_SP(arg->sysm_ag_ssel_vpro, arg->sysm_ag_psel_vpro)); + + /* SYSM_AG_CONT */ + val = arg->sysm_ag_cont_hobc_en_middle_led ? MASK_L1_SYSM_AG_CONT_M_EN : 0; + val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_M_VAL, arg->sysm_ag_cont_hobc_test_middle_led); + val |= arg->sysm_ag_cont_hobc_en_high ? MASK_L1_SYSM_AG_CONT_H_EN : 0; + val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_H_VAL, arg->sysm_ag_cont_hobc_test_high); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_HOBC01_EN, val); + + val = arg->sysm_ag_cont_hobc_en_low ? MASK_L1_SYSM_AG_CONT_L_EN : 0; + val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_L_VAL, arg->sysm_ag_cont_hobc_test_low); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_HOBC2_EN, val); + + val = arg->sysm_ag_cont_abpc_en_middle_led ? MASK_L1_SYSM_AG_CONT_M_EN : 0; + val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_M_VAL, arg->sysm_ag_cont_abpc_test_middle_led); + val |= arg->sysm_ag_cont_abpc_en_high ? MASK_L1_SYSM_AG_CONT_H_EN : 0; + val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_H_VAL, arg->sysm_ag_cont_abpc_test_high); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_ABPC01_EN, val); + + val = arg->sysm_ag_cont_abpc_en_low ? MASK_L1_SYSM_AG_CONT_L_EN : 0; + val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_L_VAL, arg->sysm_ag_cont_abpc_test_low); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_ABPC2_EN, val); + + val = arg->sysm_ag_cont_rcnr_en_middle_led ? MASK_L1_SYSM_AG_CONT_M_EN : 0; + val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_M_VAL, arg->sysm_ag_cont_rcnr_test_middle_led); + val |= arg->sysm_ag_cont_rcnr_en_high ? MASK_L1_SYSM_AG_CONT_H_EN : 0; + val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_H_VAL, arg->sysm_ag_cont_rcnr_test_high); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_RCNR01_EN, val); + + val = arg->sysm_ag_cont_rcnr_en_low ? MASK_L1_SYSM_AG_CONT_L_EN : 0; + val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_L_VAL, arg->sysm_ag_cont_rcnr_test_low); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_RCNR2_EN, val); + + val = arg->sysm_ag_cont_lssc_en ? MASK_L1_SYSM_AG_CONT_EN : 0; + val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_VAL, arg->sysm_ag_cont_lssc_test); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_LSSC_EN, val); + + val = arg->sysm_ag_cont_mpro_en ? MASK_L1_SYSM_AG_CONT_EN : 0; + val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_VAL, arg->sysm_ag_cont_mpro_test); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_MPRO_EN, val); + + val = arg->sysm_ag_cont_vpro_en ? MASK_L1_SYSM_AG_CONT_EN : 0; + val |= FIELD_PREP(MASK_L1_SYSM_AG_CONT_VAL, arg->sysm_ag_cont_vpro_test); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_CONT_VPRO_EN, val); +} + +static void viif_l1_set_ag(struct viif_device *viif_dev, const struct viif_l1_ag_config *arg) +{ + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + viif_capture_write(viif_dev, REG_L1_SYSM_AG_H, (u32)arg->gain_h); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_M, (u32)arg->gain_m); + viif_capture_write(viif_dev, REG_L1_SYSM_AG_L, (u32)arg->gain_l); +} + +static void viif_l1_set_hdre(struct viif_device *viif_dev, const struct viif_l1_hdre_config *arg) +{ + int i; + + /* value is already tested in _try*/ + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + for (i = 0; i < LEN_L1_HDRE_SRCPOINT; i++) + viif_capture_write(viif_dev, REG_L1_HDRE_SRCPOINT(i), arg->hdre_src_point[i]); + + viif_capture_write(viif_dev, REG_L1_HDRE_SRCBASE(0), 0); + for (i = 1; i < LEN_L1_HDRE_SRCBASE; i++) + viif_capture_write(viif_dev, REG_L1_HDRE_SRCBASE(i), arg->hdre_src_point[i - 1]); + + for (i = 0; i < LEN_L1_HDRE_DSTBASE; i++) + viif_capture_write(viif_dev, REG_L1_HDRE_DSTBASE(i), arg->hdre_dst_base[i]); + + for (i = 0; i < LEN_L1_HDRE_RATIO; i++) + viif_capture_write(viif_dev, REG_L1_HDRE_RATIO(i), arg->hdre_ratio[i]); + + viif_capture_write(viif_dev, REG_L1_HDRE_DSTMAXVAL, arg->hdre_dst_max_val); +} + +static void viif_l1_set_img_extraction(struct viif_device *viif_dev, + const struct viif_l1_img_extraction_config *arg) +{ + /* value is already tested in _try*/ + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + viif_capture_write(viif_dev, REG_L1_SLIC_SRCBLACKLEVEL_GR, arg->input_black_gr); + viif_capture_write(viif_dev, REG_L1_SLIC_SRCBLACKLEVEL_R, arg->input_black_r); + viif_capture_write(viif_dev, REG_L1_SLIC_SRCBLACKLEVEL_B, arg->input_black_b); + viif_capture_write(viif_dev, REG_L1_SLIC_SRCBLACKLEVEL_GB, arg->input_black_gb); +} + +static void viif_config_vdm_tgroup(struct viif_device *viif_dev, int idx) +{ + const struct { + u32 cfg; + u32 sram_base; + u32 sram_size; + } conf[] = { + /* T01: L1_SET_DPC, L1_SET_LSC */ + { VAL_TGROUP_CFG_64BIT_RD, 0x600, 0x20 }, + /* T02: L2_UNDIST grid table */ + { VAL_TGROUP_CFG_32BIT_RD, 0x620, 0x20 }, + /* T02: L2_GAMMA (path0) */ + { VAL_TGROUP_CFG_32BIT_RD, 0x640, 0x20 }, + /* T03: L2 GAMMA (path1) */ + { VAL_TGROUP_CFG_32BIT_RD, 0x660, 0x20 }, + }; + + viif_capture_write(viif_dev, REG_VDM_TGROUP_X_CFG(idx), conf[idx].cfg); + viif_capture_write(viif_dev, REG_VDM_TGROUP_X_SRAM_BASE(idx), conf[idx].sram_base); + viif_capture_write(viif_dev, REG_VDM_TGROUP_X_SRAM_SIZE(idx), conf[idx].sram_size); +} + +static void dpc_table_transmission(struct viif_device *viif_dev, uintptr_t table_h, + uintptr_t table_m, uintptr_t table_l) +{ + u32 val = 0x0U; + + viif_config_vdm_tgroup(viif_dev, IDX_TGROUP_L1_ISP); + + if (table_h) { + viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_DPC_H), + (u32)table_h); + viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_DPC_H), + VIIF_DPC_TABLE_BYTES); + val |= MASK_VDM_T_ENABLE_L1_DPC_H; + } + + if (table_m) { + viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_DPC_M), + (u32)table_m); + viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_DPC_M), + VIIF_DPC_TABLE_BYTES); + val |= MASK_VDM_T_ENABLE_L1_DPC_M; + } + + if (table_l) { + viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_DPC_L), + (u32)table_l); + viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_DPC_L), + VIIF_DPC_TABLE_BYTES); + val |= MASK_VDM_T_ENABLE_L1_DPC_L; + } + + val |= (viif_capture_read(viif_dev, REG_VDM_T_ENABLE) & ~MASK_VDM_T_ENABLE_L1_DPC); + viif_capture_write(viif_dev, REG_VDM_T_ENABLE, val); +} + +static void viif_l1_set_dpc(struct viif_device *viif_dev, const struct viif_l1_dpc_config *arg) +{ + const struct viif_l1_dpc *param_h, *param_m, *param_l; + dma_addr_t table_h = 0; + dma_addr_t table_m = 0; + dma_addr_t table_l = 0; + unsigned long irqflags; + u32 val; + + if (arg->param_h.abpc_sta_en) { + memcpy(viif_dev->tables->dpc_table_h, arg->table_h, VIIF_DPC_TABLE_BYTES); + table_h = viif_dev->tables_dma + offsetof(struct viif_table_area, dpc_table_h); + } + if (arg->param_m.abpc_sta_en) { + memcpy(viif_dev->tables->dpc_table_m, arg->table_m, VIIF_DPC_TABLE_BYTES); + table_m = viif_dev->tables_dma + offsetof(struct viif_table_area, dpc_table_m); + } + if (arg->param_l.abpc_sta_en) { + memcpy(viif_dev->tables->dpc_table_l, arg->table_l, VIIF_DPC_TABLE_BYTES); + table_l = viif_dev->tables_dma + offsetof(struct viif_table_area, dpc_table_l); + } + + spin_lock_irqsave(&viif_dev->regbuf_lock, irqflags); + hwd_viif_isp_guard_start(viif_dev); + + dpc_table_transmission(viif_dev, table_h, table_m, table_l); + + param_h = &arg->param_h; + param_m = &arg->param_m; + param_l = &arg->param_l; + + val = (param_h->abpc_sta_en) ? MASK_L1_ABPC_ENABLE_H : 0; + val |= (param_m->abpc_sta_en) ? MASK_L1_ABPC_ENABLE_M : 0; + val |= (param_l->abpc_sta_en) ? MASK_L1_ABPC_ENABLE_L : 0; + viif_capture_write(viif_dev, REG_L1_ABPC012_STA_EN, val); + + val = (param_h->abpc_dyn_en) ? MASK_L1_ABPC_ENABLE_H : 0; + val |= (param_m->abpc_dyn_en) ? MASK_L1_ABPC_ENABLE_M : 0; + val |= (param_l->abpc_dyn_en) ? MASK_L1_ABPC_ENABLE_L : 0; + viif_capture_write(viif_dev, REG_L1_ABPC012_DYN_EN, val); + + val = (param_h->abpc_dyn_mode == VIIF_L1_DPC_2PIXEL) ? MASK_L1_ABPC_DYN_MODE_2PIXEL_H : 0; + val |= (param_m->abpc_dyn_mode == VIIF_L1_DPC_2PIXEL) ? MASK_L1_ABPC_DYN_MODE_2PIXEL_M : 0; + val |= (param_l->abpc_dyn_mode == VIIF_L1_DPC_2PIXEL) ? MASK_L1_ABPC_DYN_MODE_2PIXEL_L : 0; + viif_capture_write(viif_dev, REG_L1_ABPC012_DYN_MODE, val); + + /* setup param_h */ + viif_capture_write(viif_dev, REG_L1_ABPC0_RATIO_LIMIT, param_h->abpc_ratio_limit); + viif_capture_write(viif_dev, REG_L1_ABPC0_DARK_LIMIT, param_h->abpc_dark_limit); + viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_W_AG_MIN, param_h->abpc_sn_coef_w_ag_min); + viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_W_AG_MID, param_h->abpc_sn_coef_w_ag_mid); + viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_W_AG_MAX, param_h->abpc_sn_coef_w_ag_max); + viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_B_AG_MIN, param_h->abpc_sn_coef_b_ag_min); + viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_B_AG_MID, param_h->abpc_sn_coef_b_ag_mid); + viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_B_AG_MAX, param_h->abpc_sn_coef_b_ag_max); + viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_W_TH_MIN, param_h->abpc_sn_coef_w_th_min); + viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_W_TH_MAX, param_h->abpc_sn_coef_w_th_max); + viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_B_TH_MIN, param_h->abpc_sn_coef_b_th_min); + viif_capture_write(viif_dev, REG_L1_ABPC0_SN_COEF_B_TH_MAX, param_h->abpc_sn_coef_b_th_max); + + /* setup param_m */ + viif_capture_write(viif_dev, REG_L1_ABPC1_RATIO_LIMIT, param_m->abpc_ratio_limit); + viif_capture_write(viif_dev, REG_L1_ABPC1_DARK_LIMIT, param_m->abpc_dark_limit); + viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_W_AG_MIN, param_m->abpc_sn_coef_w_ag_min); + viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_W_AG_MID, param_m->abpc_sn_coef_w_ag_mid); + viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_W_AG_MAX, param_m->abpc_sn_coef_w_ag_max); + viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_B_AG_MIN, param_m->abpc_sn_coef_b_ag_min); + viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_B_AG_MID, param_m->abpc_sn_coef_b_ag_mid); + viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_B_AG_MAX, param_m->abpc_sn_coef_b_ag_max); + viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_W_TH_MIN, param_m->abpc_sn_coef_w_th_min); + viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_W_TH_MAX, param_m->abpc_sn_coef_w_th_max); + viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_B_TH_MIN, param_m->abpc_sn_coef_b_th_min); + viif_capture_write(viif_dev, REG_L1_ABPC1_SN_COEF_B_TH_MAX, param_m->abpc_sn_coef_b_th_max); + + /* setup param_l */ + viif_capture_write(viif_dev, REG_L1_ABPC2_RATIO_LIMIT, param_l->abpc_ratio_limit); + viif_capture_write(viif_dev, REG_L1_ABPC2_DARK_LIMIT, param_l->abpc_dark_limit); + viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_W_AG_MIN, param_l->abpc_sn_coef_w_ag_min); + viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_W_AG_MID, param_l->abpc_sn_coef_w_ag_mid); + viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_W_AG_MAX, param_l->abpc_sn_coef_w_ag_max); + viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_B_AG_MIN, param_l->abpc_sn_coef_b_ag_min); + viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_B_AG_MID, param_l->abpc_sn_coef_b_ag_mid); + viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_B_AG_MAX, param_l->abpc_sn_coef_b_ag_max); + viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_W_TH_MIN, param_l->abpc_sn_coef_w_th_min); + viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_W_TH_MAX, param_l->abpc_sn_coef_w_th_max); + viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_B_TH_MIN, param_l->abpc_sn_coef_b_th_min); + viif_capture_write(viif_dev, REG_L1_ABPC2_SN_COEF_B_TH_MAX, param_l->abpc_sn_coef_b_th_max); + + hwd_viif_isp_guard_end(viif_dev); + spin_unlock_irqrestore(&viif_dev->regbuf_lock, irqflags); +} + +static void viif_l1_set_preset_white_balance(struct viif_device *viif_dev, + const struct viif_l1_preset_white_balance_config *arg) +{ + const struct viif_l1_preset_wb *param_h, *param_m, *param_l; + + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + param_h = &arg->param_h; + param_m = &arg->param_m; + param_l = &arg->param_l; + + viif_capture_write(viif_dev, REG_L1_PWHB_DSTMAXVAL, arg->dstmaxval); + + viif_capture_write(viif_dev, REG_L1_PWHB_H_GR, param_h->gain_gr); + viif_capture_write(viif_dev, REG_L1_PWHB_HR, param_h->gain_r); + viif_capture_write(viif_dev, REG_L1_PWHB_HB, param_h->gain_b); + viif_capture_write(viif_dev, REG_L1_PWHB_H_GB, param_h->gain_gb); + + viif_capture_write(viif_dev, REG_L1_PWHB_M_GR, param_m->gain_gr); + viif_capture_write(viif_dev, REG_L1_PWHB_MR, param_m->gain_r); + viif_capture_write(viif_dev, REG_L1_PWHB_MB, param_m->gain_b); + viif_capture_write(viif_dev, REG_L1_PWHB_M_GB, param_m->gain_gb); + + viif_capture_write(viif_dev, REG_L1_PWHB_L_GR, param_l->gain_gr); + viif_capture_write(viif_dev, REG_L1_PWHB_LR, param_l->gain_r); + viif_capture_write(viif_dev, REG_L1_PWHB_LB, param_l->gain_b); + viif_capture_write(viif_dev, REG_L1_PWHB_L_GB, param_l->gain_gb); +} + +static void +viif_l1_set_raw_color_noise_reduction(struct viif_device *viif_dev, + const struct viif_l1_raw_color_noise_reduction_config *arg) +{ + const struct viif_l1_raw_color_noise_reduction *params[] = { + &arg->param_h, + &arg->param_m, + &arg->param_l, + }; + int i; + + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + for (i = 0; i < 3; i++) { + const struct viif_l1_raw_color_noise_reduction *param = params[i]; + /* param_h */ + viif_capture_write(viif_dev, REG_L1_RCNR_X_SW(i), param->rcnr_sw ? 1 : 0); + + viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_DARK_AG0(i), + param->rcnr_cnf_dark_ag0); + viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_DARK_AG1(i), + param->rcnr_cnf_dark_ag1); + viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_DARK_AG2(i), + param->rcnr_cnf_dark_ag2); + + viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_RATIO_AG0(i), + param->rcnr_cnf_ratio_ag0); + viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_RATIO_AG1(i), + param->rcnr_cnf_ratio_ag1); + viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_RATIO_AG2(i), + param->rcnr_cnf_ratio_ag2); + + viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_CLIP_GAIN_R(i), + param->rcnr_cnf_clip_gain_r); + viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_CLIP_GAIN_G(i), + param->rcnr_cnf_clip_gain_g); + viif_capture_write(viif_dev, REG_L1_RCNR_X_CNF_CLIP_GAIN_B(i), + param->rcnr_cnf_clip_gain_b); + + viif_capture_write(viif_dev, REG_L1_RCNR_X_A1L_DARK_AG0(i), + param->rcnr_a1l_dark_ag0); + viif_capture_write(viif_dev, REG_L1_RCNR_X_A1L_DARK_AG1(i), + param->rcnr_a1l_dark_ag1); + viif_capture_write(viif_dev, REG_L1_RCNR_X_A1L_DARK_AG2(i), + param->rcnr_a1l_dark_ag2); + + viif_capture_write(viif_dev, REG_L1_RCNR_X_A1L_RATIO_AG0(i), + param->rcnr_a1l_ratio_ag0); + viif_capture_write(viif_dev, REG_L1_RCNR_X_A1L_RATIO_AG1(i), + param->rcnr_a1l_ratio_ag1); + viif_capture_write(viif_dev, REG_L1_RCNR_X_A1L_RATIO_AG2(i), + param->rcnr_a1l_ratio_ag2); + + viif_capture_write(viif_dev, REG_L1_RCNR_X_INF_ZERO_CLIP(i), + param->rcnr_inf_zero_clip); + + viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_D2BLEND_AG0(i), + param->rcnr_merge_d2blend_ag0); + viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_D2BLEND_AG1(i), + param->rcnr_merge_d2blend_ag1); + viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_D2BLEND_AG2(i), + param->rcnr_merge_d2blend_ag2); + viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_BLACK(i), param->rcnr_merge_black); + viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_MINDIV(i), + param->rcnr_merge_mindiv); + + viif_capture_write(viif_dev, REG_L1_RCNR_X_HRY_TYPE(i), param->rcnr_hry_type); + + viif_capture_write(viif_dev, REG_L1_RCNR_X_ANF_BLEND_AG0(i), + param->rcnr_anf_blend_ag0); + viif_capture_write(viif_dev, REG_L1_RCNR_X_ANF_BLEND_AG1(i), + param->rcnr_anf_blend_ag1); + viif_capture_write(viif_dev, REG_L1_RCNR_X_ANF_BLEND_AG2(i), + param->rcnr_anf_blend_ag2); + + viif_capture_write(viif_dev, REG_L1_RCNR_X_LPF_THRESHOLD(i), + param->rcnr_lpf_threshold); + + viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_HLBLEND_AG0(i), + param->rcnr_merge_hlblend_ag0); + viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_HLBLEND_AG1(i), + param->rcnr_merge_hlblend_ag1); + viif_capture_write(viif_dev, REG_L1_RCNR_X_MERGE_HLBLEND_AG2(i), + param->rcnr_merge_hlblend_ag2); + + viif_capture_write(viif_dev, REG_L1_RCNR_X_GNR_SW(i), param->rcnr_gnr_sw ? 1 : 0); + + if (param->rcnr_gnr_sw) { + viif_capture_write(viif_dev, REG_L1_RCNR_X_GNR_RATIO(i), + param->rcnr_gnr_ratio); + viif_capture_write(viif_dev, REG_L1_RCNR_X_GNR_WIDE_EN(i), + param->rcnr_gnr_wide_en ? 1 : 0); + } + } +} + +static void viif_l1_set_hdrs(struct viif_device *viif_dev, const struct viif_l1_hdrs_config *arg) +{ + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + viif_capture_write(viif_dev, REG_L1_HDRS_HDRMODE, arg->hdrs_hdr_mode); + + viif_capture_write(viif_dev, REG_L1_HDRS_HDRRATIO_M, arg->hdrs_hdr_ratio_m); + viif_capture_write(viif_dev, REG_L1_HDRS_HDRRATIO_L, arg->hdrs_hdr_ratio_l); + viif_capture_write(viif_dev, REG_L1_HDRS_HDRRATIO_E, arg->hdrs_hdr_ratio_e); + + viif_capture_write(viif_dev, REG_L1_HDRS_DG_H, arg->hdrs_dg_h); + viif_capture_write(viif_dev, REG_L1_HDRS_DG_M, arg->hdrs_dg_m); + viif_capture_write(viif_dev, REG_L1_HDRS_DG_L, arg->hdrs_dg_l); + viif_capture_write(viif_dev, REG_L1_HDRS_DG_E, arg->hdrs_dg_e); + + viif_capture_write(viif_dev, REG_L1_HDRS_BLENDEND_H, arg->hdrs_blendend_h); + viif_capture_write(viif_dev, REG_L1_HDRS_BLENDEND_M, arg->hdrs_blendend_m); + viif_capture_write(viif_dev, REG_L1_HDRS_BLENDEND_E, arg->hdrs_blendend_e); + + viif_capture_write(viif_dev, REG_L1_HDRS_BLENDBEG_H, arg->hdrs_blendbeg_h); + viif_capture_write(viif_dev, REG_L1_HDRS_BLENDBEG_M, arg->hdrs_blendbeg_m); + viif_capture_write(viif_dev, REG_L1_HDRS_BLENDBEG_E, arg->hdrs_blendbeg_e); + + viif_capture_write(viif_dev, REG_L1_HDRS_LEDMODE_ON, arg->hdrs_led_mode_on ? 1 : 0); + viif_capture_write(viif_dev, REG_L1_HDRS_DSTMAXVAL, arg->hdrs_dst_max_val); +} + +static void +viif_l1_set_black_level_correction(struct viif_device *viif_dev, + const struct viif_l1_black_level_correction_config *arg) +{ + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + viif_capture_write(viif_dev, REG_L1_BLVC_SRCBLACKLEVEL_GR, arg->srcblacklevel_gr); + viif_capture_write(viif_dev, REG_L1_BLVC_SRCBLACKLEVEL_R, arg->srcblacklevel_r); + viif_capture_write(viif_dev, REG_L1_BLVC_SRCBLACKLEVEL_B, arg->srcblacklevel_b); + viif_capture_write(viif_dev, REG_L1_BLVC_SRCBLACKLEVELGB, arg->srcblacklevel_gb); + + viif_capture_write(viif_dev, REG_L1_BLVC_MULTVAL_GR, arg->mulval_gr); + viif_capture_write(viif_dev, REG_L1_BLVC_MULTVAL_R, arg->mulval_r); + viif_capture_write(viif_dev, REG_L1_BLVC_MULTVAL_B, arg->mulval_b); + viif_capture_write(viif_dev, REG_L1_BLVC_MULTVAL_GB, arg->mulval_gb); + + viif_capture_write(viif_dev, REG_L1_BLVC_DSTMAXVAL, arg->dstmaxval); +} + +static void lsc_table_transmission(struct viif_device *viif_dev, dma_addr_t table_gr, + dma_addr_t table_r, dma_addr_t table_b, dma_addr_t table_gb) +{ + u32 val = 0x0U; + + viif_config_vdm_tgroup(viif_dev, IDX_TGROUP_L1_ISP); + + if (table_gr) { + viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_LSSC_GR), + (u32)table_gr); + viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_LSSC_GR), + VIIF_LSC_TABLE_BYTES); + val |= MASK_VDM_T_ENABLE_L1_LSSC_GR; + } + + if (table_r) { + viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_LSSC_R), + (u32)table_r); + viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_LSSC_R), + VIIF_LSC_TABLE_BYTES); + val |= MASK_VDM_T_ENABLE_L1_LSSC_R; + } + + if (table_b) { + viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_LSSC_B), + (u32)table_b); + viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_LSSC_B), + VIIF_LSC_TABLE_BYTES); + val |= MASK_VDM_T_ENABLE_L1_LSSC_B; + } + + if (table_gb) { + viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(IDX_TPORT_L1_LSSC_GB), + (u32)table_gb); + viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(IDX_TPORT_L1_LSSC_GB), + VIIF_LSC_TABLE_BYTES); + val |= MASK_VDM_T_ENABLE_L1_LSSC_GB; + } + + val |= (viif_capture_read(viif_dev, REG_VDM_T_ENABLE) & ~MASK_VDM_T_ENABLE_L1_LSSC); + viif_capture_write(viif_dev, REG_VDM_T_ENABLE, val); +} + +static inline u32 gen_grid_size(u32 param) +{ + switch (param) { + case 32U: + return 5U; + case 64U: + return 6U; + case 128U: + return 7U; + case 256U: + return 8U; + case 512U: + return 9U; + default: + return 0; + } +} + +#define PACK_PARA_COEF(max, min) (FIELD_PREP(0x1fff0000, (max)) | FIELD_PREP(0x1fff, (min))) + +static void viif_l1_set_lsc(struct viif_device *viif_dev, const struct viif_l1_lsc_config *arg) +{ + dma_addr_t table_gr = 0; + dma_addr_t table_gb = 0; + dma_addr_t table_r = 0; + dma_addr_t table_b = 0; + unsigned long irqflags; + u32 val; + + if (!arg->enable) { + spin_lock_irqsave(&viif_dev->regbuf_lock, irqflags); + hwd_viif_isp_guard_start(viif_dev); + + viif_capture_write(viif_dev, REG_L1_LSSC_EN, 0); + + hwd_viif_isp_guard_end(viif_dev); + spin_unlock_irqrestore(&viif_dev->regbuf_lock, irqflags); + return; + } + + if (arg->enable & VIIF_L1_LSC_GRID_EN_MASK) { + memcpy(viif_dev->tables->lsc_table_gr, arg->table_gr, VIIF_LSC_TABLE_BYTES); + memcpy(viif_dev->tables->lsc_table_r, arg->table_r, VIIF_LSC_TABLE_BYTES); + memcpy(viif_dev->tables->lsc_table_b, arg->table_b, VIIF_LSC_TABLE_BYTES); + memcpy(viif_dev->tables->lsc_table_gb, arg->table_gb, VIIF_LSC_TABLE_BYTES); + table_gr = viif_dev->tables_dma + offsetof(struct viif_table_area, lsc_table_gr); + table_r = viif_dev->tables_dma + offsetof(struct viif_table_area, lsc_table_r); + table_b = viif_dev->tables_dma + offsetof(struct viif_table_area, lsc_table_b); + table_gb = viif_dev->tables_dma + offsetof(struct viif_table_area, lsc_table_gb); + } + + spin_lock_irqsave(&viif_dev->regbuf_lock, irqflags); + hwd_viif_isp_guard_start(viif_dev); + lsc_table_transmission(viif_dev, table_gr, table_r, table_b, table_gb); + + /* parabola shading */ + if (arg->enable & VIIF_L1_LSC_PARABOLA_EN_MASK) { + const struct viif_l1_lsc_parabola_param *parabola_param = + &arg->param.lssc_parabola_param; + const struct viif_l1_lsc_parabola_ag_param *params[] = { + ¶bola_param->r_2d, ¶bola_param->r_4d, ¶bola_param->gr_2d, + ¶bola_param->gr_4d, ¶bola_param->gb_2d, ¶bola_param->gb_4d, + ¶bola_param->b_2d, ¶bola_param->b_4d, + }; + int i; + + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_EN, 1); + + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_H_CENTER, + parabola_param->lssc_para_h_center); + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_V_CENTER, + parabola_param->lssc_para_v_center); + + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_H_GAIN, + parabola_param->lssc_para_h_gain); + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_V_GAIN, + parabola_param->lssc_para_v_gain); + + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_MGSEL2, + parabola_param->lssc_para_mgsel2); + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_MGSEL4, + parabola_param->lssc_para_mgsel4); + + for (i = 0; i < 8; i++) { + const struct viif_l1_lsc_parabola_ag_param *p = params[i]; + + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_COEF_X_H_L(i), + PACK_PARA_COEF(p->lssc_paracoef_h_l_max, + p->lssc_paracoef_h_l_min)); + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_COEF_X_H_R(i), + PACK_PARA_COEF(p->lssc_paracoef_h_r_max, + p->lssc_paracoef_h_r_min)); + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_COEF_X_V_U(i), + PACK_PARA_COEF(p->lssc_paracoef_v_u_max, + p->lssc_paracoef_v_u_min)); + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_COEF_X_V_D(i), + PACK_PARA_COEF(p->lssc_paracoef_v_d_max, + p->lssc_paracoef_v_d_min)); + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_COEF_X_HV_LU(i), + PACK_PARA_COEF(p->lssc_paracoef_hv_lu_max, + p->lssc_paracoef_hv_lu_min)); + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_COEF_X_HV_RU(i), + PACK_PARA_COEF(p->lssc_paracoef_hv_ru_max, + p->lssc_paracoef_hv_ru_min)); + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_COEF_X_HV_LD(i), + PACK_PARA_COEF(p->lssc_paracoef_hv_ld_max, + p->lssc_paracoef_hv_ld_min)); + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_COEF_X_HV_RD(i), + PACK_PARA_COEF(p->lssc_paracoef_hv_rd_max, + p->lssc_paracoef_hv_rd_min)); + } + } else { + viif_capture_write(viif_dev, REG_L1_LSSC_PARA_EN, 0); + } + + /* grid shading */ + if (arg->enable & VIIF_L1_LSC_GRID_EN_MASK) { + const struct viif_l1_lsc_grid_param *grid_param = &arg->param.lssc_grid_param; + u32 grid_h_size = gen_grid_size(grid_param->lssc_grid_h_size); + u32 grid_v_size = gen_grid_size(grid_param->lssc_grid_v_size); + + viif_capture_write(viif_dev, REG_L1_LSSC_GRID_EN, 1); + viif_capture_write(viif_dev, REG_L1_LSSC_GRID_H_SIZE, grid_h_size); + viif_capture_write(viif_dev, REG_L1_LSSC_GRID_V_SIZE, grid_v_size); + viif_capture_write(viif_dev, REG_L1_LSSC_GRID_H_CENTER, + grid_param->lssc_grid_h_center); + viif_capture_write(viif_dev, REG_L1_LSSC_GRID_V_CENTER, + grid_param->lssc_grid_v_center); + viif_capture_write(viif_dev, REG_L1_LSSC_GRID_MGSEL, grid_param->lssc_grid_mgsel); + + } else { + viif_capture_write(viif_dev, REG_L1_LSSC_GRID_EN, 0); + } + + /* preset white balance */ + val = (arg->param.lssc_pwhb_r_gain_max << 16U) | (arg->param.lssc_pwhb_r_gain_min); + viif_capture_write(viif_dev, REG_L1_LSSC_PWHB_R_GAIN, val); + + val = (arg->param.lssc_pwhb_gr_gain_max << 16U) | (arg->param.lssc_pwhb_gr_gain_min); + viif_capture_write(viif_dev, REG_L1_LSSC_PWHB_GR_GAIN, val); + + val = (arg->param.lssc_pwhb_gb_gain_max << 16U) | (arg->param.lssc_pwhb_gb_gain_min); + viif_capture_write(viif_dev, REG_L1_LSSC_PWHB_GB_GAIN, val); + + val = (arg->param.lssc_pwhb_b_gain_max << 16U) | (arg->param.lssc_pwhb_b_gain_min); + viif_capture_write(viif_dev, REG_L1_LSSC_PWHB_B_GAIN, val); + + viif_capture_write(viif_dev, REG_L1_LSSC_EN, 1); + + hwd_viif_isp_guard_end(viif_dev); + spin_unlock_irqrestore(&viif_dev->regbuf_lock, irqflags); +} + +static void viif_l1_set_main_process(struct viif_device *viif_dev, + const struct viif_l1_main_process_config *arg) +{ + u32 val; + + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + viif_capture_write(viif_dev, REG_L1_MPRO_CONF, arg->damp_lsbsel << 4); + viif_capture_write(viif_dev, REG_L1_MPRO_LCS_MODE, arg->demosaic_mode); + + if (arg->colormat_enable) { + const struct viif_l1_color_matrix_correction *color_matrix = &arg->colormat_param; + + viif_capture_write(viif_dev, REG_L1_MPRO_SW, 1); + + val = (u32)color_matrix->coef_rmg_min & 0xffffU; + viif_capture_write(viif_dev, REG_L1_MPRO_LM0_RMG_MIN, val); + + val = (u32)color_matrix->coef_rmg_max & 0xffffU; + viif_capture_write(viif_dev, REG_L1_MPRO_LM0_RMG_MAX, val); + + val = (u32)color_matrix->coef_rmb_min & 0xffffU; + viif_capture_write(viif_dev, REG_L1_MPRO_LM0_RMB_MIN, val); + + val = (u32)color_matrix->coef_rmb_max & 0xffffU; + viif_capture_write(viif_dev, REG_L1_MPRO_LM0_RMB_MAX, val); + + val = (u32)color_matrix->coef_gmr_min & 0xffffU; + viif_capture_write(viif_dev, REG_L1_MPRO_LM0_GMR_MIN, val); + + val = (u32)color_matrix->coef_gmr_max & 0xffffU; + viif_capture_write(viif_dev, REG_L1_MPRO_LM0_GMR_MAX, val); + + val = (u32)color_matrix->coef_gmb_min & 0xffffU; + viif_capture_write(viif_dev, REG_L1_MPRO_LM0_GMB_MIN, val); + + val = (u32)color_matrix->coef_gmb_max & 0xffffU; + viif_capture_write(viif_dev, REG_L1_MPRO_LM0_GMB_MAX, val); + + val = (u32)color_matrix->coef_bmr_min & 0xffffU; + viif_capture_write(viif_dev, REG_L1_MPRO_LM0_BMR_MIN, val); + + val = (u32)color_matrix->coef_bmr_max & 0xffffU; + viif_capture_write(viif_dev, REG_L1_MPRO_LM0_BMR_MAX, val); + + val = (u32)color_matrix->coef_bmg_min & 0xffffU; + viif_capture_write(viif_dev, REG_L1_MPRO_LM0_BMG_MIN, val); + + val = (u32)color_matrix->coef_bmg_max & 0xffffU; + viif_capture_write(viif_dev, REG_L1_MPRO_LM0_BMG_MAX, val); + + viif_capture_write(viif_dev, REG_L1_MPRO_DST_MINVAL, (u32)color_matrix->dst_minval); + } else { + viif_capture_write(viif_dev, REG_L1_MPRO_SW, 0); + } + + viif_capture_write(viif_dev, REG_L1_MPRO_DST_MAXVAL, arg->dst_maxval); +} + +static void viif_l1_set_awb(struct viif_device *viif_dev, const struct viif_l1_awb_config *arg) +{ + const struct viif_l1_awb *param = &arg->param; + u32 val, ygate_data; + + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + viif_capture_write(viif_dev, REG_L1_AWHB_WBMRG, arg->awhb_wbmrg); + viif_capture_write(viif_dev, REG_L1_AWHB_WBMGG, arg->awhb_wbmgg); + viif_capture_write(viif_dev, REG_L1_AWHB_WBMBG, arg->awhb_wbmbg); + + val = viif_capture_read(viif_dev, REG_L1_AWHB_SW) & ~MASK_L1_AWHB_SW_EN; + + /* disabling AWB */ + if (!arg->enable) { + viif_capture_write(viif_dev, REG_L1_AWHB_SW, val); + return; + } + + /* enabling AWB */ + viif_capture_write(viif_dev, REG_L1_AWHB_SW, val | MASK_L1_AWHB_SW_EN); + + if (param->awhb_ygate_data == 64U) + ygate_data = 0U; + else if (param->awhb_ygate_data == 128U) + ygate_data = 1U; + else if (param->awhb_ygate_data == 256U) + ygate_data = 2U; + else + ygate_data = 3U; + + val = param->awhb_ygate_sel ? MASK_L1_AWHB_GATE_YGATE_SEL : 0; + val |= FIELD_PREP(MASK_L1_AWHB_GATE_YGATE_DATA, ygate_data); + val |= FIELD_PREP(MASK_L1_AWHB_GATE_CGRANGE, param->awhb_cgrange); + viif_capture_write(viif_dev, REG_L1_AWHB_GATE_CONF0, val); + + val = param->awhb_ygatesw ? MASK_L1_AWHB_GATE_YGATESW : 0; + val |= param->awhb_hexsw ? MASK_L1_AWHB_GATE_HEXSW : 0; + val |= FIELD_PREP(MASK_L1_AWHB_GATE_AREAMODE, param->awhb_areamode); + viif_capture_write(viif_dev, REG_L1_AWHB_GATE_CONF1, val); + + viif_capture_write(viif_dev, REG_L1_AWHB_AREA_HSIZE, param->awhb_area_hsize); + viif_capture_write(viif_dev, REG_L1_AWHB_AREA_VSIZE, param->awhb_area_vsize); + viif_capture_write(viif_dev, REG_L1_AWHB_AREA_HOFS, param->awhb_area_hofs); + viif_capture_write(viif_dev, REG_L1_AWHB_AREA_VOFS, param->awhb_area_vofs); + + viif_capture_write(viif_dev, REG_L1_AWHB_AREA_MASKH, param->awhb_area_maskh); + viif_capture_write(viif_dev, REG_L1_AWHB_AREA_MASKL, param->awhb_area_maskl); + + val = param->awhb_sq_sw[0] ? MASK_L1_AWHB_SQ_CONF_SQ1SW : 0; + val |= param->awhb_sq_pol[0] ? MASK_L1_AWHB_SQ_CONF_SQ1POL : 0; + val |= param->awhb_sq_sw[1] ? MASK_L1_AWHB_SQ_CONF_SQ2SW : 0; + val |= param->awhb_sq_pol[1] ? MASK_L1_AWHB_SQ_CONF_SQ2POL : 0; + val |= param->awhb_sq_sw[2] ? MASK_L1_AWHB_SQ_CONF_SQ3SW : 0; + val |= param->awhb_sq_pol[2] ? MASK_L1_AWHB_SQ_CONF_SQ3POL : 0; + viif_capture_write(viif_dev, REG_L1_AWHB_SQ_CONF, val); + + viif_capture_write(viif_dev, REG_L1_AWHB_YGATEH, (u32)param->awhb_ygateh); + viif_capture_write(viif_dev, REG_L1_AWHB_YGATEL, (u32)param->awhb_ygatel); + + viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT0P, param->awhb_bycut0p); + viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT0N, param->awhb_bycut0n); + viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT0P, param->awhb_rycut0p); + viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT0N, param->awhb_rycut0n); + + val = (u32)param->awhb_rbcut0h & 0xffU; + viif_capture_write(viif_dev, REG_L1_AWHB_RBCUT0H, val); + val = (u32)param->awhb_rbcut0l & 0xffU; + viif_capture_write(viif_dev, REG_L1_AWHB_RBCUT0L, val); + + val = (u32)param->awhb_bycut_h[0] & 0xffU; + viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT1H, val); + viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT1L, param->awhb_bycut_l[0]); + val = (u32)param->awhb_bycut_h[1] & 0xffU; + viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT2H, val); + viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT2L, param->awhb_bycut_l[1]); + val = (u32)param->awhb_bycut_h[2] & 0xffU; + viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT3H, val); + viif_capture_write(viif_dev, REG_L1_AWHB_BYCUT3L, param->awhb_bycut_l[2]); + + val = (u32)param->awhb_rycut_h[0] & 0xffU; + viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT1H, val); + viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT1L, param->awhb_rycut_l[0]); + val = (u32)param->awhb_rycut_h[1] & 0xffU; + viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT2H, val); + viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT2L, param->awhb_rycut_l[1]); + val = (u32)param->awhb_rycut_h[2] & 0xffU; + viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT3H, val); + viif_capture_write(viif_dev, REG_L1_AWHB_RYCUT3L, param->awhb_rycut_l[2]); + + val = (u32)param->awhb_awbsftu & 0xffU; + viif_capture_write(viif_dev, REG_L1_AWHB_AWBSFTU, val); + val = (u32)param->awhb_awbsftv & 0xffU; + viif_capture_write(viif_dev, REG_L1_AWHB_AWBSFTV, val); + + val = (param->awhb_awbhuecor ? MASK_L1_AWHB_AWBSPD_HUECOR : 0); + val |= FIELD_PREP(MASK_L1_AWHB_AWBSPD_SPD, param->awhb_awbspd); + viif_capture_write(viif_dev, REG_L1_AWHB_AWBSPD, val); + + viif_capture_write(viif_dev, REG_L1_AWHB_AWBULV, param->awhb_awbulv); + viif_capture_write(viif_dev, REG_L1_AWHB_AWBVLV, param->awhb_awbvlv); + viif_capture_write(viif_dev, REG_L1_AWHB_AWBWAIT, (u32)param->awhb_awbwait); + + viif_capture_write(viif_dev, REG_L1_AWHB_AWBONDOT, param->awhb_awbondot); + viif_capture_write(viif_dev, REG_L1_AWHB_AWBFZTIM, param->awhb_awbfztim); + + viif_capture_write(viif_dev, REG_L1_AWHB_WBGRMAX, (u32)param->awhb_wbgrmax); + viif_capture_write(viif_dev, REG_L1_AWHB_WBGBMAX, (u32)param->awhb_wbgbmax); + viif_capture_write(viif_dev, REG_L1_AWHB_WBGRMIN, (u32)param->awhb_wbgrmin); + viif_capture_write(viif_dev, REG_L1_AWHB_WBGBMIN, (u32)param->awhb_wbgbmin); +} + +static void viif_l1_lock_awb_gain(struct viif_device *viif_dev, const u32 *enable) +{ + u32 val; + + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + val = viif_capture_read(viif_dev, REG_L1_AWHB_SW) & ~MASK_L1_AWHB_SW_LOCK; + val |= (*enable ? MASK_L1_AWHB_SW_LOCK : 0); + viif_capture_write(viif_dev, REG_L1_AWHB_SW, val); +} + +/* Convert the unit of time-period (from sysclk, to num lines in the image) */ +static u32 sysclk_to_numlines(u32 time_in_sysclk, const struct viif_img_clk *img_clk) +{ + u64 v1 = (u64)time_in_sysclk * img_clk->pixel_clock; + u64 v2 = (u64)img_clk->htotal_size * VIIF_SYS_CLK; + + return (u32)div64_u64(v1, v2); +} + +static void viif_l1_set_hdrc(struct viif_device *viif_dev, const struct viif_l1_hdrc_config *arg) +{ + const struct viif_l1_hdrc *param = &arg->param; + u32 val, sw_delay1; + int i; + + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + /* disabling HDRC */ + if (!arg->enable) { + viif_capture_write(viif_dev, REG_L1_HDRC_THR_SFT_AMT, arg->hdrc_thr_sft_amt); + viif_capture_write(viif_dev, REG_L1_HDRC_EN, 0); + return; + } + + /* enabling HDRC */ + viif_capture_write(viif_dev, REG_L1_HDRC_RATIO, + (param->hdrc_ratio - VIIF_L1_HDRC_RATIO_OFFSET)); + viif_capture_write(viif_dev, REG_L1_HDRC_PT_RATIO, param->hdrc_pt_ratio); + + viif_capture_write(viif_dev, REG_L1_HDRC_PT_BLEND, param->hdrc_pt_blend); + viif_capture_write(viif_dev, REG_L1_HDRC_PT_BLEND2, param->hdrc_pt_blend2); + + viif_capture_write(viif_dev, REG_L1_HDRC_PT_SAT, param->hdrc_pt_sat); + viif_capture_write(viif_dev, REG_L1_HDRC_TN_TYPE, param->hdrc_tn_type); + + for (i = 0; i < LEN_L1_HDRC_UTN_TBL; i++) + viif_capture_write(viif_dev, REG_L1_HDRC_UTN_TBL(i), param->hdrc_utn_tbl[i]); + + viif_capture_write(viif_dev, REG_L1_HDRC_FLR_VAL, param->hdrc_flr_val); + viif_capture_write(viif_dev, REG_L1_HDRC_FLR_ADP, param->hdrc_flr_adp ? 1 : 0); + + viif_capture_write(viif_dev, REG_L1_HDRC_YBR_OFF, param->hdrc_ybr_off ? 1 : 0); + viif_capture_write(viif_dev, REG_L1_HDRC_ORGY_BLEND, param->hdrc_orgy_blend); + + val = ((viif_capture_read(viif_dev, REG_L1_SYSM_HEIGHT)) % 64U) / 2U; + viif_capture_write(viif_dev, REG_L1_HDRC_MAR_TOP, val); + val = ((viif_capture_read(viif_dev, REG_L1_SYSM_WIDTH)) % 64U) / 2U; + viif_capture_write(viif_dev, REG_L1_HDRC_MAR_LEFT, val); + + viif_capture_write(viif_dev, REG_L1_HDRC_EN, 1); + + /* update of sw_delay1 must be done when MAIN unit is NOT running. */ + if (!viif_dev->run_flag_main) { + sw_delay1 = sysclk_to_numlines(VIIF_REGBUF_ACCESS_TIME, &viif_dev->img_clk) + + VIIF_L1_DELAY_W_HDRC + 1U; + val = viif_capture_read(viif_dev, REG_INT_M1_LINE) & 0xffffU; + val |= (sw_delay1 << 16U); + viif_capture_write(viif_dev, REG_INT_M1_LINE, val); + /* M2_LINE is the same condition as M1_LINE */ + viif_capture_write(viif_dev, REG_INT_M2_LINE, val); + } +} + +static void viif_l1_set_hdrc_ltm(struct viif_device *viif_dev, + const struct viif_l1_hdrc_ltm_config *arg) +{ + int i; + + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + viif_capture_write(viif_dev, REG_L1_HDRC_TNP_MAX, arg->tnp_max); + viif_capture_write(viif_dev, REG_L1_HDRC_TNP_MAG, arg->tnp_mag); + + for (i = 0; i < LEN_L1_HDRC_TNP_FIL; i++) + viif_capture_write(viif_dev, REG_L1_HDRC_TNP_FIL(i), (u32)arg->tnp_fil[i]); +} + +static void viif_l1_set_gamma(struct viif_device *viif_dev, const struct viif_l1_gamma_config *arg) +{ + const struct viif_l1_gamma *param = &arg->param; + int i; + + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + /* disabling L1 gamma */ + if (!arg->enable) { + viif_capture_write(viif_dev, REG_L1_VPRO_PGC_SW, 0); + return; + } + + /* enabling L1 gamma */ + for (i = 0; i < 44; i++) + viif_capture_write(viif_dev, REG_L1_VPRO_GAMxP(i), param->gam_p[i]); + viif_capture_write(viif_dev, REG_L1_VPRO_BLKADJ, param->blkadj); + viif_capture_write(viif_dev, REG_L1_VPRO_PGC_SW, 1); +} + +static void +viif_l1_set_img_quality_adjustment(struct viif_device *viif_dev, + const struct viif_l1_img_quality_adjustment_config *arg) +{ + u32 val; + + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + /* RGB to YUV (enabled by default, should be enabled) */ + viif_capture_write(viif_dev, REG_L1_VPRO_YUVC_SW, 1); + viif_capture_write(viif_dev, REG_L1_VPRO_CB_MAT, (u32)arg->coef_cb); + viif_capture_write(viif_dev, REG_L1_VPRO_CR_MAT, (u32)arg->coef_cr); + + /* brightness */ + val = (u32)arg->brightness & 0xffffU; + if (val) { + viif_capture_write(viif_dev, REG_L1_VPRO_BRIGHT_SW, 1); + viif_capture_write(viif_dev, REG_L1_VPRO_BRIGHT, val); + } else { + viif_capture_write(viif_dev, REG_L1_VPRO_BRIGHT_SW, 0); + } + + /* linear contrast */ + if ((u32)arg->linear_contrast != 128U) { + viif_capture_write(viif_dev, REG_L1_VPRO_LCNT_SW, 1); + viif_capture_write(viif_dev, REG_L1_VPRO_LCONT_LEV, arg->linear_contrast); + } else { + viif_capture_write(viif_dev, REG_L1_VPRO_LCNT_SW, 0); + } + + /* nonlinear contrast */ + if (arg->enable & VIIF_L1_IQA_NONLINEAR_CONTRAST_EN_MASK) { + const struct viif_l1_nonlinear_contrast *nonlinear_contrast = + &arg->nonlinear_contrast; + + viif_capture_write(viif_dev, REG_L1_VPRO_NLCNT_SW, 1); + viif_capture_write(viif_dev, REG_L1_VPRO_BLK_KNEE, nonlinear_contrast->blk_knee); + viif_capture_write(viif_dev, REG_L1_VPRO_WHT_KNEE, nonlinear_contrast->wht_knee); + + viif_capture_write(viif_dev, REG_L1_VPRO_BLK_CONT0, + nonlinear_contrast->blk_cont[0]); + viif_capture_write(viif_dev, REG_L1_VPRO_BLK_CONT1, + nonlinear_contrast->blk_cont[1]); + viif_capture_write(viif_dev, REG_L1_VPRO_BLK_CONT2, + nonlinear_contrast->blk_cont[2]); + + viif_capture_write(viif_dev, REG_L1_VPRO_WHT_CONT0, + nonlinear_contrast->wht_cont[0]); + viif_capture_write(viif_dev, REG_L1_VPRO_WHT_CONT1, + nonlinear_contrast->wht_cont[1]); + viif_capture_write(viif_dev, REG_L1_VPRO_WHT_CONT2, + nonlinear_contrast->wht_cont[2]); + } else { + viif_capture_write(viif_dev, REG_L1_VPRO_NLCNT_SW, 0); + } + + /* luminance noise reduction */ + if (arg->enable & VIIF_L1_IQA_LUM_NOISE_REDUCTION_EN_MASK) { + const struct viif_l1_lum_noise_reduction *lum_noise_reduction = + &arg->lum_noise_reduction; + + viif_capture_write(viif_dev, REG_L1_VPRO_YNR_SW, 1); + viif_capture_write(viif_dev, REG_L1_VPRO_YNR_GAIN_MIN, + lum_noise_reduction->gain_min); + viif_capture_write(viif_dev, REG_L1_VPRO_YNR_GAIN_MAX, + lum_noise_reduction->gain_max); + viif_capture_write(viif_dev, REG_L1_VPRO_YNR_LIM_MIN, lum_noise_reduction->lim_min); + viif_capture_write(viif_dev, REG_L1_VPRO_YNR_LIM_MAX, lum_noise_reduction->lim_max); + } else { + viif_capture_write(viif_dev, REG_L1_VPRO_YNR_SW, 0); + } + + /* edge enhancement */ + if (arg->enable & VIIF_L1_IQA_EDGE_ENHANCEMENT_EN_MASK) { + const struct viif_l1_edge_enhancement *edge_enhancement = &arg->edge_enhancement; + + viif_capture_write(viif_dev, REG_L1_VPRO_ETE_SW, 1); + viif_capture_write(viif_dev, REG_L1_VPRO_ETE_GAIN_MIN, edge_enhancement->gain_min); + viif_capture_write(viif_dev, REG_L1_VPRO_ETE_GAIN_MAX, edge_enhancement->gain_max); + viif_capture_write(viif_dev, REG_L1_VPRO_ETE_LIM_MIN, edge_enhancement->lim_min); + viif_capture_write(viif_dev, REG_L1_VPRO_ETE_LIM_MAX, edge_enhancement->lim_max); + viif_capture_write(viif_dev, REG_L1_VPRO_ETE_CORING_MIN, + edge_enhancement->coring_min); + viif_capture_write(viif_dev, REG_L1_VPRO_ETE_CORING_MAX, + edge_enhancement->coring_max); + } else { + viif_capture_write(viif_dev, REG_L1_VPRO_ETE_SW, 0); + } + + /* UV suppression */ + if (arg->enable & VIIF_L1_IQA_UV_SUPPRESSION_EN_MASK) { + const struct viif_l1_uv_suppression *uv_suppression = &arg->uv_suppression; + + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_UVSUP_SW, 1); + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_BK_SLV, uv_suppression->bk_slv); + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_BK_MP, uv_suppression->bk_mp); + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_BLACK, uv_suppression->black); + + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_WH_SLV, uv_suppression->wh_slv); + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_WH_MP, uv_suppression->wh_mp); + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_WHITE, uv_suppression->white); + } else { + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_UVSUP_SW, 0); + } + + /* coring suppression */ + if (arg->enable & VIIF_L1_IQA_CORING_SUPPRESSION_EN_MASK) { + const struct viif_l1_coring_suppression *coring_suppression = + &arg->coring_suppression; + + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_CORING_SW, 1); + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_CORING_LV_MIN, + coring_suppression->lv_min); + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_CORING_LV_MAX, + coring_suppression->lv_max); + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_CORING_GAIN_MIN, + coring_suppression->gain_min); + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_CORING_GAIN_MAX, + coring_suppression->gain_max); + } else { + viif_capture_write(viif_dev, REG_L1_VPRO_CSUP_CORING_SW, 0); + } + + /* edge suppression */ + if (arg->enable & VIIF_L1_IQA_EDGE_SUPPRESSION_EN_MASK) { + const struct viif_l1_edge_suppression *edge_suppression = &arg->edge_suppression; + + viif_capture_write(viif_dev, REG_L1_VPRO_EDGE_SUP_SW, 1); + viif_capture_write(viif_dev, REG_L1_VPRO_EDGE_SUP_GAIN, edge_suppression->gain); + viif_capture_write(viif_dev, REG_L1_VPRO_EDGE_SUP_LIM, edge_suppression->lim); + } else { + viif_capture_write(viif_dev, REG_L1_VPRO_EDGE_SUP_SW, 0); + } + + /* color level */ + if (arg->enable & VIIF_L1_IQA_COLOR_LEVEL_EN_MASK) { + const struct viif_l1_color_level *color_level = &arg->color_level; + + viif_capture_write(viif_dev, REG_L1_VPRO_CB_GAIN, color_level->cb_gain); + viif_capture_write(viif_dev, REG_L1_VPRO_CR_GAIN, color_level->cr_gain); + viif_capture_write(viif_dev, REG_L1_VPRO_CBR_MGAIN_MIN, color_level->cbr_mgain_min); + viif_capture_write(viif_dev, REG_L1_VPRO_CB_P_GAIN_MAX, color_level->cbp_gain_max); + viif_capture_write(viif_dev, REG_L1_VPRO_CB_M_GAIN_MAX, color_level->cbm_gain_max); + viif_capture_write(viif_dev, REG_L1_VPRO_CR_P_GAIN_MAX, color_level->crp_gain_max); + viif_capture_write(viif_dev, REG_L1_VPRO_CR_M_GAIN_MAX, color_level->crm_gain_max); + } else { + /* disable */ + viif_capture_write(viif_dev, REG_L1_VPRO_CB_GAIN, 1024U); + viif_capture_write(viif_dev, REG_L1_VPRO_CR_GAIN, 1024U); + viif_capture_write(viif_dev, REG_L1_VPRO_CBR_MGAIN_MIN, 1024U); + viif_capture_write(viif_dev, REG_L1_VPRO_CB_P_GAIN_MAX, 0U); + viif_capture_write(viif_dev, REG_L1_VPRO_CB_M_GAIN_MAX, 0U); + viif_capture_write(viif_dev, REG_L1_VPRO_CR_P_GAIN_MAX, 0U); + viif_capture_write(viif_dev, REG_L1_VPRO_CR_M_GAIN_MAX, 0U); + } + + /* color noise reduction */ + viif_capture_write(viif_dev, REG_L1_VPRO_CNR_SW, + arg->enable & VIIF_L1_IQA_COLOR_NOISE_REDUCTION_EN_MASK ? 1 : 0); +} + +static inline u32 pack_weight(const u32 *vec) +{ + return (vec[0] << 14) | (vec[1] << 12) | (vec[2] << 10) | (vec[3] << 8) | (vec[4] << 6) | + (vec[5] << 4) | (vec[6] << 2U) | (vec[7]); +} + +static void viif_l1_set_avg_lum_generation(struct viif_device *viif_dev, + const struct viif_l1_avg_lum_generation_config *arg) +{ + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + /* disabling aggregation */ + if (!arg->enable) { + viif_capture_write(viif_dev, REG_L1_AEXP_ON, 0); + return; + } + + /* enabling aggregation */ + viif_capture_write(viif_dev, REG_L1_AEXP_ON, 1); + viif_capture_write(viif_dev, REG_L1_AEXP_START_X, arg->aexp_start_x); + viif_capture_write(viif_dev, REG_L1_AEXP_START_Y, arg->aexp_start_y); + viif_capture_write(viif_dev, REG_L1_AEXP_BLOCK_WIDTH, arg->aexp_block_width); + viif_capture_write(viif_dev, REG_L1_AEXP_BLOCK_HEIGHT, arg->aexp_block_height); + + viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_0, pack_weight(arg->aexp_weight[0])); + viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_1, pack_weight(arg->aexp_weight[1])); + viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_2, pack_weight(arg->aexp_weight[2])); + viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_3, pack_weight(arg->aexp_weight[3])); + viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_3, pack_weight(arg->aexp_weight[4])); + viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_4, pack_weight(arg->aexp_weight[5])); + viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_5, pack_weight(arg->aexp_weight[6])); + viif_capture_write(viif_dev, REG_L1_AEXP_WEIGHT_7, pack_weight(arg->aexp_weight[7])); + + viif_capture_write(viif_dev, REG_L1_AEXP_SATUR_RATIO, arg->aexp_satur_ratio); + viif_capture_write(viif_dev, REG_L1_AEXP_BLACK_RATIO, arg->aexp_black_ratio); + viif_capture_write(viif_dev, REG_L1_AEXP_SATUR_LEVEL, arg->aexp_satur_level); + + viif_capture_write(viif_dev, REG_L1_AEXP_AVE4LINESY0, arg->aexp_ave4linesy[0]); + viif_capture_write(viif_dev, REG_L1_AEXP_AVE4LINESY1, arg->aexp_ave4linesy[1]); + viif_capture_write(viif_dev, REG_L1_AEXP_AVE4LINESY2, arg->aexp_ave4linesy[2]); + viif_capture_write(viif_dev, REG_L1_AEXP_AVE4LINESY3, arg->aexp_ave4linesy[3]); +} + +static void undist_table_transmission(struct viif_device *viif_dev, dma_addr_t write_g, + dma_addr_t read_b, dma_addr_t read_g, dma_addr_t read_r, + u32 size) +{ + u32 val = 0U; + + if (read_b) { + viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(IDX_TPORT_L2_UNDIST_RD_B), + (u32)read_b); + viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(IDX_TPORT_L2_UNDIST_RD_B), size); + val |= MASK_VDM_T_ENABLE_L2_UNDIST_RD_B; + } + if (read_g) { + viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(IDX_TPORT_L2_UNDIST_RD_G), + (u32)read_g); + viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(IDX_TPORT_L2_UNDIST_RD_G), size); + val |= MASK_VDM_T_ENABLE_L2_UNDIST_RD_G; + } + if (read_r) { + viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(IDX_TPORT_L2_UNDIST_RD_R), + (u32)read_r); + viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(IDX_TPORT_L2_UNDIST_RD_R), size); + val |= MASK_VDM_T_ENABLE_L2_UNDIST_RD_R; + } + if (write_g) { + viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(IDX_TPORT_L2_UNDIST_WR_G), + (u32)write_g); + viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(IDX_TPORT_L2_UNDIST_WR_G), size); + val |= MASK_VDM_T_ENABLE_L2_UNDIST_WR_G; + } + + if (val) + viif_config_vdm_tgroup(viif_dev, IDX_TGROUP_L2_UNDIST); + + val |= viif_capture_read(viif_dev, REG_VDM_T_ENABLE) & ~MASK_VDM_T_ENABLE_L2_UNDIST; + viif_capture_write(viif_dev, REG_VDM_T_ENABLE, val); +} + +static void undist_setup(struct viif_device *viif_dev, const struct viif_l2_undist *param) +{ + u32 val; + unsigned int i; + + /* Undist through mode */ + if (param->through_mode) { + /* Enable through mode */ + viif_capture_write(viif_dev, REG_L2_MODE, 1); + return; + } + + /* Undist operation */ + val = (param->roi_mode[0] << 1U) | (param->roi_mode[1] << 3U); + viif_capture_write(viif_dev, REG_L2_MODE, val); + val = (u32)param->sensor_crop_ofs_h & GENMASK(13, 0); + viif_capture_write(viif_dev, REG_L2_SENSOR_CROP_OFS_H, val); + val = (u32)param->sensor_crop_ofs_v & GENMASK(12, 0); + viif_capture_write(viif_dev, REG_L2_SENSOR_CROP_OFS_V, val); + viif_capture_write(viif_dev, REG_L2_NORM_SCALE, param->norm_scale); + viif_capture_write(viif_dev, REG_L2_VALID_R_NORM2_POLY, param->valid_r_norm2_poly); + viif_capture_write(viif_dev, REG_L2_VALID_R_NORM2_GRID, param->valid_r_norm2_grid); + viif_capture_write(viif_dev, REG_L2_ROI_WRITE_AREA_DELTA(0), + param->roi_write_area_delta[0]); + viif_capture_write(viif_dev, REG_L2_ROI_WRITE_AREA_DELTA(1), + param->roi_write_area_delta[1]); + + for (i = 0; i < VIIF_L2_UNDIST_POLY_NUM; i++) { + val = (u32)param->poly_write_g_coef[i]; + viif_capture_write(viif_dev, REG_L2_POLY10_WRITE_G_COEF(i), val); + val = (u32)param->poly_read_b_coef[i]; + viif_capture_write(viif_dev, REG_L2_POLY10_READ_B_COEF(i), val); + val = (u32)param->poly_read_g_coef[i]; + viif_capture_write(viif_dev, REG_L2_POLY10_READ_G_COEF(i), val); + val = (u32)param->poly_read_r_coef[i]; + viif_capture_write(viif_dev, REG_L2_POLY10_READ_R_COEF(i), val); + } + viif_capture_write(viif_dev, REG_L2_GRID_NODE_NUM_H, param->grid_node_num_h); + viif_capture_write(viif_dev, REG_L2_GRID_NODE_NUM_V, param->grid_node_num_v); + viif_capture_write(viif_dev, REG_L2_GRID_PATCH_HSIZE_INV, param->grid_patch_hsize_inv); + viif_capture_write(viif_dev, REG_L2_GRID_PATCH_VSIZE_INV, param->grid_patch_vsize_inv); +} + +static void viif_l2_set_undist(struct viif_device *viif_dev, + const struct viif_l2_undist_config *arg) +{ + dma_addr_t table_write_g = 0; + dma_addr_t table_read_b = 0; + dma_addr_t table_read_g = 0; + dma_addr_t table_read_r = 0; + + if (arg->param.roi_mode[0] != VIIF_L2_UNDIST_POLY || + arg->param.roi_mode[1] != VIIF_L2_UNDIST_POLY) { + memcpy(viif_dev->tables->undist_write_g, arg->write_g, arg->size); + memcpy(viif_dev->tables->undist_read_b, arg->read_b, arg->size); + memcpy(viif_dev->tables->undist_read_g, arg->read_g, arg->size); + memcpy(viif_dev->tables->undist_read_r, arg->read_r, arg->size); + + table_write_g = + viif_dev->tables_dma + offsetof(struct viif_table_area, undist_write_g); + table_read_b = + viif_dev->tables_dma + offsetof(struct viif_table_area, undist_read_b); + table_read_g = + viif_dev->tables_dma + offsetof(struct viif_table_area, undist_read_g); + table_read_r = + viif_dev->tables_dma + offsetof(struct viif_table_area, undist_read_r); + } + { + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + undist_table_transmission(viif_dev, table_write_g, table_read_b, table_read_g, + table_read_r, arg->size); + undist_setup(viif_dev, &arg->param); + } +} + +void visconti_viif_l2_undist_through(struct viif_device *viif_dev) +{ + struct viif_l2_undist undist = { 0 }; + + undist.through_mode = VIIF_ENABLE; + undist.sensor_crop_ofs_h = + 1 - FIELD_GET(0x1fff, viif_capture_read(viif_dev, REG_L2_SENSOR_CROP_HSIZE)); + undist.sensor_crop_ofs_v = + 1 - FIELD_GET(0x0fff, viif_capture_read(viif_dev, REG_L2_SENSOR_CROP_VSIZE)); + undist.grid_node_num_h = 16; + undist.grid_node_num_v = 16; + + undist_setup(viif_dev, &undist); +} + +void visconti_viif_l2_undist_identity(struct viif_device *viif_dev) +{ + struct viif_l2_undist undist = { + .through_mode = 0, + .roi_mode = { 0, 0 }, + .sensor_crop_ofs_h = 0, + .sensor_crop_ofs_v = 0, + .norm_scale = 30464, + .valid_r_norm2_poly = 33554432, + .valid_r_norm2_grid = 0, + .roi_write_area_delta = { 100, 100 }, + .poly_write_g_coef = { 131072, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + .poly_read_b_coef = { 131072, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + .poly_read_g_coef = { 131072, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + .poly_read_r_coef = { 131072, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + .grid_node_num_h = 16, + .grid_node_num_v = 16, + .grid_patch_hsize_inv = 0, + .grid_patch_vsize_inv = 0, + }; + undist.sensor_crop_ofs_h = + 1 - FIELD_GET(0x1fff, viif_capture_read(viif_dev, REG_L2_SENSOR_CROP_HSIZE)); + undist.sensor_crop_ofs_v = + 1 - FIELD_GET(0x0fff, viif_capture_read(viif_dev, REG_L2_SENSOR_CROP_VSIZE)); + + undist_setup(viif_dev, &undist); +} + +static void viif_l2_set_roi(struct viif_device *viif_dev, const struct viif_l2_roi_config *roi) +{ + u32 val; + int i; + + /* update ROI parameter */ + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + /* Set the number of ROI and update resource info with roi_num */ + viif_capture_write(viif_dev, REG_L2_ROI_NUM, roi->roi_num); + viif_dev->l2_roi_path_info.roi_num = roi->roi_num; + + /* Update ROI area and input to each POST */ + visconti_viif_l2_set_roi_path(viif_dev); + + /* Set the remaining parameters */ + for (i = 0; i < 2; i++) { + viif_capture_write(viif_dev, REG_L2_ROI_X_SCALE(i), roi->roi_scale[i]); + viif_capture_write(viif_dev, REG_L2_ROI_X_SCALE_INV(i), roi->roi_scale_inv[i]); + val = (roi->corrected_wo_scale_hsize[i] << 13U) | roi->corrected_hsize[i]; + viif_capture_write(viif_dev, REG_L2_ROI_X_CORRECTED_HSIZE(i), val); + val = (roi->corrected_wo_scale_vsize[i] << 12U) | roi->corrected_vsize[i]; + viif_capture_write(viif_dev, REG_L2_ROI_X_CORRECTED_VSIZE(i), val); + } +} + +static void viif_l2_set_roi1(struct viif_device *viif_dev, const struct viif_l2_roi_config *roi) +{ + u32 val; + + /* update ROI parameter */ + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + /* Check if ROI1 is already set */ + if (viif_dev->l2_roi_path_info.roi_num != 1) + return; + viif_capture_write(viif_dev, REG_L2_ROI_NUM, roi->roi_num); + viif_dev->l2_roi_path_info.roi_num = roi->roi_num; + + /* Update ROI area and input to each POST */ + visconti_viif_l2_set_roi_path(viif_dev); + + /* Set the remaining parameters */ + viif_capture_write(viif_dev, REG_L2_ROI_X_SCALE(1), roi->roi_scale[1]); + viif_capture_write(viif_dev, REG_L2_ROI_X_SCALE_INV(1), roi->roi_scale_inv[1]); + val = (roi->corrected_wo_scale_hsize[1] << 13U) | roi->corrected_hsize[1]; + viif_capture_write(viif_dev, REG_L2_ROI_X_CORRECTED_HSIZE(1), val); + val = (roi->corrected_wo_scale_vsize[1] << 12U) | roi->corrected_vsize[1]; + viif_capture_write(viif_dev, REG_L2_ROI_X_CORRECTED_VSIZE(1), val); +} + +static void generate_scaler_parameter(struct viif_device *viif_dev, struct viif_l2_roi_config *roi, + unsigned int width, unsigned int height) +{ + u32 src_width, src_height; + u32 scale_ratio, scale_ratio_inv; + + src_width = FIELD_GET(0x1fff, viif_capture_read(viif_dev, REG_L2_SENSOR_CROP_HSIZE)); + src_height = FIELD_GET(0x0fff, viif_capture_read(viif_dev, REG_L2_SENSOR_CROP_VSIZE)); + + if (width * src_height < height * src_width) { + /* scale_ratioX < scale_ratioY: use scale_ratioX */ + scale_ratio = clamp(65536 * width / src_width, 32768, 131072); + scale_ratio_inv = clamp(65536 * src_width / width, 32768, 131072); + } else { + scale_ratio = clamp(65536 * height / src_height, 32768, 131072); + scale_ratio_inv = clamp(65536 * src_height / height, 32768, 131072); + } + + struct viif_l2_roi_config roi_scaler = { + .roi_num = 0, + .roi_scale = { scale_ratio, scale_ratio }, + .roi_scale_inv = { scale_ratio_inv, scale_ratio_inv }, + .corrected_wo_scale_hsize = { src_width, src_width }, + .corrected_wo_scale_vsize = { src_height, src_height }, + .corrected_hsize = { width, width }, + .corrected_vsize = { height, height }, + }; + + *roi = roi_scaler; +} + +void visconti_viif_l2_scale_default_roi0(struct viif_device *viif_dev, unsigned int width, + unsigned int height) +{ + struct viif_l2_roi_config roi; + + generate_scaler_parameter(viif_dev, &roi, width, height); + roi.roi_num = 1; + viif_l2_set_roi(viif_dev, &roi); +} + +void visconti_viif_l2_scale_default_roi1(struct viif_device *viif_dev, unsigned int width, + unsigned int height) +{ + struct viif_l2_roi_config roi; + + generate_scaler_parameter(viif_dev, &roi, width, height); + roi.roi_num = 2; + viif_l2_set_roi1(viif_dev, &roi); +} + +struct viif_l2_gamma_table { + dma_addr_t table[VIIF_L2_GAMMA_TABLE_CH_NUM]; +}; + +static void l2_gamma_table_transmission(struct viif_device *viif_dev, u32 post_id, + const struct viif_l2_gamma_table *gamma_table) +{ + u32 vdm_enable = 0U; + u32 i; + + /* 0: LUT0-G/Y, 1: LUT1-G/Y, 2: LUT0-B/U, 3: LUT1-B/U, 4: LUT0-R/V, 5: LUT1-R/V */ + for (i = 0; i < VIIF_L2_GAMMA_TABLE_CH_NUM; i++) { + if (gamma_table->table[i]) { + int idx = IDX_TPORT_L2_GAMMA_LUT(post_id, i); + + viif_capture_write(viif_dev, REG_VDM_TPORT_X_STADR(idx), + (u32)gamma_table->table[i]); + viif_capture_write(viif_dev, REG_VDM_TPORT_X_SIZE(idx), + VIIF_L2_GAMMA_TABLE_BYTES); + vdm_enable |= MASK_VDM_T_ENABLE_L2_GAMMA(post_id, i); + } + } + if (vdm_enable) + viif_config_vdm_tgroup(viif_dev, IDX_TGROUP_L2_GAMMA_LUT(post_id)); + + vdm_enable |= viif_capture_read(viif_dev, REG_VDM_T_ENABLE) & + ~MASK_VDM_T_ENABLE_L2_GAMMA_ALL(post_id); + + viif_capture_write(viif_dev, REG_VDM_T_ENABLE, vdm_enable); +} + +static void viif_l2_set_gamma(struct viif_device *viif_dev, int pathid, + const struct viif_l2_gamma_config *l2_gamma) +{ + struct viif_l2_gamma_table dma_table = { 0 }; + int postid = (pathid == CAPTURE_PATH_MAIN_POST0) ? VIIF_L2ISP_POST_0 : VIIF_L2ISP_POST_1; + int table_en; + u32 val; + int i; + + table_en = l2_gamma->table_en; + for (i = 0; i < 6; i++) { + if (table_en & BIT(i)) { + memcpy(viif_dev->tables->l2_gamma_table[pathid][i], l2_gamma->table[i], + VIIF_L2_GAMMA_TABLE_BYTES); + dma_table.table[i] = + viif_dev->tables_dma + + offsetof(struct viif_table_area, l2_gamma_table[pathid][i]); + } + } + { + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + l2_gamma_table_transmission(viif_dev, postid, &dma_table); + + val = (l2_gamma->vsplit << 16U) | (l2_gamma->mode << 4U) | (table_en != 0 ? 1 : 0); + viif_capture_write(viif_dev, REG_L2_POST_X_GAMMA_M(postid), val); + } +} + +static const struct viif_l1_input_mode_config defval_l1_set_input_mode = { + .mode = VIIF_L1_INPUT_HDR, +}; + +static const struct viif_l1_rgb_to_y_coef_config defval_l1_set_rgb_to_y_coef = { + /* ITU-R BT.601 */ + .coef_r = 0x4c8c, + .coef_g = 0x9644, + .coef_b = 0x1d30, +}; + +static const struct viif_l1_ag_mode_config defval_l1_set_ag_mode = { 0 }; + +static const struct viif_l1_ag_config defval_l1_set_ag = { 0 }; + +static const struct viif_l1_hdre_config defval_l1_set_hdre = { + .hdre_src_point[0] = 0x3fff, + .hdre_dst_max_val = 0xffffff, +}; + +static const struct viif_l1_img_extraction_config defval_l1_set_img_extraction = { + .input_black_gr = 256, + .input_black_r = 256, + .input_black_b = 256, + .input_black_gb = 256, +}; + +static const struct viif_l1_dpc_config defval_l1_set_dpc = { 0 }; + +static const struct viif_l1_preset_white_balance_config defval_l1_set_preset_white_balance = { + .dstmaxval = 0x0fff, + .param_h = { + .gain_gr = 0x4000, + .gain_r = 0x4000, + .gain_b = 0x4000, + .gain_gb = 0x4000, + }, + .param_m = { + .gain_gr = 0x4000, + .gain_r = 0x4000, + .gain_b = 0x4000, + .gain_gb = 0x4000, + }, + .param_l = { + .gain_gr = 0x4000, + .gain_r = 0x4000, + .gain_b = 0x4000, + .gain_gb = 0x4000, + }, +}; + +static const +struct viif_l1_raw_color_noise_reduction_config defval_l1_set_raw_color_noise_reduction = { + .param_h = { + .rcnr_cnf_clip_gain_r = 3, + .rcnr_cnf_clip_gain_g = 2, + .rcnr_cnf_clip_gain_b = 3, + .rcnr_merge_black = 0x20, + .rcnr_merge_mindiv = 4, + .rcnr_anf_blend_ag0 = 1, + .rcnr_anf_blend_ag1 = 2, + .rcnr_anf_blend_ag2 = 2, + .rcnr_lpf_threshold = 8, + }, + .param_m = { + .rcnr_cnf_clip_gain_r = 3, + .rcnr_cnf_clip_gain_g = 2, + .rcnr_cnf_clip_gain_b = 3, + .rcnr_merge_black = 0x20, + .rcnr_merge_mindiv = 4, + .rcnr_anf_blend_ag0 = 1, + .rcnr_anf_blend_ag1 = 2, + .rcnr_anf_blend_ag2 = 2, + .rcnr_lpf_threshold = 8, + }, + .param_l = { + .rcnr_cnf_clip_gain_r = 3, + .rcnr_cnf_clip_gain_g = 2, + .rcnr_cnf_clip_gain_b = 3, + .rcnr_merge_black = 0x20, + .rcnr_merge_mindiv = 4, + .rcnr_anf_blend_ag0 = 1, + .rcnr_anf_blend_ag1 = 2, + .rcnr_anf_blend_ag2 = 2, + .rcnr_lpf_threshold = 8, + }, +}; + +static const struct viif_l1_hdrs_config defval_l1_set_hdrs = { + .hdrs_hdr_mode = 1, + .hdrs_hdr_ratio_m = 0x10000, + .hdrs_hdr_ratio_l = 0x400000, + .hdrs_hdr_ratio_e = 0x400, + .hdrs_dg_h = 0x400, + .hdrs_dg_m = 0x400, + .hdrs_dg_l = 0x400, + .hdrs_dg_e = 0x400, + .hdrs_blendend_h = 0xfa0, + .hdrs_blendend_m = 0xfa0, + .hdrs_blendend_e = 0xfa0, + .hdrs_blendbeg_h = 0x12c, + .hdrs_blendbeg_m = 0x12c, + .hdrs_blendbeg_e = 0x12c, + .hdrs_dst_max_val = 0xffffff, +}; + +static const struct viif_l1_black_level_correction_config defval_l1_set_black_level_correction = { + .srcblacklevel_gr = 0x40, + .srcblacklevel_r = 0x40, + .srcblacklevel_b = 0x40, + .srcblacklevel_gb = 0x40, + .mulval_gr = 0x40000, + .mulval_r = 0x40000, + .mulval_b = 0x40000, + .mulval_gb = 0x40000, + .dstmaxval = 0xffffff, +}; + +static const struct viif_l1_lsc_config defval_l1_set_lsc = { 0 }; + +static const struct viif_l1_main_process_config defval_l1_set_main_process = { + .damp_lsbsel = 0x8, + .demosaic_mode = 1, + .colormat_enable = 0, + .dst_maxval = 0xffffff, +}; + +static const struct viif_l1_awb_config defval_l1_set_awb = { + .enable = 0, + .awhb_wbmrg = 256, + .awhb_wbmgg = 256, + .awhb_wbmbg = 256, +}; + +static const u32 defval_l1_lock_awb_gain; + +static const struct viif_l1_hdrc_config defval_l1_set_hdrc = { + .enable = 1, + .param = { + .hdrc_ratio = 0x0e + VIIF_L1_HDRC_RATIO_OFFSET, + .hdrc_pt_ratio = 7, + .hdrc_pt_sat = 0xffc0, + .hdrc_tn_type = 1, + }, +}; + +static const struct viif_l1_hdrc_ltm_config defval_l1_set_hdrc_ltm = { + .tnp_max = 0x3fffff, + .tnp_mag = 0x40, + .tnp_fil = { 0x88, 0x84, 0x7a, 0x6a, 0x54 }, +}; + +static const struct viif_l1_gamma_config defval_l1_set_gamma = { + .enable = 1, + .param = { + .gam_p = { + 0x02f, 0x01b, 0x02a, 0x023, 0x020, 0x037, 0x031, 0x057, 0x04d, 0x088, + 0x078, 0x0d6, 0x0bd, 0x14f, 0x12a, 0x20d, 0x1d3, 0x1ab, 0x18d, 0x2dc, + 0x29e, 0x271, 0x47c, 0x41b, 0x3d4, 0x70a, 0x672, 0x601, 0xb0c, 0xa1d, + 0x96c, 0x8e2, 0x874, 0xfdd, 0xec9, 0xdf2, 0xd42, 0xcb1, 0xc35, 0xbc9, + 0xb6a, 0xb16, 0xacb, 0xa86}, + .blkadj = 0x1000, + }, +}; + +static const struct viif_l1_img_quality_adjustment_config defval_l1_set_img_quality_adjustment = { + .enable = 0, + .coef_cb = 0x9078, + .coef_cr = 0xb699, + .brightness = 0, + .linear_contrast = 128, +}; + +static const struct viif_l1_avg_lum_generation_config defval_l1_set_avg_lum_generation = { + .enable = 0 +}; + +static const struct viif_l2_gamma_config defval_l2_set_gamma = { 0 }; + +static void viif_apply_default_parameter(struct viif_device *viif_dev) +{ + viif_l1_set_input_mode(viif_dev, &defval_l1_set_input_mode); + viif_l1_set_rgb_to_y_coef(viif_dev, &defval_l1_set_rgb_to_y_coef); + viif_l1_set_ag_mode(viif_dev, &defval_l1_set_ag_mode); + viif_l1_set_ag(viif_dev, &defval_l1_set_ag); + viif_l1_set_hdre(viif_dev, &defval_l1_set_hdre); + viif_l1_set_img_extraction(viif_dev, &defval_l1_set_img_extraction); + viif_l1_set_dpc(viif_dev, &defval_l1_set_dpc); + viif_l1_set_preset_white_balance(viif_dev, &defval_l1_set_preset_white_balance); + viif_l1_set_raw_color_noise_reduction(viif_dev, &defval_l1_set_raw_color_noise_reduction); + viif_l1_set_hdrs(viif_dev, &defval_l1_set_hdrs); + viif_l1_set_black_level_correction(viif_dev, &defval_l1_set_black_level_correction); + viif_l1_set_lsc(viif_dev, &defval_l1_set_lsc); + viif_l1_set_main_process(viif_dev, &defval_l1_set_main_process); + viif_l1_set_awb(viif_dev, &defval_l1_set_awb); + viif_l1_lock_awb_gain(viif_dev, &defval_l1_lock_awb_gain); + viif_l1_set_hdrc(viif_dev, &defval_l1_set_hdrc); + viif_l1_set_hdrc_ltm(viif_dev, &defval_l1_set_hdrc_ltm); + viif_l1_set_gamma(viif_dev, &defval_l1_set_gamma); + viif_l1_set_img_quality_adjustment(viif_dev, &defval_l1_set_img_quality_adjustment); + viif_l1_set_avg_lum_generation(viif_dev, &defval_l1_set_avg_lum_generation); + /* l2_undist is already set with visconti_viif_l2_undist_identity() */ + /* l2_roi is already set with visconti_viif_l2_scale_default_roiX() */ + viif_l2_set_gamma(viif_dev, CAPTURE_PATH_MAIN_POST0, &defval_l2_set_gamma); + viif_l2_set_gamma(viif_dev, CAPTURE_PATH_MAIN_POST1, &defval_l2_set_gamma); +} + +/*=======================================================================*/ +/* parameter buffer streaming interface */ +/*=======================================================================*/ +struct viif_buffer { + struct vb2_v4l2_buffer vb; + struct list_head queue; +}; + +static bool viif_params_get_buffer(struct params_dev *params_dev, struct viif_buffer **buf, + struct visconti_viif_isp_config **cfg) +{ + if (list_empty(¶ms_dev->params_queue)) + return false; + + *buf = list_first_entry(¶ms_dev->params_queue, struct viif_buffer, queue); + *cfg = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0); + + return true; +} + +static int viif_apply_queued_parameter(struct viif_device *viif_dev, bool initial_cfg) +{ + struct params_dev *params_dev = &viif_dev->params_dev; + struct visconti_viif_isp_config *new_params; + struct viif_buffer *cur_buf; + + guard(spinlock)(¶ms_dev->params_lock); + + if (!viif_params_get_buffer(params_dev, &cur_buf, &new_params)) + return 1; + + /* evaluate new_params */ + if (initial_cfg) { + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_INPUT_MODE) + viif_l1_set_input_mode(viif_dev, &new_params->l1_input_mode); + } + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_RGB_TO_Y_COEF) + viif_l1_set_rgb_to_y_coef(viif_dev, &new_params->l1_rgb_to_y_coef); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_AG_MODE) + viif_l1_set_ag_mode(viif_dev, &new_params->l1_ag_mode); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_AG) + viif_l1_set_ag(viif_dev, &new_params->l1_ag); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_HDRE) + viif_l1_set_hdre(viif_dev, &new_params->l1_hdre); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_IMG_EXTRACTION) + viif_l1_set_img_extraction(viif_dev, &new_params->l1_img_extraction); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_DPC) + viif_l1_set_dpc(viif_dev, &new_params->l1_dpc); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_PRESET_WHITE_BALANCE) + viif_l1_set_preset_white_balance(viif_dev, &new_params->l1_preset_white_balance); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_RAW_COLOR_NOISE_REDUCTION) + viif_l1_set_raw_color_noise_reduction(viif_dev, + &new_params->l1_raw_color_noise_reduction); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_HDRS) + viif_l1_set_hdrs(viif_dev, &new_params->l1_hdrs); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_BLACK_LEVEL_CORRECTION) + viif_l1_set_black_level_correction(viif_dev, + &new_params->l1_black_level_correction); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_LSC) + viif_l1_set_lsc(viif_dev, &new_params->l1_lsc); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_MAIN_PROCESS) + viif_l1_set_main_process(viif_dev, &new_params->l1_main_process); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_AWB) + viif_l1_set_awb(viif_dev, &new_params->l1_awb); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_LOCK_AWB_GAIN) + viif_l1_lock_awb_gain(viif_dev, &new_params->lock_awb_gain); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_HDRC) + viif_l1_set_hdrc(viif_dev, &new_params->l1_hdrc); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_HDRC_LTM) + viif_l1_set_hdrc_ltm(viif_dev, &new_params->l1_hdrc_ltm); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_GAMMA) + viif_l1_set_gamma(viif_dev, &new_params->l1_gamma); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_IMG_QUALITY_ADJUSTMENT) + viif_l1_set_img_quality_adjustment(viif_dev, + &new_params->l1_img_quality_adjustment); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L1_AVG_LUM_GENERATION) + viif_l1_set_avg_lum_generation(viif_dev, &new_params->l1_avg_lum_generation); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L2_UNDIST) + viif_l2_set_undist(viif_dev, &new_params->l2_undist); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L2_ROI) + viif_l2_set_roi(viif_dev, &new_params->l2_roi); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L2_GAMMA_POST0) + viif_l2_set_gamma(viif_dev, CAPTURE_PATH_MAIN_POST0, &new_params->l2_gamma_post0); + if (new_params->update_cfg & VISCONTI_VIIF_CFG_ISP_L2_GAMMA_POST1) + viif_l2_set_gamma(viif_dev, CAPTURE_PATH_MAIN_POST1, &new_params->l2_gamma_post1); + + /* release buffer */ + list_del(&cur_buf->queue); + cur_buf->vb.sequence = 0; + vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + + return 0; +} + +void visconti_viif_params_isr(struct viif_device *viif_dev) +{ + viif_apply_queued_parameter(viif_dev, false); +} + +void visconti_viif_params_eval_queue(struct viif_device *viif_dev) +{ + if (viif_apply_queued_parameter(viif_dev, true)) + viif_apply_default_parameter(viif_dev); +} + +static int viif_params_enum_fmt_meta_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) +{ + struct video_device *vdev = video_devdata(file); + + if (f->index > 0 || f->type != vdev->queue->type) + return -EINVAL; + + f->pixelformat = V4L2_META_FMT_VISCONTI_VIIF_PARAMS; + + return 0; +} + +static int viif_params_g_fmt_meta_out(struct file *file, void *fh, struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (f->type != vdev->queue->type) + return -EINVAL; + + memset(meta, 0, sizeof(*meta)); + meta->dataformat = V4L2_META_FMT_VISCONTI_VIIF_PARAMS; + meta->buffersize = sizeof(struct visconti_viif_isp_config); + + return 0; +} + +static int viif_params_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + struct video_device *vdev = video_devdata(file); + + strscpy(cap->driver, VIIF_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, vdev->name, sizeof(cap->card)); + + return 0; +} + +static const struct v4l2_ioctl_ops viif_params_ioctl = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_fmt_meta_out = viif_params_enum_fmt_meta_out, + .vidioc_g_fmt_meta_out = viif_params_g_fmt_meta_out, + .vidioc_s_fmt_meta_out = viif_params_g_fmt_meta_out, + .vidioc_try_fmt_meta_out = viif_params_g_fmt_meta_out, + .vidioc_querycap = viif_params_querycap, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations viif_params_fops = { + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, + .poll = vb2_fop_poll, + .open = v4l2_fh_open, + .release = vb2_fop_release, +}; + +static int viif_params_vb2_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + *num_buffers = clamp_t(u32, *num_buffers, 2, 8); + *num_planes = 1; + sizes[0] = sizeof(struct visconti_viif_isp_config); + return 0; +} + +static inline struct viif_buffer *vb2_to_viif(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct viif_buffer, vb); +} + +static inline struct params_dev *vb2queue_to_paramsdev(struct vb2_queue *vq) +{ + return (struct params_dev *)vb2_get_drv_priv(vq); +} + +static void viif_params_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct params_dev *params_dev = vb2queue_to_paramsdev(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct viif_buffer *buf = vb2_to_viif(vbuf); + + guard(spinlock_irq)(¶ms_dev->params_lock); + list_add_tail(&buf->queue, ¶ms_dev->params_queue); +} + +static int viif_params_vb2_buf_prepare(struct vb2_buffer *vb) +{ + if (vb2_plane_size(vb, 0) < sizeof(struct visconti_viif_isp_config)) + return -EINVAL; + + vb2_set_plane_payload(vb, 0, sizeof(struct visconti_viif_isp_config)); + return 0; +} + +static int viif_params_vb2_start_streaming(struct vb2_queue *q, unsigned int arg) +{ + return 0; +} + +static void viif_params_vb2_stop_streaming(struct vb2_queue *q) +{ + struct params_dev *params_dev = vb2queue_to_paramsdev(q); + struct viif_buffer *buf; + LIST_HEAD(tmp_list); + + { + guard(spinlock_irq)(¶ms_dev->params_lock); + list_splice_init(¶ms_dev->params_queue, &tmp_list); + } + + list_for_each_entry(buf, &tmp_list, queue) + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops viif_params_vb2_ops = { + .queue_setup = viif_params_vb2_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_queue = viif_params_vb2_buf_queue, + .buf_prepare = viif_params_vb2_buf_prepare, + .start_streaming = viif_params_vb2_start_streaming, + .stop_streaming = viif_params_vb2_stop_streaming, +}; + +int visconti_viif_params_register(struct viif_device *viif_dev) +{ + struct params_dev *params_dev = &viif_dev->params_dev; + struct video_device *vdev = ¶ms_dev->vdev; + struct vb2_queue *q = ¶ms_dev->vb2_vq; + int ret; + + mutex_init(¶ms_dev->vlock); + INIT_LIST_HEAD(¶ms_dev->params_queue); + spin_lock_init(¶ms_dev->params_lock); + + strscpy(vdev->name, "viif_params", sizeof(vdev->name)); + + /* Register the video device */ + video_set_drvdata(vdev, params_dev); + vdev->ioctl_ops = &viif_params_ioctl; + vdev->fops = &viif_params_fops; + vdev->release = video_device_release_empty; + vdev->lock = ¶ms_dev->vlock; + vdev->v4l2_dev = &viif_dev->v4l2_dev; + vdev->queue = ¶ms_dev->vb2_vq; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT; + vdev->vfl_dir = VFL_DIR_TX; + + /* Initialize vb2 queue */ + q->type = V4L2_BUF_TYPE_META_OUTPUT; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = params_dev; + q->ops = &viif_params_vb2_ops; + q->mem_ops = &vb2_vmalloc_memops; + q->buf_struct_size = sizeof(struct viif_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = ¶ms_dev->vlock; + q->dev = viif_dev->v4l2_dev.dev; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + params_dev->params_pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&vdev->entity, VIIF_PARAMS_PAD_NUM, ¶ms_dev->params_pad); + if (ret) + goto error; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(viif_dev->v4l2_dev.dev, "video_register_device failed: %d\n", ret); + goto error; + } + + return 0; + +error: + media_entity_cleanup(&vdev->entity); + mutex_destroy(¶ms_dev->vlock); + + return ret; +} + +void visconti_viif_params_unregister(struct viif_device *viif_dev) +{ + struct params_dev *params = &viif_dev->params_dev; + struct video_device *vdev = ¶ms->vdev; + + if (!video_is_registered(vdev)) + return; + + vb2_video_unregister_device(vdev); + media_entity_cleanup(&vdev->entity); + mutex_destroy(¶ms->vlock); +} diff --git a/drivers/media/platform/toshiba/visconti/viif_params.h b/drivers/media/platform/toshiba/visconti/viif_params.h new file mode 100644 index 000000000000..1110400c19c9 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_params.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#ifndef __VIIF_PARAMS_H__ +#define __VIIF_PARAMS_H__ + +struct viif_device; + +void visconti_viif_params_eval_queue(struct viif_device *viif_dev); +void visconti_viif_params_isr(struct viif_device *viif_dev); +int visconti_viif_params_register(struct viif_device *viif_dev); +void visconti_viif_params_unregister(struct viif_device *viif_dev); + +void visconti_viif_l2_undist_through(struct viif_device *viif_dev); +void visconti_viif_l2_undist_identity(struct viif_device *viif_dev); +void visconti_viif_l2_scale_default_roi0(struct viif_device *viif_dev, unsigned int width, + unsigned int height); +void visconti_viif_l2_scale_default_roi1(struct viif_device *viif_dev, unsigned int width, + unsigned int height); +#endif /* __VIIF_PARAMS_H__ */ diff --git a/drivers/media/platform/toshiba/visconti/viif_resizer.c b/drivers/media/platform/toshiba/visconti/viif_resizer.c index ec056ff3adc4..d81e8574a50c 100644 --- a/drivers/media/platform/toshiba/visconti/viif_resizer.c +++ b/drivers/media/platform/toshiba/visconti/viif_resizer.c @@ -13,6 +13,7 @@ #include "viif.h" #include "viif_common.h" +#include "viif_params.h" #include "viif_resizer.h" #include "viif_regs.h" @@ -118,12 +119,41 @@ void visconti_viif_l2_set_roi_path(struct viif_device *viif_dev) viif_l2_set_roi_num_2(viif_dev); } +static struct resizer_subdev *subdev_to_resizer(struct v4l2_subdev *sd) +{ + return container_of(sd, struct resizer_subdev, sd); +} + static int visconti_viif_resizer_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) { + struct resizer_subdev *resizer = subdev_to_resizer(sd); + struct viif_device *viif_dev = resizer->viif_dev; + struct v4l2_rect *sink_compose; struct v4l2_subdev *remote_sd; struct media_pad *remote_pad; + int i; + + if (resizer->pathid == RESIZER_PATH_MAIN_POST0) { + /* setup default undist/resize settings */ + viif_dev->l2_roi_path_info.roi_num = 0; + for (i = 0; i < VIIF_MAX_POST_NUM; i++) { + viif_dev->l2_roi_path_info.post_enable_flag[i] = false; + viif_dev->l2_roi_path_info.post_crop_x[i] = 0; + viif_dev->l2_roi_path_info.post_crop_y[i] = 0; + viif_dev->l2_roi_path_info.post_crop_w[i] = 0; + viif_dev->l2_roi_path_info.post_crop_h[i] = 0; + } + visconti_viif_l2_undist_identity(viif_dev); + sink_compose = v4l2_subdev_state_get_compose(state, VIIF_RESIZER_PAD_SINK); + visconti_viif_l2_scale_default_roi0(viif_dev, sink_compose->width, + sink_compose->height); + } else { + sink_compose = v4l2_subdev_state_get_compose(state, VIIF_RESIZER_PAD_SINK); + visconti_viif_l2_scale_default_roi1(viif_dev, sink_compose->width, + sink_compose->height); + } remote_pad = media_pad_remote_pad_first(&sd->entity.pads[VIIF_RESIZER_PAD_SINK]); if (!remote_pad) diff --git a/drivers/media/platform/toshiba/visconti/viif_stats.c b/drivers/media/platform/toshiba/visconti/viif_stats.c new file mode 100644 index 000000000000..14ced29070e9 --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_stats.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#include +#include +#include +#include +#include + +#include "viif.h" +#include "viif_common.h" +#include "viif_isp.h" +#include "viif_regs.h" +#include "viif_stats.h" + +struct viif_buffer { + struct vb2_v4l2_buffer vb; + struct list_head queue; +}; + +static void read_isp_capture_regs(struct viif_l1_info *l1_info, struct viif_device *viif_dev) +{ + int i, j; + u32 val; + + guard(spinlock_irqsave)(&viif_dev->regbuf_lock); + guard(viif_isp)(viif_dev); + + /* change register buffer to regbuf0 where driver gets information */ + viif_capture_write(viif_dev, REG_L1_CRGBF_ACC_CONF, VAL_L1_CRGBF_ACC_CONF_MODE_BUFFER0); + + /* get AWB info */ + l1_info->awb_ave_u = viif_capture_read(viif_dev, REG_L1_AWHB_AVE_USIG); + l1_info->awb_ave_v = viif_capture_read(viif_dev, REG_L1_AWHB_AVE_VSIG); + l1_info->awb_accumulated_pixel = viif_capture_read(viif_dev, REG_L1_AWHB_NUM_UVON); + l1_info->awb_gain_r = viif_capture_read(viif_dev, REG_L1_AWHB_AWBGAINR); + l1_info->awb_gain_g = viif_capture_read(viif_dev, REG_L1_AWHB_AWBGAING); + l1_info->awb_gain_b = viif_capture_read(viif_dev, REG_L1_AWHB_AWBGAINB); + val = viif_capture_read(viif_dev, REG_L1_AWHB_R_CTR_STOP); + l1_info->awb_status_u = (FIELD_GET(BIT(1), val) != 0); + l1_info->awb_status_v = (FIELD_GET(BIT(0), val) != 0); + + /* get average luminance info */ + l1_info->avg_lum_weight = viif_capture_read(viif_dev, REG_L1_AEXP_RESULT_AVE); + val = viif_capture_read(viif_dev, REG_L1_AEXP_SATUR_BLACK_PIXNUM); + l1_info->avg_satur_pixnum = FIELD_GET(GENMASK(31, 16), val); + l1_info->avg_black_pixnum = FIELD_GET(GENMASK(15, 0), val); + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + l1_info->avg_lum_block[i][j] = + viif_capture_read(viif_dev, REG_L1_AEXP_AVE(i, j)); + } + } + l1_info->avg_lum_four_line_lum[0] = viif_capture_read(viif_dev, REG_L1_AEXP_AVE4LINES0); + l1_info->avg_lum_four_line_lum[1] = viif_capture_read(viif_dev, REG_L1_AEXP_AVE4LINES1); + l1_info->avg_lum_four_line_lum[2] = viif_capture_read(viif_dev, REG_L1_AEXP_AVE4LINES2); + l1_info->avg_lum_four_line_lum[3] = viif_capture_read(viif_dev, REG_L1_AEXP_AVE4LINES3); + + /* revert to register access from register buffer access */ + viif_capture_write(viif_dev, REG_L1_CRGBF_ACC_CONF, VAL_L1_CRGBF_ACC_CONF_MODE_BYPASS); +} + +void visconti_viif_stats_isr(struct viif_device *viif_dev, unsigned int sequence, u64 timestamp) +{ + struct visconti_viif_isp_stat *cur_stat_buf; + struct stats_dev *stats_dev = &viif_dev->stats_dev; + struct viif_buffer *cur_buf; + + guard(spinlock)(&stats_dev->stats_lock); + + if (list_empty(&stats_dev->stats_queue)) + return; + + cur_buf = list_first_entry(&stats_dev->stats_queue, struct viif_buffer, queue); + list_del(&cur_buf->queue); + cur_stat_buf = (struct visconti_viif_isp_stat *)vb2_plane_vaddr(&cur_buf->vb.vb2_buf, 0); + + read_isp_capture_regs(&cur_stat_buf->isp_capture.l1_info, viif_dev); + + { + guard(spinlock)(&viif_dev->repflag_lock); + + cur_stat_buf->errors.main = viif_dev->reported_err_main; + cur_stat_buf->errors.sub = viif_dev->reported_err_sub; + viif_dev->reported_err_main = 0; + viif_dev->reported_err_sub = 0; + } + + vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0, sizeof(struct visconti_viif_isp_stat)); + + cur_buf->vb.sequence = sequence; + cur_buf->vb.vb2_buf.timestamp = timestamp; + vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); +} + +static int viif_stats_enum_fmt_meta_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) +{ + struct video_device *vdev = video_devdata(file); + + if (f->index > 0 || f->type != vdev->queue->type) + return -EINVAL; + + f->pixelformat = V4L2_META_FMT_VISCONTI_VIIF_STATS; + + return 0; +} + +static int viif_stats_g_fmt_meta_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (f->type != vdev->queue->type) + return -EINVAL; + + memset(meta, 0, sizeof(*meta)); + meta->dataformat = V4L2_META_FMT_VISCONTI_VIIF_STATS; + meta->buffersize = sizeof(struct visconti_viif_isp_stat); + + return 0; +} + +static int viif_stats_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + struct video_device *vdev = video_devdata(file); + + strscpy(cap->driver, VIIF_DRIVER_NAME, sizeof(cap->driver)); + strscpy(cap->card, vdev->name, sizeof(cap->card)); + + return 0; +} + +static const struct v4l2_ioctl_ops viif_stats_ioctl = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_fmt_meta_cap = viif_stats_enum_fmt_meta_cap, + .vidioc_g_fmt_meta_cap = viif_stats_g_fmt_meta_cap, + .vidioc_s_fmt_meta_cap = viif_stats_g_fmt_meta_cap, + .vidioc_try_fmt_meta_cap = viif_stats_g_fmt_meta_cap, + .vidioc_querycap = viif_stats_querycap, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations viif_stats_fops = { + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, + .poll = vb2_fop_poll, + .open = v4l2_fh_open, + .release = vb2_fop_release, +}; + +static int viif_stats_vb2_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + *num_planes = 1; + *num_buffers = clamp_t(u32, *num_buffers, 2, 8); + sizes[0] = sizeof(struct visconti_viif_isp_stat); + + return 0; +} + +static inline struct viif_buffer *vb2_to_viif(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct viif_buffer, vb); +} + +static inline struct stats_dev *vb2queue_to_statsdev(struct vb2_queue *q) +{ + return (struct stats_dev *)vb2_get_drv_priv(q); +} + +static void viif_stats_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct stats_dev *stats_dev = vb2queue_to_statsdev(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct viif_buffer *buf = vb2_to_viif(vbuf); + + guard(spinlock_irq)(&stats_dev->stats_lock); + list_add_tail(&buf->queue, &stats_dev->stats_queue); +} + +static int viif_stats_vb2_buf_prepare(struct vb2_buffer *vb) +{ + if (vb2_plane_size(vb, 0) < sizeof(struct visconti_viif_isp_stat)) + return -EINVAL; + + vb2_set_plane_payload(vb, 0, sizeof(struct visconti_viif_isp_stat)); + + return 0; +} + +static void viif_stats_vb2_stop_streaming(struct vb2_queue *q) +{ + struct stats_dev *stats_dev = vb2queue_to_statsdev(q); + struct viif_buffer *buf; + unsigned int i; + + guard(spinlock_irq)(&stats_dev->stats_lock); + + for (i = 0; i < 8; i++) { + if (list_empty(&stats_dev->stats_queue)) + break; + buf = list_first_entry(&stats_dev->stats_queue, struct viif_buffer, queue); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } +} + +static const struct vb2_ops viif_stats_vb2_ops = { + .queue_setup = viif_stats_vb2_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_queue = viif_stats_vb2_buf_queue, + .buf_prepare = viif_stats_vb2_buf_prepare, + .stop_streaming = viif_stats_vb2_stop_streaming, +}; + +int visconti_viif_stats_register(struct viif_device *viif_dev) +{ + struct stats_dev *stats_dev = &viif_dev->stats_dev; + struct video_device *vdev = &stats_dev->vdev; + struct vb2_queue *q = &stats_dev->vb2_vq; + int ret; + + mutex_init(&stats_dev->vlock); + INIT_LIST_HEAD(&stats_dev->stats_queue); + spin_lock_init(&stats_dev->stats_lock); + + strscpy(vdev->name, "viif_stats", sizeof(vdev->name)); + + /* Register the video device */ + video_set_drvdata(vdev, stats_dev); + vdev->ioctl_ops = &viif_stats_ioctl; + vdev->fops = &viif_stats_fops; + vdev->release = video_device_release_empty; + vdev->lock = &stats_dev->vlock; + vdev->v4l2_dev = &viif_dev->v4l2_dev; + vdev->queue = &stats_dev->vb2_vq; + vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; + vdev->vfl_dir = VFL_DIR_RX; + + /* Initialize vb2 queue */ + q->type = V4L2_BUF_TYPE_META_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = stats_dev; + q->ops = &viif_stats_vb2_ops; + q->mem_ops = &vb2_vmalloc_memops; + q->buf_struct_size = sizeof(struct viif_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &stats_dev->vlock; + q->dev = viif_dev->v4l2_dev.dev; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + stats_dev->stats_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vdev->entity, VIIF_STATS_PAD_NUM, &stats_dev->stats_pad); + if (ret) + goto error; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(viif_dev->v4l2_dev.dev, "video_register_device failed: %d\n", ret); + goto error; + } + + return 0; + +error: + media_entity_cleanup(&vdev->entity); + mutex_destroy(&stats_dev->vlock); + + return ret; +} + +void visconti_viif_stats_unregister(struct viif_device *viif_dev) +{ + struct stats_dev *stats_dev = &viif_dev->stats_dev; + struct video_device *vdev = &stats_dev->vdev; + + if (!video_is_registered(vdev)) + return; + + vb2_video_unregister_device(vdev); + media_entity_cleanup(&vdev->entity); + mutex_destroy(&stats_dev->vlock); +} diff --git a/drivers/media/platform/toshiba/visconti/viif_stats.h b/drivers/media/platform/toshiba/visconti/viif_stats.h new file mode 100644 index 000000000000..e17c0e5f5a0d --- /dev/null +++ b/drivers/media/platform/toshiba/visconti/viif_stats.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* Toshiba Visconti Video Capture Support + * + * (C) Copyright 2024 TOSHIBA CORPORATION + * (C) Copyright 2024 Toshiba Electronic Devices & Storage Corporation + */ + +#ifndef __VIIF_STATS_H__ +#define __VIIF_STATS_H__ + +void visconti_viif_stats_isr(struct viif_device *viif_dev, unsigned int sequence, u64 timestamp); +int visconti_viif_stats_register(struct viif_device *viif_dev); +void visconti_viif_stats_unregister(struct viif_device *viif_dev); +#endif /* __VIIF_STATS_H__ */ From patchwork Mon Nov 25 09:21:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuji Ishikawa X-Patchwork-Id: 845480 Received: from mo-csw-fb.securemx.jp (mo-csw-fb1801.securemx.jp [210.130.202.160]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ED4271922E7; Mon, 25 Nov 2024 10:12:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.130.202.160 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732529583; cv=none; b=W/SKql3ow7Xln+wPGbjGrn5rhC8Ap2VFA9so8i+hc0oWwfp5S4afON3q8h37+PyjeIoq1mbMpNHNLjovKZ7EN8qnU5ZbV+oXAhrcylwuCzUCHLM7lgNxVmpLtTtO1tUome9D0zfmmFyEaTLm+DKoQncOFeRPknxJu8WRg/cLfQM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732529583; c=relaxed/simple; bh=0Xke7bTPCy40jKbvH7RSfNmEJGkIw8BJ4hX6s5JfObU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=kcJGxB0RQMmn1mKxsXsFszMMKODBwMjD+lTpIO/MNVUwwSHUEHJQsJE/bO5fwDVHkSiFNldbLhQ2Hk7cSq9JDCNnsXf59mb9vgKaqaqLdWSiJd2X8dQLHvzh0X5tc6IJ+Pr1/l/2L798JC/FClI1DpJeNmYnnwdsKeW1DtBMx8U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp; spf=pass smtp.mailfrom=toshiba.co.jp; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b=FZrnAKQR; arc=none smtp.client-ip=210.130.202.160 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b="FZrnAKQR" Received: by mo-csw-fb.securemx.jp (mx-mo-csw-fb1801) id 4AP9RkLL2159793; Mon, 25 Nov 2024 18:27:46 +0900 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=toshiba.co.jp; h=From:To:Cc :Subject:Date:Message-Id:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding;i=yuji2.ishikawa@toshiba.co.jp;s=key2.smx;t= 1732526841; x=1733736441; bh=0Xke7bTPCy40jKbvH7RSfNmEJGkIw8BJ4hX6s5JfObU=; b=FZr nAKQRfNyA2lnxtzVgyW57HJemilhdJj1NCITH8lU1KGZxa/8ZkjbZlfzEHN06XsjN4ZHYh8A7d91N HW6tvDhInWFnDI1h27Lci1qGbgWhdes4cPVjjOWeYFnP6zsLiS6q3yx3aoCgWbsNeFVmOhmPId1QP tTQSHA1m9o8aUW8FDrj2SRQqSYcdIOSeYieCcpmUGa0817CDWS+8Xoxe1HGLm+02vObetSnxfvWWd LcliGFnrT/g4FZo4BX876E6UjZfR4JpCOZTRNai5e0KPzdLEjlgnDomjS1/ufPqjgDawcoQdRt3iJ Z+G6KGODZX0+tspEnkFBn9sUiUR2diQ==; Received: by mo-csw.securemx.jp (mx-mo-csw1800) id 4AP9RJIg024187; Mon, 25 Nov 2024 18:27:19 +0900 X-Iguazu-Qid: 2yAafc7cGiz43BXZue X-Iguazu-QSIG: v=2; s=0; t=1732526838; q=2yAafc7cGiz43BXZue; m=+ThVAuMIKLy377WOKXKvorPZceR13tnluF7yD+ElZ7s= Received: from imx2-a.toshiba.co.jp (imx2-a.toshiba.co.jp [106.186.93.35]) by relay.securemx.jp (mx-mr1803) id 4AP9RHSQ3470709 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 25 Nov 2024 18:27:17 +0900 X-SA-MID: 35004183 From: Yuji Ishikawa To: Laurent Pinchart , Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sakari Ailus , Hans Verkuil , Nobuhiro Iwamatsu , Yuji Ishikawa Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Subject: [PATCH v12 7/8] documentation: media: add documentation for Toshiba Visconti Video Input Interface driver Date: Mon, 25 Nov 2024 18:21:45 +0900 X-TSB-HOP2: ON Message-Id: <20241125092146.1561901-8-yuji2.ishikawa@toshiba.co.jp> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> References: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Added description of Video Input Interface driver of Toshiba Visconti architecture. It includes hardware organization, structure of the driver and metadata format for embedded image signal processor. Signed-off-by: Yuji Ishikawa --- Changelog v3: - Newly add documentation to describe SW and HW Changelog v4: - no change Changelog v5: - no change Changelog v6: - add description of CSI2RX subdevice - add ordering of ioctl(S_FMT) and ioctl(S_EXT_CTRLS) Changelog v7: - no change Changelog v8: - add usage of V4L2_CTRL_TYPE_VISCONTI_ISP Changelog v9: - fix warning: set reference target for keyword V4L2_CTRL_TYPE_VISCONTI_ISP Changelog v10: - use parameter buffers instead of compound control - removed description of vendor specific compound control - add description of parameter buffers for ISP control - update directory structure - remove documents under driver-api - add documents to admin-guide, userspace-api Changelog v11: - update usage of the driver Changelog v12: - add description of CSI2RX driver - description of resizer subdevice - add block diagrams of VIIF and ISP - update usage of the driver .../admin-guide/media/v4l-drivers.rst | 1 + .../admin-guide/media/visconti-viif.dot | 22 + .../admin-guide/media/visconti-viif.rst | 435 ++++++++++++++++++ .../userspace-api/media/v4l/meta-formats.rst | 1 + .../media/v4l/metafmt-visconti-viif.rst | 48 ++ 5 files changed, 507 insertions(+) create mode 100644 Documentation/admin-guide/media/visconti-viif.dot create mode 100644 Documentation/admin-guide/media/visconti-viif.rst create mode 100644 Documentation/userspace-api/media/v4l/metafmt-visconti-viif.rst diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst index b6af448b9fe9..81054512e768 100644 --- a/Documentation/admin-guide/media/v4l-drivers.rst +++ b/Documentation/admin-guide/media/v4l-drivers.rst @@ -32,5 +32,6 @@ Video4Linux (V4L) driver-specific documentation si476x starfive_camss vimc + visconti-viif visl vivid diff --git a/Documentation/admin-guide/media/visconti-viif.dot b/Documentation/admin-guide/media/visconti-viif.dot new file mode 100644 index 000000000000..cc75c73336fb --- /dev/null +++ b/Documentation/admin-guide/media/visconti-viif.dot @@ -0,0 +1,22 @@ +digraph board { + rankdir=TB + n00000001 [label="{{ 0 | 4} | visconti-viif:isp\n/dev/v4l-subdev0 | { 1 | 2 | 3 | 5}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port1 -> n00000008:port0 + n00000001:port2 -> n0000000b:port0 + n00000001:port3 -> n00000016 + n00000001:port5 -> n0000001e + n00000008 [label="{{ 0} | visconti-viif:resizer0\n/dev/v4l-subdev1 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000008:port1 -> n0000000e + n0000000b [label="{{ 0} | visconti-viif:resizer1\n/dev/v4l-subdev2 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000000b:port1 -> n00000012 + n0000000e [label="viif_capture_post0\n/dev/video0", shape=box, style=filled, fillcolor=yellow] + n00000012 [label="viif_capture_post1\n/dev/video1", shape=box, style=filled, fillcolor=yellow] + n00000016 [label="viif_capture_sub\n/dev/video2", shape=box, style=filled, fillcolor=yellow] + n0000001a [label="viif_params\n/dev/video3", shape=box, style=filled, fillcolor=yellow] + n0000001a -> n00000001:port4 + n0000001e [label="viif_stats\n/dev/video4", shape=box, style=filled, fillcolor=yellow] + n00000030 [label="{{ 0} | visconti_csi2rx 1c008000.csi2rx\n/dev/v4l-subdev3 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000030:port1 -> n00000001:port0 + n00000035 [label="{{} | imx219 1-0010\n/dev/v4l-subdev4 | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n00000035:port0 -> n00000030:port0 +} diff --git a/Documentation/admin-guide/media/visconti-viif.rst b/Documentation/admin-guide/media/visconti-viif.rst new file mode 100644 index 000000000000..c2e85fb6f8c1 --- /dev/null +++ b/Documentation/admin-guide/media/visconti-viif.rst @@ -0,0 +1,435 @@ +.. SPDX-License-Identifier: GPL-2.0 + +====================================================== +Visconti Video Input Interface Driver (visconti-viif) +====================================================== + +Introduction +============ + +This file documents the driver for the Video Input Interface (VIIF) that is +part of Toshiba Visconti SoCs. +The driver is located under drivers/media/platform/toshiba/visconti and uses +the Media-Controller API. + +The driver module is named visconti-viif, +and is enabled through the CONFIG_VIDEO_VISCONTI_VIIF config option. +The CSI-2 receiver part is controlled by another module named visconti-csi2rx, +which is enabled through the CONFIG_VIDEO_VISCONTI_CSI2RX config option. + +The Visconti VIIF Hardware +========================== + +The Visconti VIIF hardware is an internally developed video capture device. +Following function modules are integrated: + +* MIPI CSI-2 receiver (CSI2RX) +* L1 Image Signal Processor (L1ISP) + + * Correction, enhancement, adjustment on bayer images. + +* L2 Image Signal Processor (L2ISP) + + * Lens distortion correction + * Scaling & Cropping with up to 2 parameter sets + * Formatting picture (RGB, YUV, Grayscale, ...) + * Integrated DMAC: Writing picture into main memory + +* Video DMAC + + * Writing picture into main memory + +Visconti5 SoC has two VIIF hardware instances. + + +The hardware block diagram is shown below.:: + + The VIIF hardware + "POST0" + "RGB with scale 0" + +--------+ +----------+ +-----+ +-----+ +-----+ +--------+ + | Sensor |--->| CSI2RX |--->| | | | | |--->| memory | + +--------+ +----------+ | | | | | | +--------+ + | | | L1 | | L2 | "POST1" + | |--->| ISP |--->| ISP | "RGB with scale 1" + | | | | | | +--------+ + | MUX | | | | |--->| memory | + | | +-----+ +-----+ +--------+ + | | "SUB" + | | "RAW w/o scale" + | | +------------+ +--------+ + | |------> | Video DMAC |--->| memory | + +-----+ +------------+ +--------+ + +Topology +======== + +Graph +----- + +.. _visconti_viif_topology_graph: + +.. kernel-figure:: visconti-viif.dot + :alt: Diagram of the default media pipeline topology + :align: center + +The driver has 3 video devices for capturing images: + +- viif_capture_post0: capture device for image. + - corresponds to L2ISP. +- viif_capture_post1: capture device for image. + - corresponds to L2ISP. +- viif_capture_sub: capture device for bayer image. + - corresponds to Video DMAC. + +The driver has 2 video devices for controlling ISP. + +- viif_params: a metadata output device that receives ISP parameters. + - corresponds to L1ISP and L2ISP. +- viif_stats: a metadata capture device that sends statistics. + - corresponds to L1ISP and L2ISP. + +The driver has 2 subdevices: + +- visconti_csi2rx: CSI-2 receiver operation. + - corresponds to CSI2RX. +- visconti-viif:isp: Image Signal Processor operation. + - corresponds to L1ISP and L2ISP. +- visconti-viif:resizer: Scaling operation of Image Signal Processor. + - corresponds to L2ISP. + +visconti_csi2rx - CSI2 Receiver Subdevice Node +--------------------------------------------------- + +This subdevice node corresponds to a MIPI CSI2 receiver. +It resides between an image sensor subdevice and the ISP subdevice. +It controls CSI2 link configuration and training process. + +visconti-viif:isp - ISP Subdevice Node +-------------------------------------- + +This subdevice node corresponds to L1/L2 ISPs. +It receives pictures from an sensor (via CSI2RX), +applies multiple operations on pictures, then passes resulting images to capture nodes. + +ISP configurations/parameters are passed from userland via viif_params node. +The status of ISP operations are passed to userland via viif_stats node. + +L1 ISP provides following operations: + +- Input: accepts 8, 10, 12, 14bit bayer format + - Operation selector; :c:type:`viif_l1_input_mode_config` + - HDR image / PWL (Piecewse Linear Compression) image + - with preprocessing / without preprocessing + - HDRE: HDR expansion (only for PWL image); + see :c:type:`viif_l1_hdre_config` +- Preprocessing: generate intermediate data (24bit RAW) + - SLIC: Bit slicing (x3 12bit planes for preprocessing); + see :c:type:`viif_l1_img_extraction_config` + - ABPC/DPC: Blemish/Defect pixel correction :c:type:`viif_l1_dpc_config` + - PWHB: Preset white balance; see :c:type:`viif_l1_preset_white_balance_config` + - RCNR: RAW color noise reduction; see :c:type:`viif_l1_raw_color_noise_reduction_config` + - HDRS: HDR synthesis; see :c:type:`viif_l1_hdrs_config` +- Processing on RAW image: Main Process (MPRO) + - BLVC: black level correction and normalization; + see :c:type:`viif_l1_black_level_correction_config` + - LSSC: Lens shading correction; see :c:type:`viif_l1_lsc_config` + - MPRO: digital amplifier; see :c:type:`viif_l1_main_process_config` + - MPRO: bayer demosaicing; see :c:type:`viif_l1_main_process_config` + - MPRO: color matrix correction; see :c:type:`viif_l1_main_process_config` + - HDRC: HDR compression; + see :c:type:`viif_l1_hdrc_config`, :c:type:`viif_l1_hdrc_ltm_config`, + :c:type:`viif_l1_rgb_to_y_coef_config` +- Processing on RGB/YUV image: Video Process (VPRO) + - VPRO: gamma correction; see :c:type:`viif_l1_gamma_config` + - VPRO: RGB2YUV; + see :c:type:`viif_l1_rgb_to_y_coef_config`, + :c:type:`viif_l1_img_quality_adjustment_config` + - VPRO: image quality adjustment; see :c:type:`viif_l1_img_quality_adjustment_config` +- Output: 16bit YUV +- Feedback loop + - AWHB: auto white balance; see :c:type:`viif_l1_awb_config`, + :c:type:`viif_isp_capture_status` + - AEXP: auto exposure (average luminance calculation); + see :c:type:`viif_l1_avg_lum_generation_config`, + :c:type:`viif_l1_rgb_to_y_coef_config`, :c:type:`viif_isp_capture_status` + - AG: analog gain calculation; + see :c:type:`viif_l1_ag_mode_config`, :c:type:`viif_l1_ag_config` + +Below is the block diagram:: + + L1ISP::INPUT + + +--------+ +-----+ +-----+ + | Input |--------------->| |--------------------->| | + | 24bHDR | | | | | + +--------+ | 24b | | 24b | + | RAW | | RAW | + +--------+ +------+ | (0) | | (1) | + | Input |--->| HDRE |--->| | +------------+ | | + | 24bPWL | | | | |--->| preprocess |--->| | + +--------+ +------+ +-----+ +------------+ +-----+ + + L1ISP::INPUT::preprocess + + +-----+ +-----+ + | 24b | +------+ +------+ +------+ +------+ +------+ | 24b | + | RAW |--->| SLIC |--->| ABPC |--->| PWHB |--->| RCNR |--->| HDRS |--->| RAW | + | (0) | +------+ +------+ +------+ +------+ +------+ | (1) | + +-----+ +-----+ + + L1ISP::MainProcess(MPRO) + + +-----+ + | 24b | +------+ +------+ + | RAW |--->| BLVC |--->| LSSC |---+ + | (1) | +------+ +------+ | + +-----+ | + | + +------------------------------+ + | + | +-----------+ +-------------+ +--------+ +-----+ + +--->| MPRO | | MPRO | | MPRO | +------+ | 16b | + | Digital |--->| Demosaicing |----| Color |--->| HDRC |--->| RGB | + +--->| Amplifier | | | | Matrix | +------+ | | + | +-----------+ +-------------+ +--------+ +-----+ + | | | + | +--------------+ | | +------+ + +----| Auto |<----+ +--->| AEXP |---> Auto-Exposure statistics + | Whitebalance | +------+ + +--------------+ + | + +------------------------------> Auto-Whitebalance statistics + + L1ISP::VideoProcess(VPRO) + + +-----+ +------------+ +------------+ +---------------+ +--------+ + | 16b |--->| Gamma |--->| RGB2YUV |--->| Image Quality |--->| Output | + | RGB | | Correction | | Conversion | | Adjustment | | 16b | + | | +------------+ +------------+ +---------------+ | YUV | + +-----+ +--------+ + + L1ISP::AnalogGain + + statistics +-------------+ +------------------+ + information ---> (user SW) --->| Analog Gain |--->| ABPC, RCNR, LSSC | + +-------------+ | MPRO, VPRO | + +------------------+ + +L2 ISP provides following operations: + +- Input: accepts 16bit YUV / RGB +- Operations: + - Lens undistortion; see :c:type:`viif_l2_undist` + - Scaling; see :c:type:`viif_l2_roi` + - Cropping; see :c:type:`viif_l2_roi` + - Gamma correction; see :c:type:`viif_l2_gamma_config` + - YUV2RGB +- Output: RGB, YUV422, YUV444 + +Below is the block diagram:: + + L2ISP + + +-------+ +------------+ +--------------+ +---------+ + | Input |--->| YUV2RGB |--->| Lens |--->| Scaling |---> | + | Image | | Conversion | | Undistortion | | |---> | + +-------+ +------------+ +--------------+ +---------+ | + | + +----------------------------------------------------------+ + | + | +----------+ +------------+ +--------+ +--------+ + +--->|Gamma |--->| Colorspace |--->| Data |--->| Output | + | |Correction| | Conversion | | Packer | | Image | + | +----------+ +------------+ +--------+ +--------+ + | + | +----------+ +------------+ +--------+ +--------+ + +--->|Gamma |--->| Colorspace |--->| Data |--->| Output | + |Correction| | Conversion | | Packer | | Image | + +----------+ +------------+ +--------+ +--------+ + +visconti-viif:resizer - Resizer Subdevice Node +---------------------------------------------- + +The resizer subdevice resides between ISP subdevice and Capture device +on a capture path for post0 and post1. +It receives resize and crop parameters for the specific capture path +and controls L2ISP HW. + +following selection rectangles can be passed at VIDIOC_S_SELECTION ioctl. + +- sink pads's compose rectangle (V4L2_SEL_TGT_COMPOSE) for scaling +- source pad's crop rectangle (V4L2_SEL_TGT_CROP) for cropping + + +viif_capture_post0, viif_capture_post1 - Processed Image Capture Video Node +--------------------------------------------------------------------------- + +These video nodes are used for capturing images processed at ISPs. +Supported capture formats are as follows: + +- V4L2_PIX_FMT_RGB24 +- V4L2_PIX_FMT_ABGR32 +- V4L2_PIX_FMT_YUV422M +- V4L2_PIX_FMT_YUV444M +- V4L2_PIX_FMT_Y16 + +Bayer format is not supported. Use viif_capture_sub instead. + +POST0 and POST1 can output images from the same input image +using different cropping and scaling settings. + +viif_capture_sub - Raw Image Capture Video Node +----------------------------------------------- + +This video node is used for capturing bayer image from the sensor. +The output picture has exactly the same resolution and format as the sensor input. +The pipeline does not edit pixel values. +However, when writing pixel values to memory, they are shifted to the MSB +to match either 8bit or 16bit. + +Therefore, resulting capture formats are as follows: + +- for 8bit RAW input: + - V4L2_PIX_FMT_SRGGB8 + - V4L2_PIX_FMT_SGRBG8 + - V4L2_PIX_FMT_SGBRG8 + - V4L2_PIX_FMT_SBGGR8 +- for 10, 12, 14bit RAW input: + - V4L2_PIX_FMT_SRGGB16 + - V4L2_PIX_FMT_SGRBG16 + - V4L2_PIX_FMT_SGBRG16 + - V4L2_PIX_FMT_SBGGR16 + +.. _viif_params: + +viif_params - ISP Parameters Video Node +--------------------------------------- + +The viif_params video node receives a set of ISP parameters from userspace +to be applied to the hardware during a video stream. + +The buffer format is defined by struct :c:type:`visconti_viif_isp_config`, and userspace should set +:ref:`V4L2_META_FMT_VISCONTI_VIIF_PARAMS ` as the data format. + +.. _viif_stats: + +viif_stats - Statistics Video Node +---------------------------------- + +The viif_stats video node provides current status of ISP. + +Following information is included: + +* statistics of auto white balance +* average luminance information which can be used by auto exposure software impl. + +The buffer format is defined by struct :c:type:`visconti_viif_isp_stat`, and userspace should set +:ref:`V4L2_META_FMT_VISCONTI_VIIF_STATS ` as the data format. + +Feedback Operations +=================== + +Among the so-called 3A functions, VIIF provides only auto-whitebalance and auto-exposure. +Auto-whitebalance is a standalone hardware feature. +Some status information is available through the ISP statistics interface. + +Auto-exposure is realized through a combination of hardware and userland software. +VIIF provides weighted average luminance information through the ISP statistics interface. +The userland application calculates the sensor gain, sensor exposure and ISP digital gain. +The calculated parameters are then passed to sensor's controls and the ISP parameter interface. + +Among ISP parameters, there are parameters called AG (analog gain). +Actually, AG parameters have nothing to do with auto-exposure. +It controls "strength" in several signal correction algorithms. +Below is the list of the functions which may be affected by AG parameters: + +- ABPC/DPC +- RCNR +- LSSC +- MPRO: color matrix correction +- VPRO + +Capturing Video Frames Example +============================== + +In the following example, +imx219 camera is connected to pad 0 of 'visconti_csi2rx' subdevice. + +The following commands yield three pictures with different zoom ratio: +- main path 0: 1.5x zoom, crop 1920x1080, RGB picture +- main path 1: 0.67x zoom, crop 640x480, RGB picture +- sub path: 1920x1080 RAW picture + +.. code-block:: bash + + # set the links + media-ctl -d platform:visconti-viif-0 -r + media-ctl -d platform:visconti-viif-0 -l '"imx219 1-0010":0 -> "visconti_csi2rx 1c008000.csi2rx":0 [1]' + media-ctl -d platform:visconti-viif-0 -l '"visconti_csi2rx 1c008000.csi2rx":1 -> "visconti-viif:isp":0 [1]' + media-ctl -d platform:visconti-viif-0 -l '"visconti-viif:isp":1 -> "visconti-viif:resizer0":0 [1]' + media-ctl -d platform:visconti-viif-0 -l '"visconti-viif:isp":2 -> "visconti-viif:resizer1":0 [1]' + media-ctl -d platform:visconti-viif-0 -l '"visconti-viif:isp":3 -> "viif_capture_sub":0 [1]' + media-ctl -d platform:visconti-viif-0 -l '"visconti-viif:resizer0":1 -> "viif_capture_post0":0 [1]' + media-ctl -d platform:visconti-viif-0 -l '"visconti-viif:resizer1":1 -> "viif_capture_post1":0 [1]' + media-ctl -d platform:visconti-viif-0 -l '"viif_params":0 -> "visconti-viif:isp":4 [1]' + media-ctl -d platform:visconti-viif-0 -l '"visconti-viif:isp":5 -> "viif_stats":0 [1]' + + # set format for imx219 + media-ctl -d platform:visconti-viif-0 --set-v4l2 '"imx219 1-0010":0 [fmt:SRGGB10_1X10/1920x1080]' + + # set format for csi2rx + media-ctl -d platform:visconti-viif-0 --set-v4l2 '"visconti_csi2rx 1c008000.csi2rx":0 [fmt:SRGGB10_1X10/1920x1080 field:none colorspace:raw xfer:none ycbcr:601 quantization:full-range]' + + # set format for isp sink pad + media-ctl -d platform:visconti-viif-0 --set-v4l2 '"visconti-viif:isp":0 [fmt:SRGGB10_1X10/1920x1080]' + + # set format for resizer pads + media-ctl -d platform:visconti-viif-0 --set-v4l2 '"visconti-viif:resizer0":0 '"[fmt:YUV8_1X24/1920x1080 compose:(0,0)/2880x1620]" + media-ctl -d platform:visconti-viif-0 --set-v4l2 '"visconti-viif:resizer0":1 '"[crop:(480,16)/1920x1080]" + media-ctl -d platform:visconti-viif-0 --set-v4l2 '"visconti-viif:resizer1":0 '"[fmt:YUV8_1X24/1920x1080 compose:(0,0)/1280x720]" + media-ctl -d platform:visconti-viif-0 --set-v4l2 '"visconti-viif:resizer1":1 '"[crop:(320,32)/640x480]" + + media-ctl -d platform:visconti-viif-0 --set-v4l2 '"visconti-viif:isp":1 [fmt:YUV8_1X24/1024 crop:(640,0)/1024x1024]' + + # set format for main path0 + v4l2-ctl -z platform:visconti-viif-0 -d viif_capture_post0 -v "width=1920,height=1080" + v4l2-ctl -z platform:visconti-viif-0 -d viif_capture_post0 -v "pixelformat=RGB3" + + # set format for main path1 + v4l2-ctl -z platform:visconti-viif-0 -d viif_capture_post0 -v "width=640,height=480" + v4l2-ctl -z platform:visconti-viif-0 -d viif_capture_post0 -v "pixelformat=RGB3" + + # start streaming + v4l2-ctl -z platform:visconti-viif-0 -d viif_capture_post0 --stream-mmap --stream-count 1000 & + + # start streaming with other devices while viif_capture_post0 is running + v4l2-ctl -z platform:visconti-viif-0 -d viif_capture_post1 --stream-mmap --stream-count 10 + v4l2-ctl -z platform:visconti-viif-0 -d viif_capture_sub --stream-mmap --stream-count 10 + +Use of coherent memory +====================== + +Visconti5 SoC has two independent DDR SDRAM controllers. +Each controller is mapped to 36bit address space. + +Accelerator bus masters have two paths to access memory; +one is directly connected to SDRAM controller, +the another is connected via a cache coherency bus +which keeps coherency among CPUs. + +From accelerators and CPUs, the address map is following: + +* 0x0_8000_0000 DDR0 direct access +* 0x4_8000_0000 DDR0 coherency bus +* 0x8_8000_0000 DDR1 direct access +* 0xC_8000_0000 DDR1 coherency bus + +The base address can be specified with "memory" and "reserved-memory" elements +in a device tree description. +It's not recommended to mix direct address and coherent address. + +The Visconti5 VIIF driver always use only direct address to configure Video DMACs of the hardware. +This design is to avoid great performance loss at coherency bus caused by massive memory access. +You should not put the dma_coherent attribute to viif element in device tree. +Cache operations are done automatically by videobuf2 driver. diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst index 86ffb3bc8ade..2336842f0c26 100644 --- a/Documentation/userspace-api/media/v4l/meta-formats.rst +++ b/Documentation/userspace-api/media/v4l/meta-formats.rst @@ -19,6 +19,7 @@ These formats are used for the :ref:`metadata` interface only. metafmt-pisp-fe metafmt-rkisp1 metafmt-uvc + metafmt-visconti-viif metafmt-vivid metafmt-vsp1-hgo metafmt-vsp1-hgt diff --git a/Documentation/userspace-api/media/v4l/metafmt-visconti-viif.rst b/Documentation/userspace-api/media/v4l/metafmt-visconti-viif.rst new file mode 100644 index 000000000000..dc4b31627fe1 --- /dev/null +++ b/Documentation/userspace-api/media/v4l/metafmt-visconti-viif.rst @@ -0,0 +1,48 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _v4l2-meta-fmt-visconti-viif-params: + +.. _v4l2-meta-fmt-visconti-viif-stats: + +*************************************************************************************** +V4L2_META_FMT_VISCONTI_VIIF_PARAMS ('vifp'), V4L2_META_FMT_VISCONTI_VIIF_STATS ('vifs') +*************************************************************************************** + +Configuration parameters +======================== + +The configuration parameters are passed to the +:ref:`viif_params ` metadata output video node, using +the :c:type:`v4l2_meta_format` interface. The buffer contains +a single instance of the C structure :c:type:`visconti_viif_isp_config` defined in +``visconti_viif.h``. So the structure can be obtained from the buffer by: + +.. code-block:: c + + struct visconti_viif_isp_config *params = (struct visconti_viif_isp_config*) buffer; + +VIIF statistics +=============== + +The VIIF device collects different statistics over an input Bayer frame. +Those statistics are obtained from the :ref:`viif_stats ` +metadata capture video node, +using the :c:type:`v4l2_meta_format` interface. The buffer contains a single +instance of the C structure :c:type:`visconti_viif_isp_stat` defined in +``visconti_viif.h``. So the structure can be obtained from the buffer by: + +.. code-block:: c + + struct visconti_viif_isp_stat *stats = (struct visconti_viif_isp_stat*) buffer; + +The statistics collected are Exposure, AWB (auto white balance) and errors. +See :c:type:`visconti_viif_isp_stat` for details of the statistics. + +The statistics and configuration parameters described here are usually +consumed and produced by dedicated user space libraries that comprise the +tuning tools using software control loop. + +visconti viif uAPI data types +============================= + +.. kernel-doc:: include/uapi/linux/visconti_viif.h From patchwork Mon Nov 25 09:21:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yuji Ishikawa X-Patchwork-Id: 845477 Received: from mo-csw-fb.securemx.jp (mo-csw-fb1121.securemx.jp [210.130.202.129]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 04C821925A6; Mon, 25 Nov 2024 10:43:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.130.202.129 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732531399; cv=none; b=LZhUUM15+Gi6ZtOnLmy+K1QsYj3/3YX3IClHVhlSYLNGYuGJp4/KGnjla+Te9TlnHhSdNfEqYi4Vk0szgOQ6YeSlW1Ch2QfckkjhgZ3FXbUCVe3vVqyb+auYBZD4fhV08AAi/1kb5Zi8U0M1NJNnCzmORrde5jmVyzrpJ09OJQs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1732531399; c=relaxed/simple; bh=PCq/pu+QO5n9bATAUWThnGACe2nepbUSJDlQFbrVrx8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=bbFCK27V9VsMD2pGpX8h+fGw054hR5pk0KH5+TEZPqGq7N2lyF5QytxPduYNI28NOQ/rz6i3iCNGVOhSEjfT3hS7aliy5pdlLKU3qkEqbWexjvSR1k13HUjexGNBfpLulMX5XOejtlDarfZuh2gxk87odhfBjODy3H55Ed8wm24= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp; spf=pass smtp.mailfrom=toshiba.co.jp; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b=ssKrD2dA; arc=none smtp.client-ip=210.130.202.129 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=toshiba.co.jp Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=toshiba.co.jp header.i=yuji2.ishikawa@toshiba.co.jp header.b="ssKrD2dA" Received: by mo-csw-fb.securemx.jp (mx-mo-csw-fb1121) id 4AP9Ro6t729184; Mon, 25 Nov 2024 18:27:51 +0900 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=toshiba.co.jp; h=From:To:Cc :Subject:Date:Message-Id:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding;i=yuji2.ishikawa@toshiba.co.jp;s=key2.smx;t= 1732526841; x=1733736441; bh=PCq/pu+QO5n9bATAUWThnGACe2nepbUSJDlQFbrVrx8=; b=ssK rD2dA6ydWDQ6pOMt2C5SL8sXLaAYRAfqPxs6mSdZYsTxCkOhnxEqJ5+gXjHoqI/vi81FEUxoFDrxS Qt3jLqg0153A6aZQq6KoE/jltceTLvhDyrw2K3d+zUMn31MZMdyCXez0RjnSNgOO4GJLcJuh4WddV AXGqimEQITHWVsUwf6hzcNHSxlQ0lVcw67zkNN5Juz2h4tqj0Z/atXbLe9KYGE127KhmlKTFQEd37 yy5UBWaNAcpN1I2YuxfAN6UsIG1+WybisEaHoOLtszQ0hKd9r1J1lh/6hpNvErmBclKErBTR0AnkH 5iaYi0t1Ql7JK04F6OYVQQ+U2Wd2anQ==; Received: by mo-csw.securemx.jp (mx-mo-csw1121) id 4AP9RJmm099468; Mon, 25 Nov 2024 18:27:19 +0900 X-Iguazu-Qid: 2rWguDC9mIphlABSrL X-Iguazu-QSIG: v=2; s=0; t=1732526839; q=2rWguDC9mIphlABSrL; m=k1DP3cEVolB9mfsCCNGe71tsvk/AiYIrIbXeZwTU+I0= Received: from imx2-a.toshiba.co.jp (imx2-a.toshiba.co.jp [106.186.93.35]) by relay.securemx.jp (mx-mr1121) id 4AP9RHIp297761 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 25 Nov 2024 18:27:17 +0900 X-SA-MID: 35004190 From: Yuji Ishikawa To: Laurent Pinchart , Mauro Carvalho Chehab , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Sakari Ailus , Hans Verkuil , Nobuhiro Iwamatsu , Yuji Ishikawa Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Subject: [PATCH v12 8/8] MAINTAINERS: Add entries for Toshiba Visconti Video Input Interface Date: Mon, 25 Nov 2024 18:21:46 +0900 X-TSB-HOP2: ON Message-Id: <20241125092146.1561901-9-yuji2.ishikawa@toshiba.co.jp> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> References: <20241125092146.1561901-1-yuji2.ishikawa@toshiba.co.jp> Precedence: bulk X-Mailing-List: linux-media@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Added entries for visconti Video Input Interface driver, including; * device tree bindings * source files * documentation files Signed-off-by: Yuji Ishikawa Reviewed-by: Nobuhiro Iwamatsu --- Changelog v2: - no change Changelog v3: - added entry for driver API documentation Changelog v4: - added entry for header file Changelog v5: - no change Changelog v6: - update path to VIIF driver source files Changelog v7: - no change Changelog v8: - rename bindings description file Changelog v9: - no change Changelog v10: - add a separate entry of VIIF driver Changelog v11: - no change Changelog v12: - add a bindings description of CSI2RX driver MAINTAINERS | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index b878ddc99f94..b5c819e94e9b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23430,6 +23430,18 @@ F: Documentation/devicetree/bindings/media/i2c/tc358743.txt F: drivers/media/i2c/tc358743* F: include/media/i2c/tc358743.h +TOSHIBA VISCONTI VIIF DRIVER +M: Nobuhiro Iwamatsu +M: Yuji Ishikawa +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/admin-guide/media/visconti-viif.* +F: Documentation/devicetree/bindings/media/toshiba,visconti5-csi2rx.yaml +F: Documentation/devicetree/bindings/media/toshiba,visconti5-viif.yaml +F: Documentation/userspace-api/media/v4l/metafmt-visconti-viif.rst +F: drivers/media/platform/toshiba/visconti/ +F: include/uapi/linux/visconti_viif.h + TOSHIBA WMI HOTKEYS DRIVER M: Azael Avalos L: platform-driver-x86@vger.kernel.org