From patchwork Tue Jun 11 03:53:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jungo Lin X-Patchwork-Id: 10985801 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 43EF714E5 for ; Tue, 11 Jun 2019 03:54:34 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2FBD827F94 for ; Tue, 11 Jun 2019 03:54:34 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 237E32839C; Tue, 11 Jun 2019 03:54:34 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id BC6B127F94 for ; Tue, 11 Jun 2019 03:54:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:Reply-To:List-Subscribe:List-Help: List-Post:List-Archive:List-Unsubscribe:List-Id:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=cTUIYAWIciPvEgLMOPRkFCDRwlK8MdSsVoD7E1tGF9I=; b=ZdmzwXE9vshueQ rhFVOkZevUrXgFGvE5RVuz+f5QxkgBTbQn5U6vF26eXOrUSwwjZpM/2cQesCcnaPQyhSfJt3VIRXq btTJPv8qYn/4XHJs5hfWo3qISqOOpxX9BSDulb+sXUNRFzJB6YoyiHTihadzEB4rdeML5p9P72c/M kPNRYlYpLPme+u4wCUZhVvpjWS/vH5Mdu1qhX0EVfVpq4zK3/GFVC7G89EWTmgN54XtAiBB5DeiMA j4+s5g+p58yIwe7xb+jR7sAefBSiZ1FwFKVogbuSD65nEWj4nohJQHnw8Tgswk+zi7FLlpf3ZFnud 8qXw3mYYk0zRb76hjQjg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1haXre-0007Zm-QK; Tue, 11 Jun 2019 03:54:27 +0000 Received: from mailgw01.mediatek.com ([216.200.240.184]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1haXrI-0007J3-6f; Tue, 11 Jun 2019 03:54:05 +0000 X-UUID: 85d49f82074e4c46aa4cb56f52e69c77-20190610 X-UUID: 85d49f82074e4c46aa4cb56f52e69c77-20190610 Received: from mtkcas67.mediatek.inc [(172.29.193.45)] by mailgw01.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 860067779; Mon, 10 Jun 2019 19:53:52 -0800 Received: from mtkmbs07n1.mediatek.inc (172.21.101.16) by MTKMBS62N2.mediatek.inc (172.29.193.42) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 10 Jun 2019 20:53:51 -0700 Received: from mtkcas09.mediatek.inc (172.21.101.178) by mtkmbs07n1.mediatek.inc (172.21.101.16) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 11 Jun 2019 11:53:50 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 11 Jun 2019 11:53:49 +0800 From: Jungo Lin To: , , , , Subject: [RFC,v3 1/9] dt-bindings: mt8183: Added camera ISP Pass 1 Date: Tue, 11 Jun 2019 11:53:36 +0800 Message-ID: <20190611035344.29814-2-jungo.lin@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20190611035344.29814-1-jungo.lin@mediatek.com> References: <20190611035344.29814-1-jungo.lin@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190610_205404_243322_17E62BAA X-CRM114-Status: UNSURE ( 8.29 ) X-CRM114-Notice: Please train this message. X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: jungo.lin@mediatek.com Cc: devicetree@vger.kernel.org, sean.cheng@mediatek.com, rynn.wu@mediatek.com, srv_heupstream@mediatek.com, robh@kernel.org, ryan.yu@mediatek.com, frankie.chiu@mediatek.com, jungo.lin@mediatek.com, sj.huang@mediatek.com, linux-mediatek@lists.infradead.org, ddavenport@chromium.org, frederic.chen@mediatek.com, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds DT binding document for the Pass 1 (P1) unit in Mediatek's camera ISP system. The Pass 1 unit grabs the sensor data out from the sensor interface, applies ISP image effects from tuning data and outputs the image data or statistics data to DRAM. Signed-off-by: Jungo Lin --- .../bindings/media/mediatek,camisp.txt | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/mediatek,camisp.txt diff --git a/Documentation/devicetree/bindings/media/mediatek,camisp.txt b/Documentation/devicetree/bindings/media/mediatek,camisp.txt new file mode 100644 index 000000000000..50a8b4d9ac8e --- /dev/null +++ b/Documentation/devicetree/bindings/media/mediatek,camisp.txt @@ -0,0 +1,57 @@ +* Mediatek Image Signal Processor Pass 1 (ISP P1) + +The Pass 1 unit of Mediatek's camera ISP system grabs the sensor data out +from the sensor interface, applies ISP effects from tuning data and outputs +the image data and statistics data to DRAM. Furthermore, Pass 1 unit has +the ability to output two different resolutions frames at the same time to +increase the performance of the camera application. + +Required properties: +- compatible: Must be "mediatek,mt8183-camisp" for MT8183. +- reg: Physical base address of the camera function block registers and + length of memory mapped region. Must contain an entry for each entry + in reg-names. +- reg-names: Must include the following entries: + "cam_sys": Camsys base function block + "cam_uni": Camera UNI function block + "cam_a": Single camera ISP P1 hardware module A + "cam_b": Single camera ISP P1 hardware module B +- interrupts: Interrupt number to the CPU. +- iommus: Shall point to the respective IOMMU block with master port + as argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt + for details. +- clocks: A list of phandle and clock specifier pairs as listed + in clock-names property, see + Documentation/devicetree/bindings/clock/clock-bindings.txt for details. +- clock-names: Must be "camsys_cam_cgpdn" and "camsys_camtg_cgpdn". +- mediatek,larb: Must contain the local arbiters in the current SoCs, see + Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt + for details. +- mediatek,scp : The node of system control processor (SCP), see + Documentation/devicetree/bindings/remoteproc/mtk,scp.txt for details. + +Example: +SoC specific DT entry: + + camisp: camisp@1a000000 { + compatible = "mediatek,mt8183-camisp", "syscon"; + reg = <0 0x1a000000 0 0x1000>, + <0 0x1a003000 0 0x1000>, + <0 0x1a004000 0 0x2000>, + <0 0x1a006000 0 0x2000>; + reg-names = "cam_sys", + "cam_uni", + "cam_a", + "cam_b"; + interrupts = , + , + ; + iommus = <&iommu M4U_PORT_CAM_IMGO>; + clocks = <&camsys CLK_CAM_CAM>, + <&camsys CLK_CAM_CAMTG>; + clock-names = "camsys_cam_cgpdn", + "camsys_camtg_cgpdn"; + mediatek,larb = <&larb3>, + <&larb6>; + mediatek,scp = <&scp>; + }; \ No newline at end of file From patchwork Tue Jun 11 03:53:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jungo Lin X-Patchwork-Id: 10985825 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E817314C0 for ; Tue, 11 Jun 2019 03:57:29 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D62381FF65 for ; Tue, 11 Jun 2019 03:57:29 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CA36E271E6; Tue, 11 Jun 2019 03:57:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 78C971FF65 for ; Tue, 11 Jun 2019 03:57:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:Reply-To:List-Subscribe:List-Help: List-Post:List-Archive:List-Unsubscribe:List-Id:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=5ffFnXIcV5G1aRqQZ7gBGuPkkW2J6jekhXuaCOK7Y74=; b=WTb5VDxx1JCmDt Sd2oDwsjEjaCTx3a1g/Y7eRydHzL2abvz0XfbkjqIgNKhMIBfvq8pK1fjdF/rMaRJ2mq5zh6Aj8Km FjPRuT4MDTqmuifhsofqVdTjf0qRaDmQ1XAgiFNwrXlBGVzla/nOnoOm7Nj0JVDWLEMMgvxX92nNk hqaEkSZVaF+LsL1GKJW/54AHTYWD8PKqmLSt378H+xoOUywmSjfi8Yv1rvjafMV7aRGjBkTlqpK30 n32eUEK3PMhzD5FWgrI/cuzy4aYPQuvvto0gi3WGZ8tk4ekTrz1SqdIqDx0rkoH12x0yX8YTZaLaA ltqrsBhBmnW2lsqsuYjw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1haXuW-0002UO-2y; Tue, 11 Jun 2019 03:57:24 +0000 Received: from mailgw02.mediatek.com ([216.200.240.185]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1haXrT-0007RM-1L; Tue, 11 Jun 2019 03:54:18 +0000 X-UUID: 5928145a98b14b53b7b288fc981c6f56-20190610 X-UUID: 5928145a98b14b53b7b288fc981c6f56-20190610 Received: from mtkcas66.mediatek.inc [(172.29.193.44)] by mailgw02.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 393962352; Mon, 10 Jun 2019 19:54:01 -0800 Received: from mtkmbs08n2.mediatek.inc (172.21.101.56) by MTKMBS62DR.mediatek.inc (172.29.94.18) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 10 Jun 2019 20:53:59 -0700 Received: from mtkcas09.mediatek.inc (172.21.101.178) by mtkmbs08n2.mediatek.inc (172.21.101.56) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 11 Jun 2019 11:53:51 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 11 Jun 2019 11:53:51 +0800 From: Jungo Lin To: , , , , Subject: [RFC,v3 2/9] dts: arm64: mt8183: Add ISP Pass 1 nodes Date: Tue, 11 Jun 2019 11:53:37 +0800 Message-ID: <20190611035344.29814-3-jungo.lin@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20190611035344.29814-1-jungo.lin@mediatek.com> References: <20190611035344.29814-1-jungo.lin@mediatek.com> MIME-Version: 1.0 X-TM-SNTS-SMTP: 51E3951B510457039F7BF5A18CAC5160CF13C347259EFDA5390D689C6282BB822000:8 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190610_205415_533763_FCE3FDED X-CRM114-Status: UNSURE ( 5.96 ) X-CRM114-Notice: Please train this message. X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: jungo.lin@mediatek.com Cc: devicetree@vger.kernel.org, sean.cheng@mediatek.com, rynn.wu@mediatek.com, srv_heupstream@mediatek.com, robh@kernel.org, ryan.yu@mediatek.com, frankie.chiu@mediatek.com, jungo.lin@mediatek.com, sj.huang@mediatek.com, linux-mediatek@lists.infradead.org, ddavenport@chromium.org, frederic.chen@mediatek.com, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Add nodes for Pass 1 unit of Mediatek's camera ISP system. Pass 1 unit embedded in Mediatek SoCs, works with the co-processor to process image signal from the image sensor and output RAW image data. Signed-off-by: Jungo Lin --- arch/arm64/boot/dts/mediatek/mt8183.dtsi | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8183.dtsi b/arch/arm64/boot/dts/mediatek/mt8183.dtsi index 75c4881bbe5e..8a725357c594 100644 --- a/arch/arm64/boot/dts/mediatek/mt8183.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8183.dtsi @@ -369,5 +369,29 @@ reg = <0 0x1a000000 0 0x1000>; #clock-cells = <1>; }; + + camisp: camisp@1a000000 { + compatible = "mediatek,mt8183-camisp", "syscon"; + reg = <0 0x1a000000 0 0x1000>, + <0 0x1a003000 0 0x1000>, + <0 0x1a004000 0 0x2000>, + <0 0x1a006000 0 0x2000>; + reg-names = "cam_sys", + "cam_uni", + "cam_a", + "cam_b"; + interrupts = , + , + ; + iommus = <&iommu M4U_PORT_CAM_IMGO>; + clocks = <&camsys CLK_CAM_CAM>, + <&camsys CLK_CAM_CAMTG>; + clock-names = "camsys_cam_cgpdn", + "camsys_camtg_cgpdn"; + mediatek,larb = <&larb3>, + <&larb6>; + mediatek,scp = <&scp>; + }; + }; }; From patchwork Tue Jun 11 03:53:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jungo Lin X-Patchwork-Id: 10985791 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1DEC814C0 for ; Tue, 11 Jun 2019 03:54:15 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0E21C28712 for ; Tue, 11 Jun 2019 03:54:15 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 01CA528735; Tue, 11 Jun 2019 03:54:14 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id A4B9B28712 for ; Tue, 11 Jun 2019 03:54:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:Reply-To:List-Subscribe:List-Help: List-Post:List-Archive:List-Unsubscribe:List-Id:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=lZK16URKmfqIhSSGCUKBVpiFk5jN2Ta6PPUrxrAwX6M=; b=E+N5PAOtCIVELK W9Ve/xtk49U4ef0zbS15KsvhcCcabSsn3nRKWvBIksEbVKFjWR7OFOhFr5HHpYFJWv9CBGFDU8MKi Mn6utfRCQboEoIhDFG38cn12jN/PtuvqNn+SYO1fCe0e8k4/BZsWPbzxULXYr4bKR9WW+gEgh5ddz QCpHuhOl9Gwpint+s0w795DpYFm9Auke72++jScLgsd+PQm+4ukLR0oj73JkdL8ScLqoAlGw1EQeg Go1QdrLqn4mlM8DFbUkSdtMb7waq28ZSchIKQNB8690l/KXNpTm0sd0RDZ58vUSP31vNNK8/YGSG0 J0D+UIsOv7DAguj6wzew==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1haXrM-0007LP-Dg; Tue, 11 Jun 2019 03:54:08 +0000 Received: from mailgw01.mediatek.com ([216.200.240.184]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1haXrH-0007JA-Qn; Tue, 11 Jun 2019 03:54:05 +0000 X-UUID: 783c8a9d56974899ab0c0f216f2d0ff1-20190610 X-UUID: 783c8a9d56974899ab0c0f216f2d0ff1-20190610 Received: from mtkcas68.mediatek.inc [(172.29.94.19)] by mailgw01.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 155980320; Mon, 10 Jun 2019 19:53:55 -0800 Received: from MTKMBS07N2.mediatek.inc (172.21.101.141) by MTKMBS62DR.mediatek.inc (172.29.94.18) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 10 Jun 2019 20:53:54 -0700 Received: from mtkcas09.mediatek.inc (172.21.101.178) by mtkmbs07n2.mediatek.inc (172.21.101.141) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 11 Jun 2019 11:53:52 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 11 Jun 2019 11:53:52 +0800 From: Jungo Lin To: , , , , Subject: [RFC,v3 3/9] media: platform: Add Mediatek ISP Pass 1 driver Kconfig Date: Tue, 11 Jun 2019 11:53:38 +0800 Message-ID: <20190611035344.29814-4-jungo.lin@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20190611035344.29814-1-jungo.lin@mediatek.com> References: <20190611035344.29814-1-jungo.lin@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190610_205403_876625_DB927CFE X-CRM114-Status: UNSURE ( 8.21 ) X-CRM114-Notice: Please train this message. X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: jungo.lin@mediatek.com Cc: devicetree@vger.kernel.org, sean.cheng@mediatek.com, rynn.wu@mediatek.com, srv_heupstream@mediatek.com, robh@kernel.org, ryan.yu@mediatek.com, frankie.chiu@mediatek.com, jungo.lin@mediatek.com, sj.huang@mediatek.com, linux-mediatek@lists.infradead.org, ddavenport@chromium.org, frederic.chen@mediatek.com, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds Kconfig for Pass 1 (P1) unit driver of Mediatek's camera ISP system. ISP P1 unit is embedded in Mediatek SoCs. It provides RAW processing which includes optical black correction, defect pixel correction, W/IR imbalance correction and lens shading correction. Signed-off-by: Jungo Lin --- drivers/media/platform/Kconfig | 2 ++ drivers/media/platform/mtk-isp/Kconfig | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 drivers/media/platform/mtk-isp/Kconfig diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 011c1c2fcf19..8e2b65d757e5 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -32,6 +32,8 @@ source "drivers/media/platform/davinci/Kconfig" source "drivers/media/platform/omap/Kconfig" +source "drivers/media/platform/mtk-isp/Kconfig" + config VIDEO_ASPEED tristate "Aspeed AST2400 and AST2500 Video Engine driver" depends on VIDEO_V4L2 diff --git a/drivers/media/platform/mtk-isp/Kconfig b/drivers/media/platform/mtk-isp/Kconfig new file mode 100644 index 000000000000..983b79c261fa --- /dev/null +++ b/drivers/media/platform/mtk-isp/Kconfig @@ -0,0 +1,17 @@ +config VIDEO_MEDIATEK_ISP_PASS1 + bool "Mediatek Pass 1 image processing function" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CONTROLLER && VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + select VIDEOBUF2_DMA_CONTIG + default n + help + Pass 1 driver controls 3A (auto-focus, exposure, + and white balance) with tuning feature and outputs + the captured image buffers in Mediatek's camera system. + + Choose y if you want to use Mediatek SoCs to create image + captured application such as video recording and still image + capturing. + + From patchwork Tue Jun 11 03:53:39 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jungo Lin X-Patchwork-Id: 10985811 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id AACCA14E5 for ; Tue, 11 Jun 2019 03:55:29 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 999A327F94 for ; Tue, 11 Jun 2019 03:55:29 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8D6C32844E; Tue, 11 Jun 2019 03:55:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 022B5283C3 for ; Tue, 11 Jun 2019 03:55:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:Reply-To:List-Subscribe:List-Help: List-Post:List-Archive:List-Unsubscribe:List-Id:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=AZIDQZShWAzrNFSFAtcHLd5HfW3I75ENngGjATWOo14=; b=W/5mhugWRxnItP DwiREQmqMcRuPKnK7L9YFqLFNENmo8hq+xJ2gJGhrqzauJr1vECIHONbT9V/iJlot14BflwK65lDl zpZyMltpsIhtrHEgtjp+ZWzrRSuHrlugl+He1GjhyBsQiK4pA1Jn6le/b4BFguOo//+v9cXIn8WWk i33rKYdvDGE5hkHuQZpwCd84qUU+oFolD3CsFOfGkIWstI9D6rndXAT45D2q7QtVL9C+rgbfeGV8U 9eqvmya6c1GcFmj/+o7Zk9027a3wQr0hP2rBt7uSvOUQ4rVNgijIPnJiY0EF6jJSFoBZTsK6cR64l LnToChiZeaz+NTmNN1Vw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1haXsY-0000wO-QY; Tue, 11 Jun 2019 03:55:22 +0000 Received: from mailgw01.mediatek.com ([216.200.240.184]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1haXrJ-0007JA-Da; Tue, 11 Jun 2019 03:54:11 +0000 X-UUID: e12680ed94fa486a85a804ee247412a3-20190610 X-UUID: e12680ed94fa486a85a804ee247412a3-20190610 Received: from mtkcas66.mediatek.inc [(172.29.193.44)] by mailgw01.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 1829212336; Mon, 10 Jun 2019 19:53:56 -0800 Received: from mtkmbs07n1.mediatek.inc (172.21.101.16) by MTKMBS62N1.mediatek.inc (172.29.193.41) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 10 Jun 2019 20:53:54 -0700 Received: from mtkcas09.mediatek.inc (172.21.101.178) by mtkmbs07n1.mediatek.inc (172.21.101.16) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 11 Jun 2019 11:53:53 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 11 Jun 2019 11:53:53 +0800 From: Jungo Lin To: , , , , Subject: [RFC, v3 4/9] media: platform: Add Mediatek ISP P1 image & meta formats Date: Tue, 11 Jun 2019 11:53:39 +0800 Message-ID: <20190611035344.29814-5-jungo.lin@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20190611035344.29814-1-jungo.lin@mediatek.com> References: <20190611035344.29814-1-jungo.lin@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190610_205405_548111_C766BC69 X-CRM114-Status: UNSURE ( 8.04 ) X-CRM114-Notice: Please train this message. X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: jungo.lin@mediatek.com Cc: devicetree@vger.kernel.org, sean.cheng@mediatek.com, rynn.wu@mediatek.com, srv_heupstream@mediatek.com, robh@kernel.org, ryan.yu@mediatek.com, frankie.chiu@mediatek.com, jungo.lin@mediatek.com, sj.huang@mediatek.com, linux-mediatek@lists.infradead.org, ddavenport@chromium.org, frederic.chen@mediatek.com, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Add packed/full-g bayer formats with 8/10/12/14 bit for image output. Add Pass 1 (P1) specific meta formats for parameter processing and 3A/other statistics. (The current metadata interface used in meta input and partial meta nodes is only a temporary solution to kick off the driver development and is not ready to be reviewed yet.) Signed-off-by: Jungo Lin --- Documentation/media/uapi/v4l/pixfmt-mtb8.rst | 49 +++++++++ Documentation/media/uapi/v4l/pixfmt-mtba.rst | 62 +++++++++++ Documentation/media/uapi/v4l/pixfmt-mtbc.rst | 58 ++++++++++ Documentation/media/uapi/v4l/pixfmt-mtbe.rst | 70 ++++++++++++ Documentation/media/uapi/v4l/pixfmt-mtf8.rst | 75 +++++++++++++ Documentation/media/uapi/v4l/pixfmt-mtfa.rst | 87 +++++++++++++++ Documentation/media/uapi/v4l/pixfmt-mtfc.rst | 107 +++++++++++++++++++ Documentation/media/uapi/v4l/pixfmt-mtfe.rst | 107 +++++++++++++++++++ drivers/media/v4l2-core/v4l2-ioctl.c | 13 +++ include/uapi/linux/videodev2.h | 17 +++ 10 files changed, 645 insertions(+) create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtb8.rst create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtba.rst create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtbc.rst create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtbe.rst create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtf8.rst create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtfa.rst create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtfc.rst create mode 100644 Documentation/media/uapi/v4l/pixfmt-mtfe.rst diff --git a/Documentation/media/uapi/v4l/pixfmt-mtb8.rst b/Documentation/media/uapi/v4l/pixfmt-mtb8.rst new file mode 100644 index 000000000000..2337ccd66277 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-mtb8.rst @@ -0,0 +1,49 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-PIX-FMT-MTISP_B8: + +******************************* +V4L2_PIX_FMT_MTISP_B8 ('MTB8') +******************************* + +8-bit Packed Bayer formats. + + +Description +=========== + +The four pixel formats are used by Mediatek ISP. +This is a packed format, meaning all the data for a pixel lie +next to each other in memory, with a depth of 8 bits per pixel. +Each sample is stored in a byte. +The least significant byte is stored at lower memory addresses (little-endian). +The RGB byte order follows raw sRGB / Bayer format from sensor. +Below is an example of conventional RGB byte order BGGR. + +**Byte Order.** +Each cell is one byte. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - B\ :sub:`00` + - G\ :sub:`01` + - B\ :sub:`02` + - G\ :sub:`03` + * - start + 4: + - G\ :sub:`10` + - R\ :sub:`11` + - G\ :sub:`12` + - R\ :sub:`13` + * - start + 8: + - B\ :sub:`20` + - G\ :sub:`21` + - B\ :sub:`22` + - G\ :sub:`23` + * - start + 12: + - G\ :sub:`30` + - R\ :sub:`31` + - G\ :sub:`32` + - R\ :sub:`33` \ No newline at end of file diff --git a/Documentation/media/uapi/v4l/pixfmt-mtba.rst b/Documentation/media/uapi/v4l/pixfmt-mtba.rst new file mode 100644 index 000000000000..ade51d5472b0 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-mtba.rst @@ -0,0 +1,62 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-PIX-FMT-MTISP_B10: + +******************************* +V4L2_PIX_FMT_MTISP_B10 ('MTBA') +******************************* + +10-bit Packed Bayer formats. + + +Description +=========== + +The four pixel formats are used by Mediatek ISP. +This is a packed format, meaning all the data for a pixel lie +next to each other with no padding in memory, with a depth of 10 bits per pixel. +The least significant byte is stored at lower memory addresses (little-endian). +The RGB byte order follows raw sRGB / Bayer format from sensor. +Below is an example of conventional RGB byte order BGGR. + +**Byte Order.** +Each cell is one byte. + +pixels cross the byte boundary and have a ratio of 5 bytes for each 4 pixels. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - B\ :sub:`00low bits 7--0` + - G\ :sub:`01low bits 5--0` (bits 7--2) B\ :sub:`00high bits 9--8`\ (bits 1--0) + * - start + 2: + - B\ :sub:`02low bits 3--0`\ (bits 7--4) G\ :sub:`01high bits 9--6`\ (bits 3--0) + - G\ :sub:`03low bits 1--0`\ (bits 7--6) B\ :sub:`02high bits 9--4`\ (bits 5--0) + * - start + 4: + - G\ :sub:`03high bits 9--2` + * - start + 6: + - G\ :sub:`10low bits 7--0` + - R\ :sub:`11low bits 5--0`\ (bits 7--2) G\ :sub:`10high bits 9--8`\ (bits 1--0) + * - start + 8: + - G\ :sub:`12low bits 3--0`\ (bits 7--4) R\ :sub:`11high bits 9--6`\ (bits 3--0) + - R\ :sub:`13low bits 1--0`\ (bits 7--6) G\ :sub:`12high bits 9--4`\ (bits 5--0) + * - start + 10: + - R\ :sub:`13high bits 9--2` + * - start + 12: + - B\ :sub:`20low bits 7--0` + - G\ :sub:`21low bits 5--0`\ (bits 7--2) B\ :sub:`20high bits 9--8`\ (bits 1--0) + * - start + 14: + - B\ :sub:`22low bits 3--0`\ (bits 7--4) G\ :sub:`21high bits 9--6`\ (bits 3--0) + - G\ :sub:`23low bits 1--0`\ (bits 7--6) B\ :sub:`22high bits 9--4`\ (bits 5--0) + * - start + 16: + - G\ :sub:`23high bits 9--2` + * - start + 18: + - G\ :sub:`30low bits 7--0` + - R\ :sub:`31low bits 5--0`\ (bits 7--2) G\ :sub:`30high bits 9--8`\ (bits 1--0) + * - start + 20: + - G\ :sub:`32low bits 3--0`\ (bits 7--4) R\ :sub:`31high bits 9--6`\ (bits 3--0) + - R\ :sub:`33low bits 1--0`\ (bits 7--6) G\ :sub:`32high bits 9--4`\ (bits 5--0) + * - start + 22: + - R\ :sub:`33high bits 9--2` (bits 7--0) \ No newline at end of file diff --git a/Documentation/media/uapi/v4l/pixfmt-mtbc.rst b/Documentation/media/uapi/v4l/pixfmt-mtbc.rst new file mode 100644 index 000000000000..b122600fddb5 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-mtbc.rst @@ -0,0 +1,58 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-PIX-FMT-MTISP_B12: + +******************************* +V4L2_PIX_FMT_MTISP_B12 ('MTBC') +******************************* + +12-bit Packed Bayer formats. + + +Description +=========== + +The four pixel formats are used by Mediatek ISP. +This is a packed format, meaning all the data for a pixel lie +next to each other with no padding in memory, with a depth of 12 bits per pixel. +The least significant byte is stored at lower memory addresses (little-endian). +The RGB byte order follows raw sRGB / Bayer format from sensor. +Below is an example of conventional RGB byte order BGGR. + +**Byte Order.** +Each cell is one byte. + +pixels cross the byte boundary and have a ratio of 6 bytes for each 4 pixels. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - B\ :sub:`00lowbits 7--0` + - G\ :sub:`01lowbits 3--0`\ (bits 7--4) B\ :sub:`00highbits 11--8`\ (bits 3--0) + - G\ :sub:`01highbits 7--0` + - B\ :sub:`02lowbits 7--0` + - G\ :sub:`03lowbits 3--0`\ (bits 7--4) B\ :sub:`02highbits 11--8`\ (bits 3--0) + - G\ :sub:`03highbits 7--0` + * - start + 6: + - G\ :sub:`10lowbits 7--0` + - R\ :sub:`11lowbits 3--0`\ (bits 7--4) G\ :sub:`10highbits 11--8`\ (bits 3--0) + - R\ :sub:`11highbits 7--0` + - G\ :sub:`12lowbits 7--0` + - R\ :sub:`13lowbits 3--0`\ (bits 7--4) G\ :sub:`12highbits 11--8`\ (bits 3--0) + - R\ :sub:`13highbits 7--0` + * - start + 12: + - B\ :sub:`20lowbits 7--0` + - G\ :sub:`21lowbits 3--0`\ (bits 7--4) B\ :sub:`20highbits 11--8`\ (bits 3--0) + - G\ :sub:`21highbits 7--0` + - B\ :sub:`22lowbits 7--0` + - G\ :sub:`23lowbits 3--0`\ (bits 7--4) B\ :sub:`22highbits 11--8`\ (bits 3--0) + - G\ :sub:`23highbits 7--0` + * - start + 18: + - G\ :sub:`30lowbits 7--0` + - R\ :sub:`31lowbits 3--0`\ (bits 7--4) G\ :sub:`30highbits 11--8`\ (bits 3--0) + - R\ :sub:`31highbits 7--0` + - G\ :sub:`32lowbits 7--0` + - R\ :sub:`33lowbits 3--0`\ (bits 7--4) G\ :sub:`32highbits 11--8`\ (bits 3--0) + - R\ :sub:`33highbits 7--0` diff --git a/Documentation/media/uapi/v4l/pixfmt-mtbe.rst b/Documentation/media/uapi/v4l/pixfmt-mtbe.rst new file mode 100644 index 000000000000..4b9bc9a62504 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-mtbe.rst @@ -0,0 +1,70 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-PIX-FMT-MTISP_B14: + +******************************* +V4L2_PIX_FMT_MTISP_B14 ('MTBE') +******************************* + +14-bit Packed Bayer formats. + + +Description +=========== + +The four pixel formats are used by Mediatek ISP. +This is a packed format, meaning all the data for a pixel lie +next to each other with no padding in memory, with a depth of 14 bits per pixel. +The least significant byte is stored at lower memory addresses (little-endian). +The RGB byte order follows raw sRGB / Bayer format from sensor. +Below is an example of conventional RGB byte order BGGR. + +**Byte Order.** +Each cell is one byte. + +pixels cross the byte boundary and have a ratio of 7 bytes for each 4 pixels. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - B\ :sub:`00low bits 7--0` + - G\ :sub:`01low bits 1--0`\ (bits 7--6) B\ :sub:`00high bits 13--8`\ (bits 5--0) + - G\ :sub:`01low bits 9--2`\ + - B\ :sub:`02low bits 3--0`\ (bits 7--4) G\ :sub:`01high bits 13--10`\ (bits 3--0) + * - start + 4: + - B\ :sub:`02low bits 11--4`\ + - G\ :sub:`03low bits 5--0`\ (bits 7--2) B\ :sub:`02high bits 13--12`\ (bits 1--0) + - G\ :sub:`03high bits 13--6`\ + - + * - start + 8: + - G\ :sub:`10low bits 7--0` + - R\ :sub:`11low bits 1--0`\ (bits 7--6) G\ :sub:`10high bits 13--8`\ (bits 5--0) + - R\ :sub:`11low bits 9--2`\ + - G\ :sub:`12low bits 3--0`\ (bits 7--4) R\ :sub:`11high bits 13--10`\ (bits 3--0) + * - start + 12: + - G\ :sub:`12low bits 11--4`\ + - R\ :sub:`13low bits 5--0`\ (bits 7--2) G\ :sub:`12high bits 13--12`\ (bits 1--0) + - R\ :sub:`13high bits 13--6`\ + - + * - start + 16: + - B\ :sub:`20low bits 7--0` + - G\ :sub:`21low bits 1--0`\ (bits 7--6) B\ :sub:`20high bits 13--8`\ (bits 5--0) + - G\ :sub:`21low bits 9--2`\ + - B\ :sub:`22low bits 3--0`\ (bits 7--4) G\ :sub:`21high bits 13--10`\ (bits 3--0) + * - start + 20: + - B\ :sub:`22low bits 11--4`\ + - G\ :sub:`23low bits 5--0`\ (bits 7--2) B\ :sub:`22high bits 13--12`\ (bits 1--0) + - G\ :sub:`23high bits 13--6`\ + - + * - start + 24: + - G\ :sub:`30low bits 7--0` + - R\ :sub:`31low bits 1--0`\ (bits 7--6) G\ :sub:`30high bits 13--8`\ (bits 5--0) + - R\ :sub:`31low bits 9--2`\ + - G\ :sub:`32low bits 3--0`\ (bits 7--4) R\ :sub:`31high bits 13--10`\ (bits 3--0) + * - start + 28: + - G\ :sub:`32low bits 11--4`\ + - R\ :sub:`33low bits 5--0`\ (bits 7--2) G\ :sub:`32high bits 13--12`\ (bits 1--0) + - R\ :sub:`33high bits 13--6`\ + - \ No newline at end of file diff --git a/Documentation/media/uapi/v4l/pixfmt-mtf8.rst b/Documentation/media/uapi/v4l/pixfmt-mtf8.rst new file mode 100644 index 000000000000..51c9ddc4e20d --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-mtf8.rst @@ -0,0 +1,75 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-PIX-FMT-MTISP_F8: + +******************************* +V4L2_PIX_FMT_MTISP_F8 ('MTF8') +******************************* + +8-bit Packed Full-G Bayer formats. + + +Description +=========== + +The four pixel formats are used by Mediatek ISP. +This is a packed format with a depth of 8 bits per pixel. +Full-G means 1 more pixel for green channel every 2 pixels. +The least significant byte is stored at lower memory addresses (little-endian). +The RGB byte order follows raw sRGB / Bayer format from sensor. +Below is an example of conventional RGB byte order BGGR. + +**Bit-packed representation.** + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - B\ :sub:`00` + - FG\ :sub:`01` + - G\ :sub:`02` + - B\ :sub:`03` + - FG\ :sub:`04` + - G\ :sub:`05` + * - G\ :sub:`10` + - R\ :sub:`11` + - FG\ :sub:`12` + - G\ :sub:`13` + - R\ :sub:`14` + - FG\ :sub:`15` + +**Byte Order.** +Each cell is one byte. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - B\ :sub:`00` + - FG\ :sub:`01` + - G\ :sub:`02` + - B\ :sub:`03` + - FG\ :sub:`04` + - G\ :sub:`05` + * - start + 6: + - G\ :sub:`10` + - R\ :sub:`11` + - FG\ :sub:`12` + - G\ :sub:`13` + - R\ :sub:`14` + - FG\ :sub:`15` + * - start + 12: + - B\ :sub:`20` + - FG\ :sub:`21` + - G\ :sub:`22` + - B\ :sub:`23` + - FG\ :sub:`24` + - G\ :sub:`25` + * - start + 18: + - G\ :sub:`30` + - R\ :sub:`31` + - FG\ :sub:`32` + - G\ :sub:`33` + - R\ :sub:`34` + - FG\ :sub:`35` \ No newline at end of file diff --git a/Documentation/media/uapi/v4l/pixfmt-mtfa.rst b/Documentation/media/uapi/v4l/pixfmt-mtfa.rst new file mode 100644 index 000000000000..68421c44f5e7 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-mtfa.rst @@ -0,0 +1,87 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-PIX-FMT-MTISP_F10: + +******************************* +V4L2_PIX_FMT_MTISP_F10 ('MTFA') +******************************* + +10-bit Packed Full-G Bayer formats. + + +Description +=========== + +The four pixel formats are used by Mediatek ISP. +This is a packed format with a depth of 10 bits per pixel. +Full-G means 1 more pixel for green channel every 2 pixels. +The least significant byte is stored at lower memory addresses (little-endian). +The RGB byte order follows raw sRGB / Bayer format from sensor. +Below is an example of conventional RGB byte order BGGR. + +**Bit-packed representation.** + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - B\ :sub:`00` + - FG\ :sub:`01` + - G\ :sub:`02` + - B\ :sub:`03` + - FG\ :sub:`04` + - G\ :sub:`05` + * - G\ :sub:`10` + - R\ :sub:`11` + - FG\ :sub:`12` + - G\ :sub:`13` + - R\ :sub:`14` + - FG\ :sub:`15` + +**Byte Order.** +Each cell is one byte. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - B\ :sub:`00low bits 7--0` + - FG\ :sub:`01low bits 5--0`\ (bits 7--2) B\ :sub:`00high bits 9--8`\ (bits 1--0) + - G\ :sub:`02low bits 3--0`\ (bits 7--4) FG\ :sub:`01high bits 9--6`\ (bits 3--0) + - B\ :sub:`03low bits 1--0`\ (bits 7--6) G\ :sub:`02high bits 9--4`\ (bits 5--0) + * - start + 4: + - B\ :sub:`03high bits 9--2` + - FG\ :sub:`04low bits 7--0` + - G\ :sub:`05low bits 5--0`\ (bits 7--2) FG\ :sub:`04high bits 9--8`\ (bits 1--0) + - G\ :sub:`05high bits 3--0` + * - start + 8: + - G\ :sub:`10low bits 7--0` + - R\ :sub:`11low bits 5--0`\ (bits 7--2) G\ :sub:`10high bits 9--8`\ (bits 1--0) + - FG\ :sub:`12low bits 3--0`\ (bits 7--4) R\ :sub:`11high bits 9--6`\ (bits 3--0) + - G\ :sub:`13low bits 1--0`\ (bits 7--6) FG\ :sub:`12high bits 9--4`\ (bits 5--0) + * - start + 12: + - G\ :sub:`13high bits 9--2` + - R\ :sub:`14low bits 7--0` + - FG\ :sub:`15low bits 5--0`\ (bits 7--2) R\ :sub:`14high bits 9--8`\ (bits 1--0) + - FG\ :sub:`15high bits 3--0` + * - start + 16: + - B\ :sub:`20low bits 7--0` + - FG\ :sub:`21low bits 5--0`\ (bits 7--2) B\ :sub:`20high bits 9--8`\ (bits 1--0) + - G\ :sub:`22low bits 3--0`\ (bits 7--4) FG\ :sub:`21high bits 9--6`\ (bits 3--0) + - B\ :sub:`23low bits 1--0`\ (bits 7--6) G\ :sub:`22high bits 9--4`\ (bits 5--0) + * - start + 20: + - B\ :sub:`23high bits 9--2` + - FG\ :sub:`24low bits 7--0` + - G\ :sub:`25low bits 5--0`\ (bits 7--2) FG\ :sub:`24high bits 9--8`\ (bits 1--0) + - G\ :sub:`25high bits 3--0` + * - start + 24: + - G\ :sub:`30low bits 7--0` + - R\ :sub:`31low bits 5--0`\ (bits 7--2) G\ :sub:`30high bits 9--8`\ (bits 1--0) + - FG\ :sub:`32low bits 3--0`\ (bits 7--4) R\ :sub:`31high bits 9--6`\ (bits 3--0) + - G\ :sub:`33low bits 1--0`\ (bits 7--6) FG\ :sub:`32high bits 9--4`\ (bits 5--0) + * - start + 28: + - G\ :sub:`33high bits 9--2` + - R\ :sub:`34low bits 7--0` + - FG\ :sub:`35low bits 5--0`\ (bits 7--2) R\ :sub:`34high bits 9--8`\ (bits 1--0) + - FG\ :sub:`35high bits 3--0` \ No newline at end of file diff --git a/Documentation/media/uapi/v4l/pixfmt-mtfc.rst b/Documentation/media/uapi/v4l/pixfmt-mtfc.rst new file mode 100644 index 000000000000..a3535f5435fa --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-mtfc.rst @@ -0,0 +1,107 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-PIX-FMT-MTISP_F12: + +******************************* +V4L2_PIX_FMT_MTISP_F12 ('MTFC') +******************************* + +12-bit Packed Full-G Bayer formats. + + +Description +=========== + +The four pixel formats are used by Mediatek ISP. +This is a packed format with a depth of 12 bits per pixel. +Full-G means 1 more pixel for green channel every 2 pixels. +The least significant byte is stored at lower memory addresses (little-endian). +The RGB byte order follows raw sRGB / Bayer format from sensor. +Below is an example of conventional RGB byte order BGGR. + +**Bit-packed representation.** + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - B\ :sub:`00` + - FG\ :sub:`01` + - G\ :sub:`02` + - B\ :sub:`03` + - FG\ :sub:`04` + - G\ :sub:`05` + * - G\ :sub:`10` + - R\ :sub:`11` + - FG\ :sub:`12` + - G\ :sub:`13` + - R\ :sub:`14` + - FG\ :sub:`15` + +**Byte Order.** +Each cell is one byte. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - B\ :sub:`00low bits 7--0` + - FG\ :sub:`01low bits 3--0`\ (bits 7--4) B\ :sub:`00high bits 11--8`\ (bits 3--0) + * - start + 2: + - FG\ :sub:`01high bits 7--0` + - G\ :sub:`02low bits 7--0` + * - start + 4: + - B\ :sub:`03low bits 3--0`\ (bits 7--4) G\ :sub:`02high bits 11--8`\ (bits 3--0) + - B\ :sub:`03high bits 7--0` + * - start + 6: + - FG\ :sub:`04low bits 7--0` + - G\ :sub:`05low bits 3--0`\ (bits 7--4) FG\ :sub:`04high bits 11--8`\ (bits 3--0) + * - start + 8: + - G\ :sub:`05high bits 7--0` + - + * - start + 10: + - G\ :sub:`10low bits 7--0` + - R\ :sub:`11low bits 3--0`\ (bits 7--4) G\ :sub:`10high bits 11--8`\ (bits 3--0) + * - start + 12: + - R\ :sub:`11high bits 7--0` + - FG\ :sub:`12low bits 7--0` + * - start + 14: + - G\ :sub:`13low bits 3--0`\ (bits 7--4) FG\ :sub:`12high bits 11--8`\ (bits 3--0) + - G\ :sub:`13high bits 7--0` + * - start + 16: + - R\ :sub:`14low bits 7--0` + - FG\ :sub:`15low bits 3--0`\ (bits 7--4) R\ :sub:`14high bits 11--8`\ (bits 3--0) + * - start + 18: + - FG\ :sub:`15high bits 7--0` + - + * - start + 20: + - B\ :sub:`20low bits 7--0` + - FG\ :sub:`21low bits 3--0`\ (bits 7--4) B\ :sub:`20high bits 11--8`\ (bits 3--0) + * - start + 22: + - FG\ :sub:`21high bits 7--0` + - G\ :sub:`22low bits 7--0` + * - start + 24: + - B\ :sub:`23low bits 3--0`\ (bits 7--4) G\ :sub:`22high bits 11--8`\ (bits 3--0) + - B\ :sub:`23high bits 7--0` + * - start + 26: + - FG\ :sub:`24low bits 7--0` + - G\ :sub:`25low bits 3--0`\ (bits 7--4) FG\ :sub:`24high bits 11--8`\ (bits 3--0) + * - start + 28: + - G\ :sub:`25high bits 7--0` + - + * - start + 30: + - G\ :sub:`30low bits 7--0` + - R\ :sub:`31low bits 3--0`\ (bits 7--4) G\ :sub:`30high bits 11--8`\ (bits 3--0) + * - start + 32: + - R\ :sub:`31high bits 7--0` + - FG\ :sub:`32low bits 7--0` + * - start + 34: + - G\ :sub:`33low bits 3--0`\ (bits 7--4) FG\ :sub:`32high bits 11--8`\ (bits 3--0) + - G\ :sub:`33high bits 7--0` + * - start + 36: + - R\ :sub:`34low bits 7--0` + - FG\ :sub:`35low bits 3--0`\ (bits 7--4) R\ :sub:`34high bits 11--8`\ (bits 3--0) + * - start + 38: + - FG\ :sub:`35high bits 7--0` + - \ No newline at end of file diff --git a/Documentation/media/uapi/v4l/pixfmt-mtfe.rst b/Documentation/media/uapi/v4l/pixfmt-mtfe.rst new file mode 100644 index 000000000000..324a258e897f --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-mtfe.rst @@ -0,0 +1,107 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-PIX-FMT-MTISP_F14: + +******************************* +V4L2_PIX_FMT_MTISP_F14 ('MTFE') +******************************* + +14-bit Packed Full-G Bayer formats. + + +Description +=========== + +The four pixel formats are used by Mediatek ISP. +This is a packed format with a depth of 14 bits per pixel. +Full-G means 1 more pixel for green channel every 2 pixels. +The least significant byte is stored at lower memory addresses (little-endian). +The RGB byte order follows raw sRGB / Bayer format from sensor. +Below is an example of conventional RGB byte order BGGR. + +**Bit-packed representation.** + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - B\ :sub:`00` + - FG\ :sub:`01` + - G\ :sub:`02` + - B\ :sub:`03` + - FG\ :sub:`04` + - G\ :sub:`05` + * - G\ :sub:`10` + - R\ :sub:`11` + - FG\ :sub:`12` + - G\ :sub:`13` + - R\ :sub:`14` + - FG\ :sub:`15` + +**Byte Order.** +Each cell is one byte. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - B\ :sub:`00low bits 7--0` + - FG\ :sub:`01low bits 1--0`\ (bits 7--6) B\ :sub:`00high bits 13--8`\ (bits 5--0) + - FG\ :sub:`01low bits 9--2` + - G\ :sub:`02low bits 3--0`\ (bits 7--4) FG\ :sub:`01high bits 13--10`\ (bits 3--0) + * - start + 4: + - G\ :sub:`02low bits 11--4` + - B\ :sub:`03low bits 5--0`\ (bits 7--2) G\ :sub:`02high bits 13--12`\ (bits 1--0) + - B\ :sub:`03high bits 13--6` + - FG\ :sub:`04low bits 7--0` + * - start + 8: + - G\ :sub:`05low bits 1--0`\ (bits 7--6) FG\ :sub:`04high bits 13--8`\ (bits 5--0) + - G\ :sub:`05high bits 9--2` + - G\ :sub:`05high bits 13--10` + - + * - start + 12: + - G\ :sub:`10low bits 7--0` + - R\ :sub:`11low bits 1--0`\ (bits 7--6) G\ :sub:`10high bits 13--8`\ (bits 5--0) + - R\ :sub:`11low bits 9--2` + - FG\ :sub:`12low bits 3--0`\ (bits 7--4) R\ :sub:`11high bits 13--10`\ (bits 3--0) + * - start + 16: + - FG\ :sub:`12low bits 11--4` + - G\ :sub:`13low bits 5--0`\ (bits 7--2) FG\ :sub:`12high bits 13--12`\ (bits 1--0) + - G\ :sub:`13high bits 13--6` + - R\ :sub:`14low bits 7--0` + * - start + 20: + - FG\ :sub:`15low bits 1--0`\ (bits 7--6) R\ :sub:`14high bits 13--8`\ (bits 5--0) + - FG\ :sub:`15high bits 9--2` + - FG\ :sub:`15high bits 13--10` + - + * - start + 24: + - B\ :sub:`20low bits 7--0` + - FG\ :sub:`21low bits 1--0`\ (bits 7--6) B\ :sub:`20high bits 13--8`\ (bits 5--0) + - FG\ :sub:`21low bits 9--2` + - G\ :sub:`22low bits 3--0`\ (bits 7--4) FG\ :sub:`21high bits 13--10`\ (bits 3--0) + * - start + 28: + - G\ :sub:`22low bits 11--4` + - B\ :sub:`23low bits 5--0`\ (bits 7--2) G\ :sub:`22high bits 13--12`\ (bits 1--0) + - B\ :sub:`23high bits 13--6` + - FG\ :sub:`24low bits 7--0` + * - start + 32: + - G\ :sub:`25low bits 1--0`\ (bits 7--6) FG\ :sub:`24high bits 13--8`\ (bits 5--0) + - G\ :sub:`25high bits 9--2` + - G\ :sub:`25high bits 13--10` + - + * - start + 36: + - G\ :sub:`30low bits 7--0` + - R\ :sub:`31low bits 1--0`\ (bits 7--6) G\ :sub:`30high bits 13--8`\ (bits 5--0) + - R\ :sub:`31low bits 9--2` + - FG\ :sub:`32low bits 3--0`\ (bits 7--4) R\ :sub:`31high bits 13--10`\ (bits 3--0) + * - start + 40: + - FG\ :sub:`32low bits 11--4` + - G\ :sub:`33low bits 5--0`\ (bits 7--2) FG\ :sub:`32high bits 13--12`\ (bits 1--0) + - G\ :sub:`33high bits 13--6` + - R\ :sub:`34low bits 7--0` + * - start + 44: + - FG\ :sub:`35low bits 1--0`\ (bits 7--6) R\ :sub:`34high bits 13--8`\ (bits 5--0) + - FG\ :sub:`35high bits 9--2` + - FG\ :sub:`35high bits 13--10` + - \ No newline at end of file diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index ac87c3e37280..2f536fedd9c4 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1297,6 +1297,14 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_KONICA420: descr = "GSPCA KONICA420"; break; case V4L2_PIX_FMT_HSV24: descr = "24-bit HSV 8-8-8"; break; case V4L2_PIX_FMT_HSV32: descr = "32-bit XHSV 8-8-8-8"; break; + case V4L2_PIX_FMT_MTISP_B8: descr = "8-bit Packed Bayer format"; break; + case V4L2_PIX_FMT_MTISP_F8: descr = "8-bit Packed Full-G Bayer format"; break; + case V4L2_PIX_FMT_MTISP_B10: descr = "10-bit Packed Bayer format"; break; + case V4L2_PIX_FMT_MTISP_F10: descr = "10-bit Packed Full-G Bayer format"; break; + case V4L2_PIX_FMT_MTISP_B12: descr = "12-bit Packed Bayer format"; break; + case V4L2_PIX_FMT_MTISP_F12: descr = "12-bit Packed Full-G Bayer format"; break; + case V4L2_PIX_FMT_MTISP_B14: descr = "14-bit Packed Bayer format"; break; + case V4L2_PIX_FMT_MTISP_F14: descr = "14-bit Packed Full-G Bayer format"; break; case V4L2_SDR_FMT_CU8: descr = "Complex U8"; break; case V4L2_SDR_FMT_CU16LE: descr = "Complex U16LE"; break; case V4L2_SDR_FMT_CS8: descr = "Complex S8"; break; @@ -1312,6 +1320,11 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_META_FMT_VSP1_HGO: descr = "R-Car VSP1 1-D Histogram"; break; case V4L2_META_FMT_VSP1_HGT: descr = "R-Car VSP1 2-D Histogram"; break; case V4L2_META_FMT_UVC: descr = "UVC payload header metadata"; break; + case V4L2_META_FMT_MTISP_3A: descr = "AE/AWB Histogram"; break; + case V4L2_META_FMT_MTISP_AF: descr = "AF Histogram"; break; + case V4L2_META_FMT_MTISP_LCS: descr = "Local contrast enhanced statistics"; break; + case V4L2_META_FMT_MTISP_LMV: descr = "Local motion vector Histogram"; break; + case V4L2_META_FMT_MTISP_PARAMS: descr = "MTK ISP tuning metadata"; break; default: /* Compressed formats */ diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 1050a75fb7ef..ef51911fcfe4 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -728,6 +728,16 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_IPU3_SGRBG10 v4l2_fourcc('i', 'p', '3', 'G') /* IPU3 packed 10-bit GRBG bayer */ #define V4L2_PIX_FMT_IPU3_SRGGB10 v4l2_fourcc('i', 'p', '3', 'r') /* IPU3 packed 10-bit RGGB bayer */ +/* Vendor specific - Mediatek ISP bayer formats */ +#define V4L2_PIX_FMT_MTISP_B8 v4l2_fourcc('M', 'T', 'B', '8') /* Packed bayer format, 8-bit */ +#define V4L2_PIX_FMT_MTISP_B10 v4l2_fourcc('M', 'T', 'B', 'A') /* Packed bayer format, 10-bit */ +#define V4L2_PIX_FMT_MTISP_B12 v4l2_fourcc('M', 'T', 'B', 'C') /* Packed bayer format, 12-bit */ +#define V4L2_PIX_FMT_MTISP_B14 v4l2_fourcc('M', 'T', 'B', 'E') /* Packed bayer format, 14-bit */ +#define V4L2_PIX_FMT_MTISP_F8 v4l2_fourcc('M', 'T', 'F', '8') /* Full-G bayer format, 8-bit */ +#define V4L2_PIX_FMT_MTISP_F10 v4l2_fourcc('M', 'T', 'F', 'A') /* Full-G bayer format, 10-bit */ +#define V4L2_PIX_FMT_MTISP_F12 v4l2_fourcc('M', 'T', 'F', 'C') /* Full-G bayer format, 12-bit */ +#define V4L2_PIX_FMT_MTISP_F14 v4l2_fourcc('M', 'T', 'F', 'E') /* Full-G bayer format, 14-bit */ + /* SDR formats - used only for Software Defined Radio devices */ #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */ #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */ @@ -750,6 +760,13 @@ struct v4l2_pix_format { #define V4L2_META_FMT_UVC v4l2_fourcc('U', 'V', 'C', 'H') /* UVC Payload Header metadata */ #define V4L2_META_FMT_D4XX v4l2_fourcc('D', '4', 'X', 'X') /* D4XX Payload Header metadata */ +/* Vendor specific - Mediatek ISP parameters for firmware */ +#define V4L2_META_FMT_MTISP_PARAMS v4l2_fourcc('M', 'T', 'f', 'p') /* ISP tuning parameters */ +#define V4L2_META_FMT_MTISP_3A v4l2_fourcc('M', 'T', 'f', 'a') /* AE/AWB histogram */ +#define V4L2_META_FMT_MTISP_AF v4l2_fourcc('M', 'T', 'f', 'f') /* AF histogram */ +#define V4L2_META_FMT_MTISP_LCS v4l2_fourcc('M', 'T', 'f', 'c') /* Local contrast enhanced statistics */ +#define V4L2_META_FMT_MTISP_LMV v4l2_fourcc('M', 'T', 'f', 'm') /* Local motion vector histogram */ + /* priv field value to indicates that subsequent fields are valid. */ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe From patchwork Tue Jun 11 03:53:40 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jungo Lin X-Patchwork-Id: 10985807 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C0F5014C0 for ; Tue, 11 Jun 2019 03:55:02 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B198628387 for ; Tue, 11 Jun 2019 03:55:02 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A5B8C283C3; Tue, 11 Jun 2019 03:55:02 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 0BFDC28726 for ; Tue, 11 Jun 2019 03:55:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:Reply-To:List-Subscribe:List-Help: List-Post:List-Archive:List-Unsubscribe:List-Id:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=kcXJxqIIOWK00d5i5y4KJ/qiss+LCRgeMPzdQu9iZ8I=; b=Y5w53mMafvGxsc kcGH9aiG7ovl+6sT/1yqPRFYPAzWlxwe0CtVw/Pdv+F8av2UzYtOOxcxt9qmZEgD6Zk00x6ZuzZnL 1NLjxqSUDN+ZCl3gwM+HiBIjf2WAhF0kknCH3hJ2KCPPpm/4iYcu+Ypqzvjx3lnM8mjLDJCoS+ArH nIzVfNf0lXPM9ad+bBN/WG/7K/VKZRBiqjuuweeBXg85wtTHIm+ljdMTj2geXHUa4Y2SYDmMgGQOP Azc/NJcW2D0CBCql+LJ62j5zQpy7t6QlJgd3TVReiyrprZ2Cg4V04jQ6+j8imLC+d9bz6IToN1X0/ u85nySsM3FVaon9G2kTg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1haXs6-0007tN-DM; Tue, 11 Jun 2019 03:54:54 +0000 Received: from mailgw02.mediatek.com ([216.200.240.185]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1haXrI-0007JV-TY; Tue, 11 Jun 2019 03:54:07 +0000 X-UUID: 25661a17233e45b6aef2899f4e6bec4f-20190610 X-UUID: 25661a17233e45b6aef2899f4e6bec4f-20190610 Received: from mtkcas68.mediatek.inc [(172.29.94.19)] by mailgw02.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 343107097; Mon, 10 Jun 2019 19:53:57 -0800 Received: from mtkmbs08n1.mediatek.inc (172.21.101.55) by MTKMBS62N2.mediatek.inc (172.29.193.42) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 10 Jun 2019 20:53:56 -0700 Received: from mtkcas09.mediatek.inc (172.21.101.178) by mtkmbs08n1.mediatek.inc (172.21.101.55) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 11 Jun 2019 11:53:54 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 11 Jun 2019 11:53:54 +0800 From: Jungo Lin To: , , , , Subject: [RFC,v3 5/9] media: platform: Add Mediatek ISP P1 V4L2 control Date: Tue, 11 Jun 2019 11:53:40 +0800 Message-ID: <20190611035344.29814-6-jungo.lin@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20190611035344.29814-1-jungo.lin@mediatek.com> References: <20190611035344.29814-1-jungo.lin@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190610_205404_958005_34BE7D30 X-CRM114-Status: GOOD ( 15.17 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: jungo.lin@mediatek.com Cc: devicetree@vger.kernel.org, sean.cheng@mediatek.com, rynn.wu@mediatek.com, srv_heupstream@mediatek.com, robh@kernel.org, ryan.yu@mediatek.com, frankie.chiu@mediatek.com, jungo.lin@mediatek.com, sj.huang@mediatek.com, linux-mediatek@lists.infradead.org, ddavenport@chromium.org, frederic.chen@mediatek.com, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Reserved Mediatek ISP P1 V4L2 control number with 16. Moreover, add two V4L2 controls for ISP P1 user space usage. 1. V4L2_CID_MTK_GET_BIN_INFO - Provide the image output width & height in case camera binning mode is enabled. 2. V4L2_CID_MTK_RAW_PATH - Export the path control of the main stream to user space. One is pure raw and the other is processing raw. The default value is 0 which outputs the pure raw bayer image from sesnor, without image processing in ISP HW. Signed-off-by: Jungo Lin --- drivers/media/platform/mtk-isp/Makefile | 3 + .../media/platform/mtk-isp/isp_50/Makefile | 5 + .../platform/mtk-isp/isp_50/cam/Makefile | 5 + .../mtk-isp/isp_50/cam/mtk_cam-ctrl.c | 138 ++++++++++++++++++ .../mtk-isp/isp_50/cam/mtk_cam-ctrl.h | 38 +++++ include/uapi/linux/v4l2-controls.h | 4 + 6 files changed, 193 insertions(+) create mode 100644 drivers/media/platform/mtk-isp/Makefile create mode 100644 drivers/media/platform/mtk-isp/isp_50/Makefile create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/Makefile create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h diff --git a/drivers/media/platform/mtk-isp/Makefile b/drivers/media/platform/mtk-isp/Makefile new file mode 100644 index 000000000000..c17fb3fc3340 --- /dev/null +++ b/drivers/media/platform/mtk-isp/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y += isp_50/ diff --git a/drivers/media/platform/mtk-isp/isp_50/Makefile b/drivers/media/platform/mtk-isp/isp_50/Makefile new file mode 100644 index 000000000000..8498fe70e418 --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +ifeq ($(CONFIG_VIDEO_MEDIATEK_ISP_PASS1),y) +obj-y += cam/ +endif diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile new file mode 100644 index 000000000000..53fb69d3add6 --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +mtk-cam-isp-objs += mtk_cam-ctrl.o + +obj-$(CONFIG_VIDEO_MEDIATEK_ISP_PASS1) += mtk-cam-isp.o \ No newline at end of file diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c new file mode 100644 index 000000000000..31d801c82495 --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2018 MediaTek Inc. + +#include +#include + +#include "mtk_cam-ctrl.h" +#include "mtk_cam.h" + +static int handle_ctrl_get_bin_info(struct v4l2_ctrl *ctrl, int is_width) +{ + struct mtk_cam_dev *cam_dev = ctrl->priv; + struct v4l2_format *fmt; + + fmt = &cam_dev->vdev_nodes[MTK_CAM_P1_MAIN_STREAM_OUT].vdev_fmt; + + dev_dbg(&cam_dev->pdev->dev, "Get bin info w*h:%d*%d is_width:%d", + fmt->fmt.pix_mp.width, fmt->fmt.pix_mp.height, is_width); + + if (is_width) + ctrl->val = fmt->fmt.pix_mp.width; + else + ctrl->val = fmt->fmt.pix_mp.height; + + return 0; +} + +static int handle_ctrl_get_process_raw(struct v4l2_ctrl *ctrl) +{ + struct mtk_cam_dev *cam_dev = ctrl->priv; + struct isp_p1_device *p1_dev = get_p1_device(&cam_dev->pdev->dev); + + ctrl->val = (p1_dev->isp_ctx.isp_raw_path == ISP_PROCESS_RAW_PATH); + + dev_dbg(&cam_dev->pdev->dev, "Get process raw:%d", ctrl->val); + + return 0; +} + +static int handle_ctrl_set_process_raw(struct v4l2_ctrl *ctrl) +{ + struct mtk_cam_dev *cam_dev = ctrl->priv; + struct isp_p1_device *p1_dev = get_p1_device(&cam_dev->pdev->dev); + + p1_dev->isp_ctx.isp_raw_path = (ctrl->val) ? + ISP_PROCESS_RAW_PATH : ISP_PURE_RAW_PATH; + dev_dbg(&cam_dev->pdev->dev, "Set process raw:%d", ctrl->val); + return 0; +} + +static int mtk_cam_dev_g_ctrl(struct v4l2_ctrl *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MTK_PROCESSING_RAW: + handle_ctrl_get_process_raw(ctrl); + break; + case V4L2_CID_MTK_GET_BIN_WIDTH: + handle_ctrl_get_bin_info(ctrl, 1); + break; + case V4L2_CID_MTK_GET_BIN_HEIGTH: + handle_ctrl_get_bin_info(ctrl, 0); + break; + default: + return -EINVAL; + } + return 0; +} + +static int mtk_cam_dev_s_ctrl(struct v4l2_ctrl *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MTK_PROCESSING_RAW: + return handle_ctrl_set_process_raw(ctrl); + default: + return -EINVAL; + } +} + +static const struct v4l2_ctrl_ops mtk_cam_dev_ctrl_ops = { + .g_volatile_ctrl = mtk_cam_dev_g_ctrl, + .s_ctrl = mtk_cam_dev_s_ctrl, +}; + +struct v4l2_ctrl_config mtk_cam_controls[] = { + { + .ops = &mtk_cam_dev_ctrl_ops, + .id = V4L2_CID_MTK_PROCESSING_RAW, + .name = "MTK CAM PROCESSING RAW", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, + }, + { + .ops = &mtk_cam_dev_ctrl_ops, + .id = V4L2_CID_MTK_GET_BIN_WIDTH, + .name = "MTK CAM GET BIN WIDTH", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = IMG_MIN_WIDTH, + .max = IMG_MAX_WIDTH, + .step = 1, + .def = IMG_MAX_WIDTH, + .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, + }, + { + .ops = &mtk_cam_dev_ctrl_ops, + .id = V4L2_CID_MTK_GET_BIN_HEIGTH, + .name = "MTK CAM GET BIN HEIGHT", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = IMG_MIN_HEIGHT, + .max = IMG_MAX_HEIGHT, + .step = 1, + .def = IMG_MAX_HEIGHT, + .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, + }, +}; + +int mtk_cam_ctrl_init(struct mtk_cam_dev *cam_dev, + struct v4l2_ctrl_handler *hdl) +{ + unsigned int i; + + /* Initialized HW controls, allow V4L2_CID_MTK_CAM_MAX ctrls */ + v4l2_ctrl_handler_init(hdl, V4L2_CID_MTK_CAM_MAX); + if (hdl->error) { + v4l2_ctrl_handler_free(hdl); + return hdl->error; + } + + for (i = 0; i < ARRAY_SIZE(mtk_cam_controls); i++) + v4l2_ctrl_new_custom(hdl, &mtk_cam_controls[i], cam_dev); + + dev_dbg(&cam_dev->pdev->dev, "%s done", __func__); + + return 0; +} diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h new file mode 100644 index 000000000000..0f9349ae0b07 --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ctrl.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 MediaTek Inc. + */ + +#ifndef __MTK_CAM_CTRL_H__ +#define __MTK_CAM_CTRL_H__ + +#include + +#include "mtk_cam-v4l2-util.h" + +/* The base for the MTK Camera ISP P1 driver controls. + * We reserve 16 controls for this driver. + */ +#define V4L2_CID_MTK_CAM_BASE V4L2_CID_USER_MTK_CAM_BASE + +/* Control MTK ISP P1 main stream to process raw image data or not. + * The default value is 0 which outputs the pure raw bayer data from sensor, + * without image processing in ISP HW. + */ +#define V4L2_CID_MTK_PROCESSING_RAW (V4L2_CID_MTK_CAM_BASE + 1) + +/* MTK ISP P1 HW supports frontal binning function. + * If this function is enabled, the 3A algo. may get the new image resolution + * which is binned by ISP P1. If this function is disabled or no supported, + * the image resolution will be equal to configured image format. + * For this control, it is read only. + */ +#define V4L2_CID_MTK_GET_BIN_WIDTH (V4L2_CID_MTK_CAM_BASE + 2) +#define V4L2_CID_MTK_GET_BIN_HEIGTH (V4L2_CID_MTK_CAM_BASE + 3) + +#define V4L2_CID_MTK_CAM_MAX 16 + +int mtk_cam_ctrl_init(struct mtk_cam_dev *cam_dev, + struct v4l2_ctrl_handler *hdl); + +#endif /* __MTK_CAM_CTRL_H__ */ diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 37807f23231e..2db99716f40d 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -192,6 +192,10 @@ enum v4l2_colorfx { * We reserve 16 controls for this driver. */ #define V4L2_CID_USER_IMX_BASE (V4L2_CID_USER_BASE + 0x10b0) +/* The base for the mediatek ISP Pass 1 driver controls */ +/* We reserve 16 controls for this driver. */ +#define V4L2_CID_USER_MTK_CAM_BASE (V4L2_CID_USER_BASE + 0x10c0) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ From patchwork Tue Jun 11 03:53:41 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jungo Lin X-Patchwork-Id: 10985813 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id ED6A014E5 for ; Tue, 11 Jun 2019 03:55:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D19FF1FF65 for ; Tue, 11 Jun 2019 03:55:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C575C2839C; Tue, 11 Jun 2019 03:55:53 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 394991FF65 for ; Tue, 11 Jun 2019 03:55:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:Reply-To:List-Subscribe:List-Help: List-Post:List-Archive:List-Unsubscribe:List-Id:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=3llGPaH76T/QL1oNy0fliN1XuaKHzgGfdY3EPiLrwus=; b=bJfcfgM+VIJJDj FPLJaM6bIM+Cl1lWWv3rOCAwmrHaJgtKpclYlkT2GBEzsG/5J8LR4FeoYUer+G41ovWWcDglrWYY2 TNp5md5B0lZ1T/6FJeSYFGrLVH3ZZe/TdsceJWuF/uzeG3EOwAPP/q76Lupoubnvqw+KWs0SWYiGp ovvMfXJIdtLVEcIz/55tnc2y3KfB+kEGjNiQaMp7CRdrEJng2+CxZN64rNQPTxhxGfeTkSgs0B0VT BmukvGLpWmrpP/h7Ao/yWRulNoxlYyx124yYRNs4QE6QPlAXOUP93Nh7BaMDowQw1lI+2UZMpU341 ljhyE25FUNOF6WYSuOww==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1haXss-0001IY-RJ; Tue, 11 Jun 2019 03:55:43 +0000 Received: from mailgw01.mediatek.com ([216.200.240.184]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1haXrJ-0007J3-JV; Tue, 11 Jun 2019 03:54:13 +0000 X-UUID: 1d76a615a5a9463f9d05ddae3f27e377-20190610 X-UUID: 1d76a615a5a9463f9d05ddae3f27e377-20190610 Received: from mtkcas66.mediatek.inc [(172.29.193.44)] by mailgw01.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 432503950; Mon, 10 Jun 2019 19:53:59 -0800 Received: from MTKMBS01N2.mediatek.inc (172.21.101.79) by MTKMBS62N2.mediatek.inc (172.29.193.42) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 10 Jun 2019 20:53:58 -0700 Received: from mtkcas09.mediatek.inc (172.21.101.178) by mtkmbs01n2.mediatek.inc (172.21.101.79) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 11 Jun 2019 11:53:56 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 11 Jun 2019 11:53:56 +0800 From: Jungo Lin To: , , , , Subject: [RFC,v3 6/9] media: platform: Add Mediatek ISP P1 V4L2 functions Date: Tue, 11 Jun 2019 11:53:41 +0800 Message-ID: <20190611035344.29814-7-jungo.lin@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20190611035344.29814-1-jungo.lin@mediatek.com> References: <20190611035344.29814-1-jungo.lin@mediatek.com> MIME-Version: 1.0 X-TM-SNTS-SMTP: 58EE2E0369C3E1CCE884C4C0082A906089754A20771040D753A4F13A278960C32000:8 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190610_205405_755905_66057D12 X-CRM114-Status: GOOD ( 13.79 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: jungo.lin@mediatek.com Cc: devicetree@vger.kernel.org, sean.cheng@mediatek.com, rynn.wu@mediatek.com, srv_heupstream@mediatek.com, robh@kernel.org, ryan.yu@mediatek.com, frankie.chiu@mediatek.com, jungo.lin@mediatek.com, sj.huang@mediatek.com, linux-mediatek@lists.infradead.org, ddavenport@chromium.org, frederic.chen@mediatek.com, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Implement standard V4L2 video driver that utilizes V4L2 and media framework APIs. In this driver, supports one media device, one sub-device and seven video devices during initialization. Moreover, it also connects with sensor and seninf drivers with V4L2 async APIs. (The current metadata interface used in meta input and partial meta nodes is only a temporary solution to kick off the driver development and is not ready to be reviewed yet.) Signed-off-by: Jungo Lin --- This patch depends on "media: support Mediatek sensor interface driver"[1]. ISP P1 sub-device communicates with seninf sub-device with CIO. [1]. media: support Mediatek sensor interface driver https://patchwork.kernel.org/cover/10979135/ --- .../platform/mtk-isp/isp_50/cam/Makefile | 1 + .../mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c | 1674 +++++++++++++++++ .../mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h | 173 ++ 3 files changed, 1848 insertions(+) create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile index 53fb69d3add6..7558593e63f0 100644 --- a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile +++ b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 mtk-cam-isp-objs += mtk_cam-ctrl.o +mtk-cam-isp-objs += mtk_cam-v4l2-util.o obj-$(CONFIG_VIDEO_MEDIATEK_ISP_PASS1) += mtk-cam-isp.o \ No newline at end of file diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c new file mode 100644 index 000000000000..117398ed29d2 --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.c @@ -0,0 +1,1674 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 Mediatek Corporation. + * Copyright (c) 2017 Intel Corporation. + * + * MTK_CAM-v4l2-util is highly based on Intel IPU3 ImgU driver. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_cam.h" +#include "mtk_cam-ctrl.h" +#include "mtk_cam-smem.h" +#include "mtk_cam-v4l2-util.h" + +#define MTK_CAM_CIO_PAD_SRC 4 +#define MTK_CAM_CIO_PAD_SINK 11 + +static inline struct mtk_cam_video_device * +file_to_mtk_cam_node(struct file *__file) +{ + return container_of(video_devdata(__file), + struct mtk_cam_video_device, vdev); +} + +static inline struct mtk_cam_dev * +mtk_cam_subdev_to_dev(struct v4l2_subdev *__sd) +{ + return container_of(__sd, + struct mtk_cam_dev, subdev); +} + +static inline struct mtk_cam_dev * +mtk_cam_mdev_to_dev(struct media_device *__mdev) +{ + return container_of(__mdev, + struct mtk_cam_dev, media_dev); +} + +static inline struct mtk_cam_video_device * +mtk_cam_vbq_to_vdev(struct vb2_queue *__vq) +{ + return container_of(__vq, + struct mtk_cam_video_device, vbq); +} + +static inline struct mtk_cam_dev_request * +mtk_cam_req_to_dev_req(struct media_request *__req) +{ + return container_of(__req, + struct mtk_cam_dev_request, req); +} + +static inline struct mtk_cam_dev_buffer * +mtk_cam_vb2_buf_to_dev_buf(struct vb2_buffer *__vb) +{ + return container_of(__vb, + struct mtk_cam_dev_buffer, vbb.vb2_buf); +} + +static void mtk_cam_req_try_isp_queue(struct mtk_cam_dev *cam_dev, + struct media_request *new_req) +{ + struct mtk_cam_dev_request *req, *req_safe, *cam_dev_req; + struct device *dev = &cam_dev->pdev->dev; + + dev_dbg(dev, "%s new req:%d", __func__, !new_req); + + if (!cam_dev->streaming) { + cam_dev_req = mtk_cam_req_to_dev_req(new_req); + spin_lock(&cam_dev->req_lock); + list_add_tail(&cam_dev_req->list, &cam_dev->req_list); + spin_unlock(&cam_dev->req_lock); + dev_dbg(dev, "%s: stream off, no ISP enqueue\n", __func__); + return; + } + + /* Normal enqueue flow */ + if (new_req) { + mtk_isp_req_enqueue(dev, new_req); + return; + } + + /* Flush all media requests wehen first stream on */ + list_for_each_entry_safe(req, req_safe, &cam_dev->req_list, list) { + list_del(&req->list); + mtk_isp_req_enqueue(dev, &req->req); + } +} + +static void mtk_cam_req_queue(struct media_request *req) +{ + struct mtk_cam_dev *cam_dev = mtk_cam_mdev_to_dev(req->mdev); + + vb2_request_queue(req); + mtk_cam_req_try_isp_queue(cam_dev, req); +} + +static struct media_request *mtk_cam_req_alloc(struct media_device *mdev) +{ + struct mtk_cam_dev_request *cam_dev_req; + + cam_dev_req = kzalloc(sizeof(*cam_dev_req), GFP_KERNEL); + + return &cam_dev_req->req; +} + +static void mtk_cam_req_free(struct media_request *req) +{ + struct mtk_cam_dev_request *cam_dev_req = mtk_cam_req_to_dev_req(req); + + kfree(cam_dev_req); +} + +static __u32 img_get_pixel_byte_by_fmt(__u32 pix_fmt) +{ + switch (pix_fmt) { + case V4L2_PIX_FMT_MTISP_B8: + case V4L2_PIX_FMT_MTISP_F8: + return 8; + case V4L2_PIX_FMT_MTISP_B10: + case V4L2_PIX_FMT_MTISP_F10: + return 10; + case V4L2_PIX_FMT_MTISP_B12: + case V4L2_PIX_FMT_MTISP_F12: + return 12; + case V4L2_PIX_FMT_MTISP_B14: + case V4L2_PIX_FMT_MTISP_F14: + return 14; + default: + return 0; + } +} + +static __u32 img_cal_main_stream_stride(struct device *dev, __u32 width, + __u32 pix_fmt) +{ + __u32 stride; + __u32 pixel_byte = img_get_pixel_byte_by_fmt(pix_fmt); + + width = ALIGN(width, 4); + stride = ALIGN(DIV_ROUND_UP(width * pixel_byte, 8), 2); + + dev_dbg(dev, "main width:%d, stride:%d\n", width, stride); + + return stride; +} + +static __u32 img_cal_packed_out_stride(struct device *dev, __u32 width, + __u32 pix_fmt) +{ + __u32 stride; + __u32 pixel_byte = img_get_pixel_byte_by_fmt(pix_fmt); + + width = ALIGN(width, 4); + stride = DIV_ROUND_UP(width * 3, 2); + stride = DIV_ROUND_UP(stride * pixel_byte, 8); + + if (pix_fmt == V4L2_PIX_FMT_MTISP_F10) + stride = ALIGN(stride, 4); + + dev_dbg(dev, "packed width:%d, stride:%d\n", width, stride); + + return stride; +} + +static __u32 img_cal_stride(struct device *dev, + int node_id, + __u32 width, + __u32 pix_fmt) +{ + __u32 bpl; + + /* Currently, only support one_pixel_mode */ + if (node_id == MTK_CAM_P1_MAIN_STREAM_OUT) + bpl = img_cal_main_stream_stride(dev, width, pix_fmt); + else if (node_id == MTK_CAM_P1_PACKED_BIN_OUT) + bpl = img_cal_packed_out_stride(dev, width, pix_fmt); + + /* For DIP HW constrained, it needs 4 byte alignment */ + bpl = ALIGN(bpl, 4); + + return bpl; +} + +static const struct v4l2_format * +mtk_cam_dev_find_fmt(struct mtk_cam_dev_node_desc *desc, u32 format) +{ + unsigned int i; + const struct v4l2_format *dev_fmt; + + for (i = 0; i < desc->num_fmts; i++) { + dev_fmt = &desc->fmts[i]; + if (dev_fmt->fmt.pix_mp.pixelformat == format) + return dev_fmt; + } + + return NULL; +} + +/* Calcuate mplane pix format */ +static void +mtk_cam_dev_cal_mplane_fmt(struct device *dev, + struct v4l2_pix_format_mplane *dest_fmt, + unsigned int node_id) +{ + unsigned int i; + __u32 bpl, sizeimage, imagsize; + + imagsize = 0; + for (i = 0 ; i < dest_fmt->num_planes; ++i) { + bpl = img_cal_stride(dev, + node_id, + dest_fmt->width, + dest_fmt->pixelformat); + sizeimage = bpl * dest_fmt->height; + imagsize += sizeimage; + dest_fmt->plane_fmt[i].bytesperline = bpl; + dest_fmt->plane_fmt[i].sizeimage = sizeimage; + memset(dest_fmt->plane_fmt[i].reserved, + 0, sizeof(dest_fmt->plane_fmt[i].reserved)); + dev_dbg(dev, "plane:%d,bpl:%d,sizeimage:%u\n", + i, bpl, dest_fmt->plane_fmt[i].sizeimage); + } + + if (dest_fmt->num_planes == 1) + dest_fmt->plane_fmt[0].sizeimage = imagsize; +} + +static void +mtk_cam_dev_set_img_fmt(struct device *dev, + struct v4l2_pix_format_mplane *dest_fmt, + const struct v4l2_pix_format_mplane *src_fmt, + unsigned int node_id) +{ + dest_fmt->width = src_fmt->width; + dest_fmt->height = src_fmt->height; + dest_fmt->pixelformat = src_fmt->pixelformat; + dest_fmt->field = src_fmt->field; + dest_fmt->colorspace = src_fmt->colorspace; + dest_fmt->num_planes = src_fmt->num_planes; + /* Use default */ + dest_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + dest_fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + dest_fmt->xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(dest_fmt->colorspace); + memset(dest_fmt->reserved, 0, sizeof(dest_fmt->reserved)); + + dev_dbg(dev, "%s: Dest Fmt:%c%c%c%c, w*h:%d*%d\n", + __func__, + (dest_fmt->pixelformat & 0xFF), + (dest_fmt->pixelformat >> 8) & 0xFF, + (dest_fmt->pixelformat >> 16) & 0xFF, + (dest_fmt->pixelformat >> 24) & 0xFF, + dest_fmt->width, + dest_fmt->height); + + mtk_cam_dev_cal_mplane_fmt(dev, dest_fmt, node_id); +} + +/* Get the default format setting */ +static void +mtk_cam_dev_load_default_fmt(struct device *dev, + struct mtk_cam_dev_node_desc *queue_desc, + struct v4l2_format *dest) +{ + const struct v4l2_format *default_fmt = + &queue_desc->fmts[queue_desc->default_fmt_idx]; + + dest->type = queue_desc->buf_type; + + /* Configure default format based on node type */ + if (queue_desc->image) { + mtk_cam_dev_set_img_fmt(dev, + &dest->fmt.pix_mp, + &default_fmt->fmt.pix_mp, + queue_desc->id); + } else { + dest->fmt.meta.dataformat = default_fmt->fmt.meta.dataformat; + dest->fmt.meta.buffersize = default_fmt->fmt.meta.buffersize; + } +} + +static int mtk_cam_isp_open(struct file *file) +{ + struct mtk_cam_dev *cam_dev = video_drvdata(file); + struct device *dev = &cam_dev->pdev->dev; + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file); + int ret; + + mutex_lock(&cam_dev->lock); + ret = v4l2_fh_open(file); + if (ret) + goto unlock; + + ret = v4l2_pipeline_pm_use(&node->vdev.entity, 1); + if (ret) + dev_err(dev, "%s fail:%d", __func__, ret); + +unlock: + mutex_unlock(&cam_dev->lock); + + return ret; +} + +static int mtk_cam_isp_release(struct file *file) +{ + struct mtk_cam_dev *cam_dev = video_drvdata(file); + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file); + + mutex_lock(&cam_dev->lock); + v4l2_pipeline_pm_use(&node->vdev.entity, 0); + vb2_fop_release(file); + mutex_unlock(&cam_dev->lock); + + return 0; +} + +static struct v4l2_subdev * +mtk_cam_cio_get_active_sensor(struct mtk_cam_dev *cam_dev) +{ + struct media_device *mdev = cam_dev->seninf->entity.graph_obj.mdev; + struct media_entity *entity; + struct device *dev = &cam_dev->pdev->dev; + struct v4l2_subdev *sensor; + + media_device_for_each_entity(entity, mdev) { + dev_dbg(dev, "media entity: %s:0x%x\n", + entity->name, entity->function); + if (entity->function == MEDIA_ENT_F_CAM_SENSOR && + entity->stream_count) { + sensor = media_entity_to_v4l2_subdev(entity); + dev_dbg(dev, "Sensor found: %s\n", entity->name); + break; + } + } + + if (!sensor) + dev_err(dev, "Sensor is not connected\n"); + + return sensor; +} + +static int mtk_cam_cio_stream_on(struct mtk_cam_dev *cam_dev) +{ + struct device *dev = &cam_dev->pdev->dev; + int ret; + + /* Align vb2_core_streamon design */ + if (cam_dev->streaming) { + dev_warn(dev, "already streaming\n", dev); + return 0; + } + + if (!cam_dev->seninf) { + dev_err(dev, "no seninf connected:%d\n", ret); + return -EPERM; + } + + /* Get active sensor from graph topology */ + cam_dev->sensor = mtk_cam_cio_get_active_sensor(cam_dev); + if (!cam_dev->sensor) + return -EPERM; + + ret = mtk_isp_config(dev); + if (ret) + return -EPERM; + + /* Seninf must stream on first */ + ret = v4l2_subdev_call(cam_dev->seninf, video, s_stream, 1); + if (ret) { + dev_err(dev, "%s stream on failed:%d\n", + cam_dev->seninf->entity.name, ret); + return -EPERM; + } + + ret = v4l2_subdev_call(cam_dev->sensor, video, s_stream, 1); + if (ret) { + dev_err(dev, "%s stream on failed:%d\n", + cam_dev->sensor->entity.name, ret); + goto fail_sensor_on; + } + + cam_dev->streaming = true; + mtk_cam_req_try_isp_queue(cam_dev, NULL); + isp_composer_stream(dev, 1); + dev_dbg(dev, "streamed on Pass 1\n"); + + return 0; + +fail_sensor_on: + v4l2_subdev_call(cam_dev->seninf, video, s_stream, 0); + + return -EPERM; +} + +static int mtk_cam_cio_stream_off(struct mtk_cam_dev *cam_dev) +{ + struct device *dev = &cam_dev->pdev->dev; + int ret; + + if (!cam_dev->streaming) { + dev_warn(dev, "already stream off"); + return 0; + } + + ret = v4l2_subdev_call(cam_dev->sensor, video, s_stream, 0); + if (ret) { + dev_err(dev, "%s stream off failed:%d\n", + cam_dev->sensor->entity.name, ret); + return -EPERM; + } + + ret = v4l2_subdev_call(cam_dev->seninf, video, s_stream, 0); + if (ret) { + dev_err(dev, "%s stream off failed:%d\n", + cam_dev->seninf->entity.name, ret); + return -EPERM; + } + + isp_composer_stream(dev, 0); + cam_dev->streaming = false; + dev_dbg(dev, "streamed off Pass 1\n"); + + return 0; +} + +static int mtk_cam_sd_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd); + + if (enable) + return mtk_cam_cio_stream_on(cam_dev); + else + return mtk_cam_cio_stream_off(cam_dev); +} + +static int mtk_cam_sd_subscribe_event(struct v4l2_subdev *subdev, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_FRAME_SYNC: + return v4l2_event_subscribe(fh, sub, 0, NULL); + default: + return -EINVAL; + } +} + +static int mtk_cam_sd_s_power(struct v4l2_subdev *sd, int on) +{ + struct mtk_cam_dev *cam_dev = mtk_cam_subdev_to_dev(sd); + + dev_dbg(&cam_dev->pdev->dev, "%s:%d", __func__, on); + + return on ? mtk_isp_power_init(cam_dev) : + mtk_isp_power_release(&cam_dev->pdev->dev); +} + +static int mtk_cam_media_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct mtk_cam_dev *cam_dev = + container_of(entity, struct mtk_cam_dev, subdev.entity); + u32 pad = local->index; + + dev_dbg(&cam_dev->pdev->dev, "%s: %d -> %d flags:0x%x\n", + __func__, pad, remote->index, flags); + + if (pad < MTK_CAM_P1_TOTAL_NODES) + cam_dev->vdev_nodes[pad].enabled = + !!(flags & MEDIA_LNK_FL_ENABLED); + + return 0; +} + +static void mtk_cam_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct mtk_cam_dev *mtk_cam_dev = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue); + struct device *dev = &mtk_cam_dev->pdev->dev; + struct mtk_cam_dev_buffer *buf; + + buf = mtk_cam_vb2_buf_to_dev_buf(vb); + + dev_dbg(dev, "%s: node:%d fd:%d idx:%d\n", + __func__, + node->id, + buf->vbb.request_fd, + buf->vbb.vb2_buf.index); + + /* For request buffers en-queue, handled in mtk_cam_req_try_queue */ + if (vb->vb2_queue->uses_requests) + return; + + /* Added the buffer into the tracking list */ + spin_lock(&node->slock); + list_add_tail(&buf->list, &node->pending_list); + spin_unlock(&node->slock); + + mtk_isp_enqueue(dev, node->desc.dma_port, buf); +} + +static int mtk_cam_vb2_buf_init(struct vb2_buffer *vb) +{ + struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vb->vb2_queue); + struct device *smem_dev = cam_dev->smem_dev; + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue); + struct mtk_cam_dev_buffer *buf; + + buf = mtk_cam_vb2_buf_to_dev_buf(vb); + buf->node_id = node->id; + buf->daddr = vb2_dma_contig_plane_dma_addr(&buf->vbb.vb2_buf, 0); + buf->scp_addr = 0; + + /* scp address is only valid for meta input buffer */ + if (node->desc.smem_alloc) + buf->scp_addr = mtk_cam_smem_iova_to_scp_addr(smem_dev, + buf->daddr); + + return 0; +} + +static int mtk_cam_vb2_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue); + const struct v4l2_format *fmt = &node->vdev_fmt; + unsigned int size; + + if (vb->vb2_queue->type == V4L2_BUF_TYPE_META_OUTPUT || + vb->vb2_queue->type == V4L2_BUF_TYPE_META_CAPTURE) + size = fmt->fmt.meta.buffersize; + else + size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage; + + if (vb2_plane_size(vb, 0) < size) + return -EINVAL; + + v4l2_buf->field = V4L2_FIELD_NONE; + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static int mtk_cam_vb2_queue_setup(struct vb2_queue *vq, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq); + unsigned int max_buffer_count = node->desc.max_buf_count; + const struct v4l2_format *fmt = &node->vdev_fmt; + unsigned int size; + + /* Check the limitation of buffer size */ + if (max_buffer_count) + *num_buffers = clamp_val(*num_buffers, 1, max_buffer_count); + + if (vq->type == V4L2_BUF_TYPE_META_OUTPUT || + vq->type == V4L2_BUF_TYPE_META_CAPTURE) + size = fmt->fmt.meta.buffersize; + else + size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage; + + /* Add for q.create_bufs with fmt.g_sizeimage(p) / 2 test */ + if (*num_planes) { + if (sizes[0] < size) + return -EINVAL; + } else { + *num_planes = 1; + sizes[0] = size; + } + + return 0; +} + +static void mtk_cam_vb2_return_all_buffers(struct mtk_cam_dev *cam_dev, + struct mtk_cam_video_device *node, + enum vb2_buffer_state state) +{ + struct mtk_cam_dev_buffer *b, *b0; + struct mtk_cam_dev_request *req, *req0; + struct media_request_object *obj, *obj0; + struct vb2_buffer *vb; + + dev_dbg(&cam_dev->pdev->dev, "%s: node:%s", __func__, node->vdev.name); + + /* Return all buffers */ + spin_lock(&node->slock); + list_for_each_entry_safe(b, b0, &node->pending_list, list) { + vb = &b->vbb.vb2_buf; + if (vb->state == VB2_BUF_STATE_ACTIVE) + vb2_buffer_done(vb, state); + list_del(&b->list); + } + spin_unlock(&node->slock); + + spin_lock(&cam_dev->req_lock); + list_for_each_entry_safe(req, req0, &cam_dev->req_list, list) { + list_for_each_entry_safe(obj, obj0, &req->req.objects, list) { + vb = container_of(obj, struct vb2_buffer, req_obj); + if (vb->state == VB2_BUF_STATE_ACTIVE) + vb2_buffer_done(vb, state); + } + list_del(&req->list); + } + spin_unlock(&cam_dev->req_lock); + + if (node->vbq.uses_requests) + mtk_isp_req_flush_buffers(&cam_dev->pdev->dev); +} + +static int mtk_cam_vb2_start_streaming(struct vb2_queue *vq, + unsigned int count) +{ + struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq); + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq); + struct device *dev = &cam_dev->pdev->dev; + unsigned int node_count = cam_dev->subdev.entity.use_count; + int ret; + + if (!node->enabled) { + dev_err(dev, "Node:%d is not enable\n", node->id); + ret = -ENOLINK; + goto fail_no_link; + } + + dev_dbg(dev, "%s: count info:%d:%d", __func__, + atomic_read(&cam_dev->streamed_node_count), node_count); + + if (atomic_inc_return(&cam_dev->streamed_node_count) < node_count) + return 0; + + /* Start streaming of the whole pipeline now */ + ret = media_pipeline_start(&node->vdev.entity, &cam_dev->pipeline); + if (ret) { + dev_err(dev, "%s: Node:%d failed\n", __func__, node->id); + goto fail_start_pipeline; + } + + /* Stream on sub-devices node */ + ret = v4l2_subdev_call(&cam_dev->subdev, video, s_stream, 1); + if (ret) { + dev_err(dev, "Node:%d s_stream on failed:%d\n", node->id, ret); + goto fail_stream_on; + } + + return 0; + +fail_stream_on: + media_pipeline_stop(&node->vdev.entity); +fail_start_pipeline: + atomic_dec(&cam_dev->streamed_node_count); +fail_no_link: + mtk_cam_vb2_return_all_buffers(cam_dev, node, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void mtk_cam_vb2_stop_streaming(struct vb2_queue *vq) +{ + struct mtk_cam_dev *cam_dev = vb2_get_drv_priv(vq); + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq); + struct device *dev = &cam_dev->pdev->dev; + + if (!node->enabled) + return; + + mtk_cam_vb2_return_all_buffers(cam_dev, node, VB2_BUF_STATE_ERROR); + + dev_dbg(dev, "%s: count info:%d", __func__, + cam_dev->subdev.entity.stream_count); + + /* Check the first node to stream-off */ + if (!cam_dev->subdev.entity.stream_count) + return; + + media_pipeline_stop(&node->vdev.entity); + + if (v4l2_subdev_call(&cam_dev->subdev, video, s_stream, 0)) + dev_err(dev, "failed to stop streaming\n"); +} + +static void mtk_cam_vb2_buf_request_complete(struct vb2_buffer *vb) +{ + struct mtk_cam_dev *dev = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, + dev->v4l2_dev.ctrl_handler); +} + +static int mtk_cam_vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct mtk_cam_dev *cam_dev = video_drvdata(file); + + strscpy(cap->driver, MTK_CAM_DEV_P1_NAME, sizeof(cap->driver)); + strscpy(cap->card, MTK_CAM_DEV_P1_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(cam_dev->media_dev.dev)); + + return 0; +} + +static int mtk_cam_vidioc_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file); + + if (f->index >= node->desc.num_fmts) + return -EINVAL; + + f->pixelformat = node->desc.fmts[f->index].fmt.pix_mp.pixelformat; + f->flags = 0; + + return 0; +} + +static int mtk_cam_vidioc_g_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file); + + if (!node->desc.num_fmts) + return -EINVAL; + + f->fmt = node->vdev_fmt.fmt; + + return 0; +} + +static int mtk_cam_vidioc_try_fmt(struct file *file, void *fh, + struct v4l2_format *in_fmt) +{ + struct mtk_cam_dev *cam_dev = video_drvdata(file); + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file); + const struct v4l2_format *dev_fmt; + __u32 width, height; + + dev_dbg(&cam_dev->pdev->dev, "%s: fmt:%c%c%c%c, w*h:%u*%u\n", + __func__, + (in_fmt->fmt.pix_mp.pixelformat & 0xFF), + (in_fmt->fmt.pix_mp.pixelformat >> 8) & 0xFF, + (in_fmt->fmt.pix_mp.pixelformat >> 16) & 0xFF, + (in_fmt->fmt.pix_mp.pixelformat >> 24) & 0xFF, + in_fmt->fmt.pix_mp.width, in_fmt->fmt.pix_mp.height); + + width = in_fmt->fmt.pix_mp.width; + height = in_fmt->fmt.pix_mp.height; + + dev_fmt = mtk_cam_dev_find_fmt(&node->desc, + in_fmt->fmt.pix_mp.pixelformat); + if (dev_fmt) { + mtk_cam_dev_set_img_fmt(&cam_dev->pdev->dev, + &in_fmt->fmt.pix_mp, + &dev_fmt->fmt.pix_mp, + node->id); + } else { + mtk_cam_dev_load_default_fmt(&cam_dev->pdev->dev, + &node->desc, in_fmt); + } + in_fmt->fmt.pix_mp.width = clamp_t(u32, + width, + CAM_MIN_WIDTH, + in_fmt->fmt.pix_mp.width); + in_fmt->fmt.pix_mp.height = clamp_t(u32, + height, + CAM_MIN_HEIGHT, + in_fmt->fmt.pix_mp.height); + mtk_cam_dev_cal_mplane_fmt(&cam_dev->pdev->dev, + &in_fmt->fmt.pix_mp, node->id); + + return 0; +} + +static int mtk_cam_vidioc_s_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mtk_cam_dev *cam_dev = video_drvdata(file); + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file); + + if (cam_dev->streaming) + return -EBUSY; + + /* Get the valid format */ + mtk_cam_vidioc_try_fmt(file, fh, f); + + /* Configure to video device */ + mtk_cam_dev_set_img_fmt(&cam_dev->pdev->dev, + &node->vdev_fmt.fmt.pix_mp, + &f->fmt.pix_mp, + node->id); + + return 0; +} + +static int mtk_cam_vidioc_enum_input(struct file *file, void *fh, + struct v4l2_input *input) +{ + if (input->index) + return -EINVAL; + + strscpy(input->name, "camera", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int mtk_cam_vidioc_g_input(struct file *file, void *fh, + unsigned int *input) +{ + *input = 0; + + return 0; +} + +static int mtk_cam_vidioc_s_input(struct file *file, + void *fh, unsigned int input) +{ + return input == 0 ? 0 : -EINVAL; +} + +static int mtk_cam_vidioc_enum_framesizes(struct file *filp, void *priv, + struct v4l2_frmsizeenum *sizes) +{ + struct mtk_cam_video_device *node = file_to_mtk_cam_node(filp); + const struct v4l2_format *dev_fmt; + + dev_fmt = mtk_cam_dev_find_fmt(&node->desc, sizes->pixel_format); + if (!dev_fmt || sizes->index) + return -EINVAL; + + sizes->type = node->desc.frmsizes->type; + memcpy(&sizes->stepwise, &node->desc.frmsizes->stepwise, + sizeof(sizes->stepwise)); + + return 0; +} + +static int mtk_cam_vidioc_meta_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file); + + if (f->index) + return -EINVAL; + + strscpy(f->description, node->desc.description, + sizeof(node->desc.description)); + f->pixelformat = node->vdev_fmt.fmt.meta.dataformat; + f->flags = 0; + + return 0; +} + +static int mtk_cam_vidioc_g_meta_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file); + + f->fmt.meta.dataformat = node->vdev_fmt.fmt.meta.dataformat; + f->fmt.meta.buffersize = node->vdev_fmt.fmt.meta.buffersize; + + return 0; +} + +static const struct v4l2_subdev_core_ops mtk_cam_subdev_core_ops = { + .subscribe_event = mtk_cam_sd_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, + .s_power = mtk_cam_sd_s_power, +}; + +static const struct v4l2_subdev_video_ops mtk_cam_subdev_video_ops = { + .s_stream = mtk_cam_sd_s_stream, +}; + +static const struct v4l2_subdev_ops mtk_cam_subdev_ops = { + .core = &mtk_cam_subdev_core_ops, + .video = &mtk_cam_subdev_video_ops, +}; + +static const struct media_entity_operations mtk_cam_media_ops = { + .link_setup = mtk_cam_media_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct vb2_ops mtk_cam_vb2_ops = { + .queue_setup = mtk_cam_vb2_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_init = mtk_cam_vb2_buf_init, + .buf_prepare = mtk_cam_vb2_buf_prepare, + .start_streaming = mtk_cam_vb2_start_streaming, + .stop_streaming = mtk_cam_vb2_stop_streaming, + .buf_queue = mtk_cam_vb2_buf_queue, + .buf_request_complete = mtk_cam_vb2_buf_request_complete, +}; + +static const struct v4l2_file_operations mtk_cam_v4l2_fops = { + .unlocked_ioctl = video_ioctl2, + .open = mtk_cam_isp_open, + .release = mtk_cam_isp_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = v4l2_compat_ioctl32, +#endif +}; + +static const struct media_device_ops mtk_cam_media_req_ops = { + .link_notify = v4l2_pipeline_link_notify, + .req_alloc = mtk_cam_req_alloc, + .req_free = mtk_cam_req_free, + .req_validate = vb2_request_validate, + .req_queue = mtk_cam_req_queue, +}; + +static int mtk_cam_media_register(struct device *dev, + struct media_device *media_dev) +{ + media_dev->dev = dev; + strscpy(media_dev->model, MTK_CAM_DEV_P1_NAME, + sizeof(media_dev->model)); + snprintf(media_dev->bus_info, sizeof(media_dev->bus_info), + "platform:%s", dev_name(dev)); + media_dev->hw_revision = 0; + media_device_init(media_dev); + media_dev->ops = &mtk_cam_media_req_ops; + + return media_device_register(media_dev); +} + +static int mtk_cam_video_register_device(struct mtk_cam_dev *cam_dev, u32 i) +{ + struct device *dev = &cam_dev->pdev->dev; + struct mtk_cam_video_device *node = &cam_dev->vdev_nodes[i]; + struct video_device *vdev = &node->vdev; + struct vb2_queue *vbq = &node->vbq; + u32 output = !cam_dev->vdev_nodes[i].desc.capture; + u32 link_flags = cam_dev->vdev_nodes[i].desc.link_flags; + int ret; + + cam_dev->subdev_pads[i].flags = output ? + MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; + + /* Initialize media entities */ + ret = media_entity_pads_init(&vdev->entity, 1, &node->vdev_pad); + if (ret) { + dev_err(dev, "failed initialize media pad:%d\n", ret); + return ret; + } + node->enabled = false; + node->id = i; + node->vdev_pad.flags = cam_dev->subdev_pads[i].flags; + mtk_cam_dev_load_default_fmt(&cam_dev->pdev->dev, + &node->desc, + &node->vdev_fmt); + + /* Initialize vbq */ + vbq->type = node->vdev_fmt.type; + if (vbq->type == V4L2_BUF_TYPE_META_OUTPUT) + vbq->io_modes = VB2_MMAP; + else + vbq->io_modes = VB2_MMAP | VB2_DMABUF; + + if (node->desc.smem_alloc) { + vbq->bidirectional = 1; + vbq->dev = cam_dev->smem_dev; + } else { + vbq->dev = &cam_dev->pdev->dev; + } + + if (vbq->type == V4L2_BUF_TYPE_META_CAPTURE) + vdev->entity.function = + MEDIA_ENT_F_PROC_VIDEO_STATISTICS; + vbq->ops = &mtk_cam_vb2_ops; + vbq->mem_ops = &vb2_dma_contig_memops; + vbq->buf_struct_size = sizeof(struct mtk_cam_dev_buffer); + vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vbq->min_buffers_needed = 0; /* Can streamon w/o buffers */ + /* Put the process hub sub device in the vb2 private data */ + vbq->drv_priv = cam_dev; + vbq->lock = &node->lock; + vbq->supports_requests = true; + + ret = vb2_queue_init(vbq); + if (ret) { + dev_err(dev, "failed to init. vb2 queue:%d\n", ret); + goto fail_vb2_queue; + } + + /* Initialize vdev */ + snprintf(vdev->name, sizeof(vdev->name), "%s %s", + MTK_CAM_DEV_P1_NAME, node->desc.name); + /* set cap/type/ioctl_ops of the video device */ + vdev->device_caps = node->desc.cap | V4L2_CAP_STREAMING; + vdev->ioctl_ops = node->desc.ioctl_ops; + vdev->fops = &mtk_cam_v4l2_fops; + vdev->release = video_device_release_empty; + vdev->lock = &node->lock; + vdev->v4l2_dev = &cam_dev->v4l2_dev; + vdev->queue = &node->vbq; + vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX; + vdev->entity.ops = NULL; + /* Enable private control for image video devices */ + if (node->desc.image) { + mtk_cam_ctrl_init(cam_dev, &node->ctrl_handler); + vdev->ctrl_handler = &node->ctrl_handler; + } + video_set_drvdata(vdev, cam_dev); + dev_dbg(dev, "register vdev:%d:%s\n", i, vdev->name); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + dev_err(dev, "failed to register vde:%d\n", ret); + goto fail_vdev; + } + + /* Create link between video node and the subdev pad */ + if (output) { + ret = media_create_pad_link(&vdev->entity, 0, + &cam_dev->subdev.entity, + i, link_flags); + } else { + ret = media_create_pad_link(&cam_dev->subdev.entity, + i, &vdev->entity, 0, + link_flags); + } + if (ret) + goto fail_link; + + /* Initialize miscellaneous variables */ + mutex_init(&node->lock); + spin_lock_init(&node->slock); + INIT_LIST_HEAD(&node->pending_list); + + return 0; + +fail_link: + video_unregister_device(vdev); +fail_vdev: + vb2_queue_release(vbq); +fail_vb2_queue: + media_entity_cleanup(&vdev->entity); + + return ret; +} + +static int mtk_cam_mem2mem2_v4l2_register(struct mtk_cam_dev *cam_dev) +{ + struct device *dev = &cam_dev->pdev->dev; + /* Total pad numbers is video devices + one seninf pad */ + unsigned int num_subdev_pads = MTK_CAM_CIO_PAD_SINK + 1; + unsigned int i; + int ret; + + ret = mtk_cam_media_register(dev, + &cam_dev->media_dev); + if (ret) { + dev_err(dev, "failed to register media device:%d\n", ret); + return ret; + } + dev_info(dev, "Register media device: %s, 0x%pK", + MTK_CAM_DEV_P1_NAME, cam_dev->media_dev); + + /* Set up v4l2 device */ + cam_dev->v4l2_dev.mdev = &cam_dev->media_dev; + ret = v4l2_device_register(dev, &cam_dev->v4l2_dev); + if (ret) { + dev_err(dev, "failed to register V4L2 device:%d\n", ret); + goto fail_v4l2_dev; + } + dev_info(dev, "Register v4l2 device: 0x%pK", cam_dev->v4l2_dev); + + /* Initialize subdev media entity */ + cam_dev->subdev_pads = devm_kcalloc(dev, num_subdev_pads, + sizeof(*cam_dev->subdev_pads), + GFP_KERNEL); + if (!cam_dev->subdev_pads) { + ret = -ENOMEM; + goto fail_subdev_pads; + } + + ret = media_entity_pads_init(&cam_dev->subdev.entity, + num_subdev_pads, + cam_dev->subdev_pads); + if (ret) { + dev_err(dev, "failed initialize media pads:%d:\n", ret); + goto fail_subdev_pads; + } + + /* Initialize all pads with MEDIA_PAD_FL_SOURCE */ + for (i = 0; i < num_subdev_pads; i++) + cam_dev->subdev_pads[i].flags = MEDIA_PAD_FL_SOURCE; + + /* Customize the last one pad as CIO sink pad. */ + cam_dev->subdev_pads[MTK_CAM_CIO_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + + /* Initialize subdev */ + v4l2_subdev_init(&cam_dev->subdev, &mtk_cam_subdev_ops); + cam_dev->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS; + cam_dev->subdev.entity.ops = &mtk_cam_media_ops; + cam_dev->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + snprintf(cam_dev->subdev.name, sizeof(cam_dev->subdev.name), + "%s", MTK_CAM_DEV_P1_NAME); + v4l2_set_subdevdata(&cam_dev->subdev, cam_dev); + + ret = v4l2_device_register_subdev(&cam_dev->v4l2_dev, &cam_dev->subdev); + if (ret) { + dev_err(dev, "failed initialize subdev:%d\n", ret); + goto fail_subdev; + } + dev_info(dev, "register subdev: %s\n", cam_dev->subdev.name); + + /* Create video nodes and links */ + for (i = 0; i < MTK_CAM_P1_TOTAL_NODES; i++) { + ret = mtk_cam_video_register_device(cam_dev, i); + if (ret) + goto fail_video_register; + } + + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); + + return 0; + +fail_video_register: + i--; + for (; i >= 0; i--) { + video_unregister_device(&cam_dev->vdev_nodes[i].vdev); + media_entity_cleanup(&cam_dev->vdev_nodes[i].vdev.entity); + mutex_destroy(&cam_dev->vdev_nodes[i].lock); + } +fail_subdev: + media_entity_cleanup(&cam_dev->subdev.entity); +fail_subdev_pads: + v4l2_device_unregister(&cam_dev->v4l2_dev); +fail_v4l2_dev: + dev_err(dev, "fail_v4l2_dev mdev: 0x%pK:%d", &cam_dev->media_dev, ret); + media_device_unregister(&cam_dev->media_dev); + media_device_cleanup(&cam_dev->media_dev); + + return ret; +} + +static int mtk_cam_v4l2_unregister(struct mtk_cam_dev *cam_dev) +{ + unsigned int i; + struct mtk_cam_video_device *dev; + + for (i = 0; i < MTK_CAM_P1_TOTAL_NODES; i++) { + dev = &cam_dev->vdev_nodes[i]; + video_unregister_device(&dev->vdev); + media_entity_cleanup(&dev->vdev.entity); + mutex_destroy(&dev->lock); + if (dev->desc.image) + v4l2_ctrl_handler_free(&dev->ctrl_handler); + } + + vb2_dma_contig_clear_max_seg_size(&cam_dev->pdev->dev); + + v4l2_device_unregister_subdev(&cam_dev->subdev); + media_entity_cleanup(&cam_dev->subdev.entity); + kfree(cam_dev->subdev_pads); + + v4l2_device_unregister(&cam_dev->v4l2_dev); + media_device_unregister(&cam_dev->media_dev); + media_device_cleanup(&cam_dev->media_dev); + + return 0; +} + +static int mtk_cam_dev_complete(struct v4l2_async_notifier *notifier) +{ + struct mtk_cam_dev *cam_dev = + container_of(notifier, struct mtk_cam_dev, notifier); + struct device *dev = &cam_dev->pdev->dev; + int ret; + + ret = media_create_pad_link(&cam_dev->seninf->entity, + MTK_CAM_CIO_PAD_SRC, + &cam_dev->subdev.entity, + MTK_CAM_CIO_PAD_SINK, + 0); + if (ret) { + dev_err(dev, "fail to create pad link %s %s err:%d\n", + cam_dev->seninf->entity.name, + cam_dev->subdev.entity.name, + ret); + return ret; + } + + dev_info(dev, "Complete the v4l2 registration\n"); + + ret = v4l2_device_register_subdev_nodes(&cam_dev->v4l2_dev); + if (ret) { + dev_err(dev, "failed initialize subdev nodes:%d\n", ret); + return ret; + } + + return ret; +} + +static int mtk_cam_dev_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct mtk_cam_dev *cam_dev = + container_of(notifier, struct mtk_cam_dev, notifier); + + cam_dev->seninf = sd; + dev_info(&cam_dev->pdev->dev, "%s is bounded\n", sd->entity.name); + return 0; +} + +static void mtk_cam_dev_notifier_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct mtk_cam_dev *cam_dev = + container_of(notifier, struct mtk_cam_dev, notifier); + + cam_dev->seninf = NULL; + dev_dbg(&cam_dev->pdev->dev, "%s is unbounded\n", sd->entity.name); +} + +static int mtk_cam_dev_notifier_complete(struct v4l2_async_notifier *notifier) +{ + return mtk_cam_dev_complete(notifier); +} + +static const struct v4l2_async_notifier_operations mtk_cam_async_ops = { + .bound = mtk_cam_dev_notifier_bound, + .unbind = mtk_cam_dev_notifier_unbind, + .complete = mtk_cam_dev_notifier_complete, +}; + +static int mtk_cam_v4l2_async_register(struct mtk_cam_dev *cam_dev) +{ + struct device *dev = &cam_dev->pdev->dev; + int ret; + + ret = v4l2_async_notifier_parse_fwnode_endpoints(dev, + &cam_dev->notifier, sizeof(struct v4l2_async_subdev), + NULL); + if (ret) + return ret; + + if (!cam_dev->notifier.num_subdevs) + return -ENODEV; + + cam_dev->notifier.ops = &mtk_cam_async_ops; + dev_info(&cam_dev->pdev->dev, "mtk_cam v4l2_async_notifier_register\n"); + ret = v4l2_async_notifier_register(&cam_dev->v4l2_dev, + &cam_dev->notifier); + if (ret) { + dev_err(&cam_dev->pdev->dev, + "failed to register async notifier : %d\n", ret); + v4l2_async_notifier_cleanup(&cam_dev->notifier); + } + + return ret; +} + +static void mtk_cam_v4l2_async_unregister(struct mtk_cam_dev *cam_dev) +{ + v4l2_async_notifier_unregister(&cam_dev->notifier); + v4l2_async_notifier_cleanup(&cam_dev->notifier); +} + +static const struct v4l2_ioctl_ops mtk_cam_v4l2_vcap_ioctl_ops = { + .vidioc_querycap = mtk_cam_vidioc_querycap, + .vidioc_enum_framesizes = mtk_cam_vidioc_enum_framesizes, + .vidioc_enum_fmt_vid_cap_mplane = mtk_cam_vidioc_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = mtk_cam_vidioc_g_fmt, + .vidioc_s_fmt_vid_cap_mplane = mtk_cam_vidioc_s_fmt, + .vidioc_try_fmt_vid_cap_mplane = mtk_cam_vidioc_try_fmt, + .vidioc_enum_input = mtk_cam_vidioc_enum_input, + .vidioc_g_input = mtk_cam_vidioc_g_input, + .vidioc_s_input = mtk_cam_vidioc_s_input, + /* buffer queue management */ + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_cap_ioctl_ops = { + .vidioc_querycap = mtk_cam_vidioc_querycap, + .vidioc_enum_fmt_meta_cap = mtk_cam_vidioc_meta_enum_fmt, + .vidioc_g_fmt_meta_cap = mtk_cam_vidioc_g_meta_fmt, + .vidioc_s_fmt_meta_cap = mtk_cam_vidioc_g_meta_fmt, + .vidioc_try_fmt_meta_cap = mtk_cam_vidioc_g_meta_fmt, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, +}; + +static const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_out_ioctl_ops = { + .vidioc_querycap = mtk_cam_vidioc_querycap, + .vidioc_enum_fmt_meta_out = mtk_cam_vidioc_meta_enum_fmt, + .vidioc_g_fmt_meta_out = mtk_cam_vidioc_g_meta_fmt, + .vidioc_s_fmt_meta_out = mtk_cam_vidioc_g_meta_fmt, + .vidioc_try_fmt_meta_out = mtk_cam_vidioc_g_meta_fmt, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_expbuf = vb2_ioctl_expbuf, +}; + +static const struct v4l2_format meta_fmts[] = { + { + .fmt.meta = { + .dataformat = V4L2_META_FMT_MTISP_PARAMS, + .buffersize = 128 * PAGE_SIZE, + }, + }, + { + .fmt.meta = { + .dataformat = V4L2_META_FMT_MTISP_3A, + .buffersize = 300 * PAGE_SIZE, + }, + }, + { + .fmt.meta = { + .dataformat = V4L2_META_FMT_MTISP_AF, + .buffersize = 160 * PAGE_SIZE, + }, + }, + { + .fmt.meta = { + .dataformat = V4L2_META_FMT_MTISP_LCS, + .buffersize = 72 * PAGE_SIZE, + }, + }, + { + .fmt.meta = { + .dataformat = V4L2_META_FMT_MTISP_LMV, + .buffersize = 256, + }, + }, +}; + +static const struct v4l2_format stream_out_fmts[] = { + { + .fmt.pix_mp = { + .width = IMG_MAX_WIDTH, + .height = IMG_MAX_HEIGHT, + .pixelformat = V4L2_PIX_FMT_MTISP_B8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .num_planes = 1, + }, + }, + { + .fmt.pix_mp = { + .width = IMG_MAX_WIDTH, + .height = IMG_MAX_HEIGHT, + .pixelformat = V4L2_PIX_FMT_MTISP_B10, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .num_planes = 1, + }, + }, + { + .fmt.pix_mp = { + .width = IMG_MAX_WIDTH, + .height = IMG_MAX_HEIGHT, + .pixelformat = V4L2_PIX_FMT_MTISP_B12, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .num_planes = 1, + }, + }, + { + .fmt.pix_mp = { + .width = IMG_MAX_WIDTH, + .height = IMG_MAX_HEIGHT, + .pixelformat = V4L2_PIX_FMT_MTISP_B14, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .num_planes = 1, + }, + }, +}; + +static const struct v4l2_format bin_out_fmts[] = { + { + .fmt.pix_mp = { + .width = RRZ_MAX_WIDTH, + .height = RRZ_MAX_HEIGHT, + .pixelformat = V4L2_PIX_FMT_MTISP_F8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_RAW, + .num_planes = 1, + }, + }, + { + .fmt.pix_mp = { + .width = RRZ_MAX_WIDTH, + .height = RRZ_MAX_HEIGHT, + .pixelformat = V4L2_PIX_FMT_MTISP_F10, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_RAW, + .num_planes = 1, + }, + }, + { + .fmt.pix_mp = { + .width = RRZ_MAX_WIDTH, + .height = RRZ_MAX_HEIGHT, + .pixelformat = V4L2_PIX_FMT_MTISP_F12, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_RAW, + .num_planes = 1, + }, + }, + { + .fmt.pix_mp = { + .width = RRZ_MAX_WIDTH, + .height = RRZ_MAX_HEIGHT, + .pixelformat = V4L2_PIX_FMT_MTISP_F14, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_RAW, + .num_planes = 1, + }, + }, +}; + +static const struct v4l2_frmsizeenum img_frm_size_nums[] = { + { + .index = 0, + .type = V4L2_FRMSIZE_TYPE_CONTINUOUS, + .stepwise = { + .max_width = IMG_MAX_WIDTH, + .min_width = IMG_MIN_WIDTH, + .max_height = IMG_MAX_HEIGHT, + .min_height = IMG_MIN_HEIGHT, + .step_height = 1, + .step_width = 1, + }, + }, + { + .index = 0, + .type = V4L2_FRMSIZE_TYPE_CONTINUOUS, + .stepwise = { + .max_width = RRZ_MAX_WIDTH, + .min_width = RRZ_MIN_WIDTH, + .max_height = RRZ_MAX_HEIGHT, + .min_height = RRZ_MIN_HEIGHT, + .step_height = 1, + .step_width = 1, + }, + }, +}; + +static const struct +mtk_cam_dev_node_desc output_queues[MTK_CAM_P1_TOTAL_OUTPUT] = { + { + .id = MTK_CAM_P1_META_IN_0, + .name = "meta input", + .description = "ISP tuning parameters", + .cap = V4L2_CAP_META_OUTPUT, + .buf_type = V4L2_BUF_TYPE_META_OUTPUT, + .link_flags = 0, + .capture = false, + .image = false, + .smem_alloc = true, + .fmts = meta_fmts, + .num_fmts = ARRAY_SIZE(meta_fmts), + .default_fmt_idx = 0, + .max_buf_count = 10, + .ioctl_ops = &mtk_cam_v4l2_meta_out_ioctl_ops, + }, +}; + +static const struct +mtk_cam_dev_node_desc capture_queues[MTK_CAM_P1_TOTAL_CAPTURE] = { + { + .id = MTK_CAM_P1_MAIN_STREAM_OUT, + .name = "main stream", + .cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .link_flags = 0, + .capture = true, + .image = true, + .smem_alloc = false, + .dma_port = R_IMGO, + .fmts = stream_out_fmts, + .num_fmts = ARRAY_SIZE(stream_out_fmts), + .default_fmt_idx = 1, + .ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops, + .frmsizes = &img_frm_size_nums[0], + }, + { + .id = MTK_CAM_P1_PACKED_BIN_OUT, + .name = "packed out", + .cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .link_flags = 0, + .capture = true, + .image = true, + .smem_alloc = false, + .dma_port = R_RRZO, + .fmts = bin_out_fmts, + .num_fmts = ARRAY_SIZE(bin_out_fmts), + .default_fmt_idx = 1, + .ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops, + .frmsizes = &img_frm_size_nums[1], + }, + { + .id = MTK_CAM_P1_META_OUT_0, + .name = "partial meta 0", + .description = "AE/AWB histogram", + .cap = V4L2_CAP_META_CAPTURE, + .buf_type = V4L2_BUF_TYPE_META_CAPTURE, + .link_flags = 0, + .capture = true, + .image = false, + .smem_alloc = false, + .dma_port = R_AAO | R_FLKO | R_PSO, + .fmts = meta_fmts, + .num_fmts = ARRAY_SIZE(meta_fmts), + .default_fmt_idx = 1, + .max_buf_count = 5, + .ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops, + }, + { + .id = MTK_CAM_P1_META_OUT_1, + .name = "partial meta 1", + .description = "AF histogram", + .cap = V4L2_CAP_META_CAPTURE, + .buf_type = V4L2_BUF_TYPE_META_CAPTURE, + .link_flags = 0, + .capture = true, + .image = false, + .smem_alloc = false, + .dma_port = R_AFO, + .fmts = meta_fmts, + .num_fmts = ARRAY_SIZE(meta_fmts), + .default_fmt_idx = 2, + .max_buf_count = 5, + .ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops, + }, + { + .id = MTK_CAM_P1_META_OUT_2, + .name = "partial meta 2", + .description = "Local contrast enhanced statistics", + .cap = V4L2_CAP_META_CAPTURE, + .buf_type = V4L2_BUF_TYPE_META_CAPTURE, + .link_flags = 0, + .capture = true, + .image = false, + .smem_alloc = false, + .dma_port = R_LCSO, + .fmts = meta_fmts, + .num_fmts = ARRAY_SIZE(meta_fmts), + .default_fmt_idx = 3, + .max_buf_count = 10, + .ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops, + }, + { + .id = MTK_CAM_P1_META_OUT_3, + .name = "partial meta 3", + .description = "Local motion vector histogram", + .cap = V4L2_CAP_META_CAPTURE, + .buf_type = V4L2_BUF_TYPE_META_CAPTURE, + .link_flags = 0, + .capture = true, + .image = false, + .smem_alloc = false, + .dma_port = R_LMVO, + .fmts = meta_fmts, + .num_fmts = ARRAY_SIZE(meta_fmts), + .default_fmt_idx = 4, + .max_buf_count = 10, + .ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops, + }, +}; + +/* The helper to configure the device context */ +static void mtk_cam_dev_queue_setup(struct mtk_cam_dev *cam_dev) +{ + unsigned int i, node_idx; + + node_idx = 0; + + /* Setup the output queue */ + for (i = 0; i < MTK_CAM_P1_TOTAL_OUTPUT; i++) + cam_dev->vdev_nodes[node_idx++].desc = output_queues[i]; + + /* Setup the capture queue */ + for (i = 0; i < MTK_CAM_P1_TOTAL_CAPTURE; i++) + cam_dev->vdev_nodes[node_idx++].desc = capture_queues[i]; +} + +int mtk_cam_dev_init(struct platform_device *pdev, + struct mtk_cam_dev *cam_dev) +{ + int ret; + + cam_dev->pdev = pdev; + mtk_cam_dev_queue_setup(cam_dev); + /* v4l2 sub-device registration */ + + dev_dbg(&cam_dev->pdev->dev, "mem2mem2.name: %s\n", + MTK_CAM_DEV_P1_NAME); + ret = mtk_cam_mem2mem2_v4l2_register(cam_dev); + if (ret) + return ret; + + ret = mtk_cam_v4l2_async_register(cam_dev); + if (ret) { + mtk_cam_v4l2_unregister(cam_dev); + return ret; + } + + spin_lock_init(&cam_dev->req_lock); + INIT_LIST_HEAD(&cam_dev->req_list); + mutex_init(&cam_dev->lock); + + return 0; +} + +int mtk_cam_dev_release(struct platform_device *pdev, + struct mtk_cam_dev *cam_dev) +{ + mtk_cam_v4l2_async_unregister(cam_dev); + mtk_cam_v4l2_unregister(cam_dev); + + mutex_destroy(&cam_dev->lock); + + return 0; +} + diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h new file mode 100644 index 000000000000..825cdf20643a --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-v4l2-util.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 MediaTek Inc. + */ + +#ifndef __MTK_CAM_DEV_V4L2_H__ +#define __MTK_CAM_DEV_V4L2_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MTK_CAM_DEV_P1_NAME "MTK-ISP-P1-V4L2" + +#define MTK_CAM_P1_META_IN_0 0 +#define MTK_CAM_P1_TOTAL_OUTPUT 1 + +#define MTK_CAM_P1_MAIN_STREAM_OUT 1 +#define MTK_CAM_P1_PACKED_BIN_OUT 2 +#define MTK_CAM_P1_META_OUT_0 3 +#define MTK_CAM_P1_META_OUT_1 4 +#define MTK_CAM_P1_META_OUT_2 5 +#define MTK_CAM_P1_META_OUT_3 6 +#define MTK_CAM_P1_TOTAL_CAPTURE 6 + +#define MTK_CAM_P1_TOTAL_NODES 7 + +struct mtk_cam_dev_request { + struct media_request req; + struct list_head list; +}; + +struct mtk_cam_dev_buffer { + struct vb2_v4l2_buffer vbb; + struct list_head list; + /* Intenal part */ + dma_addr_t daddr; + dma_addr_t scp_addr; + unsigned int node_id; +}; + +/* + * struct mtk_cam_dev_node_desc - node attributes + * + * @id: id of the context queue + * @name: media entity name + * @description: descritpion of node + * @cap: mapped to V4L2 capabilities + * @buf_type: mapped to V4L2 buffer type + * @dma_port: the dma port associated to the buffer + * @link_flags: default media link flags + * @smem_alloc: using the cam_smem_drv as alloc ctx or not + * @capture: true for capture queue (device to user) + * false for output queue (from user to device) + * @image: true for image node, false for meta node + * @num_fmts: the number of supported formats + * @default_fmt_idx: default format of this node + * @max_buf_count: maximum V4L2 buffer count + * @ioctl_ops: mapped to v4l2_ioctl_ops + * @fmts: supported format + * @frmsizes: supported frame size number + * + */ +struct mtk_cam_dev_node_desc { + u8 id; + char *name; + char *description; + u32 cap; + u32 buf_type; + u32 dma_port; + u32 link_flags; + u8 smem_alloc:1; + u8 capture:1; + u8 image:1; + u8 num_fmts; + u8 default_fmt_idx; + u8 max_buf_count; + const struct v4l2_ioctl_ops *ioctl_ops; + const struct v4l2_format *fmts; + const struct v4l2_frmsizeenum *frmsizes; +}; + +/* + * struct mtk_cam_video_device - Mediatek video device structure. + * + * @id: Id for mtk_cam_dev_node_desc or mem2mem2_nodes array + * @enabled: Indicate the device is enabled or not + * @vdev_fmt: The V4L2 format of video device + * @vdev_apd: The media pad graph object of video device + * @vbq: A videobuf queue of video device + * @desc: The node attributes of video device + * @ctrl_handler: The control handler of video device + * @pending_list: List for pending buffers before enqueuing into driver + * @lock: Serializes vb2 queue and video device operations. + * @slock: Protect for pending_list. + * + */ +struct mtk_cam_video_device { + unsigned int id; + unsigned int enabled; + struct v4l2_format vdev_fmt; + struct mtk_cam_dev_node_desc desc; + struct video_device vdev; + struct media_pad vdev_pad; + struct vb2_queue vbq; + struct v4l2_ctrl_handler ctrl_handler; + struct list_head pending_list; + /* Used for vbq & vdev */ + struct mutex lock; + /* protect for pending_list */ + spinlock_t slock; +}; + +/* + * struct mtk_cam_dev - Mediatek camera device structure. + * + * @pdev: Pointer to platform device + * @smem_pdev: Pointer to shared memory platform device + * @pipeline: Media pipeline information + * @media_dev: Media device + * @subdev: The V4L2 sub-device + * @v4l2_dev: The V4L2 device driver + * @notifier: The v4l2_device notifier data + * @subdev_pads: Pointer to the number of media pads of this sub-device + * @ctrl_handler: The control handler + * @vdev_nodes: The array list of mtk_cam_video_device nodes + * @seninf: Pointer to the seninf sub-device + * @sensor: Pointer to the active sensor V4L2 sub-device when streaming on + * @lock: The mutex protecting video device open/release operations + * @streaming: Indicate the overall streaming status is on or off + * @streamed_node_count: The number of V4L2 video device nodes are streaming on + * @req_list: Lins to keep media requests before streaming on + * @req_lock: Protect the req_list data + * + * Below is the graph topology for Camera IO connection. + * sensor 1 (main) --> sensor IF --> P1 sub-device + * sensor 2 (sub) --> + * + */ +struct mtk_cam_dev { + struct platform_device *pdev; + struct device *smem_dev; + struct media_pipeline pipeline; + struct media_device media_dev; + struct v4l2_subdev subdev; + struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; + struct media_pad *subdev_pads; + struct v4l2_ctrl_handler ctrl_handler; + struct mtk_cam_video_device vdev_nodes[MTK_CAM_P1_TOTAL_NODES]; + struct v4l2_subdev *seninf; + struct v4l2_subdev *sensor; + /* protect video device open/release operations */ + struct mutex lock; + unsigned int streaming:1; + atomic_t streamed_node_count; + struct list_head req_list; + /* protect for req_list */ + spinlock_t req_lock; +}; + +int mtk_cam_dev_init(struct platform_device *pdev, + struct mtk_cam_dev *cam_dev); +int mtk_cam_dev_release(struct platform_device *pdev, + struct mtk_cam_dev *cam_dev); +#endif /* __MTK_CAM_DEV_V4L2_H__ */ From patchwork Tue Jun 11 03:53:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jungo Lin X-Patchwork-Id: 10985831 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DC8C814E5 for ; Tue, 11 Jun 2019 03:58:10 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C5C7A1FF65 for ; Tue, 11 Jun 2019 03:58:10 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B8593271E6; Tue, 11 Jun 2019 03:58:10 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 914041FF65 for ; Tue, 11 Jun 2019 03:58:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:Reply-To:List-Subscribe:List-Help: List-Post:List-Archive:List-Unsubscribe:List-Id:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=+bipv+anXir2ADkT4f4HcBEpqST/73orLwqgyxq70+c=; b=lbvyo6ePebz9r/ /RKR1ZirzUBIisRpfXKWHp/w6pHyeqvPtuaQBla1NK2+OCbtQnQY4I/juow907S/DXO4bOkT2INQE V4VDv/H7mIjK22yF/mm1+PA7KiC1X+jDqYqvqmiG2cOLFErMLl1TVwB5kewRbih1hBQ/6f7CdfHrD 2q09vGhF+oY7dxJJlprwagvPMP9UWkjIZK1rmZ30gWuqep+sQ3H0gWCDHOq26bEwY0MTJIg7UwhOt xAR7qSKqrHNI4g8cyUouJAJu3/yTkAX9j2C5SZDkYIK1MbdZBwRYetMD21EzTIPQXJb0GEYDe4CGu H5INrbKQR9/8q9ERnVFQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1haXv9-00030V-Li; Tue, 11 Jun 2019 03:58:03 +0000 Received: from mailgw02.mediatek.com ([216.200.240.185]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1haXrW-0007RM-6t; Tue, 11 Jun 2019 03:54:23 +0000 X-UUID: ecdcdd37b77645549d865e6ca1ddfc4d-20190610 X-UUID: ecdcdd37b77645549d865e6ca1ddfc4d-20190610 Received: from mtkcas67.mediatek.inc [(172.29.193.45)] by mailgw02.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 1052982599; Mon, 10 Jun 2019 19:54:01 -0800 Received: from MTKMBS07N2.mediatek.inc (172.21.101.141) by MTKMBS62N2.mediatek.inc (172.29.193.42) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 10 Jun 2019 20:53:59 -0700 Received: from mtkcas09.mediatek.inc (172.21.101.178) by mtkmbs07n2.mediatek.inc (172.21.101.141) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 11 Jun 2019 11:53:57 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 11 Jun 2019 11:53:58 +0800 From: Jungo Lin To: , , , , Subject: [RFC,v3 7/9] media: platform: Add Mediatek ISP P1 device driver Date: Tue, 11 Jun 2019 11:53:42 +0800 Message-ID: <20190611035344.29814-8-jungo.lin@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20190611035344.29814-1-jungo.lin@mediatek.com> References: <20190611035344.29814-1-jungo.lin@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190610_205418_322365_A989A036 X-CRM114-Status: GOOD ( 15.38 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: jungo.lin@mediatek.com Cc: devicetree@vger.kernel.org, sean.cheng@mediatek.com, rynn.wu@mediatek.com, srv_heupstream@mediatek.com, robh@kernel.org, ryan.yu@mediatek.com, frankie.chiu@mediatek.com, jungo.lin@mediatek.com, sj.huang@mediatek.com, linux-mediatek@lists.infradead.org, ddavenport@chromium.org, frederic.chen@mediatek.com, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds the Mediatek ISP P1 HW control device driver. It handles the ISP HW configuration, provides interrupt handling and initializes the V4L2 device nodes and other functions. (The current metadata interface used in meta input and partial meta nodes is only a temporary solution to kick off the driver development and is not ready to be reviewed yet.) Signed-off-by: Jungo Lin --- .../platform/mtk-isp/isp_50/cam/Makefile | 1 + .../mtk-isp/isp_50/cam/mtk_cam-regs.h | 126 ++ .../platform/mtk-isp/isp_50/cam/mtk_cam.c | 1087 +++++++++++++++++ .../platform/mtk-isp/isp_50/cam/mtk_cam.h | 243 ++++ 4 files changed, 1457 insertions(+) create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile index 7558593e63f0..30df10983f6a 100644 --- a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile +++ b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile @@ -2,5 +2,6 @@ mtk-cam-isp-objs += mtk_cam-ctrl.o mtk-cam-isp-objs += mtk_cam-v4l2-util.o +mtk-cam-isp-objs += mtk_cam.o obj-$(CONFIG_VIDEO_MEDIATEK_ISP_PASS1) += mtk-cam-isp.o \ No newline at end of file diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h new file mode 100644 index 000000000000..9e59a6bfc6b7 --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 MediaTek Inc. + */ + +#ifndef _CAM_REGS_H +#define _CAM_REGS_H + +/* TG Bit Mask */ +#define VFDATA_EN_BIT BIT(0) +#define CMOS_EN_BIT BIT(0) + +/* normal signal bit */ +#define VS_INT_ST BIT(0) +#define HW_PASS1_DON_ST BIT(11) +#define SOF_INT_ST BIT(12) +#define SW_PASS1_DON_ST BIT(30) + +/* err status bit */ +#define TG_ERR_ST BIT(4) +#define TG_GBERR_ST BIT(5) +#define CQ_CODE_ERR_ST BIT(6) +#define CQ_APB_ERR_ST BIT(7) +#define CQ_VS_ERR_ST BIT(8) +#define AMX_ERR_ST BIT(15) +#define RMX_ERR_ST BIT(16) +#define BMX_ERR_ST BIT(17) +#define RRZO_ERR_ST BIT(18) +#define AFO_ERR_ST BIT(19) +#define IMGO_ERR_ST BIT(20) +#define AAO_ERR_ST BIT(21) +#define PSO_ERR_ST BIT(22) +#define LCSO_ERR_ST BIT(23) +#define BNR_ERR_ST BIT(24) +#define LSCI_ERR_ST BIT(25) +#define DMA_ERR_ST BIT(29) + +/* CAM DMA done status */ +#define FLKO_DONE_ST BIT(4) +#define AFO_DONE_ST BIT(5) +#define AAO_DONE_ST BIT(7) +#define PSO_DONE_ST BIT(14) + +/* IRQ signal mask */ +#define INT_ST_MASK_CAM ( \ + VS_INT_ST |\ + SOF_INT_ST |\ + HW_PASS1_DON_ST |\ + SW_PASS1_DON_ST) + +/* IRQ Error Mask */ +#define INT_ST_MASK_CAM_ERR ( \ + TG_ERR_ST |\ + TG_GBERR_ST |\ + CQ_CODE_ERR_ST |\ + CQ_APB_ERR_ST |\ + CQ_VS_ERR_ST |\ + BNR_ERR_ST |\ + RMX_ERR_ST |\ + BMX_ERR_ST |\ + BNR_ERR_ST |\ + LSCI_ERR_ST |\ + DMA_ERR_ST) + +/* IRQ Signal Log Mask */ +#define INT_ST_LOG_MASK_CAM ( \ + SOF_INT_ST |\ + SW_PASS1_DON_ST |\ + HW_PASS1_DON_ST |\ + VS_INT_ST |\ + TG_ERR_ST |\ + TG_GBERR_ST |\ + RRZO_ERR_ST |\ + AFO_ERR_ST |\ + IMGO_ERR_ST |\ + AAO_ERR_ST |\ + DMA_ERR_ST) + +/* DMA Event Notification Mask */ +#define DMA_ST_MASK_CAM ( \ + AAO_DONE_ST |\ + AFO_DONE_ST) + +/* Status check */ +#define REG_CTL_EN 0x0004 +#define REG_CTL_DMA_EN 0x0008 +#define REG_CTL_FMT_SEL 0x0010 +#define REG_CTL_EN2 0x0018 +#define REG_CTL_RAW_INT_EN 0x0020 +#define REG_CTL_RAW_INT_STAT 0x0024 +#define REG_CTL_RAW_INT2_STAT 0x0034 + +#define REG_TG_SEN_MODE 0x0230 +#define REG_TG_VF_CON 0x0234 + +#define REG_IMGO_BASE_ADDR 0x1020 +#define REG_RRZO_BASE_ADDR 0x1050 + +/* Error status log */ +#define REG_IMGO_ERR_STAT 0x1360 +#define REG_RRZO_ERR_STAT 0x1364 +#define REG_AAO_ERR_STAT 0x1368 +#define REG_AFO_ERR_STAT 0x136c +#define REG_LCSO_ERR_STAT 0x1370 +#define REG_UFEO_ERR_STAT 0x1374 +#define REG_PDO_ERR_STAT 0x1378 +#define REG_BPCI_ERR_STAT 0x137c +#define REG_LSCI_ERR_STAT 0x1384 +#define REG_PDI_ERR_STAT 0x138c +#define REG_LMVO_ERR_STAT 0x1390 +#define REG_FLKO_ERR_STAT 0x1394 +#define REG_PSO_ERR_STAT 0x13a0 + +/* ISP command */ +#define REG_CQ_THR0_BASEADDR 0x0198 +#define REG_HW_FRAME_NUM 0x13b8 + +/* META */ +#define REG_META0_VB2_INDEX 0x14dc +#define REG_META1_VB2_INDEX 0x151c + +/* FBC */ +#define REG_AAO_FBC_STATUS 0x013c +#define REG_AFO_FBC_STATUS 0x0134 + +#endif /* _CAM_REGS_H */ diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c new file mode 100644 index 000000000000..c5a3babed69d --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c @@ -0,0 +1,1087 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2018 MediaTek Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_cam.h" +#include "mtk_cam-regs.h" +#include "mtk_cam-smem.h" + +static const struct of_device_id mtk_isp_of_ids[] = { + {.compatible = "mediatek,mt8183-camisp",}, + {} +}; +MODULE_DEVICE_TABLE(of, mtk_isp_of_ids); + +/* List of clocks required by isp cam */ +static const char * const mtk_isp_clks[] = { + "camsys_cam_cgpdn", "camsys_camtg_cgpdn" +}; + +static void isp_dump_dma_status(struct isp_device *isp_dev) +{ + dev_err(isp_dev->dev, + "IMGO:0x%x, RRZO:0x%x, AAO=0x%x, AFO=0x%x, LMVO=0x%x\n", + readl(isp_dev->regs + REG_IMGO_ERR_STAT), + readl(isp_dev->regs + REG_RRZO_ERR_STAT), + readl(isp_dev->regs + REG_AAO_ERR_STAT), + readl(isp_dev->regs + REG_AFO_ERR_STAT), + readl(isp_dev->regs + REG_LMVO_ERR_STAT)); + dev_err(isp_dev->dev, + "LCSO=0x%x, PSO=0x%x, FLKO=0x%x, BPCI:0x%x, LSCI=0x%x\n", + readl(isp_dev->regs + REG_LCSO_ERR_STAT), + readl(isp_dev->regs + REG_PSO_ERR_STAT), + readl(isp_dev->regs + REG_FLKO_ERR_STAT), + readl(isp_dev->regs + REG_BPCI_ERR_STAT), + readl(isp_dev->regs + REG_LSCI_ERR_STAT)); +} + +static void mtk_cam_dev_event_frame_sync(struct mtk_cam_dev *cam_dev, + __u32 frame_seq_no) +{ + struct v4l2_event event; + + memset(&event, 0, sizeof(event)); + event.type = V4L2_EVENT_FRAME_SYNC; + event.u.frame_sync.frame_sequence = frame_seq_no; + v4l2_event_queue(cam_dev->subdev.devnode, &event); +} + +static void mtk_cam_dev_job_finish(struct mtk_isp_p1_ctx *isp_ctx, + unsigned int request_fd, + unsigned int frame_seq_no, + struct list_head *list_buf, + enum vb2_buffer_state state) +{ + struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx); + struct mtk_cam_dev *cam_dev = &p1_dev->cam_dev; + struct mtk_cam_dev_buffer *buf, *b0; + u64 timestamp; + + if (!cam_dev->streaming) + return; + + dev_dbg(&p1_dev->pdev->dev, "%s request fd:%d frame_seq:%d state:%d\n", + __func__, request_fd, frame_seq_no, state); + + /* + * Set the buffer's VB2 status so that the user can dequeue + * the buffer. + */ + timestamp = ktime_get_ns(); + list_for_each_entry_safe(buf, b0, list_buf, list) { + list_del(&buf->list); + buf->vbb.vb2_buf.timestamp = timestamp; + buf->vbb.sequence = frame_seq_no; + if (buf->vbb.vb2_buf.state == VB2_BUF_STATE_ACTIVE) + vb2_buffer_done(&buf->vbb.vb2_buf, state); + } +} + +static void isp_deque_frame(struct isp_p1_device *p1_dev, + unsigned int node_id, int vb2_index, + int frame_seq_no) +{ + struct mtk_cam_dev *cam_dev = &p1_dev->cam_dev; + struct device *dev = &p1_dev->pdev->dev; + struct mtk_cam_video_device *node = &cam_dev->vdev_nodes[node_id]; + struct mtk_cam_dev_buffer *b, *b0; + struct vb2_buffer *vb; + + if (!cam_dev->vdev_nodes[node_id].enabled || !cam_dev->streaming) + return; + + spin_lock(&node->slock); + b = list_first_entry(&node->pending_list, + struct mtk_cam_dev_buffer, + list); + list_for_each_entry_safe(b, b0, &node->pending_list, list) { + vb = &b->vbb.vb2_buf; + if (!vb->vb2_queue->uses_requests && + vb->index == vb2_index && + vb->state == VB2_BUF_STATE_ACTIVE) { + dev_dbg(dev, "%s:%d:%d", __func__, node_id, vb2_index); + vb->timestamp = ktime_get_ns(); + b->vbb.sequence = frame_seq_no; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + list_del(&b->list); + break; + } + } + spin_unlock(&node->slock); +} + +static void isp_deque_request_frame(struct isp_p1_device *p1_dev, + int frame_seq_no) +{ + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + struct device *dev = &p1_dev->pdev->dev; + struct mtk_isp_queue_job *framejob, *tmp; + struct isp_queue *p1_enqueue_list = &isp_ctx->p1_enqueue_list; + + /* Match dequeue work and enqueue frame */ + spin_lock(&p1_enqueue_list->lock); + list_for_each_entry_safe(framejob, tmp, &p1_enqueue_list->queue, + list_entry) { + dev_dbg(dev, + "%s frame_seq_no:%d, target frame_seq_no:%d\n", + __func__, + framejob->frame_seq_no, frame_seq_no); + /* Match by the en-queued request number */ + if (framejob->frame_seq_no == frame_seq_no) { + /* Pass to user space */ + mtk_cam_dev_job_finish(isp_ctx, + framejob->request_fd, + framejob->frame_seq_no, + &framejob->list_buf, + VB2_BUF_STATE_DONE); + atomic_dec(&p1_enqueue_list->queue_cnt); + dev_dbg(dev, + "frame_seq_no:%d is done, queue_cnt:%d\n", + framejob->frame_seq_no, + atomic_read(&p1_enqueue_list->queue_cnt)); + + /* Remove only when frame ready */ + list_del(&framejob->list_entry); + kfree(framejob); + break; + } else if (framejob->frame_seq_no < frame_seq_no) { + /* Pass to user space for frame drop */ + mtk_cam_dev_job_finish(isp_ctx, + framejob->request_fd, + framejob->frame_seq_no, + &framejob->list_buf, + VB2_BUF_STATE_ERROR); + atomic_dec(&p1_enqueue_list->queue_cnt); + dev_warn(dev, + "frame_seq_no:%d drop, queue_cnt:%d\n", + framejob->frame_seq_no, + atomic_read(&p1_enqueue_list->queue_cnt)); + + /* Remove only drop frame */ + list_del(&framejob->list_entry); + kfree(framejob); + } else { + break; + } + } + spin_unlock(&p1_enqueue_list->lock); +} + +static int isp_deque_work(void *data) +{ + struct isp_p1_device *p1_dev = (struct isp_p1_device *)data; + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + struct mtk_cam_dev_stat_event_data event_data; + atomic_t *irq_data_end = &isp_ctx->irq_data_end; + atomic_t *irq_data_start = &isp_ctx->irq_data_start; + unsigned long flags; + int ret, i; + + while (1) { + ret = wait_event_interruptible(isp_ctx->isp_deque_thread.wq, + (atomic_read(irq_data_end) != + atomic_read(irq_data_start)) || + kthread_should_stop()); + + if (kthread_should_stop()) + break; + + spin_lock_irqsave(&isp_ctx->irq_dequeue_lock, flags); + i = atomic_read(&isp_ctx->irq_data_start); + memcpy(&event_data, &isp_ctx->irq_event_datas[i], + sizeof(event_data)); + atomic_set(&isp_ctx->irq_data_start, ++i & 0x3); + spin_unlock_irqrestore(&isp_ctx->irq_dequeue_lock, flags); + + if (event_data.irq_status_mask & HW_PASS1_DON_ST && + event_data.dma_status_mask & AAO_DONE_ST) { + isp_deque_frame(p1_dev, + MTK_CAM_P1_META_OUT_0, + event_data.meta0_vb2_index, + event_data.frame_seq_no); + } + if (event_data.dma_status_mask & AFO_DONE_ST) { + isp_deque_frame(p1_dev, + MTK_CAM_P1_META_OUT_1, + event_data.meta1_vb2_index, + event_data.frame_seq_no); + } + if (event_data.irq_status_mask & SW_PASS1_DON_ST) { + isp_deque_frame(p1_dev, + MTK_CAM_P1_META_OUT_0, + event_data.meta0_vb2_index, + event_data.frame_seq_no); + isp_deque_frame(p1_dev, + MTK_CAM_P1_META_OUT_1, + event_data.meta1_vb2_index, + event_data.frame_seq_no); + isp_deque_request_frame(p1_dev, + event_data.frame_seq_no); + } + } + + return 0; +} + +static int irq_handle_sof(struct isp_device *isp_dev, + dma_addr_t base_addr, + unsigned int frame_num) +{ + unsigned int addr_offset; + struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev); + int cq_num = atomic_read(&p1_dev->isp_ctx.composed_frame_id); + + isp_dev->sof_count += 1; + + if (cq_num <= frame_num) { + dev_dbg(isp_dev->dev, + "SOF_INT_ST, wait next, cq_num:%d, frame_num:%d", + cq_num, frame_num); + atomic_set(&p1_dev->isp_ctx.composing_frame, 0); + return cq_num; + } + atomic_set(&p1_dev->isp_ctx.composing_frame, cq_num - frame_num); + + addr_offset = CQ_ADDRESS_OFFSET * (frame_num % CQ_BUFFER_COUNT); + writel(base_addr + addr_offset, isp_dev->regs + REG_CQ_THR0_BASEADDR); + dev_dbg(isp_dev->dev, + "SOF_INT_ST, update next, cq_num:%d, frame_num:%d cq_addr:0x%x", + cq_num, frame_num, addr_offset); + + return cq_num; +} + +static void irq_handle_notify_event(struct isp_device *isp_dev, + unsigned int irq_status, + unsigned int dma_status, + bool sof_only) +{ + struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev); + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + struct device *dev = isp_dev->dev; + unsigned long flags; + int i; + + if (irq_status & VS_INT_ST) { + /* Notify specific HW events to user space */ + mtk_cam_dev_event_frame_sync(&p1_dev->cam_dev, + isp_dev->current_frame); + dev_dbg(dev, + "frame sync is sent:%d:%d\n", + isp_dev->sof_count, + isp_dev->current_frame); + if (sof_only) + return; + } + + if (irq_status & SW_PASS1_DON_ST) { + /* Notify TX thread to send if TX frame is blocked */ + wake_up_interruptible(&isp_ctx->composer_tx_thread.wq); + } + + spin_lock_irqsave(&isp_ctx->irq_dequeue_lock, flags); + i = atomic_read(&isp_ctx->irq_data_end); + isp_ctx->irq_event_datas[i].frame_seq_no = isp_dev->current_frame; + isp_ctx->irq_event_datas[i].meta0_vb2_index = isp_dev->meta0_vb2_index; + isp_ctx->irq_event_datas[i].meta1_vb2_index = isp_dev->meta1_vb2_index; + isp_ctx->irq_event_datas[i].irq_status_mask = + (irq_status & INT_ST_MASK_CAM); + isp_ctx->irq_event_datas[i].dma_status_mask = + (dma_status & DMA_ST_MASK_CAM); + atomic_set(&isp_ctx->irq_data_end, ++i & 0x3); + spin_unlock_irqrestore(&isp_ctx->irq_dequeue_lock, flags); + + wake_up_interruptible(&isp_ctx->isp_deque_thread.wq); + + dev_dbg(dev, + "%s IRQ:0x%x DMA:0x%x seq:%d idx0:%d idx1:%d\n", + __func__, + (irq_status & INT_ST_MASK_CAM), + (dma_status & DMA_ST_MASK_CAM), + isp_dev->current_frame, + isp_dev->meta0_vb2_index, + isp_dev->meta1_vb2_index); +} + +irqreturn_t isp_irq_cam(int irq, void *data) +{ + struct isp_device *isp_dev = (struct isp_device *)data; + struct isp_p1_device *p1_dev = get_p1_device(isp_dev->dev); + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + struct device *dev = isp_dev->dev; + unsigned int cam_idx, cq_num, hw_frame_num; + unsigned int meta0_vb2_index, meta1_vb2_index; + unsigned int irq_status, err_status, dma_status; + unsigned int aao_fbc, afo_fbc; + unsigned long flags; + + /* Check the streaming is off or not */ + if (!p1_dev->cam_dev.streaming) + return IRQ_HANDLED; + + cam_idx = isp_dev->isp_hw_module - ISP_CAM_A_IDX; + cq_num = 0; + + spin_lock_irqsave(&isp_dev->spinlock_irq, flags); + irq_status = readl(isp_dev->regs + REG_CTL_RAW_INT_STAT); + dma_status = readl(isp_dev->regs + REG_CTL_RAW_INT2_STAT); + hw_frame_num = readl(isp_dev->regs + REG_HW_FRAME_NUM); + meta0_vb2_index = readl(isp_dev->regs + REG_META0_VB2_INDEX); + meta1_vb2_index = readl(isp_dev->regs + REG_META1_VB2_INDEX); + aao_fbc = readl(isp_dev->regs + REG_AAO_FBC_STATUS); + afo_fbc = readl(isp_dev->regs + REG_AFO_FBC_STATUS); + spin_unlock_irqrestore(&isp_dev->spinlock_irq, flags); + + /* Ignore unnecessary IRQ */ + if (!irq_status && (!(dma_status & DMA_ST_MASK_CAM))) + return IRQ_HANDLED; + + err_status = irq_status & INT_ST_MASK_CAM_ERR; + + /* Sof, done order check */ + if ((irq_status & SOF_INT_ST) && (irq_status & HW_PASS1_DON_ST)) { + dev_dbg(dev, "sof_done block cnt:%d\n", isp_dev->sof_count); + + /* Notify IRQ event and enqueue frame */ + irq_handle_notify_event(isp_dev, irq_status, dma_status, 0); + isp_dev->current_frame = hw_frame_num; + isp_dev->meta0_vb2_index = meta0_vb2_index; + isp_dev->meta1_vb2_index = meta1_vb2_index; + } else { + if (irq_status & SOF_INT_ST) { + isp_dev->current_frame = hw_frame_num; + isp_dev->meta0_vb2_index = meta0_vb2_index; + isp_dev->meta1_vb2_index = meta1_vb2_index; + } + irq_handle_notify_event(isp_dev, irq_status, dma_status, 1); + } + + if (irq_status & SOF_INT_ST) + cq_num = irq_handle_sof(isp_dev, isp_ctx->scp_mem_iova, + hw_frame_num); + + /* Check ISP error status */ + if (err_status) { + dev_err(dev, + "raw_int_err:0x%x/0x%x\n", + irq_status, err_status); + /* Show DMA errors in detail */ + if (err_status & DMA_ERR_ST) + isp_dump_dma_status(isp_dev); + } + + if (irq_status & INT_ST_LOG_MASK_CAM) + dev_dbg(dev, IRQ_STAT_STR, + 'A' + cam_idx, + isp_dev->sof_count, + irq_status, + dma_status, + hw_frame_num, + cq_num, + aao_fbc, + afo_fbc); + + return IRQ_HANDLED; +} + +static int isp_setup_scp_rproc(struct isp_p1_device *p1_dev) +{ + phandle rproc_phandle; + struct device *dev = &p1_dev->pdev->dev; + int ret; + + p1_dev->scp_pdev = scp_get_pdev(p1_dev->pdev); + if (!p1_dev->scp_pdev) { + dev_err(dev, "Failed to get scp device\n"); + return -ENODEV; + } + + ret = of_property_read_u32(dev->of_node, "mediatek,scp", + &rproc_phandle); + if (ret) { + dev_err(dev, "fail to get rproc_phandle:%d\n", ret); + return -EINVAL; + } + + p1_dev->rproc_handle = rproc_get_by_phandle(rproc_phandle); + dev_dbg(dev, "p1 rproc_phandle: 0x%pK\n\n", p1_dev->rproc_handle); + if (!p1_dev->rproc_handle) { + dev_err(dev, "fail to get rproc_handle\n"); + return -EINVAL; + } + + ret = rproc_boot(p1_dev->rproc_handle); + if (ret) { + /* + * Return 0 if downloading firmware successfully, + * otherwise it is failed + */ + return -ENODEV; + } + + return 0; +} + +static int isp_init_context(struct isp_p1_device *p1_dev) +{ + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + struct device *dev = &p1_dev->pdev->dev; + unsigned int i; + + dev_dbg(dev, "init irq work thread\n"); + if (!isp_ctx->isp_deque_thread.thread) { + init_waitqueue_head(&isp_ctx->isp_deque_thread.wq); + isp_ctx->isp_deque_thread.thread = + kthread_run(isp_deque_work, (void *)p1_dev, + "isp_deque_work"); + if (IS_ERR(isp_ctx->isp_deque_thread.thread)) { + dev_err(dev, "unable to alloc kthread\n"); + isp_ctx->isp_deque_thread.thread = NULL; + return -ENOMEM; + } + } + spin_lock_init(&isp_ctx->irq_dequeue_lock); + mutex_init(&isp_ctx->lock); + + INIT_LIST_HEAD(&isp_ctx->p1_enqueue_list.queue); + atomic_set(&isp_ctx->p1_enqueue_list.queue_cnt, 0); + + for (i = 0; i < ISP_DEV_NODE_NUM; i++) + spin_lock_init(&p1_dev->isp_devs[i].spinlock_irq); + + spin_lock_init(&isp_ctx->p1_enqueue_list.lock); + spin_lock_init(&isp_ctx->composer_txlist.lock); + + atomic_set(&isp_ctx->irq_data_end, 0); + atomic_set(&isp_ctx->irq_data_start, 0); + + return 0; +} + +static int isp_uninit_context(struct device *dev) +{ + struct isp_p1_device *p1_dev = get_p1_device(dev); + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + struct mtk_isp_queue_job *framejob, *tmp_framejob; + + spin_lock_irq(&isp_ctx->p1_enqueue_list.lock); + list_for_each_entry_safe(framejob, tmp_framejob, + &isp_ctx->p1_enqueue_list.queue, list_entry) { + list_del(&framejob->list_entry); + kfree(framejob); + } + spin_unlock_irq(&isp_ctx->p1_enqueue_list.lock); + + if (isp_ctx->isp_deque_thread.thread) { + kthread_stop(isp_ctx->isp_deque_thread.thread); + wake_up_interruptible(&isp_ctx->isp_deque_thread.wq); + isp_ctx->isp_deque_thread.thread = NULL; + } + + mutex_destroy(&isp_ctx->lock); + + return 0; +} + +static unsigned int get_enabled_dma_ports(struct mtk_cam_dev *cam_dev) +{ + unsigned int enabled_dma_ports, i; + + /* Get the enabled meta DMA ports */ + enabled_dma_ports = 0; + + for (i = 0; i < MTK_CAM_P1_TOTAL_NODES; i++) + if (cam_dev->vdev_nodes[i].enabled) + enabled_dma_ports |= + cam_dev->vdev_nodes[i].desc.dma_port; + + dev_dbg(&cam_dev->pdev->dev, "%s :0x%x", __func__, enabled_dma_ports); + + return enabled_dma_ports; +} + +/* Utility functions */ +static unsigned int get_sensor_pixel_id(unsigned int fmt) +{ + switch (fmt) { + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SBGGR14_1X14: + return RAW_PXL_ID_B; + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGBRG14_1X14: + return RAW_PXL_ID_GB; + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGRBG14_1X14: + return RAW_PXL_ID_GR; + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SRGGB14_1X14: + return RAW_PXL_ID_R; + default: + return RAW_PXL_ID_B; + } +} + +static unsigned int get_sensor_fmt(unsigned int fmt) +{ + switch (fmt) { + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + return IMG_FMT_BAYER8; + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + return IMG_FMT_BAYER10; + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + return IMG_FMT_BAYER12; + case MEDIA_BUS_FMT_SBGGR14_1X14: + case MEDIA_BUS_FMT_SGBRG14_1X14: + case MEDIA_BUS_FMT_SGRBG14_1X14: + case MEDIA_BUS_FMT_SRGGB14_1X14: + return IMG_FMT_BAYER14; + default: + return IMG_FMT_UNKNOWN; + } +} + +static unsigned int get_img_fmt(unsigned int fourcc) +{ + switch (fourcc) { + case V4L2_PIX_FMT_MTISP_B8: + return IMG_FMT_BAYER8; + case V4L2_PIX_FMT_MTISP_F8: + return IMG_FMT_FG_BAYER8; + case V4L2_PIX_FMT_MTISP_B10: + return IMG_FMT_BAYER10; + case V4L2_PIX_FMT_MTISP_F10: + return IMG_FMT_FG_BAYER10; + case V4L2_PIX_FMT_MTISP_B12: + return IMG_FMT_BAYER12; + case V4L2_PIX_FMT_MTISP_F12: + return IMG_FMT_FG_BAYER12; + case V4L2_PIX_FMT_MTISP_B14: + return IMG_FMT_BAYER14; + case V4L2_PIX_FMT_MTISP_F14: + return IMG_FMT_FG_BAYER14; + default: + return IMG_FMT_UNKNOWN; + } +} + +static unsigned int get_pixel_byte(unsigned int fourcc) +{ + switch (fourcc) { + case V4L2_PIX_FMT_MTISP_B8: + case V4L2_PIX_FMT_MTISP_F8: + return 8; + case V4L2_PIX_FMT_MTISP_B10: + case V4L2_PIX_FMT_MTISP_F10: + return 10; + case V4L2_PIX_FMT_MTISP_B12: + case V4L2_PIX_FMT_MTISP_F12: + return 12; + case V4L2_PIX_FMT_MTISP_B14: + case V4L2_PIX_FMT_MTISP_F14: + return 14; + default: + return 10; + } +} + +static void config_img_fmt(struct device *dev, struct p1_img_output *out_fmt, + const struct v4l2_format *in_fmt, + const struct v4l2_subdev_format *sd_format) +{ + out_fmt->img_fmt = get_img_fmt(in_fmt->fmt.pix_mp.pixelformat); + out_fmt->pixel_byte = get_pixel_byte(in_fmt->fmt.pix_mp.pixelformat); + out_fmt->size.w = in_fmt->fmt.pix_mp.width; + out_fmt->size.h = in_fmt->fmt.pix_mp.height; + + out_fmt->size.stride = in_fmt->fmt.pix_mp.plane_fmt[0].bytesperline; + out_fmt->size.xsize = in_fmt->fmt.pix_mp.plane_fmt[0].bytesperline; + + out_fmt->crop.left = 0x0; + out_fmt->crop.top = 0x0; + + out_fmt->crop.width = sd_format->format.width; + out_fmt->crop.height = sd_format->format.height; + + WARN_ONCE(in_fmt->fmt.pix_mp.width > out_fmt->crop.width || + in_fmt->fmt.pix_mp.height > out_fmt->crop.height, + "img out:%d:%d in:%d:%d", + in_fmt->fmt.pix_mp.width, in_fmt->fmt.pix_mp.height, + out_fmt->crop.width, out_fmt->crop.height); + + dev_dbg(dev, "pixel_byte:%d img_fmt:0x%x\n", + out_fmt->pixel_byte, + out_fmt->img_fmt); + dev_dbg(dev, + "param:size=%0dx%0d, stride:%d, xsize:%d, crop=%0dx%0d\n", + out_fmt->size.w, out_fmt->size.h, + out_fmt->size.stride, out_fmt->size.xsize, + out_fmt->crop.width, out_fmt->crop.height); +} + +/* ISP P1 interface functions */ +int mtk_isp_power_init(struct mtk_cam_dev *cam_dev) +{ + struct device *dev = &cam_dev->pdev->dev; + struct isp_p1_device *p1_dev = get_p1_device(dev); + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + int ret; + + ret = isp_setup_scp_rproc(p1_dev); + if (ret) + return ret; + + ret = isp_init_context(p1_dev); + if (ret) + return ret; + + ret = isp_composer_init(dev); + if (ret) + goto composer_err; + + pm_runtime_get_sync(dev); + + /* ISP HW INIT */ + isp_ctx->isp_hw_module = ISP_CAM_B_IDX; + /* Use pure RAW as default HW path */ + isp_ctx->isp_raw_path = ISP_PURE_RAW_PATH; + atomic_set(&p1_dev->cam_dev.streamed_node_count, 0); + + isp_composer_hw_init(dev); + /* Check enabled DMAs which is configured by media setup */ + isp_composer_meta_config(dev, get_enabled_dma_ports(cam_dev)); + + dev_dbg(dev, "%s done\n", __func__); + + return 0; + +composer_err: + isp_uninit_context(dev); + + return ret; +} + +int mtk_isp_power_release(struct device *dev) +{ + isp_composer_hw_deinit(dev); + isp_uninit_context(dev); + + dev_dbg(dev, "%s done\n", __func__); + + return 0; +} + +int mtk_isp_config(struct device *dev) +{ + struct isp_p1_device *p1_dev = get_p1_device(dev); + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + struct p1_config_param config_param; + struct mtk_cam_dev *cam_dev = &p1_dev->cam_dev; + struct v4l2_subdev_format sd_fmt; + unsigned int enabled_dma_ports; + struct v4l2_format *img_fmt; + int ret; + + p1_dev->isp_devs[isp_ctx->isp_hw_module].current_frame = 0; + p1_dev->isp_devs[isp_ctx->isp_hw_module].sof_count = 0; + + isp_ctx->frame_seq_no = 1; + atomic_set(&isp_ctx->composed_frame_id, 0); + + /* Get the enabled DMA ports */ + enabled_dma_ports = get_enabled_dma_ports(cam_dev); + dev_dbg(dev, "%s enable_dma_ports:0x%x", __func__, enabled_dma_ports); + + /* Sensor config */ + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(cam_dev->sensor, pad, get_fmt, NULL, &sd_fmt); + + if (ret) { + dev_dbg(dev, "sensor g_fmt on failed:%d\n", ret); + return -EPERM; + } + + dev_dbg(dev, + "get_fmt ret=%d, w=%d, h=%d, code=0x%x, field=%d, color=%d\n", + ret, sd_fmt.format.width, sd_fmt.format.height, + sd_fmt.format.code, sd_fmt.format.field, + sd_fmt.format.colorspace); + + config_param.cfg_in_param.continuous = 0x1; + config_param.cfg_in_param.subsample = 0x0; + /* Fix to one pixel mode in default */ + config_param.cfg_in_param.pixel_mode = 0x1; + /* Support normal pattern in default */ + config_param.cfg_in_param.data_pattern = 0x0; + + config_param.cfg_in_param.crop.left = 0x0; + config_param.cfg_in_param.crop.top = 0x0; + + config_param.cfg_in_param.raw_pixel_id = + get_sensor_pixel_id(sd_fmt.format.code); + config_param.cfg_in_param.img_fmt = get_sensor_fmt(sd_fmt.format.code); + config_param.cfg_in_param.crop.width = sd_fmt.format.width; + config_param.cfg_in_param.crop.height = sd_fmt.format.height; + + config_param.cfg_main_param.bypass = 1; + img_fmt = &cam_dev->vdev_nodes[MTK_CAM_P1_MAIN_STREAM_OUT].vdev_fmt; + if ((enabled_dma_ports & R_IMGO) == R_IMGO) { + config_param.cfg_main_param.bypass = 0; + config_param.cfg_main_param.pure_raw = isp_ctx->isp_raw_path; + config_param.cfg_main_param.pure_raw_pack = 1; + config_img_fmt(dev, &config_param.cfg_main_param.output, + img_fmt, &sd_fmt); + } + + config_param.cfg_resize_param.bypass = 1; + img_fmt = &cam_dev->vdev_nodes[MTK_CAM_P1_PACKED_BIN_OUT].vdev_fmt; + if ((enabled_dma_ports & R_RRZO) == R_RRZO) { + config_param.cfg_resize_param.bypass = 0; + config_img_fmt(dev, &config_param.cfg_resize_param.output, + img_fmt, &sd_fmt); + } + + /* Configure meta DMAs info. */ + config_param.cfg_meta_param.enabled_meta_dmas = enabled_dma_ports; + + isp_composer_hw_config(dev, &config_param); + + dev_dbg(dev, "%s done\n", __func__); + + return 0; +} + +void mtk_isp_enqueue(struct device *dev, unsigned int dma_port, + struct mtk_cam_dev_buffer *buffer) +{ + struct mtk_isp_scp_p1_cmd frameparams; + + memset(&frameparams, 0, sizeof(frameparams)); + frameparams.cmd_id = ISP_CMD_ENQUEUE_META; + frameparams.meta_frame.enabled_dma = dma_port; + frameparams.meta_frame.vb_index = buffer->vbb.vb2_buf.index; + frameparams.meta_frame.meta_addr.iova = buffer->daddr; + frameparams.meta_frame.meta_addr.scp_addr = buffer->scp_addr; + + isp_composer_enqueue(dev, &frameparams, SCP_ISP_CMD); +} + +void mtk_isp_req_flush_buffers(struct device *dev) +{ + struct isp_p1_device *p1_dev = get_p1_device(dev); + struct mtk_isp_queue_job *job, *j0; + struct mtk_cam_dev_buffer *buf, *b0; + struct isp_queue *p1_list = &p1_dev->isp_ctx.p1_enqueue_list; + + if (!atomic_read(&p1_list->queue_cnt)) + return; + + spin_lock(&p1_list->lock); + list_for_each_entry_safe(job, j0, &p1_list->queue, list_entry) { + list_for_each_entry_safe(buf, b0, &job->list_buf, list) { + list_del(&buf->list); + if (buf->vbb.vb2_buf.state == VB2_BUF_STATE_ACTIVE) + vb2_buffer_done(&buf->vbb.vb2_buf, + VB2_BUF_STATE_ERROR); + } + list_del(&job->list_entry); + atomic_dec(&p1_list->queue_cnt); + kfree(job); + } + spin_unlock(&p1_list->lock); +} + +void mtk_isp_req_enqueue(struct device *dev, struct media_request *req) +{ + struct isp_p1_device *p1_dev = get_p1_device(dev); + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + struct p1_frame_param frameparams; + struct mtk_isp_queue_job *framejob; + struct media_request_object *obj, *obj_safe; + struct vb2_buffer *vb; + struct mtk_cam_dev_buffer *buf; + + framejob = kzalloc(sizeof(*framejob), GFP_ATOMIC); + memset(framejob, 0, sizeof(*framejob)); + memset(&frameparams, 0, sizeof(frameparams)); + INIT_LIST_HEAD(&framejob->list_buf); + + frameparams.frame_seq_no = isp_ctx->frame_seq_no++; + frameparams.sof_idx = + p1_dev->isp_devs[isp_ctx->isp_hw_module].sof_count; + framejob->frame_seq_no = frameparams.frame_seq_no; + + list_for_each_entry_safe(obj, obj_safe, &req->objects, list) { + vb = container_of(obj, struct vb2_buffer, req_obj); + buf = container_of(vb, struct mtk_cam_dev_buffer, vbb.vb2_buf); + framejob->request_fd = buf->vbb.request_fd; + frameparams.dma_buffers[buf->node_id].iova = buf->daddr; + frameparams.dma_buffers[buf->node_id].scp_addr = buf->scp_addr; + list_add_tail(&buf->list, &framejob->list_buf); + } + + spin_lock(&isp_ctx->p1_enqueue_list.lock); + list_add_tail(&framejob->list_entry, &isp_ctx->p1_enqueue_list.queue); + atomic_inc(&isp_ctx->p1_enqueue_list.queue_cnt); + spin_unlock(&isp_ctx->p1_enqueue_list.lock); + + isp_composer_enqueue(dev, &frameparams, SCP_ISP_FRAME); + dev_dbg(dev, "request fd:%d frame_seq_no:%d is queued cnt:%d\n", + framejob->request_fd, + frameparams.frame_seq_no, + atomic_read(&isp_ctx->p1_enqueue_list.queue_cnt)); +} + +static int enable_sys_clock(struct isp_p1_device *p1_dev) +{ + struct device *dev = &p1_dev->pdev->dev; + int ret; + + dev_info(dev, "- %s\n", __func__); + + ret = clk_bulk_prepare_enable(p1_dev->isp_ctx.num_clks, + p1_dev->isp_ctx.clk_list); + if (ret) + goto clk_err; + + return 0; + +clk_err: + dev_err(dev, "cannot pre-en isp_cam clock:%d\n", ret); + clk_bulk_disable_unprepare(p1_dev->isp_ctx.num_clks, + p1_dev->isp_ctx.clk_list); + return ret; +} + +static void disable_sys_clock(struct isp_p1_device *p1_dev) +{ + dev_info(&p1_dev->pdev->dev, "- %s\n", __func__); + clk_bulk_disable_unprepare(p1_dev->isp_ctx.num_clks, + p1_dev->isp_ctx.clk_list); +} + +static int mtk_isp_suspend(struct device *dev) +{ + struct isp_p1_device *p1_dev = get_p1_device(dev); + int module = p1_dev->isp_ctx.isp_hw_module; + struct isp_device *isp_dev = &p1_dev->isp_devs[module]; + unsigned int reg_val; + + dev_dbg(dev, "- %s\n", __func__); + + isp_dev = &p1_dev->isp_devs[module]; + reg_val = readl(isp_dev->regs + REG_TG_VF_CON); + if (reg_val & VFDATA_EN_BIT) { + dev_dbg(dev, "Cam:%d suspend, disable VF\n", module); + /* Disable view finder */ + writel((reg_val & (~VFDATA_EN_BIT)), + isp_dev->regs + REG_TG_VF_CON); + /* + * After VF enable, the TG frame count will be reset to 0; + */ + reg_val = readl(isp_dev->regs + REG_TG_SEN_MODE); + writel((reg_val & (~CMOS_EN_BIT)), + isp_dev->regs + + REG_TG_SEN_MODE); + } + + disable_sys_clock(p1_dev); + + return 0; +} + +static int mtk_isp_resume(struct device *dev) +{ + struct isp_p1_device *p1_dev = get_p1_device(dev); + int module = p1_dev->isp_ctx.isp_hw_module; + struct isp_device *isp_dev = &p1_dev->isp_devs[module]; + unsigned int reg_val; + + dev_dbg(dev, "- %s\n", __func__); + + enable_sys_clock(p1_dev); + + /* V4L2 stream-on phase & restore HW stream-on status */ + if (p1_dev->cam_dev.streaming) { + dev_dbg(dev, "Cam:%d resume,enable VF\n", module); + /* Enable CMOS */ + reg_val = readl(isp_dev->regs + REG_TG_SEN_MODE); + writel((reg_val | CMOS_EN_BIT), + isp_dev->regs + REG_TG_SEN_MODE); + /* Enable VF */ + reg_val = readl(isp_dev->regs + REG_TG_VF_CON); + writel((reg_val | VFDATA_EN_BIT), + isp_dev->regs + REG_TG_VF_CON); + } + + return 0; +} + +static int mtk_isp_probe(struct platform_device *pdev) +{ + struct isp_p1_device *p1_dev; + struct mtk_isp_p1_ctx *isp_ctx; + struct isp_device *isp_dev; + struct device *dev = &pdev->dev; + struct resource *res; + int irq; + int ret; + unsigned int i; + + p1_dev = devm_kzalloc(dev, sizeof(*p1_dev), GFP_KERNEL); + if (!p1_dev) + return -ENOMEM; + + dev_set_drvdata(dev, p1_dev); + isp_ctx = &p1_dev->isp_ctx; + p1_dev->pdev = pdev; + + for (i = ISP_CAMSYS_CONFIG_IDX; i < ISP_DEV_NODE_NUM; i++) { + isp_dev = &p1_dev->isp_devs[i]; + isp_dev->isp_hw_module = i; + isp_dev->dev = dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + isp_dev->regs = devm_ioremap_resource(dev, res); + + dev_dbg(dev, "cam%u, map_addr=0x%lx\n", + i, (unsigned long)isp_dev->regs); + + if (!isp_dev->regs) + return PTR_ERR(isp_dev->regs); + + /* Support IRQ from ISP_CAM_A_IDX */ + if (i >= ISP_CAM_A_IDX) { + /* Reg & interrupts index is shifted with 1 */ + irq = platform_get_irq(pdev, i - 1); + if (irq) { + ret = devm_request_irq(dev, irq, + isp_irq_cam, + IRQF_SHARED, + dev_driver_string(dev), + (void *)isp_dev); + if (ret) { + dev_err(dev, + "req_irq fail, dev:%s irq=%d\n", + dev->of_node->name, + irq); + return ret; + } + dev_dbg(dev, "Registered irq=%d, ISR:%s\n", + irq, dev_driver_string(dev)); + } + } + spin_lock_init(&isp_dev->spinlock_irq); + } + + p1_dev->isp_ctx.num_clks = ARRAY_SIZE(mtk_isp_clks); + p1_dev->isp_ctx.clk_list = + devm_kcalloc(dev, + p1_dev->isp_ctx.num_clks, + sizeof(*p1_dev->isp_ctx.clk_list), + GFP_KERNEL); + if (!p1_dev->isp_ctx.clk_list) + return -ENOMEM; + + for (i = 0; i < p1_dev->isp_ctx.num_clks; ++i) + p1_dev->isp_ctx.clk_list->id = mtk_isp_clks[i]; + + ret = devm_clk_bulk_get(dev, + p1_dev->isp_ctx.num_clks, + p1_dev->isp_ctx.clk_list); + if (ret) { + dev_err(dev, "cannot get isp cam clock:%d\n", ret); + return ret; + } + + /* Initialize reserved DMA memory */ + ret = mtk_cam_reserved_memory_init(p1_dev); + if (ret) { + dev_err(dev, "failed to configure DMA memory:%d\n", ret); + goto err_init; + } + + /* Initialize the v4l2 common part */ + ret = mtk_cam_dev_init(pdev, &p1_dev->cam_dev); + if (ret) + goto err_init; + + spin_lock_init(&isp_ctx->p1_enqueue_list.lock); + pm_runtime_enable(dev); + + return 0; + +err_init: + if (p1_dev->cam_dev.smem_dev) + device_unregister(p1_dev->cam_dev.smem_dev); + + return ret; +} + +static int mtk_isp_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct isp_p1_device *p1_dev = dev_get_drvdata(dev); + + pm_runtime_disable(dev); + mtk_cam_dev_release(pdev, &p1_dev->cam_dev); + + return 0; +} + +static const struct dev_pm_ops mtk_isp_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mtk_isp_suspend, mtk_isp_resume) + SET_RUNTIME_PM_OPS(mtk_isp_suspend, mtk_isp_resume, NULL) +}; + +static struct platform_driver mtk_isp_driver = { + .probe = mtk_isp_probe, + .remove = mtk_isp_remove, + .driver = { + .name = "mtk-cam", + .of_match_table = of_match_ptr(mtk_isp_of_ids), + .pm = &mtk_isp_pm_ops, + } +}; + +module_platform_driver(mtk_isp_driver); + +MODULE_DESCRIPTION("Camera ISP driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h new file mode 100644 index 000000000000..6af3f569664c --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 MediaTek Inc. + */ + +#ifndef __CAMERA_ISP_H +#define __CAMERA_ISP_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_cam-scp.h" +#include "mtk_cam-v4l2-util.h" + +#define CAM_A_MAX_WIDTH 3328 +#define CAM_A_MAX_HEIGHT 2496 +#define CAM_B_MAX_WIDTH 5376 +#define CAM_B_MAX_HEIGHT 4032 + +#define CAM_MIN_WIDTH 80 +#define CAM_MIN_HEIGHT 60 + +#define IMG_MAX_WIDTH CAM_B_MAX_WIDTH +#define IMG_MAX_HEIGHT CAM_B_MAX_HEIGHT +#define IMG_MIN_WIDTH CAM_MIN_WIDTH +#define IMG_MIN_HEIGHT CAM_MIN_HEIGHT + +#define RRZ_MAX_WIDTH CAM_B_MAX_WIDTH +#define RRZ_MAX_HEIGHT CAM_B_MAX_HEIGHT +#define RRZ_MIN_WIDTH CAM_MIN_WIDTH +#define RRZ_MIN_HEIGHT CAM_MIN_HEIGHT + +#define R_IMGO BIT(0) +#define R_RRZO BIT(1) +#define R_AAO BIT(3) +#define R_AFO BIT(4) +#define R_LCSO BIT(5) +#define R_PDO BIT(6) +#define R_LMVO BIT(7) +#define R_FLKO BIT(8) +#define R_RSSO BIT(9) +#define R_PSO BIT(10) + +#define CQ_BUFFER_COUNT 3 +#define IRQ_DATA_BUF_SIZE 4 +#define CQ_ADDRESS_OFFSET 0x640 + +#define ISP_COMPOSING_MAX_NUM 4 +#define ISP_FRAME_COMPOSING_MAX_NUM 3 + +#define IRQ_STAT_STR "cam%c, SOF_%d irq(0x%x), " \ + "dma(0x%x), frame_num(%d)/cq_num(%d), " \ + "fbc1(0x%x), fbc2(0x%x)\n" + +/* + * In order with the sequence of device nodes defined in dtsi rule, + * one hardware module should be mapping to one node. + */ +enum isp_dev_node_enum { + ISP_CAMSYS_CONFIG_IDX = 0, + ISP_CAM_UNI_IDX, + ISP_CAM_A_IDX, + ISP_CAM_B_IDX, + ISP_DEV_NODE_NUM +}; + +/* Image RAW path for ISP P1 module. */ +enum isp_raw_path_enum { + ISP_PROCESS_RAW_PATH = 0, + ISP_PURE_RAW_PATH +}; + +/* State for struct mtk_isp_p1_ctx: composer_state */ +enum { + SCP_ON = 0, + SCP_OFF +}; + +enum { + IMG_FMT_UNKNOWN = 0x0000, + IMG_FMT_RAW_START = 0x2200, + IMG_FMT_BAYER8 = IMG_FMT_RAW_START, + IMG_FMT_BAYER10, + IMG_FMT_BAYER12, + IMG_FMT_BAYER14, + IMG_FMT_FG_BAYER8, + IMG_FMT_FG_BAYER10, + IMG_FMT_FG_BAYER12, + IMG_FMT_FG_BAYER14, +}; + +enum { + RAW_PXL_ID_B = 0, + RAW_PXL_ID_GB, + RAW_PXL_ID_GR, + RAW_PXL_ID_R +}; + +struct isp_queue { + struct list_head queue; + atomic_t queue_cnt; + spinlock_t lock; /* queue attributes protection */ +}; + +struct isp_thread { + struct task_struct *thread; + wait_queue_head_t wq; +}; + +struct mtk_isp_queue_work { + union { + struct mtk_isp_scp_p1_cmd cmd; + struct p1_frame_param frameparams; + }; + struct list_head list_entry; + enum mtk_isp_scp_type type; +}; + +struct mtk_cam_dev_stat_event_data { + __u32 frame_seq_no; + __u32 meta0_vb2_index; + __u32 meta1_vb2_index; + __u32 irq_status_mask; + __u32 dma_status_mask; +}; + +struct mtk_isp_queue_job { + struct list_head list_entry; + struct list_head list_buf; + unsigned int request_fd; + unsigned int frame_seq_no; +}; + +/* + * struct isp_device - the ISP device information + * + * @dev: Pointer to struct device + * @regs: Camera ISP base register address + * @spinlock_irq: Used to protect register read/write data + * @current_frame: Current frame sequence number, set when SOF + * @meta0_vb2_index: Meta0 vb2 buffer index, set when SOF + * @meta1_vb2_index: Meta1 vb2 buffer index, set when SOF + * @sof_count: The accumulated SOF counter + * @isp_hw_module: Identity camera A or B + * + */ +struct isp_device { + struct device *dev; + void __iomem *regs; + spinlock_t spinlock_irq; /* ISP reg setting integrity */ + unsigned int current_frame; + unsigned int meta0_vb2_index; + unsigned int meta1_vb2_index; + u8 sof_count; + u8 isp_hw_module; +}; + +/* + * struct mtk_isp_p1_ctx - the ISP device information + * + * @composer_txlist: Queue for SCP TX data including SCP_ISP_CMD & SCP_ISP_FRAME + * @composer_tx_thread: TX Thread for SCP data tranmission + * @cmd_queued: The number of SCP_ISP_CMD commands will be sent + * @ipi_occupied: The total number of SCP TX data has beent sent + * @scp_state: The state of SCP control + * @composing_frame: The total number of SCP_ISP_FRAME has beent sent + * @composed_frame_id: The ack. frame sequence by SCP + * @composer_deinit_thread: The de-initialized thread + * @p1_enqueue_list: Queue for ISP frame buffers + * @isp_deque_thread: Thread for handling ISP frame buffers dequeue + * @irq_event_datas: Ring buffer for struct mtk_cam_dev_stat_event_data data + * @irq_data_start: Start index of irq_event_datas ring buffer + * @irq_data_end: End index of irq_event_datas ring buffer + * @irq_dequeue_lock: Lock to protect irq_event_datas ring buffer + * @scp_mem_pa: DMA address for SCP device + * @scp_mem_iova: DMA address for ISP HW DMA devices + * @frame_seq_no: Sequence number for ISP frame buffer + * @isp_hw_module: Active camera HW module + * @num_clks: The number of driver's clock + * @clk_list: The list of clock data + * @lock: Lock to protect context operations + * + */ +struct mtk_isp_p1_ctx { + struct isp_queue composer_txlist; + struct isp_thread composer_tx_thread; + atomic_t cmd_queued; + atomic_t ipi_occupied; + atomic_t scp_state; + atomic_t composing_frame; + atomic_t composed_frame_id; + struct isp_thread composer_deinit_thread; + struct isp_queue p1_enqueue_list; + struct isp_thread isp_deque_thread; + struct mtk_cam_dev_stat_event_data irq_event_datas[IRQ_DATA_BUF_SIZE]; + atomic_t irq_data_start; + atomic_t irq_data_end; + spinlock_t irq_dequeue_lock; /* ISP frame dequeuq protection */ + dma_addr_t scp_mem_pa; + dma_addr_t scp_mem_iova; + int frame_seq_no; + unsigned int isp_hw_module; + unsigned int isp_raw_path; + unsigned int num_clks; + struct clk_bulk_data *clk_list; + struct mutex lock; /* Protect context operations */ +}; + +struct isp_p1_device { + struct platform_device *pdev; + struct platform_device *scp_pdev; + struct rproc *rproc_handle; + struct mtk_isp_p1_ctx isp_ctx; + struct mtk_cam_dev cam_dev; + struct isp_device isp_devs[ISP_DEV_NODE_NUM]; +}; + +static inline struct isp_p1_device * +p1_ctx_to_dev(const struct mtk_isp_p1_ctx *__p1_ctx) +{ + return container_of(__p1_ctx, struct isp_p1_device, isp_ctx); +} + +static inline struct isp_p1_device *get_p1_device(struct device *dev) +{ + return ((struct isp_p1_device *)dev_get_drvdata(dev)); +} + +int mtk_isp_power_init(struct mtk_cam_dev *cam_dev); +int mtk_isp_power_release(struct device *dev); +int mtk_isp_config(struct device *dev); +void mtk_isp_req_enqueue(struct device *dev, struct media_request *req); +void mtk_isp_enqueue(struct device *dev, unsigned int dma_port, + struct mtk_cam_dev_buffer *buffer); +void mtk_isp_req_flush_buffers(struct device *dev); + +#endif /*__CAMERA_ISP_H*/ From patchwork Tue Jun 11 03:53:43 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jungo Lin X-Patchwork-Id: 10985821 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4DA1214C0 for ; Tue, 11 Jun 2019 03:56:39 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 39E0F1FF65 for ; Tue, 11 Jun 2019 03:56:39 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2C538271E6; Tue, 11 Jun 2019 03:56:39 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 1A0981FF65 for ; Tue, 11 Jun 2019 03:56:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:Reply-To:List-Subscribe:List-Help: List-Post:List-Archive:List-Unsubscribe:List-Id:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=pYNrlYN7VJMqJHfBTI7nwOwAhZ2ZUghMR9x3eBc/OaE=; b=cEUVi/rjfKKsp6 w3zx87g7naUqznpLi2i+LHY7bYb5yvrM3aKJnz6sFv1bNl48ECsNLHoUUIr9Ax7rPGIEtOhRe+VS1 x+gb6/cAc+xlH3alsMf8C6qkrP0Sy91VZsxtJA9NjA4PTHHMchVXhGQ70Jv7FDjV7PPuj2wv+Wp0L +O3WT3YbRFvNFEdHamLSMYA7WbIDdO3LITrNhMxf0J5jdYde85CI+vYBCLBHJzhIWYMSypTv51vi3 0SShqtwK/dm+AxUvS1IVzoUlyabK0FGEzJkKA/PryMZkDWmhEdz8zVI9GSTMeVCiqeEWNbrytK3ny tLLkgD3KY+fT7fGrwnNg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1haXtg-0001uC-Ni; Tue, 11 Jun 2019 03:56:32 +0000 Received: from mailgw01.mediatek.com ([216.200.240.184]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1haXrP-0007JA-6i; Tue, 11 Jun 2019 03:54:15 +0000 X-UUID: beb3420eace0460f82692c5f42d500a0-20190610 X-UUID: beb3420eace0460f82692c5f42d500a0-20190610 Received: from mtkcas67.mediatek.inc [(172.29.193.45)] by mailgw01.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 949932070; Mon, 10 Jun 2019 19:54:02 -0800 Received: from MTKMBS01N1.mediatek.inc (172.21.101.68) by MTKMBS62DR.mediatek.inc (172.29.94.18) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 10 Jun 2019 20:54:01 -0700 Received: from mtkcas09.mediatek.inc (172.21.101.178) by mtkmbs01n1.mediatek.inc (172.21.101.68) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 11 Jun 2019 11:53:59 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 11 Jun 2019 11:53:59 +0800 From: Jungo Lin To: , , , , Subject: [RFC,v3 8/9] media: platform: Add Mediatek ISP P1 SCP communication Date: Tue, 11 Jun 2019 11:53:43 +0800 Message-ID: <20190611035344.29814-9-jungo.lin@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20190611035344.29814-1-jungo.lin@mediatek.com> References: <20190611035344.29814-1-jungo.lin@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190610_205411_434115_28CE2B7D X-CRM114-Status: GOOD ( 16.18 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: jungo.lin@mediatek.com Cc: devicetree@vger.kernel.org, sean.cheng@mediatek.com, rynn.wu@mediatek.com, srv_heupstream@mediatek.com, robh@kernel.org, ryan.yu@mediatek.com, frankie.chiu@mediatek.com, jungo.lin@mediatek.com, sj.huang@mediatek.com, linux-mediatek@lists.infradead.org, ddavenport@chromium.org, frederic.chen@mediatek.com, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds communication with the co-processor on the SoC through the SCP driver. It supports bi-directional commands to exchange data and perform command flow control function. Signed-off-by: Jungo Lin --- This patch depends on "Add support for mt8183 SCP"[1]. [1] https://patchwork.kernel.org/cover/10972143/ --- .../platform/mtk-isp/isp_50/cam/Makefile | 1 + .../platform/mtk-isp/isp_50/cam/mtk_cam-scp.c | 371 ++++++++++++++++++ .../platform/mtk-isp/isp_50/cam/mtk_cam-scp.h | 207 ++++++++++ 3 files changed, 579 insertions(+) create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile index 30df10983f6a..95f0b1c8fa1c 100644 --- a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile +++ b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile @@ -3,5 +3,6 @@ mtk-cam-isp-objs += mtk_cam-ctrl.o mtk-cam-isp-objs += mtk_cam-v4l2-util.o mtk-cam-isp-objs += mtk_cam.o +mtk-cam-isp-objs += mtk_cam-scp.o obj-$(CONFIG_VIDEO_MEDIATEK_ISP_PASS1) += mtk-cam-isp.o \ No newline at end of file diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c new file mode 100644 index 000000000000..04519d0b942f --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2018 MediaTek Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_cam.h" + +static void isp_composer_deinit(struct mtk_isp_p1_ctx *isp_ctx) +{ + struct mtk_isp_queue_work *ipi_job, *tmp_ipi_job; + struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx); + + atomic_set(&isp_ctx->cmd_queued, 0); + atomic_set(&isp_ctx->composer_txlist.queue_cnt, 0); + atomic_set(&isp_ctx->composing_frame, 0); + atomic_set(&isp_ctx->ipi_occupied, 0); + + spin_lock(&isp_ctx->composer_txlist.lock); + list_for_each_entry_safe(ipi_job, tmp_ipi_job, + &isp_ctx->composer_txlist.queue, + list_entry) { + list_del(&ipi_job->list_entry); + kfree(ipi_job); + } + atomic_set(&isp_ctx->composer_txlist.queue_cnt, 0); + spin_unlock(&isp_ctx->composer_txlist.lock); + + mutex_lock(&isp_ctx->lock); + if (isp_ctx->composer_tx_thread.thread) { + kthread_stop(isp_ctx->composer_tx_thread.thread); + wake_up_interruptible(&isp_ctx->composer_tx_thread.wq); + isp_ctx->composer_tx_thread.thread = NULL; + } + + if (isp_ctx->composer_deinit_thread.thread) { + wake_up(&isp_ctx->composer_deinit_thread.wq); + isp_ctx->composer_deinit_thread.thread = NULL; + } + mutex_unlock(&isp_ctx->lock); + + pm_runtime_put_sync(&p1_dev->pdev->dev); +} + +/* + * Two kinds of flow control in isp_composer_tx_work. + * + * Case 1: IPI commands flow control. The maximum number of command queues is 3. + * There are two types of IPI commands (SCP_ISP_CMD/SCP_ISP_FRAME) in P1 driver. + * It is controlled by ipi_occupied. + * The priority of SCP_ISP_CMD is higher than SCP_ISP_FRAME. + * + * Case 2: Frame buffers flow control. The maximum number of frame buffers is 3. + * It is controlled by composing_frame. + * Frame buffer is sent by SCP_ISP_FRAME command. + */ +static int isp_composer_tx_work(void *data) +{ + struct mtk_isp_p1_ctx *isp_ctx = (struct mtk_isp_p1_ctx *)data; + struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx); + struct device *dev = &p1_dev->pdev->dev; + struct mtk_isp_queue_work *isp_composer_work, *tmp_ipi_job; + struct isp_queue *composer_txlist = &isp_ctx->composer_txlist; + int ret; + + while (1) { + ret = wait_event_interruptible + (isp_ctx->composer_tx_thread.wq, + (atomic_read(&composer_txlist->queue_cnt) > 0 && + atomic_read(&isp_ctx->ipi_occupied) + < ISP_COMPOSING_MAX_NUM && + atomic_read(&isp_ctx->composing_frame) + < ISP_FRAME_COMPOSING_MAX_NUM) || + (atomic_read(&isp_ctx->cmd_queued) > 0 && + atomic_read(&isp_ctx->ipi_occupied) + < ISP_COMPOSING_MAX_NUM) || + kthread_should_stop()); + + if (kthread_should_stop()) + break; + + spin_lock(&composer_txlist->lock); + if (atomic_read(&isp_ctx->cmd_queued) > 0) { + list_for_each_entry_safe(isp_composer_work, tmp_ipi_job, + &composer_txlist->queue, + list_entry) { + if (isp_composer_work->type == SCP_ISP_CMD) { + dev_dbg(dev, "Found a cmd\n"); + break; + } + } + } else { + if (atomic_read(&isp_ctx->composing_frame) >= + ISP_FRAME_COMPOSING_MAX_NUM) { + spin_unlock(&composer_txlist->lock); + continue; + } + isp_composer_work = + list_first_entry_or_null + (&composer_txlist->queue, + struct mtk_isp_queue_work, + list_entry); + } + + list_del(&isp_composer_work->list_entry); + atomic_dec(&composer_txlist->queue_cnt); + spin_unlock(&composer_txlist->lock); + + if (isp_composer_work->type == SCP_ISP_CMD) { + scp_ipi_send + (p1_dev->scp_pdev, + SCP_IPI_ISP_CMD, + &isp_composer_work->cmd, + sizeof(isp_composer_work->cmd), + 0); + atomic_dec(&isp_ctx->cmd_queued); + atomic_inc(&isp_ctx->ipi_occupied); + dev_dbg(dev, + "%s cmd id %d sent, %d ipi buf occupied", + __func__, + isp_composer_work->cmd.cmd_id, + atomic_read(&isp_ctx->ipi_occupied)); + } else if (isp_composer_work->type == SCP_ISP_FRAME) { + scp_ipi_send + (p1_dev->scp_pdev, + SCP_IPI_ISP_FRAME, + &isp_composer_work->frameparams, + sizeof(isp_composer_work->frameparams), + 0); + atomic_inc(&isp_ctx->ipi_occupied); + atomic_inc(&isp_ctx->composing_frame); + dev_dbg(dev, + "%s frame %d sent, %d ipi, %d CQ bufs occupied", + __func__, + isp_composer_work->frameparams.frame_seq_no, + atomic_read(&isp_ctx->ipi_occupied), + atomic_read(&isp_ctx->composing_frame)); + } else { + dev_err(dev, + "ignore IPI type: %d!\n", + isp_composer_work->type); + } + kfree(isp_composer_work); + } + return ret; +} + +static int isp_composer_deinit_work(void *data) +{ + struct mtk_isp_p1_ctx *isp_ctx = (struct mtk_isp_p1_ctx *)data; + struct isp_p1_device *p1_dev = p1_ctx_to_dev(data); + struct device *dev = &p1_dev->pdev->dev; + + wait_event_interruptible(isp_ctx->composer_deinit_thread.wq, + atomic_read(&isp_ctx->scp_state) == SCP_OFF || + kthread_should_stop()); + + dev_dbg(dev, "%s run deinit", __func__); + isp_composer_deinit(isp_ctx); + + return 0; +} + +static void isp_composer_handler(void *data, unsigned int len, void *priv) +{ + struct mtk_isp_p1_ctx *isp_ctx = (struct mtk_isp_p1_ctx *)priv; + struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx); + struct device *dev = &p1_dev->pdev->dev; + struct mtk_isp_scp_p1_cmd *ipi_msg; + + ipi_msg = (struct mtk_isp_scp_p1_cmd *)data; + + if (ipi_msg->cmd_id != ISP_CMD_ACK) + return; + + if (ipi_msg->ack_info.cmd_id == ISP_CMD_FRAME_ACK) { + dev_dbg(dev, "ack frame_num:%d", + ipi_msg->ack_info.frame_seq_no); + atomic_set(&isp_ctx->composed_frame_id, + ipi_msg->ack_info.frame_seq_no); + } else if (ipi_msg->ack_info.cmd_id == ISP_CMD_DEINIT) { + dev_dbg(dev, "ISP_CMD_DEINIT is acked"); + atomic_set(&isp_ctx->scp_state, SCP_OFF); + wake_up_interruptible(&isp_ctx->composer_deinit_thread.wq); + } + + atomic_dec_return(&isp_ctx->ipi_occupied); + wake_up_interruptible(&isp_ctx->composer_tx_thread.wq); +} + +int isp_composer_init(struct device *dev) +{ + struct isp_p1_device *p1_dev = get_p1_device(dev); + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + int ret; + + ret = scp_ipi_register(p1_dev->scp_pdev, + SCP_IPI_ISP_CMD, + isp_composer_handler, + isp_ctx); + if (ret) + return ret; + + atomic_set(&isp_ctx->cmd_queued, 0); + atomic_set(&isp_ctx->composer_txlist.queue_cnt, 0); + atomic_set(&isp_ctx->composing_frame, 0); + atomic_set(&isp_ctx->ipi_occupied, 0); + atomic_set(&isp_ctx->scp_state, SCP_ON); + + mutex_lock(&isp_ctx->lock); + if (!isp_ctx->composer_tx_thread.thread) { + init_waitqueue_head(&isp_ctx->composer_tx_thread.wq); + INIT_LIST_HEAD(&isp_ctx->composer_txlist.queue); + spin_lock_init(&isp_ctx->composer_txlist.lock); + isp_ctx->composer_tx_thread.thread = + kthread_run(isp_composer_tx_work, isp_ctx, + "isp_composer_tx"); + if (IS_ERR(isp_ctx->composer_tx_thread.thread)) { + dev_err(dev, "unable to start kthread\n"); + isp_ctx->composer_tx_thread.thread = NULL; + goto nomem; + } + } else { + dev_warn(dev, "old tx thread is existed\n"); + } + + if (!isp_ctx->composer_deinit_thread.thread) { + init_waitqueue_head(&isp_ctx->composer_deinit_thread.wq); + isp_ctx->composer_deinit_thread.thread = + kthread_run(isp_composer_deinit_work, isp_ctx, + "isp_composer_deinit_work"); + if (IS_ERR(isp_ctx->composer_deinit_thread.thread)) { + dev_err(dev, "unable to start kthread\n"); + isp_ctx->composer_deinit_thread.thread = NULL; + goto nomem; + } + } else { + dev_warn(dev, "old rx thread is existed\n"); + } + mutex_unlock(&isp_ctx->lock); + + return 0; + +nomem: + mutex_unlock(&isp_ctx->lock); + + return -ENOMEM; +} + +void isp_composer_enqueue(struct device *dev, + void *data, + enum mtk_isp_scp_type type) +{ + struct mtk_isp_queue_work *isp_composer_work; + struct isp_p1_device *p1_dev = get_p1_device(dev); + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + + isp_composer_work = kzalloc(sizeof(*isp_composer_work), GFP_KERNEL); + isp_composer_work->type = type; + + switch (type) { + case SCP_ISP_CMD: + memcpy(&isp_composer_work->cmd, data, + sizeof(isp_composer_work->cmd)); + dev_dbg(dev, "Enq ipi cmd id:%d\n", + isp_composer_work->cmd.cmd_id); + + spin_lock(&isp_ctx->composer_txlist.lock); + list_add_tail(&isp_composer_work->list_entry, + &isp_ctx->composer_txlist.queue); + atomic_inc(&isp_ctx->composer_txlist.queue_cnt); + spin_unlock(&isp_ctx->composer_txlist.lock); + + atomic_inc(&isp_ctx->cmd_queued); + wake_up_interruptible(&isp_ctx->composer_tx_thread.wq); + break; + case SCP_ISP_FRAME: + memcpy(&isp_composer_work->frameparams, data, + sizeof(isp_composer_work->frameparams)); + dev_dbg(dev, "Enq ipi frame_num:%d\n", + isp_composer_work->frameparams.frame_seq_no); + + spin_lock(&isp_ctx->composer_txlist.lock); + list_add_tail(&isp_composer_work->list_entry, + &isp_ctx->composer_txlist.queue); + atomic_inc(&isp_ctx->composer_txlist.queue_cnt); + spin_unlock(&isp_ctx->composer_txlist.lock); + + wake_up_interruptible(&isp_ctx->composer_tx_thread.wq); + break; + default: + break; + } +} + +void isp_composer_hw_init(struct device *dev) +{ + struct mtk_isp_scp_p1_cmd composer_tx_cmd; + struct isp_p1_device *p1_dev = get_p1_device(dev); + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + + memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd)); + composer_tx_cmd.cmd_id = ISP_CMD_INIT; + composer_tx_cmd.frameparam.hw_module = isp_ctx->isp_hw_module; + composer_tx_cmd.frameparam.cq_addr.iova = isp_ctx->scp_mem_iova; + composer_tx_cmd.frameparam.cq_addr.scp_addr = isp_ctx->scp_mem_pa; + isp_composer_enqueue(dev, &composer_tx_cmd, SCP_ISP_CMD); +} + +void isp_composer_meta_config(struct device *dev, + unsigned int dma) +{ + struct mtk_isp_scp_p1_cmd composer_tx_cmd; + + memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd)); + composer_tx_cmd.cmd_id = ISP_CMD_CONFIG_META; + composer_tx_cmd.cfg_meta_out_param.enabled_meta_dmas = dma; + isp_composer_enqueue(dev, &composer_tx_cmd, SCP_ISP_CMD); +} + +void isp_composer_hw_config(struct device *dev, + struct p1_config_param *config_param) +{ + struct mtk_isp_scp_p1_cmd composer_tx_cmd; + + memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd)); + composer_tx_cmd.cmd_id = ISP_CMD_CONFIG; + memcpy(&composer_tx_cmd.config_param, config_param, + sizeof(*config_param)); + isp_composer_enqueue(dev, &composer_tx_cmd, SCP_ISP_CMD); +} + +void isp_composer_stream(struct device *dev, int on) +{ + struct mtk_isp_scp_p1_cmd composer_tx_cmd; + + memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd)); + composer_tx_cmd.cmd_id = ISP_CMD_STREAM; + composer_tx_cmd.is_stream_on = on; + isp_composer_enqueue(dev, &composer_tx_cmd, SCP_ISP_CMD); +} + +void isp_composer_hw_deinit(struct device *dev) +{ + struct mtk_isp_scp_p1_cmd composer_tx_cmd; + struct isp_p1_device *p1_dev = get_p1_device(dev); + struct mtk_isp_p1_ctx *isp_ctx = &p1_dev->isp_ctx; + int ret; + + memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd)); + composer_tx_cmd.cmd_id = ISP_CMD_DEINIT; + isp_composer_enqueue(dev, &composer_tx_cmd, SCP_ISP_CMD); + + /* Wait for ISP_CMD_DEINIT command is handled done */ + ret = wait_event_timeout(isp_ctx->composer_deinit_thread.wq, + atomic_read(&isp_ctx->scp_state) == SCP_OFF, + msecs_to_jiffies(2000)); + if (ret) + return; + + dev_warn(dev, "Timeout & local de-init\n"); + isp_composer_deinit(isp_ctx); +} diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h new file mode 100644 index 000000000000..fbd8593e9c2d --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-scp.h @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 MediaTek Inc. + */ + +#ifndef _MTK_ISP_SCP_H +#define _MTK_ISP_SCP_H + +#include + +#include "mtk_cam-v4l2-util.h" + +/* + * struct img_size - image size information. + * + * @w: image width, the unit is pixel + * @h: image height, the unit is pixel + * @xsize: bytes per line based on width. + * @stride: bytes per line when changing line. + * Normally, calculate new STRIDE based on + * xsize + HW constrain(page or align). + * + */ +struct img_size { + __u32 w; + __u32 h; + __u32 xsize; + __u32 stride; +} __packed; + +/* + * struct img_buffer - buffer address information. + * + * @iova: DMA address for external devices. + * @scp_addr: SCP address for external co-process unit. + * + */ +struct img_buffer { + __u32 iova; + __u32 scp_addr; +} __packed; + +struct p1_img_crop { + __u32 left; + __u32 top; + __u32 width; + __u32 height; +} __packed; + +struct p1_img_output { + struct img_buffer buffer; + struct img_size size; + struct p1_img_crop crop; + __u8 pixel_byte; + __u32 img_fmt; +} __packed; + +/* + * struct cfg_in_param - image input parameters structure. + * Normally, it comes from sensor information. + * + * @continuous: indicate the sensor mode. + * 1: continuous + * 0: single + * @subsample: indicate to enables SOF subsample or not. + * @pixel_mode: describe 1/2/4 pixels per clock cycle. + * @data_pattern: describe input data pattern. + * @raw_pixel_id: bayer sequence. + * @tg_fps: the fps rate of TG (time generator). + * @img_fmt: the image format of input source. + * @p1_img_crop: the crop configuration of input source. + * + */ +struct cfg_in_param { + __u8 continuous; + __u8 subsample; + __u8 pixel_mode; + __u8 data_pattern; + __u8 raw_pixel_id; + __u16 tg_fps; + __u32 img_fmt; + struct p1_img_crop crop; +} __packed; + +/* + * struct cfg_main_out_param - the image output parameters of main stream. + * + * @bypass: indicate this device is enabled or disabled or not . + * @pure_raw: indicate the image path control. + * 1: pure raw + * 0: processing raw + * @pure_raw_pack: indicate the image is packed or not. + * 1: packed mode + * 0: unpacked mode + * @p1_img_output: the output image information. + * + */ +struct cfg_main_out_param { + /* Bypass main out parameters */ + __u8 bypass; + /* Control HW image raw path */ + __u8 pure_raw; + /* Control HW image pack function */ + __u8 pure_raw_pack; + struct p1_img_output output; +} __packed; + +/* + * struct cfg_resize_out_param - the image output parameters of + * packed out stream. + * + * @bypass: indicate this device is enabled or disabled or not . + * @p1_img_output: the output image information. + * + */ +struct cfg_resize_out_param { + /* Bypass resize parameters */ + __u8 bypass; + struct p1_img_output output; +} __packed; + +/* + * struct cfg_meta_out_param - output meta information. + * + * @enabled_meta_dmas: indicate which meta DMAs are enabled. + * + */ +struct cfg_meta_out_param { + __u32 enabled_meta_dmas; +} __packed; + +struct p1_config_param { + /* Sensor/TG info */ + struct cfg_in_param cfg_in_param; + /* IMGO DMA */ + struct cfg_main_out_param cfg_main_param; + /* RRZO DMA */ + struct cfg_resize_out_param cfg_resize_param; + /* 3A DMAs and other. */ + struct cfg_meta_out_param cfg_meta_param; +} __packed; + +struct p1_frame_param { + /* frame sequence number */ + __u32 frame_seq_no; + /* SOF index */ + __u32 sof_idx; + /* The memory address of tuning buffer from user space */ + struct img_buffer dma_buffers[MTK_CAM_P1_TOTAL_NODES]; +} __packed; + +struct P1_meta_frame { + __u32 enabled_dma; + __u32 vb_index; + struct img_buffer meta_addr; +} __packed; + +struct isp_init_info { + __u8 hw_module; + struct img_buffer cq_addr; +} __packed; + +struct isp_ack_info { + __u8 cmd_id; + __u32 frame_seq_no; +} __packed; + +enum mtk_isp_scp_cmds { + ISP_CMD_INIT, + ISP_CMD_CONFIG, + ISP_CMD_STREAM, + ISP_CMD_DEINIT, + ISP_CMD_ACK, + ISP_CMD_FRAME_ACK, + ISP_CMD_CONFIG_META, + ISP_CMD_ENQUEUE_META, + ISP_CMD_RESERVED, +}; + +struct mtk_isp_scp_p1_cmd { + __u8 cmd_id; + union { + struct isp_init_info frameparam; + struct p1_config_param config_param; + struct cfg_meta_out_param cfg_meta_out_param; + struct P1_meta_frame meta_frame; + __u8 is_stream_on; + struct isp_ack_info ack_info; + }; +} __packed; + +enum mtk_isp_scp_type { + SCP_ISP_CMD = 0, + SCP_ISP_FRAME, +}; + +int isp_composer_init(struct device *dev); +void isp_composer_enqueue(struct device *dev, void *data, + enum mtk_isp_scp_type type); +void isp_composer_hw_init(struct device *dev); +void isp_composer_hw_config(struct device *dev, + struct p1_config_param *config_param); +void isp_composer_hw_deinit(struct device *dev); +void isp_composer_meta_config(struct device *dev, unsigned int dma); +void isp_composer_stream(struct device *dev, int on); + +#endif /* _MTK_ISP_SCP_H */ From patchwork Tue Jun 11 03:53:44 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jungo Lin X-Patchwork-Id: 10985829 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id ECA8214C0 for ; Tue, 11 Jun 2019 03:57:48 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DA4FE1FF65 for ; Tue, 11 Jun 2019 03:57:48 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CC99927F94; Tue, 11 Jun 2019 03:57:48 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 1698922B27 for ; Tue, 11 Jun 2019 03:57:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:Reply-To:List-Subscribe:List-Help: List-Post:List-Archive:List-Unsubscribe:List-Id:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=qroD86cgHf+Uy5TLyGlJBGiIVFIuYF4587qU+vtdAc8=; b=N3whEXRlCh+hO1 TEX0fbRXzkKtD/TyGS+XBjel230TOdZ7Vx3C0dXiE7GS99N1HcX5bvX4bRYB7W+0QQ5mt/BW4b2gQ Yit/iDolyYotty6eVS6jj7o2c0RyHZRMmU4FnBi00Um7A2dSaI//P0d7Vbi/pbMO7OGLj8Zxwu8MA I1da07No3fL4jIo0UL0hoFliP2Gb1lzcQuQ7TwfZhjnRLFE3gsA+A0c7PTwUxc4hw4XndibNfLfrO oOlSSUpBSx0m+58TOwJkUIaETMU5EulqGTcyOGVI/C/x46cQYJBYgx4lsIPHt0cDiqyzAyhCXBYCM kCBM7T08Y+HXTFkTiAIA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1haXup-0002lm-H0; Tue, 11 Jun 2019 03:57:43 +0000 Received: from mailgw02.mediatek.com ([216.200.240.185]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1haXrT-0007JQ-Hu; Tue, 11 Jun 2019 03:54:18 +0000 X-UUID: 6d71adea25104562bd4f071927f3406c-20190610 X-UUID: 6d71adea25104562bd4f071927f3406c-20190610 Received: from mtkcas67.mediatek.inc [(172.29.193.45)] by mailgw02.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 1643697015; Mon, 10 Jun 2019 19:54:03 -0800 Received: from MTKMBS01N2.mediatek.inc (172.21.101.79) by MTKMBS62N1.mediatek.inc (172.29.193.41) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 10 Jun 2019 20:54:02 -0700 Received: from mtkcas09.mediatek.inc (172.21.101.178) by mtkmbs01n2.mediatek.inc (172.21.101.79) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 11 Jun 2019 11:54:00 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkcas09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 11 Jun 2019 11:54:00 +0800 From: Jungo Lin To: , , , , Subject: [RFC, v3 9/9] media: platform: Add Mediatek ISP P1 shared memory device Date: Tue, 11 Jun 2019 11:53:44 +0800 Message-ID: <20190611035344.29814-10-jungo.lin@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20190611035344.29814-1-jungo.lin@mediatek.com> References: <20190611035344.29814-1-jungo.lin@mediatek.com> MIME-Version: 1.0 X-TM-SNTS-SMTP: F12E07B93EAFA609DC7C0D7FC742FE10CD2D4D1AB945F72210C6651DCF160D492000:8 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190610_205415_996171_7E16CC94 X-CRM114-Status: GOOD ( 15.04 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: jungo.lin@mediatek.com Cc: devicetree@vger.kernel.org, sean.cheng@mediatek.com, rynn.wu@mediatek.com, srv_heupstream@mediatek.com, robh@kernel.org, ryan.yu@mediatek.com, frankie.chiu@mediatek.com, jungo.lin@mediatek.com, sj.huang@mediatek.com, linux-mediatek@lists.infradead.org, ddavenport@chromium.org, frederic.chen@mediatek.com, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP The purpose of this child device is to provide shared memory management for exchanging tuning data between co-processor and the Pass 1 unit of the camera ISP system, including cache buffer handling. Signed-off-by: Jungo Lin --- This patch depends on "Add support for mt8183 SCP"[1]. [1] https://patchwork.kernel.org/cover/10972143/ --- .../platform/mtk-isp/isp_50/cam/Makefile | 1 + .../mtk-isp/isp_50/cam/mtk_cam-smem.c | 304 ++++++++++++++++++ .../mtk-isp/isp_50/cam/mtk_cam-smem.h | 18 ++ 3 files changed, 323 insertions(+) create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.c create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile index 95f0b1c8fa1c..d545ca6f09c5 100644 --- a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile +++ b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile @@ -4,5 +4,6 @@ mtk-cam-isp-objs += mtk_cam-ctrl.o mtk-cam-isp-objs += mtk_cam-v4l2-util.o mtk-cam-isp-objs += mtk_cam.o mtk-cam-isp-objs += mtk_cam-scp.o +mtk-cam-isp-objs += mtk_cam-smem.o obj-$(CONFIG_VIDEO_MEDIATEK_ISP_PASS1) += mtk-cam-isp.o \ No newline at end of file diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.c new file mode 100644 index 000000000000..a9845668ce10 --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2018 MediaTek Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_cam-smem.h" + +static struct dma_map_ops smem_dma_ops; + +struct mtk_cam_smem_dev { + struct device *dev; + struct sg_table sgt; + struct page **smem_pages; + dma_addr_t smem_base; + dma_addr_t smem_dma_base; + int smem_size; +}; + +struct dma_coherent_mem { + void *virt_base; + dma_addr_t device_base; + unsigned long pfn_base; + int size; + int flags; + unsigned long *bitmap; + spinlock_t spinlock; /* dma_coherent_mem attributes protection */ + bool use_dev_dma_pfn_offset; +}; + +dma_addr_t mtk_cam_smem_iova_to_scp_addr(struct device *dev, + dma_addr_t iova) +{ + struct iommu_domain *domain; + dma_addr_t addr, limit; + struct mtk_cam_smem_dev *smem_dev = dev_get_drvdata(dev); + + domain = iommu_get_domain_for_dev(dev); + if (!domain) { + dev_warn(dev, "No iommu group domain\n"); + return 0; + } + + addr = iommu_iova_to_phys(domain, iova); + limit = smem_dev->smem_base + smem_dev->smem_size; + if (addr < smem_dev->smem_base || addr >= limit) { + dev_err(dev, + "Unexpected scp_addr:%pad must >= %pad and < %pad)\n", + &addr, &smem_dev->smem_base, &limit); + return 0; + } + return addr; +} + +static int mtk_cam_smem_get_sgtable(struct device *dev, + struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, + size_t size, unsigned long attrs) +{ + struct mtk_cam_smem_dev *smem_dev = dev_get_drvdata(dev); + size_t pages_count = PAGE_ALIGN(size) >> PAGE_SHIFT; + dma_addr_t scp_addr = mtk_cam_smem_iova_to_scp_addr(dev, dma_addr); + u32 pages_start = (scp_addr - smem_dev->smem_base) >> PAGE_SHIFT; + + dev_dbg(dev, + "%s:page:%u va:%pK scp addr:%pad, aligned size:%zu pages:%zu\n", + __func__, pages_start, cpu_addr, &scp_addr, size, pages_count); + + return sg_alloc_table_from_pages(sgt, + smem_dev->smem_pages + pages_start, + pages_count, 0, size, GFP_KERNEL); +} + +static void *mtk_cam_smem_get_cpu_addr(struct mtk_cam_smem_dev *smem_dev, + dma_addr_t addr) +{ + struct device *dev = smem_dev->dev; + struct dma_coherent_mem *dma_mem = dev->dma_mem; + + if (addr < smem_dev->smem_base || + addr > smem_dev->smem_base + smem_dev->smem_size) { + dev_err(dev, "Invalid scp_addr %pad from sg\n", &addr); + return NULL; + } + return dma_mem->virt_base + (addr - smem_dev->smem_base); +} + +static void mtk_cam_smem_sync_sg_for_cpu(struct device *dev, + struct scatterlist *sgl, int nelems, + enum dma_data_direction dir) +{ + struct mtk_cam_smem_dev *smem_dev = dev_get_drvdata(dev); + dma_addr_t scp_addr = sg_phys(sgl); + void *cpu_addr = mtk_cam_smem_get_cpu_addr(smem_dev, scp_addr); + + dev_dbg(dev, + "__dma_unmap_area:scp_addr:%pad,vaddr:%pK,size:%d,dir:%d\n", + &scp_addr, cpu_addr, sgl->length, dir); + __dma_unmap_area(cpu_addr, sgl->length, dir); +} + +static void mtk_cam_smem_sync_sg_for_device(struct device *dev, + struct scatterlist *sgl, + int nelems, + enum dma_data_direction dir) +{ + struct mtk_cam_smem_dev *smem_dev = dev_get_drvdata(dev); + dma_addr_t scp_addr = sg_phys(sgl); + void *cpu_addr = mtk_cam_smem_get_cpu_addr(smem_dev, scp_addr); + + dev_dbg(dev, + "__dma_map_area:scp_addr:%pad,vaddr:%pK,size:%d,dir:%d\n", + &scp_addr, cpu_addr, sgl->length, dir); + __dma_map_area(cpu_addr, sgl->length, dir); +} + +static void mtk_cam_smem_setup_dma_ops(struct device *dev, + struct dma_map_ops *smem_ops) +{ + memcpy((void *)smem_ops, dev->dma_ops, sizeof(*smem_ops)); + smem_ops->get_sgtable = mtk_cam_smem_get_sgtable; + smem_ops->sync_sg_for_device = mtk_cam_smem_sync_sg_for_device; + smem_ops->sync_sg_for_cpu = mtk_cam_smem_sync_sg_for_cpu; + set_dma_ops(dev, smem_ops); +} + +static int mtk_cam_reserved_drm_sg_init(struct mtk_cam_smem_dev *smem_dev) +{ + u32 size_align, n_pages; + struct device *dev = smem_dev->dev; + struct sg_table *sgt = &smem_dev->sgt; + struct page **pages; + dma_addr_t dma_addr; + unsigned int i; + int ret; + + smem_dev->smem_base = scp_get_reserve_mem_phys(SCP_ISP_MEM2_ID); + smem_dev->smem_size = scp_get_reserve_mem_size(SCP_ISP_MEM2_ID); + if (!smem_dev->smem_base || !smem_dev->smem_size) + return -EPROBE_DEFER; + + dev_info(dev, "%s dev:0x%pK base:%pad size:%u MiB\n", + __func__, + smem_dev->dev, + &smem_dev->smem_base, + (smem_dev->smem_size / SZ_1M)); + + size_align = PAGE_ALIGN(smem_dev->smem_size); + n_pages = size_align >> PAGE_SHIFT; + + pages = kmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL); + if (!pages) + return -ENOMEM; + + for (i = 0; i < n_pages; i++) + pages[i] = phys_to_page(smem_dev->smem_base + i * PAGE_SIZE); + + ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, + size_align, GFP_KERNEL); + if (ret) { + dev_err(dev, "failed to alloca sg table:%d\n", ret); + goto fail_table_alloc; + } + sgt->nents = dma_map_sg_attrs(dev, sgt->sgl, sgt->orig_nents, + DMA_BIDIRECTIONAL, + DMA_ATTR_SKIP_CPU_SYNC); + if (!sgt->nents) { + dev_err(dev, "failed to dma sg map\n"); + goto fail_map; + } + + dma_addr = sg_dma_address(sgt->sgl); + ret = dma_declare_coherent_memory(dev, smem_dev->smem_base, + dma_addr, size_align, + DMA_MEMORY_EXCLUSIVE); + if (ret) { + dev_err(dev, "Unable to declare smem memory:%d\n", ret); + goto fail_map; + } + + dev_info(dev, "Coherent mem pa:%pad/%pad, size:%d\n", + &smem_dev->smem_base, &dma_addr, size_align); + + smem_dev->smem_size = size_align; + smem_dev->smem_pages = pages; + smem_dev->smem_dma_base = dma_addr; + + return 0; + +fail_map: + sg_free_table(sgt); +fail_table_alloc: + while (n_pages--) + __free_page(pages[n_pages]); + kfree(pages); + + return -ENOMEM; +} + +/* DMA memory related helper functions */ +static void mtk_cam_memdev_release(struct device *dev) +{ + vb2_dma_contig_clear_max_seg_size(dev); +} + +static struct device *mtk_cam_alloc_smem_dev(struct device *dev, + const char *name) +{ + struct device *child; + int ret; + + child = devm_kzalloc(dev, sizeof(*child), GFP_KERNEL); + if (!child) + return NULL; + + child->parent = dev; + child->iommu_group = dev->iommu_group; + child->release = mtk_cam_memdev_release; + dev_set_name(child, name); + set_dma_ops(child, get_dma_ops(dev)); + child->dma_mask = dev->dma_mask; + ret = dma_set_coherent_mask(child, DMA_BIT_MASK(32)); + if (ret) + return NULL; + + vb2_dma_contig_set_max_seg_size(child, DMA_BIT_MASK(32)); + + if (device_register(child)) { + device_del(child); + return NULL; + } + + return child; +} + +static int mtk_cam_composer_dma_init(struct mtk_isp_p1_ctx *isp_ctx) +{ + struct isp_p1_device *p1_dev = p1_ctx_to_dev(isp_ctx); + struct device *dev = &p1_dev->pdev->dev; + u32 size; + dma_addr_t addr; + + isp_ctx->scp_mem_pa = scp_get_reserve_mem_phys(SCP_ISP_MEM_ID); + size = PAGE_ALIGN(scp_get_reserve_mem_size(SCP_ISP_MEM_ID)); + if (!isp_ctx->scp_mem_pa || !size) + return -EPROBE_DEFER; + + dev_info(dev, "scp addr:%pad size:0x%x\n", &isp_ctx->scp_mem_pa, size); + + /* get iova address */ + addr = dma_map_page_attrs(dev, phys_to_page(isp_ctx->scp_mem_pa), 0, + size, DMA_BIDIRECTIONAL, + DMA_ATTR_SKIP_CPU_SYNC); + if (dma_mapping_error(dev, addr)) { + isp_ctx->scp_mem_pa = 0; + dev_err(dev, "Failed to map scp iova\n"); + return -ENOMEM; + } + + isp_ctx->scp_mem_iova = addr; + + return 0; +} + +int mtk_cam_reserved_memory_init(struct isp_p1_device *p1_dev) +{ + struct device *dev = &p1_dev->pdev->dev; + struct mtk_cam_smem_dev *smem_dev; + int ret; + + ret = mtk_cam_composer_dma_init(&p1_dev->isp_ctx); + if (ret) + return ret; + + /* Allocate context */ + smem_dev = devm_kzalloc(dev, sizeof(*smem_dev), GFP_KERNEL); + if (!smem_dev) + return -ENOMEM; + + smem_dev->dev = mtk_cam_alloc_smem_dev(dev, "cam-smem"); + if (!smem_dev->dev) { + dev_err(dev, "failed to alloc smem device\n"); + return -ENODEV; + } + dev_set_drvdata(smem_dev->dev, smem_dev); + p1_dev->cam_dev.smem_dev = smem_dev->dev; + + ret = mtk_cam_reserved_drm_sg_init(smem_dev); + if (ret) + return ret; + + mtk_cam_smem_setup_dma_ops(smem_dev->dev, &smem_dma_ops); + + return 0; +} diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h new file mode 100644 index 000000000000..981d47178e99 --- /dev/null +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-smem.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 MediaTek Inc. + */ + +#ifndef __MTK_CAM_ISP_SMEM_H +#define __MTK_CAM_ISP_SMEM_H + +#include + +#include "mtk_cam.h" + +int mtk_cam_reserved_memory_init(struct isp_p1_device *p1_dev); +dma_addr_t mtk_cam_smem_iova_to_scp_addr(struct device *smem_dev, + dma_addr_t iova); + +#endif +