From patchwork Wed Sep 30 15:48:33 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nguyen Minh Tien X-Patchwork-Id: 3544 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from tor.lore.kernel.org (tor.lore.kernel.org [172.105.105.114]) by mxe881.netcup.net (Postfix) with ESMTPS id 6F7A01C0D99 for ; Wed, 30 Sep 2026 18:07:22 +0200 (CEST) Authentication-Results: mxe881; dkim=temperror header.d=embeddedlinux.blog; spf=pass (sender IP is 172.105.105.114) smtp.mailfrom=linux-sunxi+bounces-26431-noreply=patchwork.local@lists.linux.dev smtp.helo=tor.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.105.105.114 as permitted sender) client-ip=172.105.105.114; envelope-from=linux-sunxi+bounces-26431-noreply=patchwork.local@lists.linux.dev; helo=tor.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by tor.lore.kernel.org (Postfix) with ESMTP id 5F81084470 for ; Wed, 30 Sep 2026 15:50:09 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4252F509F06; Wed, 30 Sep 2026 15:48:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=embeddedlinux.blog header.i=@embeddedlinux.blog header.b="LF/b6qFU" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-pj2-f11.google.com (mail-pj2-f11.google.com [74.125.227.139]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CC763380FEB for ; Wed, 30 Sep 2026 15:48:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.227.139 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790783334; cv=none; b=ldW9KtGyGNVPYFMAGfzw2ryo7HcC2xNGGpfgnFfH3JtjNpWODvTpcocXmf0v5u3+2Rtj/T7CF+TUknNsRQBNvvyTxMtL+xLopKp4Jm2Z7yWx6/Lc1yT2ZHGJ23478DFbXwrBmjR8pKtX5gg4AiJoFdXA75f2PZHuNoSrFuRgb2s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790783334; c=relaxed/simple; bh=8hfxOJPdIcGIvY3nw8uhTRifk/anp+VB7FLMnAUGf+w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=KgzJ/YXepswlpANZizxhJDj0Rlu9eFo5ygm8r3NM74qGzwgh624i1dWleEB/Iwv2xgskyDwBjj7X788M9f8EfNTBTL5EGk5S1O5t2Cl9tzCAlyU6+2dZ6u5NY0lVRPbRqGOu1/5FMzIezG565Y4esut+XAlS0fq+wmH1yx2eUH0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=embeddedlinux.blog; spf=pass smtp.mailfrom=embeddedlinux.blog; dkim=pass (2048-bit key) header.d=embeddedlinux.blog header.i=@embeddedlinux.blog header.b=LF/b6qFU; arc=none smtp.client-ip=74.125.227.139 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=embeddedlinux.blog Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=embeddedlinux.blog Received: by mail-pj2-f11.google.com with SMTP id 98e67ed59e1d1-3a0aa5e2b20so1745111a91.1 for ; Wed, 30 Sep 2026 08:48:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embeddedlinux.blog; s=google; t=1790783327; x=1791388127; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=RylIL5PtKoKSEB6fKFx6teI8foZIXlgeBAHnwrC12eE=; b=LF/b6qFUxRtT0voyae23IyAgbROgcqf2YKnHE4VU8g55+rtnwjGt1bTmx5EqerlfUa VVuLaTlTGQiWYL2TdiW7CGK+RzVTGhmUUdRBfnZQlxjF/1GXoKlMtg7NTbhL965DqXxN Vrf9oCUCQsz25uiXkYs3Wo4UvDzHDxJfxIG9IPDCS07mP7HXJHmnVrHfOM5oOvWXCkTM r5Bv9qd1Awn6dJHbumNCAhS6F8dpUrHsXKHPGymeea4sapKtrRk/fyd1AkPMSsFX7mJE g5CVga5Qtgwf2bHQ+w2spBHxPpZobLra9OWwIsqwwG/pvDI9zw8KgNYjJ7kNmFXkIkzg yF0g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790783327; x=1791388127; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=RylIL5PtKoKSEB6fKFx6teI8foZIXlgeBAHnwrC12eE=; b=HCFEWAMx6/yvq1CEpBpCNvAMIVHH2TetuJ0zMdPK1zUJCYMEAiz5dTnwqL3qQRQs69 ImSupbvy1X6Qlgay0OfFx/TFfktCuVCFTwVNErC2294uJfRD15biJO4Q89U+YqOvHtOE cRP6EUhwnLb24pdVNQ57emzXsF323tube0zCknrzQHEdpFVf7HpDPcVUHRIoM2jyHqLL EPxrEyjYU2gExi6YyHCTaQRe3XsCN9gD+0FmN5baYORe60cZXDi6E2+S13kZ9EolMt81 1AKTMi8sTzD3SycTSFrznXyOLZrt093+E8CodsuFBLJZ9gECXdsCQiwpoCos2aUbwpAJ GwOg== X-Forwarded-Encrypted: i=1; AKwUvBwoMRVeAS510mP2x2WkgkUtdPjeBM5kMF5JaCeN5/dF4mM9bfJC88oi8qXQMPdEvM9MorIebo744rBW1A==@lists.linux.dev X-Gm-Message-State: AFq9FYIJJgYDYSnmUjp/8NtjW4OoCqIatwgcNzRSd74G1PpV+1wR+G+e sF24MRCAD95u8lpfPj0hH9Fp39Nam8CnTmrIp4cqv6OC1Ut7xSyV1Tw6535/8dtZDSu/ X-Gm-Gg: AYBFou0kIiPWih+WUpqL8bu1rSLDoEvfSP+ojpHw0XICfCltEOsalaqeXP1lRxVoMb1 lI4IbXq8/H8c5M+NzJF7YfhNDhVplq+lrhHxwjfIqf7yLnl2XPnCsV1mxIAWg6N05JiCH3FQ+gl xaNV+z+cbJhJJUqYvzuxbTg7r/vSrw0pSnUFilzmHqt7qiLi2wWRbmKFJ8GReIw9/crq9OfjdjP 4DosMgZOfWISJ8ChMvI5wdeqac9gdjxBoLWke9C48RQEk4w0Kq3CJ6SUzaOQA81FcFLSds0Qkjo 5K5ByG5Z5Ka8uce2w9I0nPKnobj16Hjy5rMLKXUNvAaF0tcckS6Z6fwDAIm8NCXJoqakjCLlKrN XSX/7TOMok2vF4fMaoa/anuJQrwMYMor8LsawuUkpepvxLEhXAQbHLvzzdH1rBSJsKJVs/zwBJd V46aIX2icxlcYTWmc6uDkojfYiml1ytK5OuzfYYxi13iBMVndTL564mGoOf7zGAdHcBPfBxVJvL JvztTPl X-Received: by 2002:a17:90b:2888:b0:3a2:b465:fb8a with SMTP id 98e67ed59e1d1-3a4d1ddc911mr1860992a91.67.1790783327152; Wed, 30 Sep 2026 08:48:47 -0700 (PDT) Received: from ltu.. ([171.245.4.151]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3a4ee4d0af2sm308136a91.2.2026.09.30.08.48.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Sep 2026 08:48:46 -0700 (PDT) From: Nguyen Minh Tien To: Mauro Carvalho Chehab , Hans Verkuil Cc: Nguyen Minh Tien , Sakari Ailus , Laurent Pinchart , Paul Kocialkowski , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Philipp Zabel , linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/3] dt-bindings: media: Add Allwinner D1 CSIC Date: Wed, 30 Sep 2026 22:48:33 +0700 Message-Id: <20260930154835.402684-2-tien.nguyenminh@embeddedlinux.blog> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260930154835.402684-1-tien.nguyenminh@embeddedlinux.blog> References: <20260930154835.402684-1-tien.nguyenminh@embeddedlinux.blog> Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [5.84 / 15.00]; RBL_SENDERSCORE(2.00)[172.105.105.114:from]; SUSPICIOUS_RECIPS(1.50)[]; MID_CONTAINS_FROM(1.00)[]; DMARC_POLICY_SOFTFAIL(1.00)[embeddedlinux.blog : SPF not aligned (relaxed), No valid DKIM,none]; R_MISSING_CHARSET(0.50)[]; MAILLIST(-0.15)[generic]; MIME_GOOD(-0.10)[text/plain]; BAD_REP_POLICIES(0.10)[]; HAS_LIST_UNSUB(-0.01)[]; TAGGED_RCPT(0.00)[dt]; RCVD_VIA_SMTP_AUTH(0.00)[]; DBL_BLOCKED_OPENRESOLVER(0.00)[devicetree.org:url,tor.lore.kernel.org:rdns,tor.lore.kernel.org:helo,embeddedlinux.blog:email]; RCPT_COUNT_TWELVE(0.00)[23]; FORGED_SENDER_MAILLIST(0.00)[]; DBL_PROHIBIT(0.00)[0.88.128.64:email]; FREEMAIL_CC(0.00)[embeddedlinux.blog,linux.intel.com,ideasonboard.com,sys-base.io,kernel.org,gmail.com,sholland.org,dabbelt.com,eecs.berkeley.edu,ghiti.fr,pengutronix.de,vger.kernel.org,lists.infradead.org,lists.linux.dev]; PRECEDENCE_BULK(0.00)[]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; RCVD_COUNT_FIVE(0.00)[6]; ASN(0.00)[asn:63949, ipnet:172.105.96.0/20, country:SG]; R_SPF_ALLOW(0.00)[+ip4:172.105.105.114]; URIBL_MULTI_FAIL(0.00)[devicetree.org:server fail,0.88.128.64:server fail,embeddedlinux.blog:server fail]; TO_DN_SOME(0.00)[]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[100.90.174.1:received,74.125.227.139:received]; FROM_HAS_DN(0.00)[]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; MIME_TRACE(0.00)[0:+]; TAGGED_FROM(0.00)[bounces-26431-noreply=patchwork.local]; RECEIVED_SPAMHAUS_PBL(0.00)[171.245.4.151:received]; FROM_NEQ_ENVFROM(0.00)[tien.nguyenminh@embeddedlinux.blog,linux-sunxi@lists.linux.dev]; RCVD_TLS_LAST(0.00)[]; RBL_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[172.105.105.114:from] X-Rspamd-Queue-Id: 6F7A01C0D99 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Add a binding for the camera interface (CSIC) of the Allwinner D1, D1s and T113. It is not the A31 CSI: its parser and DMA engines are separate blocks. Only the 8-bit parallel input is pinned out on these SoCs. Signed-off-by: Nguyen Minh Tien --- .../media/allwinner,sun20i-d1-csi.yaml | 111 ++++++++++++++++++ MAINTAINERS | 7 ++ 2 files changed, 118 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/allwinner,sun20i-d1-csi.yaml diff --git a/Documentation/devicetree/bindings/media/allwinner,sun20i-d1-csi.yaml b/Documentation/devicetree/bindings/media/allwinner,sun20i-d1-csi.yaml new file mode 100644 index 0000000000..2bfe2cc9af --- /dev/null +++ b/Documentation/devicetree/bindings/media/allwinner,sun20i-d1-csi.yaml @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/allwinner,sun20i-d1-csi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner D1 CMOS Sensor Interface Controller (CSIC) + +maintainers: + - Nguyen Minh Tien + +description: + The CSIC found in the Allwinner D1, D1s and T113 receives video from a + parallel camera interface and writes it to memory. It contains a parser for + the camera bus and two DMA engines. + +properties: + compatible: + const: allwinner,sun20i-d1-csi + + reg: + maxItems: 1 + + interrupts: + items: + - description: DMA engine 0 + - description: DMA engine 1 + - description: Parser 0 + - description: Top-level block (CSI_TOP_PKT) + + clocks: + items: + - description: Bus clock + - description: Module clock + - description: DRAM clock + + clock-names: + items: + - const: bus + - const: mod + - const: ram + + resets: + maxItems: 1 + + iommus: + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + description: Parallel input port, connect to a parallel sensor + unevaluatedProperties: false + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + bus-width: + const: 8 + + pclk-sample: true + hsync-active: true + vsync-active: true + + required: + - bus-width + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + - port + +additionalProperties: false + +examples: + - | + #include + #include + #include + + csi@5800000 { + compatible = "allwinner,sun20i-d1-csi"; + reg = <0x05800000 0x400000>; + interrupts = <111 IRQ_TYPE_LEVEL_HIGH>, + <112 IRQ_TYPE_LEVEL_HIGH>, + <116 IRQ_TYPE_LEVEL_HIGH>, + <122 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_CSI>, + <&ccu CLK_CSI_TOP>, + <&ccu CLK_MBUS_CSI>; + clock-names = "bus", "mod", "ram"; + resets = <&ccu RST_BUS_CSI>; + + port { + endpoint { + remote-endpoint = <&ov5640_ep>; + bus-width = <8>; + hsync-active = <1>; /* Active high */ + vsync-active = <0>; /* Active low */ + pclk-sample = <1>; /* Rising */ + }; + }; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index f5eff489a8..f4c45c4678 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -905,6 +905,13 @@ L: linux-crypto@vger.kernel.org S: Maintained F: drivers/crypto/allwinner/ +ALLWINNER D1 CSIC DRIVER +M: Nguyen Minh Tien +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media.git +F: Documentation/devicetree/bindings/media/allwinner,sun20i-d1-csi.yaml + ALLWINNER DMIC DRIVERS M: Ban Tao L: linux-sound@vger.kernel.org From patchwork Wed Sep 30 15:48:34 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nguyen Minh Tien X-Patchwork-Id: 3545 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from tor.lore.kernel.org (tor.lore.kernel.org [172.105.105.114]) by mxe881.netcup.net (Postfix) with ESMTPS id 931571C1375 for ; Wed, 30 Sep 2026 18:19:30 +0200 (CEST) Authentication-Results: mxe881; dkim=temperror header.d=embeddedlinux.blog; spf=pass (sender IP is 172.105.105.114) smtp.mailfrom=linux-sunxi+bounces-26432-noreply=patchwork.local@lists.linux.dev smtp.helo=tor.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.105.105.114 as permitted sender) client-ip=172.105.105.114; envelope-from=linux-sunxi+bounces-26432-noreply=patchwork.local@lists.linux.dev; helo=tor.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by tor.lore.kernel.org (Postfix) with ESMTP id E3AE243342 for ; Wed, 30 Sep 2026 15:50:27 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 2A9F55111AE; Wed, 30 Sep 2026 15:49:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=embeddedlinux.blog header.i=@embeddedlinux.blog header.b="SVU76+R/" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-pj2-f7.google.com (mail-pj2-f7.google.com [74.125.227.135]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3FE1237DE8D for ; Wed, 30 Sep 2026 15:48:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.227.135 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790783343; cv=none; b=A+a73Oeq4KAp/o1FusttnSamsXQegw+hYzxGV+1zI71iPyBLiaLkcw4kqxuXxCMp/FW9W9auzla8Fbqsm2QKDLpvI+l/2ODdR4BPgb2krOGGk4ZYqVKg8RBa2aP5XFXc1Kj2XmdICoMs30sCcvrGWWuEKZshUhjd6vNUpQgHS9E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790783343; c=relaxed/simple; bh=Zc2t57CHENcb7vvZQWICKCr0awAomnry6bya68Pp9lY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=k7M375/6ksL51d+ss9z7r6ffDTIatQqGviVKxDVZCj+cAuYpgDLoYELtxFdLZlOEKe2PMe9zem2Yv5PNWGobVeQFl/A8EioSF0RUNaOEtpUlfgnMIGJVpAji/3YPHvEALn00eueTK1e0SEdFlRRBxzL92HK1g5scGbOoN2+bSik= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=embeddedlinux.blog; spf=pass smtp.mailfrom=embeddedlinux.blog; dkim=pass (2048-bit key) header.d=embeddedlinux.blog header.i=@embeddedlinux.blog header.b=SVU76+R/; arc=none smtp.client-ip=74.125.227.135 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=embeddedlinux.blog Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=embeddedlinux.blog Received: by mail-pj2-f7.google.com with SMTP id 98e67ed59e1d1-39e501442e2so2745824a91.0 for ; Wed, 30 Sep 2026 08:48:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embeddedlinux.blog; s=google; t=1790783333; x=1791388133; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=u1GYpRAgHPKUydj50ps4ltNEgthFJ1M5MKcbjxsHTyM=; b=SVU76+R/p0+kFTilAnWDQ9O8qfuMzalUYEvXqhoDMxvaMHJLiM6d9PitIhHuLwdaI2 vfmI3vKgiixSJ5w7P3MpHIGHqf/U1iPdLwGBwda5gayt/hy2WywdwfU4IYf1akvOu2VB toQ6ARMb9x2AMEerAAs8rTtRK9kNwcG1Itjfp4nOj3gSwUG4GqhHRhqmWMHNCT0GXZ/1 A62H/fX+igA6q0fd7PQs3fafzSD5aQGnq44nzILFXCBrmMq0pjSgquJ4ArV7xGXEy/IC 23lmLyHxRDcDdOwsGe9UUJZe2PqaNwjwFEDpsznhjtlDOAAtyCWnxU6jma2QqIH3IwHK t/LA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790783333; x=1791388133; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=u1GYpRAgHPKUydj50ps4ltNEgthFJ1M5MKcbjxsHTyM=; b=hV4WMFnE7BDF1aME1s7qIBBWSOlDxO6MjyRHX6jYiBLY+62A/fSe6IBYCc7M+D7pwQ PAposJn1lf//BxP2/n43XGZyFdDKcD298DU6viMKrJL8GRyt/0ErFQTvtIlOVwVs2knQ GRM2bqPyeOVYiAj2PnaIOLORU+wx5rt3X7/vZKlzeLHXbjqoSZvNl68048Zw8kIvZXBH paHZ3oTL1g4zr36HhYSEKAvOes5podna7RaiaOjD8y2kwlkFpkfQFHU2jbPlbctrmSub iO0e8QNOE1mXxsRzUW5ZXeHZBTyH8kQMaODJsapHwJETZeLiy8QQ5HgBV4xXA+Pr24Xg kNPg== X-Forwarded-Encrypted: i=1; AKwUvBxefhw7zRxDYxVPN2YECfyOfMlhJXspcnBDmH02H7tRCHO4Tm83hlSxhYVb1hIkTVM2RP3xhv4WoVdpXA==@lists.linux.dev X-Gm-Message-State: AFq9FYLGCRCU+ySWHuenkUFZeEnROV71lGQHFh7p1OpNdziEzvCT9FRq XYw5AVAX81AglcasXDtSYyxETqXYy2kcTDlBn6+/FjccsrGNXQL8PasPiwSWC9O0w8w8 X-Gm-Gg: AYBFou29X811MhtXGBhLlkoJ7hX9FbjcWcVBuEiwPrRbIrgR/PeLbpKZk4E/j/NFXjM +ApQ0ZMCDXlo1AAaG1fcmTmkpptvoawfOKas1LVkwhhS845YWfOiHSXlspkL2sLPx3v7bcB4v7B 0i/rVRxK//egyq5fuJkAYc48pDiLFjLQrQGpK87U7Zex4YwtSpiuOJ1ureRuIKpZL88mZU3d6H2 vnTD08j2Umfq4rXpWnYbqcWi8Bg8Tx5haPFP1+Nt9l0PadIsdUonw5ZaI9/PJVjsL4mwj7S3VpA aVaLLraMvx51a5qzvKdfOY8g303qCc6it1cXXz4lxsd+Wkko+q2HxFaoUkjZUSvM1O7549GdF2q TVdoQ3lcPeq6Pb4tS6Xdo50wI9HaJuBwRa21zuKrLZedBMPUpgnTV6Zvao8Kqr06sYJimigObDL XwDdFDO6nPvpLVc8BJoVNMnAIr5hBAi3T2gbrxENC5iw2VT+7Pu+mAwQgS0sgtWhiyryWJoH4cf cYT/TIv X-Received: by 2002:a17:90b:3c45:b0:3a4:a356:a716 with SMTP id 98e67ed59e1d1-3a4d178f2ffmr1683613a91.29.1790783332427; Wed, 30 Sep 2026 08:48:52 -0700 (PDT) Received: from ltu.. ([171.245.4.151]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3a4ee4d0af2sm308136a91.2.2026.09.30.08.48.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Sep 2026 08:48:51 -0700 (PDT) From: Nguyen Minh Tien To: Mauro Carvalho Chehab , Hans Verkuil Cc: Nguyen Minh Tien , Sakari Ailus , Laurent Pinchart , Paul Kocialkowski , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Philipp Zabel , linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/3] media: sunxi: Add support for the D1 CSIC Date: Wed, 30 Sep 2026 22:48:34 +0700 Message-Id: <20260930154835.402684-3-tien.nguyenminh@embeddedlinux.blog> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260930154835.402684-1-tien.nguyenminh@embeddedlinux.blog> References: <20260930154835.402684-1-tien.nguyenminh@embeddedlinux.blog> Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Add a driver for the parallel input of the D1 CSIC and its first DMA engine. The parser is a bridge subdev and the DMA engine a capture video device, set up through the media controller API. The DMA engine converts YUV 4:2:2 to NV12, NV16, planar or grey output, and stores other formats as received. Two things the manual doesn't say were found on the hardware: the buffer addresses are in 32-bit words, and frame done also comes for a frame cut short, so a frame only counts when the line counter saw its last line. Signed-off-by: Nguyen Minh Tien --- MAINTAINERS | 1 + drivers/media/platform/sunxi/Kconfig | 1 + drivers/media/platform/sunxi/Makefile | 1 + .../media/platform/sunxi/sun20i-csi/Kconfig | 17 + .../media/platform/sunxi/sun20i-csi/Makefile | 4 + .../platform/sunxi/sun20i-csi/sun20i_csi.c | 1357 +++++++++++++++++ 6 files changed, 1381 insertions(+) create mode 100644 drivers/media/platform/sunxi/sun20i-csi/Kconfig create mode 100644 drivers/media/platform/sunxi/sun20i-csi/Makefile create mode 100644 drivers/media/platform/sunxi/sun20i-csi/sun20i_csi.c diff --git a/MAINTAINERS b/MAINTAINERS index f4c45c4678..b67850e98e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -911,6 +911,7 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git F: Documentation/devicetree/bindings/media/allwinner,sun20i-d1-csi.yaml +F: drivers/media/platform/sunxi/sun20i-csi/ ALLWINNER DMIC DRIVERS M: Ban Tao diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig index 2dd15083a1..710a38b939 100644 --- a/drivers/media/platform/sunxi/Kconfig +++ b/drivers/media/platform/sunxi/Kconfig @@ -8,3 +8,4 @@ source "drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig" source "drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig" source "drivers/media/platform/sunxi/sun8i-di/Kconfig" source "drivers/media/platform/sunxi/sun8i-rotate/Kconfig" +source "drivers/media/platform/sunxi/sun20i-csi/Kconfig" diff --git a/drivers/media/platform/sunxi/Makefile b/drivers/media/platform/sunxi/Makefile index 9aa01cb018..bb117f7048 100644 --- a/drivers/media/platform/sunxi/Makefile +++ b/drivers/media/platform/sunxi/Makefile @@ -6,3 +6,4 @@ obj-y += sun6i-mipi-csi2/ obj-y += sun8i-a83t-mipi-csi2/ obj-y += sun8i-di/ obj-y += sun8i-rotate/ +obj-y += sun20i-csi/ diff --git a/drivers/media/platform/sunxi/sun20i-csi/Kconfig b/drivers/media/platform/sunxi/sun20i-csi/Kconfig new file mode 100644 index 0000000000..83ec00bf6a --- /dev/null +++ b/drivers/media/platform/sunxi/sun20i-csi/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_SUN20I_CSI + tristate "Allwinner D1 CMOS Sensor Interface Controller (CSIC) Driver" + depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV + depends on ARCH_SUNXI || COMPILE_TEST + depends on PM && COMMON_CLK && RESET_CONTROLLER && HAS_DMA + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + help + Support for the parallel camera interface of the Allwinner D1 + CMOS Sensor Interface Controller (CSIC), also found on the D1s + and the T113. + + To compile this driver as a module, choose M here: the module + will be called sun20i-csi. diff --git a/drivers/media/platform/sunxi/sun20i-csi/Makefile b/drivers/media/platform/sunxi/sun20i-csi/Makefile new file mode 100644 index 0000000000..d799c60763 --- /dev/null +++ b/drivers/media/platform/sunxi/sun20i-csi/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +sun20i-csi-y += sun20i_csi.o + +obj-$(CONFIG_VIDEO_SUN20I_CSI) += sun20i-csi.o diff --git a/drivers/media/platform/sunxi/sun20i-csi/sun20i_csi.c b/drivers/media/platform/sunxi/sun20i-csi/sun20i_csi.c new file mode 100644 index 0000000000..eed29bc613 --- /dev/null +++ b/drivers/media/platform/sunxi/sun20i-csi/sun20i_csi.c @@ -0,0 +1,1357 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Allwinner D1/T113 CMOS Sensor Interface Controller (CSIC) driver + * + * Copyright (C) 2026 Nguyen Minh Tien + * + * The CSIC is split into a parser, which receives the parallel bus, and a + * DMA engine, which writes frames to memory and can convert YUV 4:2:2 input + * to semi-planar, planar or luma-only output. The parser is exposed as a + * bridge subdev and the DMA engine as the capture video device. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register and field names as in the T113-S3 user manual v1.3, section 6.1. */ +#define SUN20I_CSI_TOP_EN_REG 0x0800 +#define SUN20I_CSI_TOP_EN_CSIC_TOP_EN BIT(0) + +#define SUN20I_CSI_PRS_EN_REG 0x1000 +#define SUN20I_CSI_PRS_EN_NCSIC_EN BIT(16) +#define SUN20I_CSI_PRS_EN_PCLK_EN BIT(15) +#define SUN20I_CSI_PRS_EN_PRS_EN BIT(0) + +/* + * The parser takes HREF and VREF, which are the inverse of HSYNC and VSYNC, + * so an active low sync signal needs the positive (set) HREF_POL or VREF_POL, + * as explained for the A10 CSI in commit 1948dcf0f928 ("media: sun4i-csi: + * Fix [HV]sync polarity handling"). With CLK_POL set, the data changes on the + * falling edge of the pixel clock and is sampled on the rising edge. + */ +#define SUN20I_CSI_PRS_NCSIC_IF_CFG_REG 0x1004 +#define SUN20I_CSI_PRS_NCSIC_IF_CFG_VREF_POL BIT(18) +#define SUN20I_CSI_PRS_NCSIC_IF_CFG_HREF_POL BIT(17) +#define SUN20I_CSI_PRS_NCSIC_IF_CFG_CLK_POL BIT(16) +#define SUN20I_CSI_PRS_NCSIC_IF_CFG_INPUT_SEQ GENMASK(7, 6) +#define SUN20I_CSI_INPUT_SEQ_YUYV 0 +#define SUN20I_CSI_INPUT_SEQ_UYVY 2 + +#define SUN20I_CSI_PRS_CAP_REG 0x100c +#define SUN20I_CSI_PRS_CAP_CH0_VCAP_ON BIT(1) + +#define SUN20I_CSI_PRS_CH0_INFMT_REG 0x1024 +#define SUN20I_CSI_INPUT_FMT_RAW 0 +#define SUN20I_CSI_INPUT_FMT_YUV422 3 +#define SUN20I_CSI_PRS_CH0_OUTPUT_HSIZE_REG 0x1028 +#define SUN20I_CSI_PRS_CH0_OUTPUT_VSIZE_REG 0x102c + +/* HOR_LEN and VER_LEN of the parser and DMA size registers, from bit 16. */ +#define SUN20I_CSI_LEN GENMASK(28, 16) + +#define SUN20I_CSI_DMA_EN_REG 0x9000 +/* VFLIP_BUF_ADDR, BUF_LENGTH and FLIP_SIZE set by software, as on reset. */ +#define SUN20I_CSI_DMA_EN_SW_CFG_MODE GENMASK(30, 28) +#define SUN20I_CSI_DMA_EN_DMA_EN BIT(4) +#define SUN20I_CSI_DMA_EN_BK_TOP_EN BIT(0) +#define SUN20I_CSI_DMA_CFG_REG 0x9004 +#define SUN20I_CSI_DMA_CFG_OUTPUT_FMT GENMASK(19, 16) +#define SUN20I_CSI_DMA_HSIZE_REG 0x9010 +#define SUN20I_CSI_DMA_VSIZE_REG 0x9014 +/* The buffer addresses are in 32-bit words, which the manual doesn't say. */ +#define SUN20I_CSI_DMA_F0_BUFA_REG 0x9020 +#define SUN20I_CSI_DMA_F1_BUFA_REG 0x9028 +#define SUN20I_CSI_DMA_F2_BUFA_REG 0x9030 +#define SUN20I_CSI_DMA_BUF_LEN_REG 0x9038 +#define SUN20I_CSI_DMA_BUF_LEN_BUF_LEN_C GENMASK(29, 16) +#define SUN20I_CSI_DMA_BUF_LEN_BUF_LEN GENMASK(13, 0) +#define SUN20I_CSI_DMA_INT_EN_REG 0x9050 +#define SUN20I_CSI_DMA_INT_STA_REG 0x9054 +#define SUN20I_CSI_DMA_INT_VS BIT(7) +#define SUN20I_CSI_DMA_INT_HB_OF BIT(6) +#define SUN20I_CSI_DMA_INT_LC BIT(5) +#define SUN20I_CSI_DMA_INT_FIFO2_OF BIT(4) +#define SUN20I_CSI_DMA_INT_FIFO1_OF BIT(3) +#define SUN20I_CSI_DMA_INT_FIFO0_OF BIT(2) +#define SUN20I_CSI_DMA_INT_FD BIT(1) +#define SUN20I_CSI_DMA_INT_OF (SUN20I_CSI_DMA_INT_HB_OF | \ + SUN20I_CSI_DMA_INT_FIFO2_OF | \ + SUN20I_CSI_DMA_INT_FIFO1_OF | \ + SUN20I_CSI_DMA_INT_FIFO0_OF) +#define SUN20I_CSI_DMA_LINE_CNT_REG 0x9058 +#define SUN20I_CSI_DMA_LINE_CNT_LINE_CNT_NUM GENMASK(12, 0) + +/* OUTPUT_FMT values: "frame" modes, for raw or YUV 4:2:2 input. */ +#define SUN20I_CSI_OUT_RAW_8 0x8 +#define SUN20I_CSI_OUT_PLANAR_420 0x2 +#define SUN20I_CSI_OUT_PLANAR_422 0x3 +#define SUN20I_CSI_OUT_UV_420 0x6 +#define SUN20I_CSI_OUT_UV_422 0x7 +#define SUN20I_CSI_OUT_VU_420 0xa +#define SUN20I_CSI_OUT_VU_422 0xb +#define SUN20I_CSI_OUT_Y400 0xf + +/* Rate of the module clock, csi_top_clk. */ +#define SUN20I_CSI_MOD_RATE 300000000 +#define SUN20I_CSI_MIN_SIZE 32 +/* HOR_LEN is 13 bits and counts bytes for raw input, 2 bytes per pixel. */ +#define SUN20I_CSI_MAX_WIDTH 4088 +#define SUN20I_CSI_MAX_HEIGHT 4096 + +enum { + SUN20I_CSI_PAD_SINK, + SUN20I_CSI_PAD_SOURCE, + SUN20I_CSI_NUM_PADS, +}; + +struct sun20i_csi_format { + u32 fourcc; + /* The only bus code, or 0 for any YUV 4:2:2 code (converted). */ + u32 code; + u8 output; + u8 bpp; /* Bytes per pixel in the first plane */ + u8 planes; /* 1: packed or luma, 2: semi-planar, 3: planar */ + u8 vsub; /* Vertical chroma subsampling */ + bool swap_uv; /* Planar: V before U */ + bool raw; /* Stored as received, no conversion */ + bool bayer; +}; + +static const u32 sun20i_csi_yuv_codes[] = { + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_UYVY8_2X8, +}; + +static const u32 sun20i_csi_raw_codes[] = { + MEDIA_BUS_FMT_RGB565_2X8_LE, + MEDIA_BUS_FMT_RGB565_2X8_BE, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, +}; + +#define SUN20I_CSI_YUV(_fourcc, _output, _planes, _vsub, _swap_uv) \ + { .fourcc = _fourcc, .output = _output, .bpp = 1, \ + .planes = _planes, .vsub = _vsub, .swap_uv = _swap_uv } +#define SUN20I_CSI_RAW(_fourcc, _code, _bpp, _bayer) \ + { .fourcc = _fourcc, .code = _code, .output = SUN20I_CSI_OUT_RAW_8, \ + .bpp = _bpp, .planes = 1, .vsub = 1, .raw = true, .bayer = _bayer } + +static const struct sun20i_csi_format sun20i_csi_formats[] = { + SUN20I_CSI_YUV(V4L2_PIX_FMT_NV12, SUN20I_CSI_OUT_UV_420, 2, 2, false), + SUN20I_CSI_YUV(V4L2_PIX_FMT_NV21, SUN20I_CSI_OUT_VU_420, 2, 2, false), + SUN20I_CSI_YUV(V4L2_PIX_FMT_NV16, SUN20I_CSI_OUT_UV_422, 2, 1, false), + SUN20I_CSI_YUV(V4L2_PIX_FMT_NV61, SUN20I_CSI_OUT_VU_422, 2, 1, false), + SUN20I_CSI_YUV(V4L2_PIX_FMT_YUV420, SUN20I_CSI_OUT_PLANAR_420, + 3, 2, false), + SUN20I_CSI_YUV(V4L2_PIX_FMT_YVU420, SUN20I_CSI_OUT_PLANAR_420, + 3, 2, true), + SUN20I_CSI_YUV(V4L2_PIX_FMT_YUV422P, SUN20I_CSI_OUT_PLANAR_422, + 3, 1, false), + SUN20I_CSI_YUV(V4L2_PIX_FMT_GREY, SUN20I_CSI_OUT_Y400, 1, 1, false), + SUN20I_CSI_RAW(V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8, 2, false), + SUN20I_CSI_RAW(V4L2_PIX_FMT_UYVY, MEDIA_BUS_FMT_UYVY8_2X8, 2, false), + SUN20I_CSI_RAW(V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_RGB565_2X8_LE, + 2, false), + SUN20I_CSI_RAW(V4L2_PIX_FMT_RGB565X, MEDIA_BUS_FMT_RGB565_2X8_BE, + 2, false), + SUN20I_CSI_RAW(V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8, 1, true), + SUN20I_CSI_RAW(V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8, 1, true), + SUN20I_CSI_RAW(V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8, 1, true), + SUN20I_CSI_RAW(V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8, 1, true), +}; + +struct sun20i_csi_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct sun20i_csi { + struct device *dev; + void __iomem *regs; + struct clk *bus_clk; + struct clk *mod_clk; + struct clk *ram_clk; + struct reset_control *reset; + int irq; + + struct media_device mdev; + struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; + struct v4l2_mbus_config_parallel bus; + + /* The parser, as a bridge subdev */ + struct v4l2_subdev subdev; + struct media_pad pads[SUN20I_CSI_NUM_PADS]; + struct v4l2_subdev *source; + u16 source_pad; + + /* The DMA engine, as the capture video device */ + struct video_device vdev; + struct media_pad vdev_pad; + struct vb2_queue queue; + struct mutex lock; /* The video device and the queue */ + struct v4l2_pix_format format; + const struct sun20i_csi_format *fmt; + + spinlock_t irqlock; /* The fields below */ + struct list_head queued; + /* The buffer being written, and the one in the address registers */ + struct sun20i_csi_buffer *active; + struct sun20i_csi_buffer *next; + bool active_complete; + bool active_error; + u32 sequence; + u32 errors; +}; + +static inline void sun20i_csi_write(struct sun20i_csi *csi, u32 reg, u32 val) +{ + writel(val, csi->regs + reg); +} + +static inline u32 sun20i_csi_read(struct sun20i_csi *csi, u32 reg) +{ + return readl(csi->regs + reg); +} + +static bool sun20i_csi_code_supported(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sun20i_csi_yuv_codes); i++) + if (sun20i_csi_yuv_codes[i] == code) + return true; + for (i = 0; i < ARRAY_SIZE(sun20i_csi_raw_codes); i++) + if (sun20i_csi_raw_codes[i] == code) + return true; + return false; +} + +static bool sun20i_csi_format_takes(const struct sun20i_csi_format *fmt, + u32 code) +{ + unsigned int i; + + if (fmt->code) + return fmt->code == code; + + for (i = 0; i < ARRAY_SIZE(sun20i_csi_yuv_codes); i++) + if (sun20i_csi_yuv_codes[i] == code) + return true; + return false; +} + +static const struct sun20i_csi_format *sun20i_csi_find_format(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sun20i_csi_formats); i++) + if (sun20i_csi_formats[i].fourcc == fourcc) + return &sun20i_csi_formats[i]; + return NULL; +} + +/* Number of units in a line for the parser and the DMA engine. */ +static u32 sun20i_csi_line_units(const struct sun20i_csi_format *fmt, u32 width) +{ + /* Raw input is counted in bytes, YUV input in pixels. */ + return fmt->raw ? width * fmt->bpp : width; +} + +/* ------------------------------------------------------------------------- + * Buffers and interrupt + */ + +static void sun20i_csi_set_buffer(struct sun20i_csi *csi, + struct sun20i_csi_buffer *buf) +{ + dma_addr_t addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + u32 width = csi->format.width, height = csi->format.height; + const struct sun20i_csi_format *fmt = csi->fmt; + dma_addr_t cb, cr; + + sun20i_csi_write(csi, SUN20I_CSI_DMA_F0_BUFA_REG, addr >> 2); + if (fmt->planes == 1) + return; + + cb = addr + width * height; + if (fmt->planes == 3) { + cr = cb + width / 2 * (height / fmt->vsub); + if (fmt->swap_uv) + swap(cb, cr); + sun20i_csi_write(csi, SUN20I_CSI_DMA_F2_BUFA_REG, cr >> 2); + } + sun20i_csi_write(csi, SUN20I_CSI_DMA_F1_BUFA_REG, cb >> 2); +} + +/* + * Frame done. It also comes for a frame cut short, such as the first one + * after the sensor starts, so only a frame whose last line was written (the + * line counter interrupt) counts. + */ +static void sun20i_csi_frame_done(struct sun20i_csi *csi) +{ + struct sun20i_csi_buffer *buf = csi->active; + + csi->active = NULL; + + /* No buffer was queued: the next frame is going into this one. */ + if (buf == csi->next) + buf = NULL; + + if (!csi->active_complete) { + if (buf) + list_add(&buf->list, &csi->queued); + return; + } + + if (buf) { + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + buf->vb.sequence = csi->sequence; + buf->vb.field = V4L2_FIELD_NONE; + vb2_buffer_done(&buf->vb.vb2_buf, csi->active_error ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + } + csi->sequence++; +} + +/* + * Frame start. The manual: "at this time load the buffer address for the + * coming frame. So after this irq come, changing the buffer address could + * only effect next frame". The address registers take the next buffer here. + */ +static void sun20i_csi_frame_start(struct sun20i_csi *csi) +{ + /* A frame that started but was never done: reuse its buffer. */ + if (csi->active && csi->active != csi->next) + list_add(&csi->active->list, &csi->queued); + + csi->active = csi->next; + csi->active_complete = false; + csi->active_error = false; + + csi->next = list_first_entry_or_null(&csi->queued, + struct sun20i_csi_buffer, list); + if (csi->next) { + list_del(&csi->next->list); + sun20i_csi_set_buffer(csi, csi->next); + } else { + /* Nothing queued: the next frame goes into the same buffer. */ + csi->next = csi->active; + } +} + +static irqreturn_t sun20i_csi_irq(int irq, void *data) +{ + struct sun20i_csi *csi = data; + u32 status; + + status = sun20i_csi_read(csi, SUN20I_CSI_DMA_INT_STA_REG); + sun20i_csi_write(csi, SUN20I_CSI_DMA_INT_STA_REG, status); + status &= SUN20I_CSI_DMA_INT_VS | SUN20I_CSI_DMA_INT_LC | + SUN20I_CSI_DMA_INT_FD | SUN20I_CSI_DMA_INT_OF; + if (!status) + return IRQ_NONE; + + spin_lock(&csi->irqlock); + + /* The line counter is set to the last line of the frame. */ + if (status & SUN20I_CSI_DMA_INT_LC) + csi->active_complete = true; + + if (status & SUN20I_CSI_DMA_INT_OF) { + csi->active_error = true; + csi->errors++; + } + + /* A frame ends before the next one starts. */ + if (status & SUN20I_CSI_DMA_INT_FD) + sun20i_csi_frame_done(csi); + if (status & SUN20I_CSI_DMA_INT_VS) + sun20i_csi_frame_start(csi); + + spin_unlock(&csi->irqlock); + + return IRQ_HANDLED; +} + +/* ------------------------------------------------------------------------- + * Bridge subdev (the parser) + */ + +static const struct v4l2_mbus_framefmt sun20i_csi_default_fmt = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, +}; + +static struct sun20i_csi *sd_to_csi(struct v4l2_subdev *sd) +{ + return container_of(sd, struct sun20i_csi, subdev); +} + +static int sun20i_csi_bridge_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + *v4l2_subdev_state_get_format(state, SUN20I_CSI_PAD_SINK) = + sun20i_csi_default_fmt; + *v4l2_subdev_state_get_format(state, SUN20I_CSI_PAD_SOURCE) = + sun20i_csi_default_fmt; + + return 0; +} + +static int +sun20i_csi_bridge_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + unsigned int nyuv = ARRAY_SIZE(sun20i_csi_yuv_codes); + + /* The source pad passes on the sink pad format. */ + if (code->pad == SUN20I_CSI_PAD_SOURCE) { + const struct v4l2_mbus_framefmt *fmt; + + if (code->index) + return -EINVAL; + fmt = v4l2_subdev_state_get_format(state, SUN20I_CSI_PAD_SINK); + code->code = fmt->code; + return 0; + } + + if (code->index < nyuv) + code->code = sun20i_csi_yuv_codes[code->index]; + else if (code->index - nyuv < ARRAY_SIZE(sun20i_csi_raw_codes)) + code->code = sun20i_csi_raw_codes[code->index - nyuv]; + else + return -EINVAL; + + return 0; +} + +static int +sun20i_csi_bridge_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index) + return -EINVAL; + + if (fse->pad == SUN20I_CSI_PAD_SOURCE) { + const struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, SUN20I_CSI_PAD_SINK); + if (fse->code != fmt->code) + return -EINVAL; + fse->min_width = fmt->width; + fse->max_width = fmt->width; + fse->min_height = fmt->height; + fse->max_height = fmt->height; + return 0; + } + + if (!sun20i_csi_code_supported(fse->code)) + return -EINVAL; + + fse->min_width = SUN20I_CSI_MIN_SIZE; + fse->max_width = SUN20I_CSI_MAX_WIDTH; + fse->min_height = SUN20I_CSI_MIN_SIZE; + fse->max_height = SUN20I_CSI_MAX_HEIGHT; + + return 0; +} + +static int sun20i_csi_bridge_set_fmt(struct v4l2_subdev *sd, + const struct v4l2_subdev_client_info *ci, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt = &format->format; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && + v4l2_subdev_is_streaming(sd)) + return -EBUSY; + + /* The source pad format follows the sink pad format. */ + if (format->pad == SUN20I_CSI_PAD_SOURCE) + return v4l2_subdev_get_fmt(sd, state, format); + + if (!sun20i_csi_code_supported(fmt->code)) + fmt->code = sun20i_csi_default_fmt.code; + fmt->width = clamp_t(u32, ALIGN(fmt->width, 8), SUN20I_CSI_MIN_SIZE, + SUN20I_CSI_MAX_WIDTH); + fmt->height = clamp_t(u32, ALIGN(fmt->height, 2), SUN20I_CSI_MIN_SIZE, + SUN20I_CSI_MAX_HEIGHT); + fmt->field = V4L2_FIELD_NONE; + if (fmt->colorspace == V4L2_COLORSPACE_DEFAULT || + fmt->colorspace > V4L2_COLORSPACE_DCI_P3) + fmt->colorspace = V4L2_COLORSPACE_SRGB; + if (fmt->ycbcr_enc > V4L2_YCBCR_ENC_SMPTE240M) + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + if (fmt->quantization > V4L2_QUANTIZATION_LIM_RANGE) + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + if (fmt->xfer_func > V4L2_XFER_FUNC_SMPTE2084) + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + *v4l2_subdev_state_get_format(state, SUN20I_CSI_PAD_SINK) = *fmt; + *v4l2_subdev_state_get_format(state, SUN20I_CSI_PAD_SOURCE) = *fmt; + + return 0; +} + +static int sun20i_csi_bridge_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + const struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_state_get_format(state, SUN20I_CSI_PAD_SINK); + struct sun20i_csi *csi = sd_to_csi(sd); + u32 cfg = 0, seq, infmt, width; + int ret; + + if (csi->bus.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + cfg |= SUN20I_CSI_PRS_NCSIC_IF_CFG_VREF_POL; + if (csi->bus.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + cfg |= SUN20I_CSI_PRS_NCSIC_IF_CFG_HREF_POL; + if (csi->bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + cfg |= SUN20I_CSI_PRS_NCSIC_IF_CFG_CLK_POL; + if (fmt->code == MEDIA_BUS_FMT_UYVY8_2X8) + seq = SUN20I_CSI_INPUT_SEQ_UYVY; + else + seq = SUN20I_CSI_INPUT_SEQ_YUYV; + cfg |= FIELD_PREP(SUN20I_CSI_PRS_NCSIC_IF_CFG_INPUT_SEQ, seq); + + /* The capture format is fixed while streaming. */ + infmt = csi->fmt->raw ? SUN20I_CSI_INPUT_FMT_RAW : + SUN20I_CSI_INPUT_FMT_YUV422; + width = sun20i_csi_line_units(csi->fmt, fmt->width); + + sun20i_csi_write(csi, SUN20I_CSI_PRS_EN_REG, + SUN20I_CSI_PRS_EN_NCSIC_EN | + SUN20I_CSI_PRS_EN_PCLK_EN | SUN20I_CSI_PRS_EN_PRS_EN); + sun20i_csi_write(csi, SUN20I_CSI_PRS_NCSIC_IF_CFG_REG, cfg); + sun20i_csi_write(csi, SUN20I_CSI_PRS_CH0_INFMT_REG, infmt); + sun20i_csi_write(csi, SUN20I_CSI_PRS_CH0_OUTPUT_HSIZE_REG, + FIELD_PREP(SUN20I_CSI_LEN, width)); + sun20i_csi_write(csi, SUN20I_CSI_PRS_CH0_OUTPUT_VSIZE_REG, + FIELD_PREP(SUN20I_CSI_LEN, fmt->height)); + sun20i_csi_write(csi, SUN20I_CSI_PRS_CAP_REG, + SUN20I_CSI_PRS_CAP_CH0_VCAP_ON); + + ret = v4l2_subdev_enable_streams(csi->source, csi->source_pad, + BIT_ULL(0)); + if (ret) { + sun20i_csi_write(csi, SUN20I_CSI_PRS_CAP_REG, 0); + sun20i_csi_write(csi, SUN20I_CSI_PRS_EN_REG, 0); + } + + return ret; +} + +static int sun20i_csi_bridge_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct sun20i_csi *csi = sd_to_csi(sd); + int ret; + + ret = v4l2_subdev_disable_streams(csi->source, csi->source_pad, + BIT_ULL(0)); + + sun20i_csi_write(csi, SUN20I_CSI_PRS_CAP_REG, 0); + sun20i_csi_write(csi, SUN20I_CSI_PRS_EN_REG, 0); + + return ret; +} + +static const struct v4l2_subdev_pad_ops sun20i_csi_bridge_pad_ops = { + .enum_mbus_code = sun20i_csi_bridge_enum_mbus_code, + .enum_frame_size = sun20i_csi_bridge_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = sun20i_csi_bridge_set_fmt, + .enable_streams = sun20i_csi_bridge_enable_streams, + .disable_streams = sun20i_csi_bridge_disable_streams, +}; + +static const struct v4l2_subdev_ops sun20i_csi_bridge_ops = { + .pad = &sun20i_csi_bridge_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops sun20i_csi_bridge_internal_ops = { + .init_state = sun20i_csi_bridge_init_state, +}; + +static const struct media_entity_operations sun20i_csi_bridge_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* ------------------------------------------------------------------------- + * Capture video device (the DMA engine) + */ + +static void sun20i_csi_fill_format(struct v4l2_pix_format *pix) +{ + const struct sun20i_csi_format *fmt; + u32 size, width, height; + + fmt = sun20i_csi_find_format(pix->pixelformat); + if (!fmt) + fmt = &sun20i_csi_formats[0]; + + width = clamp_t(u32, ALIGN(pix->width, 8), SUN20I_CSI_MIN_SIZE, + SUN20I_CSI_MAX_WIDTH); + height = clamp_t(u32, ALIGN(pix->height, 2), SUN20I_CSI_MIN_SIZE, + SUN20I_CSI_MAX_HEIGHT); + + pix->pixelformat = fmt->fourcc; + pix->width = width; + pix->height = height; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = width * fmt->bpp; + + size = pix->bytesperline * height; + /* Semi-planar: full-width UV lines. Planar: two half-width planes. */ + if (fmt->planes > 1) + size += width * (height / fmt->vsub); + pix->sizeimage = size; + + pix->colorspace = fmt->bayer ? V4L2_COLORSPACE_RAW : + V4L2_COLORSPACE_SRGB; + pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix->quantization = V4L2_QUANTIZATION_DEFAULT; + pix->xfer_func = V4L2_XFER_FUNC_DEFAULT; + pix->flags = 0; +} + +static int sun20i_csi_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, "sun20i-csi", sizeof(cap->driver)); + strscpy(cap->card, "Allwinner D1 CSIC", sizeof(cap->card)); + + return 0; +} + +static int sun20i_csi_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + unsigned int i, index = 0; + + for (i = 0; i < ARRAY_SIZE(sun20i_csi_formats); i++) { + const struct sun20i_csi_format *fmt = &sun20i_csi_formats[i]; + + if (f->mbus_code && !sun20i_csi_format_takes(fmt, f->mbus_code)) + continue; + if (index++ == f->index) { + f->pixelformat = fmt->fourcc; + return 0; + } + } + + return -EINVAL; +} + +static int sun20i_csi_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->index || !sun20i_csi_find_format(fsize->pixel_format)) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = SUN20I_CSI_MIN_SIZE; + fsize->stepwise.max_width = SUN20I_CSI_MAX_WIDTH; + fsize->stepwise.step_width = 8; + fsize->stepwise.min_height = SUN20I_CSI_MIN_SIZE; + fsize->stepwise.max_height = SUN20I_CSI_MAX_HEIGHT; + fsize->stepwise.step_height = 2; + + return 0; +} + +static int sun20i_csi_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct sun20i_csi *csi = video_drvdata(file); + + f->fmt.pix = csi->format; + + return 0; +} + +static int sun20i_csi_try_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + sun20i_csi_fill_format(&f->fmt.pix); + + return 0; +} + +static int sun20i_csi_s_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct sun20i_csi *csi = video_drvdata(file); + + if (vb2_is_busy(&csi->queue)) + return -EBUSY; + + sun20i_csi_fill_format(&f->fmt.pix); + csi->format = f->fmt.pix; + csi->fmt = sun20i_csi_find_format(f->fmt.pix.pixelformat); + + return 0; +} + +static const struct v4l2_ioctl_ops sun20i_csi_ioctl_ops = { + .vidioc_querycap = sun20i_csi_querycap, + .vidioc_enum_fmt_vid_cap = sun20i_csi_enum_fmt, + .vidioc_enum_framesizes = sun20i_csi_enum_framesizes, + .vidioc_g_fmt_vid_cap = sun20i_csi_g_fmt, + .vidioc_try_fmt_vid_cap = sun20i_csi_try_fmt, + .vidioc_s_fmt_vid_cap = sun20i_csi_s_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_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct v4l2_file_operations sun20i_csi_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +static int sun20i_csi_queue_setup(struct vb2_queue *queue, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct sun20i_csi *csi = vb2_get_drv_priv(queue); + + if (*num_planes) + return sizes[0] < csi->format.sizeimage ? -EINVAL : 0; + + *num_planes = 1; + sizes[0] = csi->format.sizeimage; + + return 0; +} + +static int sun20i_csi_buf_prepare(struct vb2_buffer *vb) +{ + struct sun20i_csi *csi = vb2_get_drv_priv(vb->vb2_queue); + + if (vb2_plane_size(vb, 0) < csi->format.sizeimage) + return -EINVAL; + + vb2_set_plane_payload(vb, 0, csi->format.sizeimage); + + return 0; +} + +static void sun20i_csi_buf_queue(struct vb2_buffer *vb) +{ + struct sun20i_csi *csi = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct sun20i_csi_buffer *buf = + container_of(vbuf, struct sun20i_csi_buffer, vb); + unsigned long flags; + + spin_lock_irqsave(&csi->irqlock, flags); + list_add_tail(&buf->list, &csi->queued); + spin_unlock_irqrestore(&csi->irqlock, flags); +} + +static void sun20i_csi_return_buffers(struct sun20i_csi *csi, + enum vb2_buffer_state state) +{ + struct sun20i_csi_buffer *buf, *tmp; + unsigned long flags; + + spin_lock_irqsave(&csi->irqlock, flags); + + if (csi->active) + vb2_buffer_done(&csi->active->vb.vb2_buf, state); + if (csi->next && csi->next != csi->active) + vb2_buffer_done(&csi->next->vb.vb2_buf, state); + csi->active = NULL; + csi->next = NULL; + + list_for_each_entry_safe(buf, tmp, &csi->queued, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + spin_unlock_irqrestore(&csi->irqlock, flags); +} + +static void sun20i_csi_dma_start(struct sun20i_csi *csi) +{ + const struct sun20i_csi_format *fmt = csi->fmt; + u32 width = csi->format.width, height = csi->format.height; + u32 line_c = fmt->planes == 3 ? width / 2 : width; + unsigned long flags; + + /* + * This relies on two reset values: the CSIC's own clock gates are + * bypassed (CCU_CLK_MODE_REG), and DMA0 takes input 0, "ISP0 CH0", + * which carries the parser's channel 0 (CSIC_DMA0_INPUT_SEL_REG). + */ + sun20i_csi_write(csi, SUN20I_CSI_TOP_EN_REG, + SUN20I_CSI_TOP_EN_CSIC_TOP_EN); + + sun20i_csi_write(csi, SUN20I_CSI_DMA_CFG_REG, + FIELD_PREP(SUN20I_CSI_DMA_CFG_OUTPUT_FMT, + fmt->output)); + sun20i_csi_write(csi, SUN20I_CSI_DMA_HSIZE_REG, + FIELD_PREP(SUN20I_CSI_LEN, + sun20i_csi_line_units(fmt, width))); + sun20i_csi_write(csi, SUN20I_CSI_DMA_VSIZE_REG, + FIELD_PREP(SUN20I_CSI_LEN, height)); + sun20i_csi_write(csi, SUN20I_CSI_DMA_BUF_LEN_REG, + FIELD_PREP(SUN20I_CSI_DMA_BUF_LEN_BUF_LEN_C, line_c) | + FIELD_PREP(SUN20I_CSI_DMA_BUF_LEN_BUF_LEN, + csi->format.bytesperline)); + + spin_lock_irqsave(&csi->irqlock, flags); + + csi->sequence = 0; + csi->errors = 0; + csi->active = NULL; + /* vb2 makes sure that min_queued_buffers are queued. */ + csi->next = list_first_entry(&csi->queued, struct sun20i_csi_buffer, + list); + list_del(&csi->next->list); + sun20i_csi_set_buffer(csi, csi->next); + + spin_unlock_irqrestore(&csi->irqlock, flags); + + /* The line index counts from 0: this fires on the last line. */ + sun20i_csi_write(csi, SUN20I_CSI_DMA_LINE_CNT_REG, + FIELD_PREP(SUN20I_CSI_DMA_LINE_CNT_LINE_CNT_NUM, + height - 1)); + sun20i_csi_write(csi, SUN20I_CSI_DMA_INT_STA_REG, ~0); + sun20i_csi_write(csi, SUN20I_CSI_DMA_INT_EN_REG, + SUN20I_CSI_DMA_INT_VS | SUN20I_CSI_DMA_INT_LC | + SUN20I_CSI_DMA_INT_FD | SUN20I_CSI_DMA_INT_OF); + sun20i_csi_write(csi, SUN20I_CSI_DMA_EN_REG, + SUN20I_CSI_DMA_EN_SW_CFG_MODE | + SUN20I_CSI_DMA_EN_DMA_EN | + SUN20I_CSI_DMA_EN_BK_TOP_EN); +} + +static void sun20i_csi_dma_stop(struct sun20i_csi *csi) +{ + sun20i_csi_write(csi, SUN20I_CSI_DMA_INT_EN_REG, 0); + sun20i_csi_write(csi, SUN20I_CSI_DMA_EN_REG, + SUN20I_CSI_DMA_EN_SW_CFG_MODE); + sun20i_csi_write(csi, SUN20I_CSI_DMA_INT_STA_REG, ~0); + sun20i_csi_write(csi, SUN20I_CSI_TOP_EN_REG, 0); + synchronize_irq(csi->irq); +} + +static int sun20i_csi_start_streaming(struct vb2_queue *queue, + unsigned int count) +{ + struct sun20i_csi *csi = vb2_get_drv_priv(queue); + int ret; + + ret = pm_runtime_resume_and_get(csi->dev); + if (ret) + goto err_return_buffers; + + ret = video_device_pipeline_alloc_start(&csi->vdev); + if (ret) + goto err_pm_put; + + sun20i_csi_dma_start(csi); + + ret = v4l2_subdev_enable_streams(&csi->subdev, SUN20I_CSI_PAD_SOURCE, + BIT_ULL(0)); + if (ret) + goto err_dma_stop; + + return 0; + +err_dma_stop: + sun20i_csi_dma_stop(csi); + video_device_pipeline_stop(&csi->vdev); +err_pm_put: + pm_runtime_put(csi->dev); +err_return_buffers: + sun20i_csi_return_buffers(csi, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void sun20i_csi_stop_streaming(struct vb2_queue *queue) +{ + struct sun20i_csi *csi = vb2_get_drv_priv(queue); + + v4l2_subdev_disable_streams(&csi->subdev, SUN20I_CSI_PAD_SOURCE, + BIT_ULL(0)); + sun20i_csi_dma_stop(csi); + video_device_pipeline_stop(&csi->vdev); + sun20i_csi_return_buffers(csi, VB2_BUF_STATE_ERROR); + pm_runtime_put(csi->dev); + + if (csi->errors) + dev_dbg(csi->dev, "%u FIFO overflows in %u frames\n", + csi->errors, csi->sequence); +} + +static const struct vb2_ops sun20i_csi_vb2_ops = { + .queue_setup = sun20i_csi_queue_setup, + .buf_prepare = sun20i_csi_buf_prepare, + .buf_queue = sun20i_csi_buf_queue, + .start_streaming = sun20i_csi_start_streaming, + .stop_streaming = sun20i_csi_stop_streaming, +}; + +static int sun20i_csi_capture_link_validate(struct media_link *link) +{ + struct video_device *vdev = + media_entity_to_video_device(link->sink->entity); + struct sun20i_csi *csi = video_get_drvdata(vdev); + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; + int ret = 0; + + state = v4l2_subdev_lock_and_get_active_state(&csi->subdev); + fmt = v4l2_subdev_state_get_format(state, SUN20I_CSI_PAD_SOURCE); + + if (fmt->width != csi->format.width || + fmt->height != csi->format.height) { + dev_dbg(csi->dev, "size mismatch: %ux%u on the bridge, %ux%u on the video device\n", + fmt->width, fmt->height, + csi->format.width, csi->format.height); + ret = -EPIPE; + } else if (!sun20i_csi_format_takes(csi->fmt, fmt->code)) { + dev_dbg(csi->dev, "%p4cc can't be captured from bus code 0x%04x\n", + &csi->format.pixelformat, fmt->code); + ret = -EPIPE; + } + + v4l2_subdev_unlock_state(state); + + return ret; +} + +static const struct media_entity_operations sun20i_csi_capture_entity_ops = { + .link_validate = sun20i_csi_capture_link_validate, +}; + +/* ------------------------------------------------------------------------- + * Probe + */ + +static int sun20i_csi_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asc) +{ + struct sun20i_csi *csi = container_of(notifier, struct sun20i_csi, + notifier); + struct media_pad *pad = &csi->pads[SUN20I_CSI_PAD_SINK]; + int ret; + + ret = v4l2_create_fwnode_links_to_pad(sd, pad, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) + return ret; + + pad = media_pad_remote_pad_first(pad); + if (!pad) + return -ENOLINK; + + csi->source = sd; + csi->source_pad = pad->index; + + return 0; +} + +static int sun20i_csi_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct sun20i_csi *csi = container_of(notifier, struct sun20i_csi, + notifier); + int ret; + + ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev); + if (ret) + return ret; + + return media_device_register(&csi->mdev); +} + +static const struct v4l2_async_notifier_operations sun20i_csi_notify_ops = { + .bound = sun20i_csi_notify_bound, + .complete = sun20i_csi_notify_complete, +}; + +static int sun20i_csi_parse_dt(struct sun20i_csi *csi) +{ + struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL }; + struct v4l2_async_connection *asc; + struct fwnode_handle *ep; + int ret; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0, 0); + if (!ep) + return dev_err_probe(csi->dev, -ENODEV, "no input endpoint\n"); + + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) { + dev_err_probe(csi->dev, ret, "invalid input endpoint\n"); + goto out; + } + if (vep.bus.parallel.bus_width != 8 || vep.bus.parallel.data_shift) { + ret = dev_err_probe(csi->dev, -EINVAL, + "only an 8-bit bus is supported\n"); + goto out; + } + csi->bus = vep.bus.parallel; + + asc = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep, + struct v4l2_async_connection); + if (IS_ERR(asc)) + ret = PTR_ERR(asc); + +out: + fwnode_handle_put(ep); + return ret; +} + +static int sun20i_csi_register_bridge(struct sun20i_csi *csi) +{ + struct v4l2_subdev *sd = &csi->subdev; + int ret; + + v4l2_subdev_init(sd, &sun20i_csi_bridge_ops); + sd->internal_ops = &sun20i_csi_bridge_internal_ops; + sd->owner = THIS_MODULE; + sd->dev = csi->dev; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + strscpy(sd->name, "sun20i-csi-bridge", sizeof(sd->name)); + + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->entity.ops = &sun20i_csi_bridge_entity_ops; + csi->pads[SUN20I_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; + csi->pads[SUN20I_CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE | + MEDIA_PAD_FL_MUST_CONNECT; + ret = media_entity_pads_init(&sd->entity, SUN20I_CSI_NUM_PADS, + csi->pads); + if (ret) + return ret; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_entity; + + ret = v4l2_device_register_subdev(&csi->v4l2_dev, sd); + if (ret) + goto err_subdev; + + return 0; + +err_subdev: + v4l2_subdev_cleanup(sd); +err_entity: + media_entity_cleanup(&sd->entity); + return ret; +} + +static void sun20i_csi_unregister_bridge(struct sun20i_csi *csi) +{ + v4l2_device_unregister_subdev(&csi->subdev); + v4l2_subdev_cleanup(&csi->subdev); + media_entity_cleanup(&csi->subdev.entity); +} + +static int sun20i_csi_register_capture(struct sun20i_csi *csi) +{ + struct video_device *vdev = &csi->vdev; + struct vb2_queue *queue = &csi->queue; + int ret; + + csi->format.pixelformat = sun20i_csi_formats[0].fourcc; + csi->format.width = sun20i_csi_default_fmt.width; + csi->format.height = sun20i_csi_default_fmt.height; + sun20i_csi_fill_format(&csi->format); + csi->fmt = &sun20i_csi_formats[0]; + + queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + queue->io_modes = VB2_MMAP | VB2_DMABUF; + queue->dev = csi->dev; + queue->drv_priv = csi; + queue->buf_struct_size = sizeof(struct sun20i_csi_buffer); + queue->ops = &sun20i_csi_vb2_ops; + queue->mem_ops = &vb2_dma_contig_memops; + queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + queue->min_queued_buffers = 2; + queue->lock = &csi->lock; + ret = vb2_queue_init(queue); + if (ret) + return ret; + + csi->vdev_pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; + vdev->entity.ops = &sun20i_csi_capture_entity_ops; + ret = media_entity_pads_init(&vdev->entity, 1, &csi->vdev_pad); + if (ret) + return ret; + + strscpy(vdev->name, "sun20i-csi-capture", sizeof(vdev->name)); + vdev->v4l2_dev = &csi->v4l2_dev; + vdev->fops = &sun20i_csi_fops; + vdev->ioctl_ops = &sun20i_csi_ioctl_ops; + vdev->release = video_device_release_empty; + vdev->lock = &csi->lock; + vdev->queue = queue; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_IO_MC; + video_set_drvdata(vdev, csi); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) + goto err_entity; + + ret = media_create_pad_link(&csi->subdev.entity, SUN20I_CSI_PAD_SOURCE, + &vdev->entity, 0, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) + goto err_video; + + return 0; + +err_video: + vb2_video_unregister_device(vdev); +err_entity: + media_entity_cleanup(&vdev->entity); + return ret; +} + +static int sun20i_csi_runtime_suspend(struct device *dev) +{ + struct sun20i_csi *csi = dev_get_drvdata(dev); + + clk_disable_unprepare(csi->mod_clk); + clk_disable_unprepare(csi->ram_clk); + clk_disable_unprepare(csi->bus_clk); + reset_control_assert(csi->reset); + + return 0; +} + +static int sun20i_csi_runtime_resume(struct device *dev) +{ + struct sun20i_csi *csi = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(csi->reset); + if (ret) + return ret; + + ret = clk_prepare_enable(csi->bus_clk); + if (ret) + goto err_reset; + + ret = clk_prepare_enable(csi->ram_clk); + if (ret) + goto err_bus_clk; + + ret = clk_prepare_enable(csi->mod_clk); + if (ret) + goto err_ram_clk; + + return 0; + +err_ram_clk: + clk_disable_unprepare(csi->ram_clk); +err_bus_clk: + clk_disable_unprepare(csi->bus_clk); +err_reset: + reset_control_assert(csi->reset); + return ret; +} + +static int sun20i_csi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sun20i_csi *csi; + int ret; + + csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL); + if (!csi) + return -ENOMEM; + + csi->dev = dev; + platform_set_drvdata(pdev, csi); + spin_lock_init(&csi->irqlock); + INIT_LIST_HEAD(&csi->queued); + + ret = devm_mutex_init(dev, &csi->lock); + if (ret) + return ret; + + csi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(csi->regs)) + return PTR_ERR(csi->regs); + + csi->bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(csi->bus_clk)) + return dev_err_probe(dev, PTR_ERR(csi->bus_clk), + "failed to get the bus clock\n"); + + csi->mod_clk = devm_clk_get(dev, "mod"); + if (IS_ERR(csi->mod_clk)) + return dev_err_probe(dev, PTR_ERR(csi->mod_clk), + "failed to get the module clock\n"); + + csi->ram_clk = devm_clk_get(dev, "ram"); + if (IS_ERR(csi->ram_clk)) + return dev_err_probe(dev, PTR_ERR(csi->ram_clk), + "failed to get the DRAM clock\n"); + + csi->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(csi->reset)) + return dev_err_probe(dev, PTR_ERR(csi->reset), + "failed to get the reset\n"); + + ret = devm_clk_rate_exclusive_get(dev, csi->mod_clk); + if (ret) + return dev_err_probe(dev, ret, + "failed to lock the module clock rate\n"); + + ret = clk_set_rate(csi->mod_clk, SUN20I_CSI_MOD_RATE); + if (ret) + return dev_err_probe(dev, ret, + "failed to set the module clock rate\n"); + + csi->irq = platform_get_irq(pdev, 0); + if (csi->irq < 0) + return csi->irq; + + ret = devm_request_irq(dev, csi->irq, sun20i_csi_irq, 0, + dev_name(dev), csi); + if (ret) + return dev_err_probe(dev, ret, "failed to request the IRQ\n"); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + csi->mdev.dev = dev; + strscpy(csi->mdev.model, "Allwinner D1 CSIC", sizeof(csi->mdev.model)); + media_device_init(&csi->mdev); + csi->v4l2_dev.mdev = &csi->mdev; + + ret = v4l2_device_register(dev, &csi->v4l2_dev); + if (ret) + goto err_media; + + /* Check the DT before any device node is created. */ + v4l2_async_nf_init(&csi->notifier, &csi->v4l2_dev); + csi->notifier.ops = &sun20i_csi_notify_ops; + + ret = sun20i_csi_parse_dt(csi); + if (ret) + goto err_notifier; + + ret = sun20i_csi_register_bridge(csi); + if (ret) + goto err_notifier; + + ret = sun20i_csi_register_capture(csi); + if (ret) + goto err_bridge; + + ret = v4l2_async_nf_register(&csi->notifier); + if (ret) + goto err_capture; + + return 0; + +err_capture: + vb2_video_unregister_device(&csi->vdev); + media_entity_cleanup(&csi->vdev.entity); +err_bridge: + sun20i_csi_unregister_bridge(csi); +err_notifier: + v4l2_async_nf_cleanup(&csi->notifier); + v4l2_device_unregister(&csi->v4l2_dev); +err_media: + media_device_cleanup(&csi->mdev); + return ret; +} + +static void sun20i_csi_remove(struct platform_device *pdev) +{ + struct sun20i_csi *csi = platform_get_drvdata(pdev); + + /* This stops streaming, which needs the whole pipeline. */ + vb2_video_unregister_device(&csi->vdev); + media_entity_cleanup(&csi->vdev.entity); + v4l2_async_nf_unregister(&csi->notifier); + v4l2_async_nf_cleanup(&csi->notifier); + sun20i_csi_unregister_bridge(csi); + media_device_unregister(&csi->mdev); + v4l2_device_unregister(&csi->v4l2_dev); + media_device_cleanup(&csi->mdev); +} + +/* Runtime PM only: a system sleep must not reset the block while streaming. */ +static const struct dev_pm_ops sun20i_csi_pm_ops = { + RUNTIME_PM_OPS(sun20i_csi_runtime_suspend, sun20i_csi_runtime_resume, + NULL) +}; + +static const struct of_device_id sun20i_csi_of_match[] = { + { .compatible = "allwinner,sun20i-d1-csi" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sun20i_csi_of_match); + +static struct platform_driver sun20i_csi_driver = { + .probe = sun20i_csi_probe, + .remove = sun20i_csi_remove, + .driver = { + .name = "sun20i-csi", + .of_match_table = sun20i_csi_of_match, + .pm = pm_ptr(&sun20i_csi_pm_ops), + }, +}; +module_platform_driver(sun20i_csi_driver); + +MODULE_DESCRIPTION("Allwinner D1/T113 CSIC driver"); +MODULE_AUTHOR("Nguyen Minh Tien "); +MODULE_LICENSE("GPL"); From patchwork Wed Sep 30 15:48:35 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nguyen Minh Tien X-Patchwork-Id: 3546 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sin.lore.kernel.org (sin.lore.kernel.org [104.64.211.4]) by mxe881.netcup.net (Postfix) with ESMTPS id 8EF821C02D1 for ; Wed, 30 Sep 2026 18:56:00 +0200 (CEST) Authentication-Results: mxe881; dkim=temperror header.d=embeddedlinux.blog; spf=pass (sender IP is 104.64.211.4) smtp.mailfrom=linux-sunxi+bounces-26433-noreply=patchwork.local@lists.linux.dev smtp.helo=sin.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 104.64.211.4 as permitted sender) client-ip=104.64.211.4; envelope-from=linux-sunxi+bounces-26433-noreply=patchwork.local@lists.linux.dev; helo=sin.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sin.lore.kernel.org (Postfix) with ESMTP id BD706147992 for ; Wed, 30 Sep 2026 15:50:30 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id C221B50276F; Wed, 30 Sep 2026 15:49:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=embeddedlinux.blog header.i=@embeddedlinux.blog header.b="rVllSq4k" X-Original-To: linux-sunxi@lists.linux.dev Received: from mail-pj2-f37.google.com (mail-pj2-f37.google.com [74.125.227.165]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E5BFB51118B for ; Wed, 30 Sep 2026 15:49:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.227.165 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790783344; cv=none; b=CCiC46Tbqeiq0K7/tVBrWUe8XNDTXNbLik2azZwflQ2fTDu3WsPe/Bj/JRVLu0G8Qblpq5klgd2ZAEkCDEOVegfaCdFpc6ZpN9VcDMK4Etz/6hR4XVcU70V7HeXG+J29UjpWTqXIUa0MxSSIKyAweozJL6sG5L/PkERMgt95oAQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790783344; c=relaxed/simple; bh=QCdSHI2SuhoD7qC/wY8SB6ThI20K2KtGW46geTtY5QQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=OnkCEB9/hacwQpSFgByA/cYmgwd6gK1oJPaTnkUV1U5ojw6wcAV6DvXWmK9q30WyAwgBZQ/LtIQcz6mGNXHMMZ8o7sgrZaZn+elndne2ST3bsEXA7azfCybBwLq0SUpXJZvZwfWH7Tmv8ZUdOYF2cAwAgvJvZTxyLiiciayeDt4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=embeddedlinux.blog; spf=pass smtp.mailfrom=embeddedlinux.blog; dkim=pass (2048-bit key) header.d=embeddedlinux.blog header.i=@embeddedlinux.blog header.b=rVllSq4k; arc=none smtp.client-ip=74.125.227.165 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=embeddedlinux.blog Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=embeddedlinux.blog Received: by mail-pj2-f37.google.com with SMTP id 98e67ed59e1d1-3a4eda6744bso250586a91.1 for ; Wed, 30 Sep 2026 08:49:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embeddedlinux.blog; s=google; t=1790783337; x=1791388137; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Y41Q9WFkOR6sDX/bibH4WaOpuLUrtGQ8SMNMm1pPqNc=; b=rVllSq4kiSSo39vrzxv0N14mX1+BbdhCTP4JoQ1expH5KFkx1kMwfvKtMXXpXsWbV1 6E41tY83Cl8A+MNFntYP75apNe11PjnFy0byejGuFx+iGXr7fj3Sy8OUWSH94FsTZ87Q 0pAOExDVG8m8A6wDZo3MAO4PbrBGTjaqM9sP/iSHdb1rvThmFeu6fanmwR6BJws2Lass R2sjJExyEZ/WC3NvYb0S407ijVMt0v97AWD5zHbUf/2w6kKZekVFWK4dmFJaN+IR4wSR wTvzbS8CliuLf5WRnLD3JWWZYKmMs71cNq6gcOK+XEhh903Jq1n+Pq2q74Dtnvr1kpzm xAig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1790783337; x=1791388137; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Y41Q9WFkOR6sDX/bibH4WaOpuLUrtGQ8SMNMm1pPqNc=; b=K4g/HD9FtHf1hYpWbqnEo5tMCOpe0uu6OkjQj7VNKODH+3hiyazKthvk8h042OY0rN RAuTlSIAMoihn1hmQ8HqRKzg08uUTR4HJA/+m6r49pM6hmy9Z+6G1WsMj9BWhaQEnRuJ wHbEjn/TKG41Cw73Sa0eF2F51us43oryr/MnVvSM2aZaUdu4rFv419W7kvSKB9eJ2Oha 9vFL6Hz694xIlOy9fsJNcZFnziDq+SPqcwtzu/Q1PcG8iHgYsdqOf+Xqny4pXhzjXMOD CheY75HHoH4zSGgRrDn3ktx3TtClPYUwo+wVAAz4DGzKO52zcKXcWYuFjY9r0WkXVLVj Kxwg== X-Forwarded-Encrypted: i=1; AKwUvBxZCh3X5ai3D/aYiq3EicKL3qT6b5EwH8fwgL8+kbvZNVQB4+y5j9iMMUeputb80I8/oU/455epRzXy9Q==@lists.linux.dev X-Gm-Message-State: AFq9FYK+3DufI/8jM1acEWFLynvjlYv6wkBojNURP48OaJcd5gBGoUGn pPjyUeqsEBNgYzIj1u1eeFjN2hB9OMO19N5SFsv0lkBXhdjv3FnPnLCsm5Pz1yN6G7nM X-Gm-Gg: AYBFou2iOR1N3HYHphCJvqqec1dHFACefC4J1e87YzhBaqi+PKur/VuJ0YSCnU9tfwB OPZDAGqhVo2PPcag/tG86SaV8Kk0OlYwzanjJ/vpQdg+Bsl1g2HqlT1T1c49fhZi6alMOiBhjOv wX77jgiZflwzXOQy7jW82GF2IezTBdJAlBySPKPltqc4zxzvF6nOJOc3NqkL89aC1/JRch+dQ0Y My707Rjt/rgcmmJ2r3zPWmlLzZIO+ceHRkkCNvX96VP2W45J7DKKMNBoezrbVWJn4pQyzfqWyY1 zNuBEoc4J1/qibMV8GwRWgGuTBX8wOdAf/RCnXswCnWmnw+brNIOlk2SpiAOJ18ZngIZXmFjflr 5mOt2Vsxc3oBZh23+nlwLW3GM1110lYbOKbJvklEdoPqkilTTjaoKYp9IpjJrF/LMVZNO9Dl4oA 72sXKQnGWZnfKxBdiiLuxh35+fMr5GHoadNFBV6IMEO4FFWNHA7xD10I8f97Svp8HaFWI/Lxqq4 Bx0TLqb X-Received: by 2002:a17:90b:35c3:b0:3a0:ca03:3e1f with SMTP id 98e67ed59e1d1-3a4d154522bmr1584586a91.32.1790783337518; Wed, 30 Sep 2026 08:48:57 -0700 (PDT) Received: from ltu.. ([171.245.4.151]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3a4ee4d0af2sm308136a91.2.2026.09.30.08.48.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Sep 2026 08:48:57 -0700 (PDT) From: Nguyen Minh Tien To: Mauro Carvalho Chehab , Hans Verkuil Cc: Nguyen Minh Tien , Sakari Ailus , Laurent Pinchart , Paul Kocialkowski , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Philipp Zabel , linux-media@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/3] riscv: dts: allwinner: d1s-t113: Add the CSIC node Date: Wed, 30 Sep 2026 22:48:35 +0700 Message-Id: <20260930154835.402684-4-tien.nguyenminh@embeddedlinux.blog> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260930154835.402684-1-tien.nguyenminh@embeddedlinux.blog> References: <20260930154835.402684-1-tien.nguyenminh@embeddedlinux.blog> Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [0.34 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[104.64.211.4:from]; SUSPICIOUS_RECIPS(1.50)[]; DMARC_POLICY_SOFTFAIL(1.00)[embeddedlinux.blog : SPF not aligned (relaxed), No valid DKIM,none]; MID_CONTAINS_FROM(1.00)[]; R_MISSING_CHARSET(0.50)[]; MAILLIST(-0.15)[generic]; MIME_GOOD(-0.10)[text/plain]; BAD_REP_POLICIES(0.10)[]; HAS_LIST_UNSUB(-0.01)[]; TAGGED_RCPT(0.00)[dt]; RCVD_VIA_SMTP_AUTH(0.00)[]; DBL_BLOCKED_OPENRESOLVER(0.00)[0.88.128.64:email,sin.lore.kernel.org:rdns,sin.lore.kernel.org:helo,0.106.211.168:email]; FREEMAIL_CC(0.00)[embeddedlinux.blog,linux.intel.com,ideasonboard.com,sys-base.io,kernel.org,gmail.com,sholland.org,dabbelt.com,eecs.berkeley.edu,ghiti.fr,pengutronix.de,vger.kernel.org,lists.infradead.org,lists.linux.dev]; RCPT_COUNT_TWELVE(0.00)[23]; FORGED_SENDER_MAILLIST(0.00)[]; PRECEDENCE_BULK(0.00)[]; RCVD_COUNT_FIVE(0.00)[6]; FROM_NEQ_ENVFROM(0.00)[tien.nguyenminh@embeddedlinux.blog,linux-sunxi@lists.linux.dev]; R_SPF_ALLOW(0.00)[+ip4:104.64.211.4]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; TO_DN_SOME(0.00)[]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[74.125.227.165:received,171.245.4.151:received,100.90.174.1:received]; FROM_HAS_DN(0.00)[]; MIME_TRACE(0.00)[0:+]; ASN(0.00)[asn:63949, ipnet:104.64.192.0/19, country:SG]; RCVD_TLS_LAST(0.00)[]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; TAGGED_FROM(0.00)[bounces-26433-noreply=patchwork.local]; RBL_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[104.64.211.4:from] X-Rspamd-Queue-Id: 8EF821C02D1 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= Add the CSIC node to the dtsi shared by the D1, D1s and T113, with the pin groups of its parallel input. MCLK has its own group, for the sensor node. Signed-off-by: Nguyen Minh Tien --- .../boot/dts/allwinner/sunxi-d1s-t113.dtsi | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi b/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi index 13b1e57166..dd4c873457 100644 --- a/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi +++ b/arch/riscv/boot/dts/allwinner/sunxi-d1s-t113.dtsi @@ -70,6 +70,20 @@ clk_pg11_pin: clk-pg11-pin { function = "clk"; }; + /omit-if-no-ref/ + csi0_8bit_pins: csi0-8bit-pins { + pins = "PE0", "PE1", "PE2", "PE4", "PE5", + "PE6", "PE7", "PE8", "PE9", "PE10", + "PE11"; + function = "ncsi0"; + }; + + /omit-if-no-ref/ + csi0_mclk_pin: csi0-mclk-pin { + pins = "PE3"; + function = "ncsi0"; + }; + /omit-if-no-ref/ dsi_4lane_pins: dsi-4lane-pins { pins = "PD0", "PD1", "PD2", "PD3", "PD4", "PD5", @@ -997,6 +1011,23 @@ tcon_tv0_out_tcon_top_hdmi: endpoint { }; }; + csi: csi@5800000 { + compatible = "allwinner,sun20i-d1-csi"; + reg = <0x5800000 0x400000>; + interrupts = , + , + , + ; + clocks = <&ccu CLK_BUS_CSI>, + <&ccu CLK_CSI_TOP>, + <&ccu CLK_MBUS_CSI>; + clock-names = "bus", "mod", "ram"; + resets = <&ccu RST_BUS_CSI>; + pinctrl-names = "default"; + pinctrl-0 = <&csi0_8bit_pins>; + status = "disabled"; + }; + ppu: power-controller@7001000 { compatible = "allwinner,sun20i-d1-ppu"; reg = <0x7001000 0x1000>;