From patchwork Wed Mar 5 21:24:21 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Frattaroli X-Patchwork-Id: 14003433 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 2740FC19F32 for ; Wed, 5 Mar 2025 21:31:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=l0WQdUrdZJvKyXOHu07xCpPjCZtIyq2fPB6xG3qYqGM=; b=H2O/0p1NAlgbxuv/QoJRgXUNPu vz9GnO0XVMS5AGtoViW7r1ebarpzZKUONDOTlQsYGoVCN8JAfykVSwJN3W3uV3YXfQSaVtfRfhjTp dFGr2+AEeO83bDPL187LhBMDzLW2X2ZvPPdVcEe4Xdi5MhMXhu0vJJxQ+vMD3S8s1NP43LZRwNcx8 NuZTZZ94YIjcacaHq9o1/9z2JzGjWFldvMvW+xbTsr2GRVEYI4g6sgB5YQ6fjRYZP+QF0ckFovHAj 2Wc6qPqNbi6Y7DTNRO28rSdhCMCtIdgzKbT6PlL7WrFK899JJ/dtcHKzM7gYD5wSXFW2aCXFY3Cuk u97Le/yA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1tpwLC-00000009M6V-1dmZ; Wed, 05 Mar 2025 21:31:46 +0000 Received: from sender4-pp-f112.zoho.com ([136.143.188.112]) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1tpwEt-00000009L7R-0MJY; Wed, 05 Mar 2025 21:25:16 +0000 ARC-Seal: i=1; a=rsa-sha256; t=1741209902; cv=none; d=zohomail.com; s=zohoarc; b=LPpRpTOTpGBW1N1KXO6lG4fjXGnEfUiE2GVgGQIVE+onZ0oz0XRV/3pYPdxqmI4AdN//bdvfIbxWXruEFW0GpX8P2CFVTmeGjeyI1Mn7TJOH1pZ8GPVIZIxlLWz6v2ZalWhEEEM7hpChHz6Z71kYoK7xpq1QyBlPJqYq2Swt7UI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1741209902; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=l0WQdUrdZJvKyXOHu07xCpPjCZtIyq2fPB6xG3qYqGM=; b=UnXK7L/fOkC2itQLu9yR5izY2gYsVhw2vm4+yXW+H0GB96q/zcRDpe94dsfOFrAkBdFPUBC/yJgASa87J/b4+yUihim/79xDEX6hggg+RH7bGqMWPvwRT3Ahj6CmKJMpY3RDywP7V/2ygDrKwHtRzEsAnRhDNclNCq4OkvhJ8Qk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=nicolas.frattaroli@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1741209902; s=zohomail; d=collabora.com; i=nicolas.frattaroli@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=l0WQdUrdZJvKyXOHu07xCpPjCZtIyq2fPB6xG3qYqGM=; b=iRw7kNwdCDEIeYtbxWouwWu2FNaj4vprd51PoVeSVewffIOQifb9676ctXebrfAJ ovozdcDgcO6Spmld312QBVlUleu561VWuZCdQ5HG1ziK6PNIrK40TlG14a7llo5A3ly ooBwyIg3Y+S5iUxx1aroLsvsS2lBs7EANhMb60zM= Received: by mx.zohomail.com with SMTPS id 1741209899333471.5790605921402; Wed, 5 Mar 2025 13:24:59 -0800 (PST) From: Nicolas Frattaroli Date: Wed, 05 Mar 2025 22:24:21 +0100 Subject: [PATCH 1/7] dt-bindings: clock: rk3576: add IOC gated clocks MIME-Version: 1.0 Message-Id: <20250305-rk3576-sai-v1-1-64e6cf863e9a@collabora.com> References: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> In-Reply-To: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> To: Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiko Stuebner , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , Philipp Zabel , Catalin Marinas , Will Deacon , Sugar Zhang Cc: Luca Ceresoli , Sebastian Reichel , kernel@collabora.com, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-sound@vger.kernel.org, Nicolas Frattaroli X-Mailer: b4 0.14.2 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20250305_132515_208399_CFEA1753 X-CRM114-Status: GOOD ( 11.77 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Certain clocks on the RK3576 are additionally essentially "gated" behind some bit toggles in the IOC GRF range. Downstream ungates these by adding a separate clock driver that maps over the GRF range and leaks their implementation of this into the DT. Instead, define some new clock IDs for these, so that consumers of these types of clocks can properly articulate which clock they're using, so that we can then add them to the clock driver for SoCs that need them. Signed-off-by: Nicolas Frattaroli Acked-by: Krzysztof Kozlowski --- include/dt-bindings/clock/rockchip,rk3576-cru.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/dt-bindings/clock/rockchip,rk3576-cru.h b/include/dt-bindings/clock/rockchip,rk3576-cru.h index 25aed298ac2c2e2d37e2b441c9d92ac68801be6e..9dbccd8ed4689ee85c9220c1404f971b73c7d4ea 100644 --- a/include/dt-bindings/clock/rockchip,rk3576-cru.h +++ b/include/dt-bindings/clock/rockchip,rk3576-cru.h @@ -589,4 +589,14 @@ #define PCLK_EDP_S 569 #define ACLK_KLAD 570 +/* IOC-controlled output clocks */ +#define CLK_SAI0_MCLKOUT_TO_IO 571 +#define CLK_SAI1_MCLKOUT_TO_IO 572 +#define CLK_SAI2_MCLKOUT_TO_IO 573 +#define CLK_SAI3_MCLKOUT_TO_IO 574 +#define CLK_SAI4_MCLKOUT_TO_IO 575 +#define CLK_SAI4_MCLKOUT_TO_IO 575 +#define CLK_FSPI0_TO_IO 576 +#define CLK_FSPI1_TO_IO 577 + #endif From patchwork Wed Mar 5 21:24:22 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Frattaroli X-Patchwork-Id: 14003434 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 13023C282EC for ; Wed, 5 Mar 2025 21:33:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=tkhunWzeT1J1bjdMLmmRSm+eqBFEg4KtTqCIoKPHFw4=; b=j+TorJKv2AAQYXz+iC/0blTPQ8 j0PZPOHvjOKDeJXJ/AjPL16sorJhnuNuSTXZNKSLB+6Cjve0Feewb7KJu9oAF2BQ4NiBYvKngHmEm UZ4nlha0swNEhU/0h47oe1pIxd+wuL+dSsGpq1Fdc9QvQGdodL8W61w1kK0oLNLbAjBsRsFH9jLZx H7UiOf/AOtiVMvt9lqI2zUGxZgutuQ4rcpTLX0cP7PLeMzVpdk9o++86bdOkdiDpxrspXkgDhC8Vh v3tHjk2evCXvy7m/9TqTf1jakg+feTtvAU7QUGM+ylfrTbfLSo2Hsxc2sRifpD4Y8AwsRl2ZUWTWE oCFW1I0w==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1tpwMm-00000009MIp-1yJm; Wed, 05 Mar 2025 21:33:24 +0000 Received: from sender3-pp-f112.zoho.com ([136.143.184.112]) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1tpwF1-00000009L9V-1TZv; Wed, 05 Mar 2025 21:25:24 +0000 ARC-Seal: i=1; a=rsa-sha256; t=1741209907; cv=none; d=zohomail.com; s=zohoarc; b=QsMYAxnFjRwP0TMPlg0/OFGq9tLFB/MXY0UeMOUT+PihlLKALi3hZN8umCkrLj7Mf94f3K8UYpTHEtgPpoyrQSN2U6nM+RDVygSSlctPT0rdHojZSC4WnH62WwTcaA1+9dy3K4+sxoqEoI2kSkOz6IBvg5p+rxdv5a1R9I5uH/w= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1741209907; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=tkhunWzeT1J1bjdMLmmRSm+eqBFEg4KtTqCIoKPHFw4=; b=MjpBCV/9JmjoWNSj2+XXe5c0RRCxiDTVi1VQ6RKwK9rTYggF3BT5d3FRxemh8I/c5NIVAUT1tdIrnUWz3m6qc2knmO2SSjxEYY7qjAKUYFuO3/+dCaGmS71lALS7hXU6QcaUG9CfcRVCANI+lj7KimatHjRLKUs+kdpD2ysg5yg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=nicolas.frattaroli@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1741209907; s=zohomail; d=collabora.com; i=nicolas.frattaroli@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=tkhunWzeT1J1bjdMLmmRSm+eqBFEg4KtTqCIoKPHFw4=; b=afSO5S9MSGQURQrMeTAUJORZ7WyxaJRCxHH4Rw65OIsoOCPjCb3a4EuFcgK8kIDF 9U8crzWCz+JOWEgpt66UNTIr17N6I5d7LcvkOyX7zF2sEhTz3wKbUOn6ZIa6cMtvb6J e0A4EuuDqKgMzf6iAmDJr/DQmcSQKBRO7PZJxIbk= Received: by mx.zohomail.com with SMTPS id 1741209905068926.4403017565099; Wed, 5 Mar 2025 13:25:05 -0800 (PST) From: Nicolas Frattaroli Date: Wed, 05 Mar 2025 22:24:22 +0100 Subject: [PATCH 2/7] clk: rockchip: add support for GRF gated clocks MIME-Version: 1.0 Message-Id: <20250305-rk3576-sai-v1-2-64e6cf863e9a@collabora.com> References: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> In-Reply-To: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> To: Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiko Stuebner , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , Philipp Zabel , Catalin Marinas , Will Deacon , Sugar Zhang Cc: Luca Ceresoli , Sebastian Reichel , kernel@collabora.com, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-sound@vger.kernel.org, Nicolas Frattaroli X-Mailer: b4 0.14.2 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20250305_132523_488386_7F057048 X-CRM114-Status: GOOD ( 30.47 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Certain Rockchip SoCs, the RK3576 in particular, have some clocks that are essentially gated behind an additional GRF write. Downstream uses an additional entirely separate clock driver that maps over the same address range as ioc_grf in the DT. Instead, this implementation introduces a new gate type, GRF gates. These gates function quite like regular gates. In effect, this means they'll only be enabled if the clock is used, which I feel is a more appropriate way to describe this compared to doing it in, say, pinctrl, or even in the drivers of the respective clock consumers such as SAI. It should be noted that RK3588 has similar GRF-gated clocks, but has gotten away with not having to deal with any of this because the clocks are ungated by the hardware's register reset value by default. The RK3576 is not so lucky, and the hardware's reset value gates them instead, which means we'll have to ungate them somewhere. In order to facilitate the GRF gating on RK3576, we introduce the concept of auxiliary GRFs. The RK3576 has several defined GRF nodes, and so far it could get away with just using one for MUXGRF by reassigning the clock provider's grf member. However, with the IOC GRF gated clocks, we now also need access to the IOC GRF, so we can't get away with this anymore. Instead, we add a hashtable to the clock provider struct, keyed by a grf type enum. The clock branches can then specify through the use of a new member of that enum's type (with corresponding changes to relevant macros) which GRF range they would like to use. The SoC-specific clk_init can then populate the hashtable with the GRFs that it needs. This way, GRF-dependent clock branches don't have to be registered in a different step than everything else, as they would need to be had I extended the branch struct to instead take a pointer to a GRF, which isn't available at the time most of our branches are defined. Signed-off-by: Nicolas Frattaroli --- drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-rk3328.c | 6 +-- drivers/clk/rockchip/clk-rk3568.c | 2 +- drivers/clk/rockchip/clk-rk3576.c | 59 +++++++++++++++++---- drivers/clk/rockchip/clk.c | 24 ++++++++- drivers/clk/rockchip/clk.h | 49 +++++++++++++++++- drivers/clk/rockchip/gate-grf.c | 105 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 230 insertions(+), 16 deletions(-) diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index e8ece20aebfdeffbdc5bfffd2c6f35f2cfcf74f6..f0e0b2c6e876a84610fb3ecf33cc1935b10058d9 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -14,6 +14,7 @@ clk-rockchip-y += clk-mmc-phase.o clk-rockchip-y += clk-muxgrf.o clk-rockchip-y += clk-ddr.o clk-rockchip-y += gate-link.o +clk-rockchip-y += gate-grf.o clk-rockchip-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-$(CONFIG_CLK_PX30) += clk-px30.o diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c index cf60fcf2fa5cde4ef97a05852f071763a4ccee40..cd5f65b6cdf55074c753f1dee96972c2a589c260 100644 --- a/drivers/clk/rockchip/clk-rk3328.c +++ b/drivers/clk/rockchip/clk-rk3328.c @@ -677,9 +677,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { RK3328_CLKSEL_CON(27), 15, 1, MFLAGS, 8, 5, DFLAGS, RK3328_CLKGATE_CON(3), 5, GFLAGS), MUXGRF(SCLK_MAC2IO, "clk_mac2io", mux_mac2io_src_p, CLK_SET_RATE_NO_REPARENT, - RK3328_GRF_MAC_CON1, 10, 1, MFLAGS), + RK3328_GRF_MAC_CON1, 10, 1, MFLAGS, grf_type_sys), MUXGRF(SCLK_MAC2IO_EXT, "clk_mac2io_ext", mux_mac2io_ext_p, CLK_SET_RATE_NO_REPARENT, - RK3328_GRF_SOC_CON4, 14, 1, MFLAGS), + RK3328_GRF_SOC_CON4, 14, 1, MFLAGS, grf_type_sys), COMPOSITE(SCLK_MAC2PHY_SRC, "clk_mac2phy_src", mux_2plls_p, 0, RK3328_CLKSEL_CON(26), 7, 1, MFLAGS, 0, 5, DFLAGS, @@ -692,7 +692,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { RK3328_CLKSEL_CON(26), 8, 2, DFLAGS, RK3328_CLKGATE_CON(9), 2, GFLAGS), MUXGRF(SCLK_MAC2PHY, "clk_mac2phy", mux_mac2phy_src_p, CLK_SET_RATE_NO_REPARENT, - RK3328_GRF_MAC_CON2, 10, 1, MFLAGS), + RK3328_GRF_MAC_CON2, 10, 1, MFLAGS, grf_type_sys), FACTOR(0, "xin12m", "xin24m", 0, 1, 2), diff --git a/drivers/clk/rockchip/clk-rk3568.c b/drivers/clk/rockchip/clk-rk3568.c index 7d9279291e7692c5ebdda16563fd09f87732e6d1..5f250bb8260ca0447f7249f212a49597c6c54887 100644 --- a/drivers/clk/rockchip/clk-rk3568.c +++ b/drivers/clk/rockchip/clk-rk3568.c @@ -590,7 +590,7 @@ static struct rockchip_clk_branch rk3568_clk_branches[] __initdata = { RK3568_CLKSEL_CON(9), 6, 2, MFLAGS, 0, 5, DFLAGS, RK3568_CLKGATE_CON(4), 0, GFLAGS), MUXGRF(CLK_DDR1X, "clk_ddr1x", clk_ddr1x_p, CLK_SET_RATE_PARENT, - RK3568_CLKSEL_CON(9), 15, 1, MFLAGS), + RK3568_CLKSEL_CON(9), 15, 1, MFLAGS, grf_type_sys), COMPOSITE_NOMUX(CLK_MSCH, "clk_msch", "clk_ddr1x", CLK_IGNORE_UNUSED, RK3568_CLKSEL_CON(10), 0, 2, DFLAGS, diff --git a/drivers/clk/rockchip/clk-rk3576.c b/drivers/clk/rockchip/clk-rk3576.c index be703f250197af2097fa591837de03accb8c51da..1e269490ac7f6b56ed4e53a711c7d4d922b6e149 100644 --- a/drivers/clk/rockchip/clk-rk3576.c +++ b/drivers/clk/rockchip/clk-rk3576.c @@ -15,6 +15,7 @@ #define RK3576_GRF_SOC_STATUS0 0x600 #define RK3576_PMU0_GRF_OSC_CON6 0x18 +#define RK3576_VCCIO_IOC_MISC_CON0 0x6400 enum rk3576_plls { bpll, lpll, vpll, aupll, cpll, gpll, ppll, @@ -1481,6 +1482,14 @@ static struct rockchip_clk_branch rk3576_clk_branches[] __initdata = { RK3576_CLKGATE_CON(10), 0, GFLAGS), GATE(CLK_SAI0_MCLKOUT, "clk_sai0_mclkout", "mclk_sai0_8ch", 0, RK3576_CLKGATE_CON(10), 1, GFLAGS), + GATE_GRF(CLK_SAI0_MCLKOUT_TO_IO, "mclk_sai0_to_io", "clk_sai0_mclkout", + 0, RK3576_VCCIO_IOC_MISC_CON0, 0, GFLAGS, grf_type_ioc), + GATE_GRF(CLK_SAI1_MCLKOUT_TO_IO, "mclk_sai1_to_io", "clk_sai1_mclkout", + 0, RK3576_VCCIO_IOC_MISC_CON0, 1, GFLAGS, grf_type_ioc), + GATE_GRF(CLK_SAI2_MCLKOUT_TO_IO, "mclk_sai2_to_io", "clk_sai2_mclkout", + 0, RK3576_VCCIO_IOC_MISC_CON0, 2, GFLAGS, grf_type_ioc), + GATE_GRF(CLK_SAI3_MCLKOUT_TO_IO, "mclk_sai3_to_io", "clk_sai3_mclkout", + 0, RK3576_VCCIO_IOC_MISC_CON0, 3, GFLAGS, grf_type_ioc), /* sdgmac */ COMPOSITE_NODIV(HCLK_SDGMAC_ROOT, "hclk_sdgmac_root", mux_200m_100m_50m_24m_p, 0, @@ -1678,13 +1687,13 @@ static struct rockchip_clk_branch rk3576_clk_branches[] __initdata = { /* phy ref */ MUXGRF(CLK_PHY_REF_SRC, "clk_phy_ref_src", clk_phy_ref_src_p, 0, - RK3576_PMU0_GRF_OSC_CON6, 4, 1, MFLAGS), + RK3576_PMU0_GRF_OSC_CON6, 4, 1, MFLAGS, grf_type_pmu0), MUXGRF(CLK_USBPHY_REF_SRC, "clk_usbphy_ref_src", clk_usbphy_ref_src_p, 0, - RK3576_PMU0_GRF_OSC_CON6, 2, 1, MFLAGS), + RK3576_PMU0_GRF_OSC_CON6, 2, 1, MFLAGS, grf_type_pmu0), MUXGRF(CLK_CPLL_REF_SRC, "clk_cpll_ref_src", clk_cpll_ref_src_p, 0, - RK3576_PMU0_GRF_OSC_CON6, 1, 1, MFLAGS), + RK3576_PMU0_GRF_OSC_CON6, 1, 1, MFLAGS, grf_type_pmu0), MUXGRF(CLK_AUPLL_REF_SRC, "clk_aupll_ref_src", clk_aupll_ref_src_p, 0, - RK3576_PMU0_GRF_OSC_CON6, 0, 1, MFLAGS), + RK3576_PMU0_GRF_OSC_CON6, 0, 1, MFLAGS, grf_type_pmu0), /* secure ns */ COMPOSITE_NODIV(ACLK_SECURE_NS, "aclk_secure_ns", mux_350m_175m_116m_24m_p, CLK_IS_CRITICAL, @@ -1727,17 +1736,26 @@ static void __init rk3576_clk_init(struct device_node *np) struct rockchip_clk_provider *ctx; unsigned long clk_nr_clks; void __iomem *reg_base; - struct regmap *grf; + struct rockchip_aux_grf *ioc_grf_e; + struct rockchip_aux_grf *pmu0_grf_e; + struct regmap *ioc_grf; + struct regmap *pmu0_grf; clk_nr_clks = rockchip_clk_find_max_clk_id(rk3576_clk_branches, ARRAY_SIZE(rk3576_clk_branches)) + 1; - grf = syscon_regmap_lookup_by_compatible("rockchip,rk3576-pmu0-grf"); - if (IS_ERR(grf)) { + pmu0_grf = syscon_regmap_lookup_by_compatible("rockchip,rk3576-pmu0-grf"); + if (IS_ERR(pmu0_grf)) { pr_err("%s: could not get PMU0 GRF syscon\n", __func__); return; } + ioc_grf = syscon_regmap_lookup_by_compatible("rockchip,rk3576-ioc-grf"); + if (IS_ERR(ioc_grf)) { + pr_err("%s: could not get IOC GRF syscon\n", __func__); + return; + } + reg_base = of_iomap(np, 0); if (!reg_base) { pr_err("%s: could not map cru region\n", __func__); @@ -1747,11 +1765,24 @@ static void __init rk3576_clk_init(struct device_node *np) ctx = rockchip_clk_init(np, reg_base, clk_nr_clks); if (IS_ERR(ctx)) { pr_err("%s: rockchip clk init failed\n", __func__); - iounmap(reg_base); - return; + goto err_unmap; } - ctx->grf = grf; + pmu0_grf_e = kzalloc(sizeof(*pmu0_grf_e), GFP_KERNEL); + if (!pmu0_grf_e) + goto err_unmap; + + pmu0_grf_e->grf = pmu0_grf; + pmu0_grf_e->type = grf_type_pmu0; + hash_add(ctx->aux_grf_table, &pmu0_grf_e->node, grf_type_pmu0); + + ioc_grf_e = kzalloc(sizeof(*ioc_grf_e), GFP_KERNEL); + if (!ioc_grf_e) + goto err_free_pmu0; + + ioc_grf_e->grf = ioc_grf; + ioc_grf_e->type = grf_type_ioc; + hash_add(ctx->aux_grf_table, &ioc_grf_e->node, grf_type_ioc); rockchip_clk_register_plls(ctx, rk3576_pll_clks, ARRAY_SIZE(rk3576_pll_clks), @@ -1774,6 +1805,14 @@ static void __init rk3576_clk_init(struct device_node *np) rockchip_register_restart_notifier(ctx, RK3576_GLB_SRST_FST, NULL); rockchip_clk_of_add_provider(np, ctx); + + return; + +err_free_pmu0: + kfree(pmu0_grf_e); +err_unmap: + iounmap(reg_base); + return; } CLK_OF_DECLARE(rk3576_cru, "rockchip,rk3576-cru", rk3576_clk_init); diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index cbf93ea119a9e25c037607ded1f6f358918e8656..8e035b08697eb5bf193ddb9ab4c6154332eb3af1 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -382,6 +382,8 @@ static struct rockchip_clk_provider *rockchip_clk_init_base( ctx->cru_node = np; spin_lock_init(&ctx->lock); + hash_init(ctx->aux_grf_table); + ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node, "rockchip,grf"); @@ -496,6 +498,8 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, struct rockchip_clk_branch *list, unsigned int nr_clk) { + struct regmap *grf = ctx->grf; + struct rockchip_aux_grf *agrf; struct clk *clk; unsigned int idx; unsigned long flags; @@ -504,6 +508,17 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, flags = list->flags; clk = NULL; + /* for GRF-dependant branches, choose the right grf first */ + if ((list->branch_type == branch_muxgrf || list->branch_type == branch_grf_gate) && + list->grf_type != grf_type_sys) { + hash_for_each_possible(ctx->aux_grf_table, agrf, node, list->grf_type) { + if (agrf->type == list->grf_type) { + grf = agrf->grf; + break; + } + } + } + /* catch simple muxes */ switch (list->branch_type) { case branch_mux: @@ -526,7 +541,7 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, case branch_muxgrf: clk = rockchip_clk_register_muxgrf(list->name, list->parent_names, list->num_parents, - flags, ctx->grf, list->muxdiv_offset, + flags, grf, list->muxdiv_offset, list->mux_shift, list->mux_width, list->mux_flags); break; @@ -573,6 +588,13 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, ctx->reg_base + list->gate_offset, list->gate_shift, list->gate_flags, &ctx->lock); break; + case branch_grf_gate: + flags |= CLK_SET_RATE_PARENT; + clk = rockchip_clk_register_gate_grf(list->name, + list->parent_names[0], flags, grf, + list->gate_offset, list->gate_shift, + list->gate_flags); + break; case branch_composite: clk = rockchip_clk_register_branch(list->name, list->parent_names, list->num_parents, diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index df2b2d706450f38dfe2b3c7762763c5e832b9892..ebaed429a30dc2c41c866f973f8cf78b32136311 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -19,6 +19,7 @@ #include #include +#include struct clk; @@ -440,12 +441,35 @@ enum rockchip_pll_type { .k = _k, \ } +enum rockchip_grf_type { + grf_type_sys = 0, + grf_type_pmu0, + grf_type_pmu1, + grf_type_ioc, +}; + +/* ceil(sqrt(enums in rockchip_grf_type - 1)) */ +#define GRF_HASH_ORDER 2 + +/** + * struct rockchip_aux_grf - entry for the aux_grf_table hashtable + * @grf: pointer to the grf this entry references + * @type: what type of GRF this is + * @node: hlist node + */ +struct rockchip_aux_grf { + struct regmap *grf; + enum rockchip_grf_type type; + struct hlist_node node; +}; + /** * struct rockchip_clk_provider - information about clock provider * @reg_base: virtual address for the register base. * @clk_data: holds clock related data like clk* and number of clocks. * @cru_node: device-node of the clock-provider * @grf: regmap of the general-register-files syscon + * @aux_grf_table: hashtable of auxiliary GRF regmaps, indexed by grf_type * @lock: maintains exclusion between callbacks for a given clock-provider. */ struct rockchip_clk_provider { @@ -453,6 +477,7 @@ struct rockchip_clk_provider { struct clk_onecell_data clk_data; struct device_node *cru_node; struct regmap *grf; + DECLARE_HASHTABLE(aux_grf_table, GRF_HASH_ORDER); spinlock_t lock; }; @@ -622,6 +647,11 @@ struct clk *rockchip_clk_register_muxgrf(const char *name, int flags, struct regmap *grf, int reg, int shift, int width, int mux_flags); +struct clk *rockchip_clk_register_gate_grf(const char *name, + const char *parent_name, unsigned long flags, + struct regmap *regmap, unsigned int reg, + unsigned int shift, u8 gate_flags); + #define PNAME(x) static const char *const x[] __initconst enum rockchip_clk_branch_type { @@ -631,6 +661,7 @@ enum rockchip_clk_branch_type { branch_divider, branch_fraction_divider, branch_gate, + branch_grf_gate, branch_linked_gate, branch_mmc, branch_inverter, @@ -660,6 +691,7 @@ struct rockchip_clk_branch { u8 gate_shift; u8 gate_flags; unsigned int linked_clk_id; + enum rockchip_grf_type grf_type; struct rockchip_clk_branch *child; }; @@ -900,7 +932,7 @@ struct rockchip_clk_branch { .mux_table = mt, \ } -#define MUXGRF(_id, cname, pnames, f, o, s, w, mf) \ +#define MUXGRF(_id, cname, pnames, f, o, s, w, mf, gt) \ { \ .id = _id, \ .branch_type = branch_muxgrf, \ @@ -913,6 +945,7 @@ struct rockchip_clk_branch { .mux_width = w, \ .mux_flags = mf, \ .gate_offset = -1, \ + .grf_type = gt, \ } #define DIV(_id, cname, pname, f, o, s, w, df) \ @@ -958,6 +991,20 @@ struct rockchip_clk_branch { .gate_flags = gf, \ } +#define GATE_GRF(_id, cname, pname, f, o, b, gf, gt) \ + { \ + .id = _id, \ + .branch_type = branch_grf_gate, \ + .name = cname, \ + .parent_names = (const char *[]){ pname }, \ + .num_parents = 1, \ + .flags = f, \ + .gate_offset = o, \ + .gate_shift = b, \ + .gate_flags = gf, \ + .grf_type = gt, \ + } + #define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf) \ { \ .id = _id, \ diff --git a/drivers/clk/rockchip/gate-grf.c b/drivers/clk/rockchip/gate-grf.c new file mode 100644 index 0000000000000000000000000000000000000000..8122f471f39134a7298be8daf0718cfa4f8852a7 --- /dev/null +++ b/drivers/clk/rockchip/gate-grf.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Collabora Ltd. + * Author: Nicolas Frattaroli + * + * Certain clocks on Rockchip are "gated" behind an additional register bit + * write in a GRF register, such as the SAI MCLKs on RK3576. This code + * implements a clock driver for these types of gates, based on regmaps. + */ + +#include +#include +#include +#include +#include "clk.h" + +struct rockchip_gate_grf { + struct clk_hw hw; + struct regmap *regmap; + unsigned int reg; + unsigned int shift; + u8 flags; +}; + +#define to_gate_grf(_hw) container_of(_hw, struct rockchip_gate_grf, hw) + +static int rockchip_gate_grf_enable(struct clk_hw *hw) +{ + struct rockchip_gate_grf *gate = to_gate_grf(hw); + u32 val = !(gate->flags & CLK_GATE_SET_TO_DISABLE) ? BIT(gate->shift) : 0; + u32 hiword = ((gate->flags & CLK_GATE_HIWORD_MASK) ? 1 : 0) << (gate->shift + 16); + int ret; + + ret = regmap_update_bits(gate->regmap, gate->reg, + hiword | BIT(gate->shift), hiword | val); + + return ret; +} + +static void rockchip_gate_grf_disable(struct clk_hw *hw) +{ + struct rockchip_gate_grf *gate = to_gate_grf(hw); + u32 val = !(gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : BIT(gate->shift); + u32 hiword = ((gate->flags & CLK_GATE_HIWORD_MASK) ? 1 : 0) << (gate->shift + 16); + + regmap_update_bits(gate->regmap, gate->reg, + hiword | BIT(gate->shift), hiword | val); +} + +static int rockchip_gate_grf_is_enabled(struct clk_hw *hw) +{ + struct rockchip_gate_grf *gate = to_gate_grf(hw); + bool invert = !!(gate->flags & CLK_GATE_SET_TO_DISABLE); + int ret; + + ret = regmap_test_bits(gate->regmap, gate->reg, BIT(gate->shift)); + if (ret < 0) + ret = 0; + + return invert ? 1 - ret : ret; + +} + +static const struct clk_ops rockchip_gate_grf_ops = { + .enable = rockchip_gate_grf_enable, + .disable = rockchip_gate_grf_disable, + .is_enabled = rockchip_gate_grf_is_enabled, +}; + +struct clk *rockchip_clk_register_gate_grf(const char *name, + const char *parent_name, unsigned long flags, + struct regmap *regmap, unsigned int reg, unsigned int shift, + u8 gate_flags) +{ + struct rockchip_gate_grf *gate; + struct clk_init_data init; + struct clk *clk; + + if (IS_ERR(regmap)) { + pr_err("%s: regmap not available\n", __func__); + return ERR_PTR(-EOPNOTSUPP); + } + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = flags; + init.num_parents = parent_name ? 1 : 0; + init.parent_names = parent_name ? &parent_name : NULL; + init.ops = &rockchip_gate_grf_ops; + + gate->hw.init = &init; + gate->regmap = regmap; + gate->reg = reg; + gate->shift = shift; + gate->flags = gate_flags; + + clk = clk_register(NULL, &gate->hw); + if (IS_ERR(clk)) + kfree(gate); + + return clk; +} From patchwork Wed Mar 5 21:24:23 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Frattaroli X-Patchwork-Id: 14003438 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id E59BFC282EC for ; Wed, 5 Mar 2025 21:35:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=j1d+wU9HbyuCcHJ1x5iw3HpFGebD1mUTzyLgQ69TKIs=; b=geMVengh6w359EHeTAEXmQWULr ClUSNAp+6ZmAU/6eKUqlQN71NhNA0DKMMayU3xHDqEf/utznUayEvCrQpQe+iz1D/EHRIIsZa0QhR 9/S4BDyVDFS5/aEixwdnk10W24YQiumEds4kNBc3p5FzpzrjtWnRTCO22odEhfgU1bokDJfpnn47s sE0bbSPv0qoozv7Q0Dhd8xSpHto741AB0Nlg2piXAIRuj4jk0DjWD+0stTmKzuVnocw50WCPT4C3I RgcqLXPMqK6983iIth5/l+2mlCQXrri/31c+33tprlBo2d+Q2HKxjQY+bHru0tYRk0SfFn2sczbUD P8IVI9xg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1tpwOM-00000009Mqw-1Gx2; Wed, 05 Mar 2025 21:35:02 +0000 Received: from sender4-pp-f112.zoho.com ([136.143.188.112]) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1tpwF2-00000009LA1-37g3; Wed, 05 Mar 2025 21:25:26 +0000 ARC-Seal: i=1; a=rsa-sha256; t=1741209913; cv=none; d=zohomail.com; s=zohoarc; b=E8PM/x/VhWtwVwJtIUaf6T97P1i1ptg1XRIHZtsHQPYh9Xyfu8ejxx5i4MWWn5WR23UoNpfEcJWW6+GyhYf16EO5AukFqB7Vw1EN354WDouIy1KKoWnfIaAonBV3PhL1rPhZkELP9z6DnBi8LU8MODWSq/58tf8ZnlGMENxvtmE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1741209913; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=j1d+wU9HbyuCcHJ1x5iw3HpFGebD1mUTzyLgQ69TKIs=; b=Xx4zBl2jfDpCHhVgutxuOvhfuChrYNUCirntHGmnkeAWpHqkK6xih+5OX9Xbaunfn5UhnREmJaGGovDRZZeWckH9Y2/dEOqVbCxn4EfI9nWmrpWPF/xwbyVWK56/6Qv2dCplzElwbjNrSKrMIj5G0PD9TRg+BeZRbhrXnPoTk6M= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=nicolas.frattaroli@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1741209913; s=zohomail; d=collabora.com; i=nicolas.frattaroli@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=j1d+wU9HbyuCcHJ1x5iw3HpFGebD1mUTzyLgQ69TKIs=; b=HV9cBFswLKdX7cfqw0vSV69RkYYtc+qgW4mgG7DHw/nV55NxjatoeZ/SnwUhq4wt Xqe6y5FvZ5FWy49Tm9cwVZRO33LcYTfw36nwS6o756FDBTdVHhlVPJk+QjUOTYwctmK 3V2ycnv6BKI3tOwgMCGRBWSt9Fn+cQH1g9uKOqgo= Received: by mx.zohomail.com with SMTPS id 1741209910870600.8165634885264; Wed, 5 Mar 2025 13:25:10 -0800 (PST) From: Nicolas Frattaroli Date: Wed, 05 Mar 2025 22:24:23 +0100 Subject: [PATCH 3/7] ASoC: dt-bindings: add schema for rockchip SAI controllers MIME-Version: 1.0 Message-Id: <20250305-rk3576-sai-v1-3-64e6cf863e9a@collabora.com> References: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> In-Reply-To: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> To: Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiko Stuebner , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , Philipp Zabel , Catalin Marinas , Will Deacon , Sugar Zhang Cc: Luca Ceresoli , Sebastian Reichel , kernel@collabora.com, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-sound@vger.kernel.org, Nicolas Frattaroli X-Mailer: b4 0.14.2 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20250305_132524_827006_7B79438D X-CRM114-Status: GOOD ( 17.63 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Rockchip introduced a new audio controller called the "Serial Audio Interface", or "SAI" for short, on some of their newer SoCs. In particular, this controller is used several times on the RK3576 SoC. Add a schema for it, with only an RK3576 compatible for now. Other SoCs may follow as mainline support for them lands. Signed-off-by: Nicolas Frattaroli --- .../devicetree/bindings/sound/rockchip,sai.yaml | 151 +++++++++++++++++++++ MAINTAINERS | 6 + 2 files changed, 157 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/rockchip,sai.yaml b/Documentation/devicetree/bindings/sound/rockchip,sai.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8f5a292a7f2a6c737d983d00cbe40ec45bfa6249 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip,sai.yaml @@ -0,0 +1,151 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/rockchip,sai.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip Serial Audio Interface Controller + +description: + The Rockchip Serial Audio Interface (SAI) controller is a flexible audio + controller that implements the I2S, I2S/TDM and the PDM standards. + +maintainers: + - Nicolas Frattaroli + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: rockchip,rk3576-sai + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + dmas: + minItems: 1 + maxItems: 2 + + dma-names: + minItems: 1 + maxItems: 2 + oneOf: + - const: tx + - const: rx + - items: + - const: tx + - const: rx + + clocks: + items: + - description: master audio clock + - description: AHB clock driving the interface + + clock-names: + items: + - const: mclk + - const: hclk + + resets: + minItems: 1 + maxItems: 2 + description: resets for the mclk domain and ahb domain + + reset-names: + minItems: 1 + items: + - const: m + - const: h + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + + power-domains: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + rockchip,sai-rx-route: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + Defines the mapping of the controller's SDI ports to actual input lanes, + as well as the number of input lanes. + rockchip,sai-rx-route = <3> would mean sdi3 is receiving from data0, and + that there is only one receiving lane. + This property's absence is to be understood as only one receiving lane + being used if the controller has capture capabilities. + maxItems: 4 + items: + enum: [0, 1, 2, 3] + + rockchip,sai-tx-route: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + Defines the mapping of the controller's SDO ports to actual output lanes, + as well as the number of output lanes. + rockchip,sai-tx-route = <3> would mean sdo3 is sending to data0, and + that there is only one transmitting lane. + This property's absence is to be understood as only one transmitting lane + being used if the controller has playback capabilities. + maxItems: 4 + items: + enum: [0, 1, 2, 3] + + rockchip,always-on: + type: boolean + description: + The hardware requires this controller to remain turned on. + + +required: + - compatible + - reg + - dmas + - dma-names + - clocks + - clock-names + - "#sound-dai-cells" + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + #include + #include + + bus { + #address-cells = <2>; + #size-cells = <2>; + sai1: sai@2a610000 { + compatible = "rockchip,rk3576-sai"; + reg = <0x0 0x2a610000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SAI1_8CH>, <&cru HCLK_SAI1_8CH>; + clock-names = "mclk", "hclk"; + dmas = <&dmac0 2>, <&dmac0 3>; + dma-names = "tx", "rx"; + power-domains = <&power RK3576_PD_AUDIO>; + resets = <&cru SRST_M_SAI1_8CH>, <&cru SRST_H_SAI1_8CH>; + reset-names = "m", "h"; + pinctrl-names = "default"; + pinctrl-0 = <&sai1m0_lrck + &sai1m0_sclk + &sai1m0_sdi0 + &sai1m0_sdo0 + &sai1m0_sdo1 + &sai1m0_sdo2 + &sai1m0_sdo3>; + rockchip,sai-tx-route = <3 1 2 0>; + #sound-dai-cells = <0>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 4e9e0e52f92e3c649835ddfb65b2fde0a486198b..a68bf5391df6d3785f376c68ae61669d310cfb17 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20661,6 +20661,12 @@ F: Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.yaml F: sound/soc/codecs/rk3308_codec.c F: sound/soc/codecs/rk3308_codec.h +ROCKCHIP SAI DRIVER +M: Nicolas Frattaroli +L: linux-rockchip@lists.infradead.org +S: Maintained +F: Documentation/devicetree/bindings/sound/rockchip,sai.yaml + ROCKCHIP VIDEO DECODER DRIVER M: Ezequiel Garcia L: linux-media@vger.kernel.org From patchwork Wed Mar 5 21:24:24 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Frattaroli X-Patchwork-Id: 14003456 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id A4B6FC19F32 for ; Wed, 5 Mar 2025 21:36:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=vG52dUrWHterwTMqxAe8aMFIF6DxuGj5R97DgJmyxRs=; b=IrjOsDrDVL7hOehr06ISSgOW59 6Bpjy7qe75aXvs4i1sHYUtI4McY8yPHfyYhS0WTHd88nhxBujqkOpS8iEW28m1ZZqurF+7HIYTEaS ag1SzN4jN8fOghp7QAcJjgncjKMiBcD4fGnDDXTGcgr7bUkr1TDoA5+6CKowi4ub3xnGGyWVhIgWP 3pmGX68SiBfy6DxxCTreyTKSB/GFX9cu91J/6sj2J36IbUxWDQ/4LWIfmXZmseU9H98WsbS+JR3wZ ZJ75HM4O5ltJvXIkwZOdJmHFsFdsgUIeSEpV3KhpqucrvUKmGq01cEuqimlwd9FFuXs/kXzIJvxLK 0wH7RhlA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1tpwPw-00000009NJ8-3eOG; Wed, 05 Mar 2025 21:36:40 +0000 Received: from sender4-pp-f112.zoho.com ([136.143.188.112]) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1tpwF8-00000009LBe-0O6x; Wed, 05 Mar 2025 21:25:32 +0000 ARC-Seal: i=1; a=rsa-sha256; t=1741209919; cv=none; d=zohomail.com; s=zohoarc; b=lFA1s9pZrvZsafQvz+arSzrw+AgMq3e25fyFL6MkwVoQ9HBBliWRrI2TIrNd8THCbn6AJ8Ke/QJuOUN2HalevrGvwAqfStXwu4SMYp6G/zdds98c9I+cX5ro83PYDx2MdxIkVyC9RNruhUrGBwwW/Fb8ssjZUOxnkIX7EHNTkmo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1741209919; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=vG52dUrWHterwTMqxAe8aMFIF6DxuGj5R97DgJmyxRs=; b=jMSuoT3k2ytMgiXYTZ5a3zT7d5ZXc5rPaJot8pxcTiPQjxgolVViQ/tFwWzpEcBEbZdj8i3yymhCWLgq6M3yrzxOeq47NcR/LpdrdciOT8V/OimWNVdiIyv2gAfq9KSDpOx5SXQFw+XJCJ/g6SJgB98YtmbEppSKOC+ic6I1XRU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=nicolas.frattaroli@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1741209919; s=zohomail; d=collabora.com; i=nicolas.frattaroli@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=vG52dUrWHterwTMqxAe8aMFIF6DxuGj5R97DgJmyxRs=; b=Ohu9I9cz9iul3HDpZWgYUk/q/ae8g4oMO4xNolIdX/X/pzcyoGjJZl+nwUBnZkzf gBEzVjxyXJJcKVf4oOluzTUx5f7wTPD1TxAnEP1NoBeyXZkNHk9jHpUbygcMc5b4+FE HWZ7dqcj6nDxuplSWT7du4L4nwnquMWIlxYEWezI= Received: by mx.zohomail.com with SMTPS id 1741209916921335.0703557782225; Wed, 5 Mar 2025 13:25:16 -0800 (PST) From: Nicolas Frattaroli Date: Wed, 05 Mar 2025 22:24:24 +0100 Subject: [PATCH 4/7] ASoC: rockchip: add Serial Audio Interface (SAI) driver MIME-Version: 1.0 Message-Id: <20250305-rk3576-sai-v1-4-64e6cf863e9a@collabora.com> References: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> In-Reply-To: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> To: Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiko Stuebner , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , Philipp Zabel , Catalin Marinas , Will Deacon , Sugar Zhang Cc: Luca Ceresoli , Sebastian Reichel , kernel@collabora.com, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-sound@vger.kernel.org, Nicolas Frattaroli X-Mailer: b4 0.14.2 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20250305_132530_268668_C315202F X-CRM114-Status: GOOD ( 40.53 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org The Rockchip RK3576 SoC features a new audio controller, the Serial Audio Interface, or SAI for short. It is capable of both sending and receiving audio over up to 4 lanes for each direction using the I2S, PCM or TDM formats. This driver is derived from the downstream vendor driver. That is why its original author, Sugar Zhang, is listed as a Co-developer, with their signoff. Since adjustments to make the driver suitable for upstream were quite extensive, I've added myself to the authors and put myself as the commit author; all added bugs are my fault alone, and not that of the original author at Rockchip. The hardware is somewhat similar to the Rockchip I2S-TDM hardware when judged based on their register map, except it uses the same mclk for tx and rx. It appears to be much more flexible with regards to TDM. The loopback stuff and mono mode are new as well. In line with the changes that were made to the Rockchip I2S-TDM driver after upstreaming, the mclk-calibrate functionality was dropped, and setting the mclk rate properly is now left up to the Common Clock Framework, similar to how it is in the upstream I2S-TDM driver now. A spinlock has been introduced to protect register write accesses that depend on the bclk/fs to be stopped, i.e. XFER[1:0] being 0. I couldn't find whether the asoc core held a per-instance lock so only one callback can run at a time, and so it seemed prudent to add this. I couldn't successfully test whether TDM was working, though I've tried with a TAS6424 codec board. I'm not sure yet whether to blame the codec driver, this version of the SAI driver, or the vendor implementation of the SAI driver. The TDM mask registers remain untouched in both this version and the downstream vendor version, which is suspicious, though the Linux ASoC core wouldn't be able to support the 128 (!!!) slots of TDM the hardware supports anyway. Regular old 2-channel stereo I2S thrown at an I2S stereo codec works well though. I tested with the CPU-side SAI controller in provider mode and an Everest ES8388 codec as the consumer. Some vendor driver features (no-dmaengine, fifo rockchip performance monitoring, many kcontrols) were dropped for this initial upstream version. They can always be added later if they make sense for upstream. Co-developed-by: Sugar Zhang Signed-off-by: Sugar Zhang Signed-off-by: Nicolas Frattaroli --- MAINTAINERS | 1 + sound/soc/rockchip/Kconfig | 8 + sound/soc/rockchip/Makefile | 2 + sound/soc/rockchip/rockchip_sai.c | 1623 +++++++++++++++++++++++++++++++++++++ sound/soc/rockchip/rockchip_sai.h | 251 ++++++ 5 files changed, 1885 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index a68bf5391df6d3785f376c68ae61669d310cfb17..a787ebc0b597022410b9d6fd149d6056ca46fa01 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20666,6 +20666,7 @@ M: Nicolas Frattaroli L: linux-rockchip@lists.infradead.org S: Maintained F: Documentation/devicetree/bindings/sound/rockchip,sai.yaml +F: sound/soc/rockchip/rockchip_sai.* ROCKCHIP VIDEO DECODER DRIVER M: Ezequiel Garcia diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index f98a2fa85edd6bb45d46e915e05bf8e64dc704aa..951e3c926e2aca227ba92ee57cc8e216aa7b7e55 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -37,6 +37,14 @@ config SND_SOC_ROCKCHIP_PDM Rockchip PDM Controller. The Controller supports up to maximum of 8 channels record. +config SND_SOC_ROCKCHIP_SAI + tristate "Rockchip SAI Controller Driver" + depends on HAVE_CLK && SND_SOC_ROCKCHIP + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for the Rockchip Serial Audio + Interface controller found on Rockchip SoCs such as the RK3576. + config SND_SOC_ROCKCHIP_SPDIF tristate "Rockchip SPDIF Device Driver" depends on HAVE_CLK && SND_SOC_ROCKCHIP diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 2ee9c08131d1ef0ec0b49e126309a9e1dae11439..af6dc116534724ba88154501f0f74d088e38a27d 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -3,10 +3,12 @@ snd-soc-rockchip-i2s-y := rockchip_i2s.o snd-soc-rockchip-i2s-tdm-y := rockchip_i2s_tdm.o snd-soc-rockchip-pdm-y := rockchip_pdm.o +snd-soc-rockchip-sai-y := rockchip_sai.o snd-soc-rockchip-spdif-y := rockchip_spdif.o obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_SAI) += snd-soc-rockchip-sai.o obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S_TDM) += snd-soc-rockchip-i2s-tdm.o diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c new file mode 100644 index 0000000000000000000000000000000000000000..52bd4c15b802384211edb49a5c0b6fec75211394 --- /dev/null +++ b/sound/soc/rockchip/rockchip_sai.c @@ -0,0 +1,1623 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ALSA SoC Audio Layer - Rockchip SAI Controller driver + * + * Copyright (c) 2022 Rockchip Electronics Co. Ltd. + * Copyright (c) 2025 Collabora Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rockchip_sai.h" + +#define DRV_NAME "rockchip-sai" + +#define CLK_SHIFT_RATE_HZ_MAX 5 +#define FW_RATIO_MAX 8 +#define FW_RATIO_MIN 1 +#define MAXBURST_PER_FIFO 8 + +#define DEFAULT_FS 48000 +#define TIMEOUT_US 1000 +#define WAIT_TIME_MS_MAX 10000 +#define QUIRK_ALWAYS_ON BIT(0) + +#define MAX_LANES 4 + +enum fpw_mode { + FPW_ONE_BCLK_WIDTH, + FPW_ONE_SLOT_WIDTH, + FPW_HALF_FRAME_WIDTH, +}; + +struct rk_sai_dev { + struct device *dev; + struct clk *hclk; + struct clk *mclk; + struct regmap *regmap; + struct reset_control *rst_h; + struct reset_control *rst_m; + struct snd_dmaengine_dai_dma_data capture_dma_data; + struct snd_dmaengine_dai_dma_data playback_dma_data; + struct snd_pcm_substream *substreams[SNDRV_PCM_STREAM_LAST + 1]; + unsigned int mclk_rate; + unsigned int wait_time[SNDRV_PCM_STREAM_LAST + 1]; + unsigned int tx_lanes; + unsigned int rx_lanes; + unsigned int sdi[MAX_LANES]; + unsigned int sdo[MAX_LANES]; + unsigned int quirks; + unsigned int version; + enum fpw_mode fpw; + int fw_ratio; + bool has_capture; + bool has_playback; + bool is_master_mode; + bool is_tdm; + bool initialized; + /* protects register writes that depend on the state of XFER[1:0] */ + spinlock_t xfer_lock; +}; + +static const struct sai_of_quirks { + char *quirk; + int id; +} of_quirks[] = { + { + .quirk = "rockchip,always-on", + .id = QUIRK_ALWAYS_ON, + }, +}; + +static bool rockchip_sai_stream_valid(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + + if (!substream) + return false; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + sai->has_playback) + return true; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + sai->has_capture) + return true; + + return false; +} + +static int rockchip_sai_fsync_lost_detect(struct rk_sai_dev *sai, bool en) +{ + unsigned int fw, cnt; + + if (sai->is_master_mode || sai->version < SAI_VER_2311) + return 0; + + regmap_read(sai->regmap, SAI_FSCR, &fw); + cnt = SAI_FSCR_FW_V(fw) << 1; /* two fsync lost */ + + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_FSLOSTC, SAI_INTCR_FSLOSTC); + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_FSLOST_MASK, + SAI_INTCR_FSLOST(en)); + /* + * The `cnt` is the number of SCLK cycles of the CRU's SCLK signal that + * should be used as timeout. Consequently, in slave mode, this value + * is only correct if the CRU SCLK is equal to the external SCLK. + */ + regmap_update_bits(sai->regmap, SAI_FS_TIMEOUT, + SAI_FS_TIMEOUT_VAL_MASK | SAI_FS_TIMEOUT_EN_MASK, + SAI_FS_TIMEOUT_VAL(cnt) | SAI_FS_TIMEOUT_EN(en)); + + return 0; +} + +static int rockchip_sai_fsync_err_detect(struct rk_sai_dev *sai, + bool en) +{ + if (sai->is_master_mode || sai->version < SAI_VER_2311) + return 0; + + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_FSERRC, SAI_INTCR_FSERRC); + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_FSERR_MASK, + SAI_INTCR_FSERR(en)); + + return 0; +} + +static int rockchip_sai_poll_clk_idle(struct rk_sai_dev *sai) +{ + unsigned int reg, idle, val; + int ret; + + if (sai->version >= SAI_VER_2307) { + reg = SAI_STATUS; + idle = SAI_STATUS_FS_IDLE; + idle = sai->version >= SAI_VER_2311 ? idle >> 1 : idle; + } else { + reg = SAI_XFER; + idle = SAI_XFER_FS_IDLE; + } + + ret = regmap_read_poll_timeout_atomic(sai->regmap, reg, val, + (val & idle), 10, TIMEOUT_US); + if (ret < 0) + dev_warn(sai->dev, "Failed to idle FS\n"); + + return ret; +} + +static int rockchip_sai_poll_stream_idle(struct rk_sai_dev *sai, bool playback, bool capture) +{ + unsigned int reg, val; + unsigned int idle = 0; + int ret; + + if (sai->version >= SAI_VER_2307) { + reg = SAI_STATUS; + if (playback) + idle |= SAI_STATUS_TX_IDLE; + if (capture) + idle |= SAI_STATUS_RX_IDLE; + idle = sai->version >= SAI_VER_2311 ? idle >> 1 : idle; + } else { + reg = SAI_XFER; + if (playback) + idle |= SAI_XFER_TX_IDLE; + if (capture) + idle |= SAI_XFER_RX_IDLE; + } + + ret = regmap_read_poll_timeout_atomic(sai->regmap, reg, val, + (val & idle), 10, TIMEOUT_US); + if (ret < 0) + dev_warn(sai->dev, "Failed to idle stream\n"); + + return ret; +} + +/** + * rockchip_sai_xfer_clk_stop_and_wait() - stop the xfer clock and wait for it to be idle + * @sai: pointer to the driver instance's rk_sai_dev struct + * @to_restore: pointer to store the CLK/FSS register values in as they were + * found before they were cleared, or NULL. + * + * Clear the XFER_CLK and XFER_FSS registers if needed, then busy-waits for the + * XFER clocks to be idle. Before clearing the bits, it stores the state of the + * registers as it encountered them in to_restore if it isn't NULL. + * + * Context: Any context. Expects sai->xfer_lock to be held by caller. + */ +static void rockchip_sai_xfer_clk_stop_and_wait(struct rk_sai_dev *sai, unsigned int *to_restore) +{ + unsigned int mask = SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK; + unsigned int disable = SAI_XFER_CLK_DIS | SAI_XFER_FSS_DIS; + unsigned int val; + + assert_spin_locked(&sai->xfer_lock); + + regmap_read(sai->regmap, SAI_XFER, &val); + if ((val & mask) == disable) + goto wait_for_idle; + + if (sai->is_master_mode) + regmap_update_bits(sai->regmap, SAI_XFER, mask, disable); + +wait_for_idle: + rockchip_sai_poll_clk_idle(sai); + + if (to_restore) + *to_restore = val; +} + +static int rockchip_sai_runtime_suspend(struct device *dev) +{ + struct rk_sai_dev *sai = dev_get_drvdata(dev); + unsigned long flags; + + dev_dbg(dev, "Runtime suspending device!\n"); + + rockchip_sai_fsync_lost_detect(sai, 0); + rockchip_sai_fsync_err_detect(sai, 0); + + spin_lock_irqsave(&sai->xfer_lock, flags); + rockchip_sai_xfer_clk_stop_and_wait(sai, NULL); + spin_unlock_irqrestore(&sai->xfer_lock, flags); + + regcache_cache_only(sai->regmap, true); + /* + * After FS is idle, we should wait at least 2 BCLK cycles to make sure + * the CLK gate operation has completed, and only then disable mclk. + * + * Otherwise, the BCLK is still ungated, and once the mclk is enabled, + * there is a risk that a few BCLK cycles leak. This is true especially + * at low speeds, such as with a samplerate of 8k. + * + * Ideally we'd adjust the delay based on the samplerate, but it's such + * a tiny value that we can just delay for the maximum clock period + * for the sake of simplicity. + * + * The maximum BCLK period is 31us @ 8K-8Bit (64kHz BCLK). We wait for + * 40us to give ourselves a safety margin in case udelay falls short. + */ + udelay(40); + clk_disable_unprepare(sai->mclk); + clk_disable_unprepare(sai->hclk); + + return 0; +} + +static int rockchip_sai_runtime_resume(struct device *dev) +{ + struct rk_sai_dev *sai = dev_get_drvdata(dev); + unsigned long flags; + int ret; + + dev_dbg(dev, "Runtime resuming device!\n"); + + ret = clk_prepare_enable(sai->hclk); + if (ret) + goto err_hclk; + + ret = clk_prepare_enable(sai->mclk); + if (ret) + goto err_mclk; + + regcache_cache_only(sai->regmap, false); + regcache_mark_dirty(sai->regmap); + ret = regcache_sync(sai->regmap); + if (ret) + goto err_regmap; + + if (sai->quirks & QUIRK_ALWAYS_ON && sai->is_master_mode) { + spin_lock_irqsave(&sai->xfer_lock, flags); + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | + SAI_XFER_FSS_MASK, + SAI_XFER_CLK_EN | + SAI_XFER_FSS_EN); + spin_unlock_irqrestore(&sai->xfer_lock, flags); + } + + return 0; + +err_regmap: + clk_disable_unprepare(sai->mclk); +err_mclk: + clk_disable_unprepare(sai->hclk); +err_hclk: + return ret; +} + +static void rockchip_sai_fifo_xrun_detect(struct rk_sai_dev *sai, + int stream, bool en) +{ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* clear irq status which was asserted before TXUIE enabled */ + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_TXUIC, SAI_INTCR_TXUIC); + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_TXUIE_MASK, + SAI_INTCR_TXUIE(en)); + } else { + /* clear irq status which was asserted before RXOIE enabled */ + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_RXOIC, SAI_INTCR_RXOIC); + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_RXOIE_MASK, + SAI_INTCR_RXOIE(en)); + } +} + +static void rockchip_sai_dma_ctrl(struct rk_sai_dev *sai, + int stream, bool en) +{ + if (!en) + rockchip_sai_fifo_xrun_detect(sai, stream, 0); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(sai->regmap, SAI_DMACR, + SAI_DMACR_TDE_MASK, + SAI_DMACR_TDE(en)); + } else { + regmap_update_bits(sai->regmap, SAI_DMACR, + SAI_DMACR_RDE_MASK, + SAI_DMACR_RDE(en)); + } + + if (en) + rockchip_sai_fifo_xrun_detect(sai, stream, 1); +} + +static void rockchip_sai_reset(struct rk_sai_dev *sai) +{ + /* + * It is advised to reset the hclk domain before resetting the mclk + * domain, especially in slave mode without a clock input. + * + * To deal with the aforementioned case of slave mode without a clock + * input, we work around a potential issue by resetting the whole + * controller, bringing it back into master mode, and then recovering + * the controller configuration in the regmap. + */ + reset_control_assert(sai->rst_h); + udelay(10); + reset_control_deassert(sai->rst_h); + udelay(10); + reset_control_assert(sai->rst_m); + udelay(10); + reset_control_deassert(sai->rst_m); + udelay(10); + + /* recover regmap config */ + regcache_mark_dirty(sai->regmap); + regcache_sync(sai->regmap); +} + +static int rockchip_sai_clear(struct rk_sai_dev *sai, unsigned int clr) +{ + unsigned int val = 0; + int ret = 0; + + regmap_update_bits(sai->regmap, SAI_CLR, clr, clr); + ret = regmap_read_poll_timeout_atomic(sai->regmap, SAI_CLR, val, + !(val & clr), 10, TIMEOUT_US); + if (ret < 0) { + dev_warn(sai->dev, "Failed to clear %u\n", clr); + rockchip_sai_reset(sai); + } + + return ret; +} + +static void rockchip_sai_xfer_start(struct rk_sai_dev *sai, int stream) +{ + unsigned int msk, val; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + msk = SAI_XFER_TXS_MASK; + val = SAI_XFER_TXS_EN; + + } else { + msk = SAI_XFER_RXS_MASK; + val = SAI_XFER_RXS_EN; + } + + regmap_update_bits(sai->regmap, SAI_XFER, msk, val); +} + +static void rockchip_sai_xfer_stop(struct rk_sai_dev *sai, int stream) +{ + unsigned int msk = 0, val = 0, clr = 0; + bool playback; + bool capture; + + if (stream < 0) { + playback = true; + capture = true; + } else if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + playback = true; + capture = false; + } else { + playback = true; + capture = false; + } + + if (playback) { + msk |= SAI_XFER_TXS_MASK; + val |= SAI_XFER_TXS_DIS; + clr |= SAI_CLR_TXC; + } + if (capture) { + msk |= SAI_XFER_RXS_MASK; + val |= SAI_XFER_RXS_DIS; + clr |= SAI_CLR_RXC; + } + + regmap_update_bits(sai->regmap, SAI_XFER, msk, val); + rockchip_sai_poll_stream_idle(sai, playback, capture); + + rockchip_sai_clear(sai, clr); +} + +static void rockchip_sai_start(struct rk_sai_dev *sai, int stream) +{ + rockchip_sai_dma_ctrl(sai, stream, 1); + rockchip_sai_xfer_start(sai, stream); +} + +static void rockchip_sai_stop(struct rk_sai_dev *sai, int stream) +{ + rockchip_sai_dma_ctrl(sai, stream, 0); + rockchip_sai_xfer_stop(sai, stream); +} + +static void rockchip_sai_fmt_create(struct rk_sai_dev *sai, unsigned int fmt) +{ + unsigned int xcr_mask = 0, xcr_val = 0, xsft_mask = 0, xsft_val = 0; + unsigned int fscr_mask = 0, fscr_val = 0; + + assert_spin_locked(&sai->xfer_lock); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK; + xcr_val = SAI_XCR_VDJ_R | SAI_XCR_EDGE_SHIFT_0; + xsft_mask = SAI_XSHIFT_RIGHT_MASK; + xsft_val = SAI_XSHIFT_RIGHT(0); + fscr_mask = SAI_FSCR_EDGE_MASK; + fscr_val = SAI_FSCR_EDGE_DUAL; + sai->fpw = FPW_HALF_FRAME_WIDTH; + break; + case SND_SOC_DAIFMT_LEFT_J: + xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK; + xcr_val = SAI_XCR_VDJ_L | SAI_XCR_EDGE_SHIFT_0; + xsft_mask = SAI_XSHIFT_RIGHT_MASK; + xsft_val = SAI_XSHIFT_RIGHT(0); + fscr_mask = SAI_FSCR_EDGE_MASK; + fscr_val = SAI_FSCR_EDGE_DUAL; + sai->fpw = FPW_HALF_FRAME_WIDTH; + break; + case SND_SOC_DAIFMT_I2S: + xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK; + xcr_val = SAI_XCR_VDJ_L | SAI_XCR_EDGE_SHIFT_1; + xsft_mask = SAI_XSHIFT_RIGHT_MASK; + xsft_val = SAI_XSHIFT_RIGHT(2); + fscr_mask = SAI_FSCR_EDGE_MASK; + fscr_val = SAI_FSCR_EDGE_DUAL; + sai->fpw = FPW_HALF_FRAME_WIDTH; + break; + case SND_SOC_DAIFMT_DSP_A: + xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK; + xcr_val = SAI_XCR_VDJ_L | SAI_XCR_EDGE_SHIFT_0; + xsft_mask = SAI_XSHIFT_RIGHT_MASK; + xsft_val = SAI_XSHIFT_RIGHT(2); + fscr_mask = SAI_FSCR_EDGE_MASK; + fscr_val = SAI_FSCR_EDGE_RISING; + sai->fpw = FPW_ONE_BCLK_WIDTH; + break; + case SND_SOC_DAIFMT_DSP_B: + xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK; + xcr_val = SAI_XCR_VDJ_L | SAI_XCR_EDGE_SHIFT_0; + xsft_mask = SAI_XSHIFT_RIGHT_MASK; + xsft_val = SAI_XSHIFT_RIGHT(0); + fscr_mask = SAI_FSCR_EDGE_MASK; + fscr_val = SAI_FSCR_EDGE_RISING; + sai->fpw = FPW_ONE_BCLK_WIDTH; + break; + default: + dev_err(sai->dev, "Unsupported fmt %u\n", fmt); + break; + } + + regmap_update_bits(sai->regmap, SAI_TXCR, xcr_mask, xcr_val); + regmap_update_bits(sai->regmap, SAI_RXCR, xcr_mask, xcr_val); + regmap_update_bits(sai->regmap, SAI_TX_SHIFT, xsft_mask, xsft_val); + regmap_update_bits(sai->regmap, SAI_RX_SHIFT, xsft_mask, xsft_val); + regmap_update_bits(sai->regmap, SAI_FSCR, fscr_mask, fscr_val); +} + +static int rockchip_sai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + unsigned int mask = 0, val = 0; + unsigned int clk_gates; + unsigned long flags; + int ret = 0; + + pm_runtime_get_sync(dai->dev); + + mask = SAI_CKR_MSS_MASK; + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BP_FP: + val = SAI_CKR_MSS_MASTER; + sai->is_master_mode = true; + break; + case SND_SOC_DAIFMT_BC_FC: + val = SAI_CKR_MSS_SLAVE; + sai->is_master_mode = false; + break; + default: + ret = -EINVAL; + goto err_pm_put; + } + + spin_lock_irqsave(&sai->xfer_lock, flags); + rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates); + if (sai->initialized) { + if (sai->has_capture && sai->has_playback) + rockchip_sai_xfer_stop(sai, -1); + else if (sai->has_capture) + rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_CAPTURE); + else + rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_PLAYBACK); + } else { + rockchip_sai_clear(sai, 0); + sai->initialized = true; + } + + regmap_update_bits(sai->regmap, SAI_CKR, mask, val); + + mask = SAI_CKR_CKP_MASK | SAI_CKR_FSP_MASK; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_NORMAL; + break; + case SND_SOC_DAIFMT_NB_IF: + val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_INVERTED; + break; + case SND_SOC_DAIFMT_IB_NF: + val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_NORMAL; + break; + case SND_SOC_DAIFMT_IB_IF: + val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_INVERTED; + break; + default: + ret = -EINVAL; + goto err_xfer_unlock; + } + + regmap_update_bits(sai->regmap, SAI_CKR, mask, val); + + rockchip_sai_fmt_create(sai, fmt); + +err_xfer_unlock: + if (clk_gates) + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK, + clk_gates); + spin_unlock_irqrestore(&sai->xfer_lock, flags); +err_pm_put: + pm_runtime_put(dai->dev); + + return ret; +} + +static int rockchip_sai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + struct snd_dmaengine_dai_dma_data *dma_data; + unsigned int mclk_rate, mclk_req_rate, bclk_rate, div_bclk; + unsigned int ch_per_lane, slot_width; + unsigned int val, fscr, reg; + unsigned int lanes; + unsigned long flags; + int ret = 0; + + if (!rockchip_sai_stream_valid(substream, dai)) + return 0; + + dma_data = snd_soc_dai_get_dma_data(dai, substream); + dma_data->maxburst = MAXBURST_PER_FIFO * params_channels(params) / 2; + + pm_runtime_get_sync(sai->dev); + + regmap_read(sai->regmap, SAI_DMACR, &val); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + reg = SAI_TXCR; + lanes = sai->tx_lanes; + } else { + reg = SAI_RXCR; + lanes = sai->rx_lanes; + } + + if (!sai->is_tdm && lanes != DIV_ROUND_UP(params_channels(params), 2)) { + dev_err(sai->dev, "not enough lanes (%d) for requested number of %s channels (%d)\n", + lanes, reg == SAI_TXCR ? "playback" : "capture", + params_channels(params)); + ret = -EINVAL; + goto err_pm_put; + } + + dev_dbg(sai->dev, "using %d lanes totalling %d%s channels for %s\n", + lanes, params_channels(params), sai->is_tdm ? " (TDM)" : "", + reg == SAI_TXCR ? "playback" : "capture"); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + val = SAI_XCR_VDW(8); + break; + case SNDRV_PCM_FORMAT_S16_LE: + val = SAI_XCR_VDW(16); + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = SAI_XCR_VDW(24); + break; + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + val = SAI_XCR_VDW(32); + break; + default: + ret = -EINVAL; + goto err_pm_put; + } + + val |= SAI_XCR_CSR(lanes); + + spin_lock_irqsave(&sai->xfer_lock, flags); + + regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK | SAI_XCR_CSR_MASK, val); + + regmap_read(sai->regmap, reg, &val); + + slot_width = SAI_XCR_SBW_V(val); + ch_per_lane = params_channels(params) / lanes; + + regmap_update_bits(sai->regmap, reg, SAI_XCR_SNB_MASK, + SAI_XCR_SNB(ch_per_lane)); + + fscr = SAI_FSCR_FW(sai->fw_ratio * slot_width * ch_per_lane); + + switch (sai->fpw) { + case FPW_ONE_BCLK_WIDTH: + fscr |= SAI_FSCR_FPW(1); + break; + case FPW_ONE_SLOT_WIDTH: + fscr |= SAI_FSCR_FPW(slot_width); + break; + case FPW_HALF_FRAME_WIDTH: + fscr |= SAI_FSCR_FPW(sai->fw_ratio * slot_width * ch_per_lane / 2); + break; + default: + dev_err(sai->dev, "Invalid Frame Pulse Width %d\n", sai->fpw); + ret = -EINVAL; + goto err_xfer_unlock; + } + + regmap_update_bits(sai->regmap, SAI_FSCR, + SAI_FSCR_FW_MASK | SAI_FSCR_FPW_MASK, fscr); + + if (sai->is_master_mode) { + bclk_rate = sai->fw_ratio * slot_width * ch_per_lane * params_rate(params); + ret = clk_set_rate(sai->mclk, sai->mclk_rate); + if (ret) { + dev_err(sai->dev, "Failed to set mclk to %u: %pe\n", + sai->mclk_rate, ERR_PTR(ret)); + goto err_xfer_unlock; + } + + mclk_rate = clk_get_rate(sai->mclk); + if (mclk_rate < bclk_rate) { + dev_err(sai->dev, "Mismatch mclk: %u, at least %u\n", + mclk_rate, bclk_rate); + ret = -EINVAL; + goto err_xfer_unlock; + } + + div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); + mclk_req_rate = bclk_rate * div_bclk; + + if (mclk_rate < mclk_req_rate - CLK_SHIFT_RATE_HZ_MAX || + mclk_rate > mclk_req_rate + CLK_SHIFT_RATE_HZ_MAX) { + dev_err(sai->dev, "Mismatch mclk: %u, expected %u (+/- %dHz)\n", + mclk_rate, mclk_req_rate, CLK_SHIFT_RATE_HZ_MAX); + ret = -EINVAL; + goto err_xfer_unlock; + } + + regmap_update_bits(sai->regmap, SAI_CKR, SAI_CKR_MDIV_MASK, + SAI_CKR_MDIV(div_bclk)); + } + +err_xfer_unlock: + spin_unlock_irqrestore(&sai->xfer_lock, flags); +err_pm_put: + pm_runtime_put(sai->dev); + + return ret; +} + +static int rockchip_sai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + unsigned long flags; + + if (!rockchip_sai_stream_valid(substream, dai)) + return 0; + + if (sai->is_master_mode) { + /* + * We should wait for the first BCLK pulse to have definitely + * occurred after any DIV settings have potentially been + * changed in order to guarantee a clean clock signal once we + * ungate the clock. + * + * Ideally, this would be done depending on the samplerate, but + * for the sake of simplicity, we'll just delay for the maximum + * possible clock offset time, which is quite a small value. + * + * The maximum BCLK offset is 15.6us @ 8K-8Bit (64kHz BCLK). We + * wait for 20us in order to give us a safety margin in case + * udelay falls short. + */ + udelay(20); + spin_lock_irqsave(&sai->xfer_lock, flags); + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | + SAI_XFER_FSS_MASK, + SAI_XFER_CLK_EN | + SAI_XFER_FSS_EN); + spin_unlock_irqrestore(&sai->xfer_lock, flags); + } + + rockchip_sai_fsync_lost_detect(sai, 1); + rockchip_sai_fsync_err_detect(sai, 1); + + return 0; +} + +static void rockchip_sai_path_config(struct rk_sai_dev *sai, + int num, bool is_rx) +{ + int i; + + if (is_rx) + for (i = 0; i < num; i++) + regmap_update_bits(sai->regmap, SAI_PATH_SEL, + SAI_RX_PATH_MASK(i), + SAI_RX_PATH(i, sai->sdi[i])); + else + for (i = 0; i < num; i++) + regmap_update_bits(sai->regmap, SAI_PATH_SEL, + SAI_TX_PATH_MASK(i), + SAI_TX_PATH(i, sai->sdo[i])); +} + +static int rockchip_sai_path_prepare(struct rk_sai_dev *sai, + struct device_node *np, + bool is_rx) +{ + const char *path_prop; + unsigned int *data; + unsigned int *lanes; + int i, num, ret; + + if (is_rx) { + path_prop = "rockchip,sai-rx-route"; + data = sai->sdi; + lanes = &sai->rx_lanes; + } else { + path_prop = "rockchip,sai-tx-route"; + data = sai->sdo; + lanes = &sai->tx_lanes; + } + + num = of_count_phandle_with_args(np, path_prop, NULL); + if (num == -ENOENT) { + return 0; + } else if (num > MAX_LANES || num == 0) { + dev_err(sai->dev, "found %d entries in %s, outside of range 1 to %d\n", + num, path_prop, MAX_LANES); + return -EINVAL; + } else if (num < 0) { + dev_err(sai->dev, "error in %s property: %pe\n", path_prop, + ERR_PTR(num)); + return num; + } + + *lanes = num; + ret = device_property_read_u32_array(sai->dev, path_prop, data, num); + if (ret < 0) { + dev_err(sai->dev, "failed to read property '%s': %pe\n", + path_prop, ERR_PTR(ret)); + return ret; + } + + for (i = 0; i < num; i++) { + if (data[i] >= MAX_LANES) { + dev_err(sai->dev, "%s[%d] is %d, should be less than %d\n", + path_prop, i, data[i], MAX_LANES); + return -EINVAL; + } + } + + rockchip_sai_path_config(sai, num, is_rx); + + return 0; +} + +static int rockchip_sai_parse_paths(struct rk_sai_dev *sai, + struct device_node *np) +{ + int ret; + + if (sai->has_playback) { + sai->tx_lanes = 1; + ret = rockchip_sai_path_prepare(sai, np, false); + if (ret < 0) { + dev_err(sai->dev, "Failed to prepare TX path: %pe\n", + ERR_PTR(ret)); + return ret; + } + } + + if (sai->has_capture) { + sai->rx_lanes = 1; + ret = rockchip_sai_path_prepare(sai, np, true); + if (ret < 0) { + dev_err(sai->dev, "Failed to prepare RX path: %pe\n", + ERR_PTR(ret)); + return ret; + } + } + + return 0; +} + +static int rockchip_sai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + if (!rockchip_sai_stream_valid(substream, dai)) + return 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + rockchip_sai_start(sai, substream->stream); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + rockchip_sai_stop(sai, substream->stream); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + + +static int rockchip_sai_dai_probe(struct snd_soc_dai *dai) +{ + struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, + sai->has_playback ? &sai->playback_dma_data : NULL, + sai->has_capture ? &sai->capture_dma_data : NULL); + + return 0; +} + +static int rockchip_sai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + int stream = substream->stream; + + if (!rockchip_sai_stream_valid(substream, dai)) + return 0; + + if (sai->substreams[stream]) + return -EBUSY; + + if (sai->wait_time[stream]) + substream->wait_time = sai->wait_time[stream]; + + sai->substreams[stream] = substream; + + return 0; +} + +static void rockchip_sai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + + if (!rockchip_sai_stream_valid(substream, dai)) + return; + + sai->substreams[substream->stream] = NULL; +} + +static int rockchip_sai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + unsigned long flags; + unsigned int clk_gates; + + if (slot_width < 16 || slot_width > 32) + return -EINVAL; + + pm_runtime_get_sync(dai->dev); + spin_lock_irqsave(&sai->xfer_lock, flags); + rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates); + regmap_update_bits(sai->regmap, SAI_TXCR, SAI_XCR_SBW_MASK, + SAI_XCR_SBW(slot_width)); + regmap_update_bits(sai->regmap, SAI_RXCR, SAI_XCR_SBW_MASK, + SAI_XCR_SBW(slot_width)); + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK, + clk_gates); + spin_unlock_irqrestore(&sai->xfer_lock, flags); + pm_runtime_put(dai->dev); + + sai->is_tdm = true; + + return 0; +} + +static int rockchip_sai_set_sysclk(struct snd_soc_dai *dai, int stream, + unsigned int freq, int dir) +{ + struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + + sai->mclk_rate = freq; + + return 0; +} + +static const struct snd_soc_dai_ops rockchip_sai_dai_ops = { + .probe = rockchip_sai_dai_probe, + .startup = rockchip_sai_startup, + .shutdown = rockchip_sai_shutdown, + .hw_params = rockchip_sai_hw_params, + .set_fmt = rockchip_sai_set_fmt, + .set_sysclk = rockchip_sai_set_sysclk, + .prepare = rockchip_sai_prepare, + .trigger = rockchip_sai_trigger, + .set_tdm_slot = rockchip_sai_set_tdm_slot, +}; + +static const struct snd_soc_dai_driver rockchip_sai_dai = { + .ops = &rockchip_sai_dai_ops, + .symmetric_rate = 1, +}; + +static bool rockchip_sai_wr_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SAI_TXCR: + case SAI_FSCR: + case SAI_RXCR: + case SAI_MONO_CR: + case SAI_XFER: + case SAI_CLR: + case SAI_CKR: + case SAI_DMACR: + case SAI_INTCR: + case SAI_TXDR: + case SAI_PATH_SEL: + case SAI_TX_SLOT_MASK0: + case SAI_TX_SLOT_MASK1: + case SAI_TX_SLOT_MASK2: + case SAI_TX_SLOT_MASK3: + case SAI_RX_SLOT_MASK0: + case SAI_RX_SLOT_MASK1: + case SAI_RX_SLOT_MASK2: + case SAI_RX_SLOT_MASK3: + case SAI_TX_SHIFT: + case SAI_RX_SHIFT: + case SAI_FSXN: + case SAI_FS_TIMEOUT: + case SAI_LOOPBACK_LR: + return true; + default: + return false; + } +} + +static bool rockchip_sai_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SAI_TXCR: + case SAI_FSCR: + case SAI_RXCR: + case SAI_MONO_CR: + case SAI_XFER: + case SAI_CLR: + case SAI_CKR: + case SAI_TXFIFOLR: + case SAI_RXFIFOLR: + case SAI_DMACR: + case SAI_INTCR: + case SAI_INTSR: + case SAI_TXDR: + case SAI_RXDR: + case SAI_PATH_SEL: + case SAI_TX_SLOT_MASK0: + case SAI_TX_SLOT_MASK1: + case SAI_TX_SLOT_MASK2: + case SAI_TX_SLOT_MASK3: + case SAI_RX_SLOT_MASK0: + case SAI_RX_SLOT_MASK1: + case SAI_RX_SLOT_MASK2: + case SAI_RX_SLOT_MASK3: + case SAI_TX_DATA_CNT: + case SAI_RX_DATA_CNT: + case SAI_TX_SHIFT: + case SAI_RX_SHIFT: + case SAI_STATUS: + case SAI_VERSION: + case SAI_FSXN: + case SAI_FS_TIMEOUT: + case SAI_LOOPBACK_LR: + return true; + default: + return false; + } +} + +static bool rockchip_sai_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SAI_XFER: + case SAI_INTCR: + case SAI_INTSR: + case SAI_CLR: + case SAI_TXFIFOLR: + case SAI_RXFIFOLR: + case SAI_TXDR: + case SAI_RXDR: + case SAI_TX_DATA_CNT: + case SAI_RX_DATA_CNT: + case SAI_STATUS: + case SAI_VERSION: + return true; + default: + return false; + } +} + +static bool rockchip_sai_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SAI_RXDR: + return true; + default: + return false; + } +} + +static const struct reg_default rockchip_sai_reg_defaults[] = { + { SAI_TXCR, 0x00000bff }, + { SAI_FSCR, 0x0001f03f }, + { SAI_RXCR, 0x00000bff }, + { SAI_PATH_SEL, 0x0000e4e4 }, +}; + +static const struct regmap_config rockchip_sai_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SAI_LOOPBACK_LR, + .reg_defaults = rockchip_sai_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rockchip_sai_reg_defaults), + .writeable_reg = rockchip_sai_wr_reg, + .readable_reg = rockchip_sai_rd_reg, + .volatile_reg = rockchip_sai_volatile_reg, + .precious_reg = rockchip_sai_precious_reg, + .cache_type = REGCACHE_FLAT, +}; + +static const struct of_device_id rockchip_sai_match[] = { + { .compatible = "rockchip,rk3576-sai", }, + {}, +}; + +static int rockchip_sai_init_dai(struct rk_sai_dev *sai, struct resource *res, + struct snd_soc_dai_driver **dp) +{ + struct device_node *node = sai->dev->of_node; + struct snd_soc_dai_driver *dai; + struct property *dma_names; + const char *dma_name; + + of_property_for_each_string(node, "dma-names", dma_names, dma_name) { + if (!strcmp(dma_name, "tx")) + sai->has_playback = true; + if (!strcmp(dma_name, "rx")) + sai->has_capture = true; + } + + dai = devm_kmemdup(sai->dev, &rockchip_sai_dai, + sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + if (sai->has_playback) { + dai->playback.stream_name = "Playback"; + dai->playback.channels_min = 1; + dai->playback.channels_max = 512; + dai->playback.rates = SNDRV_PCM_RATE_8000_384000; + dai->playback.formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; + + sai->playback_dma_data.addr = res->start + SAI_TXDR; + sai->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + sai->playback_dma_data.maxburst = MAXBURST_PER_FIFO; + } + + if (sai->has_capture) { + dai->capture.stream_name = "Capture"; + dai->capture.channels_min = 1; + dai->capture.channels_max = 512; + dai->capture.rates = SNDRV_PCM_RATE_8000_384000; + dai->capture.formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; + + sai->capture_dma_data.addr = res->start + SAI_RXDR; + sai->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + sai->capture_dma_data.maxburst = MAXBURST_PER_FIFO; + } + + regmap_update_bits(sai->regmap, SAI_DMACR, SAI_DMACR_TDL_MASK, + SAI_DMACR_TDL(16)); + regmap_update_bits(sai->regmap, SAI_DMACR, SAI_DMACR_RDL_MASK, + SAI_DMACR_RDL(16)); + + if (dp) + *dp = dai; + + return 0; +} + +static const char * const mono_text[] = { "Disable", "Enable" }; + +static DECLARE_TLV_DB_SCALE(rmss_tlv, 0, 128, 0); + +static const char * const lplrc_text[] = { "L:MIC R:LP", "L:LP R:MIC" }; +static const char * const lplr_text[] = { "Disable", "Enable" }; + +static const char * const lpx_text[] = { + "From SDO0", "From SDO1", "From SDO2", "From SDO3" }; + +static const char * const lps_text[] = { "Disable", "Enable" }; +static const char * const sync_out_text[] = { "From CRU", "From IO" }; +static const char * const sync_in_text[] = { "From IO", "From Sync Port" }; + +static const char * const rpaths_text[] = { + "From SDI0", "From SDI1", "From SDI2", "From SDI3" }; + +static const char * const tpaths_text[] = { + "From PATH0", "From PATH1", "From PATH2", "From PATH3" }; + +/* MONO_CR */ +static SOC_ENUM_SINGLE_DECL(rmono_switch, SAI_MONO_CR, 1, mono_text); +static SOC_ENUM_SINGLE_DECL(tmono_switch, SAI_MONO_CR, 0, mono_text); + +/* PATH_SEL */ +static SOC_ENUM_SINGLE_DECL(lp3_enum, SAI_PATH_SEL, 28, lpx_text); +static SOC_ENUM_SINGLE_DECL(lp2_enum, SAI_PATH_SEL, 26, lpx_text); +static SOC_ENUM_SINGLE_DECL(lp1_enum, SAI_PATH_SEL, 24, lpx_text); +static SOC_ENUM_SINGLE_DECL(lp0_enum, SAI_PATH_SEL, 22, lpx_text); +static SOC_ENUM_SINGLE_DECL(lp3_switch, SAI_PATH_SEL, 21, lps_text); +static SOC_ENUM_SINGLE_DECL(lp2_switch, SAI_PATH_SEL, 20, lps_text); +static SOC_ENUM_SINGLE_DECL(lp1_switch, SAI_PATH_SEL, 19, lps_text); +static SOC_ENUM_SINGLE_DECL(lp0_switch, SAI_PATH_SEL, 18, lps_text); +static SOC_ENUM_SINGLE_DECL(sync_out_switch, SAI_PATH_SEL, 17, sync_out_text); +static SOC_ENUM_SINGLE_DECL(sync_in_switch, SAI_PATH_SEL, 16, sync_in_text); +static SOC_ENUM_SINGLE_DECL(rpath3_enum, SAI_PATH_SEL, 14, rpaths_text); +static SOC_ENUM_SINGLE_DECL(rpath2_enum, SAI_PATH_SEL, 12, rpaths_text); +static SOC_ENUM_SINGLE_DECL(rpath1_enum, SAI_PATH_SEL, 10, rpaths_text); +static SOC_ENUM_SINGLE_DECL(rpath0_enum, SAI_PATH_SEL, 8, rpaths_text); +static SOC_ENUM_SINGLE_DECL(tpath3_enum, SAI_PATH_SEL, 6, tpaths_text); +static SOC_ENUM_SINGLE_DECL(tpath2_enum, SAI_PATH_SEL, 4, tpaths_text); +static SOC_ENUM_SINGLE_DECL(tpath1_enum, SAI_PATH_SEL, 2, tpaths_text); +static SOC_ENUM_SINGLE_DECL(tpath0_enum, SAI_PATH_SEL, 0, tpaths_text); + +/* LOOPBACK_LR */ +static SOC_ENUM_SINGLE_DECL(lp3lrc_enum, SAI_LOOPBACK_LR, 7, lplrc_text); +static SOC_ENUM_SINGLE_DECL(lp2lrc_enum, SAI_LOOPBACK_LR, 6, lplrc_text); +static SOC_ENUM_SINGLE_DECL(lp1lrc_enum, SAI_LOOPBACK_LR, 5, lplrc_text); +static SOC_ENUM_SINGLE_DECL(lp0lrc_enum, SAI_LOOPBACK_LR, 4, lplrc_text); +static SOC_ENUM_SINGLE_DECL(lp3lr_switch, SAI_LOOPBACK_LR, 3, lplr_text); +static SOC_ENUM_SINGLE_DECL(lp2lr_switch, SAI_LOOPBACK_LR, 2, lplr_text); +static SOC_ENUM_SINGLE_DECL(lp1lr_switch, SAI_LOOPBACK_LR, 1, lplr_text); +static SOC_ENUM_SINGLE_DECL(lp0lr_switch, SAI_LOOPBACK_LR, 0, lplr_text); + +static int rockchip_sai_wait_time_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = WAIT_TIME_MS_MAX; + uinfo->value.integer.step = 1; + + return 0; +} + +static int rockchip_sai_rd_wait_time_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = sai->wait_time[SNDRV_PCM_STREAM_CAPTURE]; + + return 0; +} + +static int rockchip_sai_rd_wait_time_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component); + + if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX) + return -EINVAL; + + sai->wait_time[SNDRV_PCM_STREAM_CAPTURE] = ucontrol->value.integer.value[0]; + + return 1; +} + +static int rockchip_sai_wr_wait_time_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = sai->wait_time[SNDRV_PCM_STREAM_PLAYBACK]; + + return 0; +} + +static int rockchip_sai_wr_wait_time_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component); + + if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX) + return -EINVAL; + + sai->wait_time[SNDRV_PCM_STREAM_PLAYBACK] = ucontrol->value.integer.value[0]; + + return 1; +} + +#define SAI_PCM_WAIT_TIME(xname, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, \ + .info = rockchip_sai_wait_time_info, \ + .get = xhandler_get, .put = xhandler_put } + +static const struct snd_kcontrol_new rockchip_sai_controls[] = { + SOC_SINGLE_TLV("Receive Mono Slot Select", SAI_MONO_CR, + 2, 128, 0, rmss_tlv), + SOC_ENUM("Receive Mono Switch", rmono_switch), + SOC_ENUM("Transmit Mono Switch", tmono_switch), + + SOC_ENUM("SDI3 Loopback I2S LR Channel Sel", lp3lrc_enum), + SOC_ENUM("SDI2 Loopback I2S LR Channel Sel", lp2lrc_enum), + SOC_ENUM("SDI1 Loopback I2S LR Channel Sel", lp1lrc_enum), + SOC_ENUM("SDI0 Loopback I2S LR Channel Sel", lp0lrc_enum), + SOC_ENUM("SDI3 Loopback I2S LR Switch", lp3lr_switch), + SOC_ENUM("SDI2 Loopback I2S LR Switch", lp2lr_switch), + SOC_ENUM("SDI1 Loopback I2S LR Switch", lp1lr_switch), + SOC_ENUM("SDI0 Loopback I2S LR Switch", lp0lr_switch), + + SOC_ENUM("SDI3 Loopback Src Select", lp3_enum), + SOC_ENUM("SDI2 Loopback Src Select", lp2_enum), + SOC_ENUM("SDI1 Loopback Src Select", lp1_enum), + SOC_ENUM("SDI0 Loopback Src Select", lp0_enum), + SOC_ENUM("SDI3 Loopback Switch", lp3_switch), + SOC_ENUM("SDI2 Loopback Switch", lp2_switch), + SOC_ENUM("SDI1 Loopback Switch", lp1_switch), + SOC_ENUM("SDI0 Loopback Switch", lp0_switch), + SOC_ENUM("Sync Out Switch", sync_out_switch), + SOC_ENUM("Sync In Switch", sync_in_switch), + SOC_ENUM("Receive PATH3 Source Select", rpath3_enum), + SOC_ENUM("Receive PATH2 Source Select", rpath2_enum), + SOC_ENUM("Receive PATH1 Source Select", rpath1_enum), + SOC_ENUM("Receive PATH0 Source Select", rpath0_enum), + SOC_ENUM("Transmit SDO3 Source Select", tpath3_enum), + SOC_ENUM("Transmit SDO2 Source Select", tpath2_enum), + SOC_ENUM("Transmit SDO1 Source Select", tpath1_enum), + SOC_ENUM("Transmit SDO0 Source Select", tpath0_enum), + + SAI_PCM_WAIT_TIME("PCM Read Wait Time MS", + rockchip_sai_rd_wait_time_get, + rockchip_sai_rd_wait_time_put), + SAI_PCM_WAIT_TIME("PCM Write Wait Time MS", + rockchip_sai_wr_wait_time_get, + rockchip_sai_wr_wait_time_put), +}; + +static const struct snd_soc_component_driver rockchip_sai_component = { + .name = DRV_NAME, + .controls = rockchip_sai_controls, + .num_controls = ARRAY_SIZE(rockchip_sai_controls), + .legacy_dai_naming = 1, +}; + +static irqreturn_t rockchip_sai_isr(int irq, void *devid) +{ + struct rk_sai_dev *sai = (struct rk_sai_dev *)devid; + struct snd_pcm_substream *substream; + u32 val; + + regmap_read(sai->regmap, SAI_INTSR, &val); + if (val & SAI_INTSR_TXUI_ACT) { + dev_warn_ratelimited(sai->dev, "TX FIFO Underrun\n"); + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_TXUIC, SAI_INTCR_TXUIC); + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_TXUIE_MASK, + SAI_INTCR_TXUIE(0)); + substream = sai->substreams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream) + snd_pcm_stop_xrun(substream); + } + + if (val & SAI_INTSR_RXOI_ACT) { + dev_warn_ratelimited(sai->dev, "RX FIFO Overrun\n"); + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_RXOIC, SAI_INTCR_RXOIC); + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_RXOIE_MASK, + SAI_INTCR_RXOIE(0)); + substream = sai->substreams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream) + snd_pcm_stop_xrun(substream); + } + + if (val & SAI_INTSR_FSERRI_ACT) { + dev_warn_ratelimited(sai->dev, "Frame Sync Error\n"); + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_FSERRC, SAI_INTCR_FSERRC); + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_FSERR_MASK, + SAI_INTCR_FSERR(0)); + } + + if (val & SAI_INTSR_FSLOSTI_ACT) { + dev_warn_ratelimited(sai->dev, "Frame Sync Lost\n"); + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_FSLOSTC, SAI_INTCR_FSLOSTC); + regmap_update_bits(sai->regmap, SAI_INTCR, + SAI_INTCR_FSLOST_MASK, + SAI_INTCR_FSLOST(0)); + } + + return IRQ_HANDLED; +} + +static int rockchip_sai_keep_clk_always_on(struct rk_sai_dev *sai) +{ + unsigned int mclk_rate, bclk_rate, div_bclk; + + sai->is_master_mode = true; + + /* init I2S fmt default */ + rockchip_sai_fmt_create(sai, SND_SOC_DAIFMT_I2S); + + regmap_update_bits(sai->regmap, SAI_FSCR, + SAI_FSCR_FW_MASK | + SAI_FSCR_FPW_MASK, + SAI_FSCR_FW(64) | + SAI_FSCR_FPW(32)); + + mclk_rate = clk_get_rate(sai->mclk); + bclk_rate = DEFAULT_FS * 64; + div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); + + regmap_update_bits(sai->regmap, SAI_CKR, SAI_CKR_MDIV_MASK, + SAI_CKR_MDIV(div_bclk)); + + pm_runtime_forbid(sai->dev); + + dev_info(sai->dev, "CLK-ALWAYS-ON: mclk: %d, bclk: %d, fsync: %d\n", + mclk_rate, bclk_rate, DEFAULT_FS); + + return 0; +} + +static int rockchip_sai_parse_quirks(struct rk_sai_dev *sai) +{ + int ret = 0, i = 0; + + for (i = 0; i < ARRAY_SIZE(of_quirks); i++) + if (device_property_read_bool(sai->dev, of_quirks[i].quirk)) + sai->quirks |= of_quirks[i].id; + + if (sai->quirks & QUIRK_ALWAYS_ON) + ret = rockchip_sai_keep_clk_always_on(sai); + + return ret; +} + +static int rockchip_sai_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct rk_sai_dev *sai; + struct snd_soc_dai_driver *dai; + struct resource *res; + void __iomem *regs; + int ret, irq; + + sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); + if (!sai) + return -ENOMEM; + + sai->dev = &pdev->dev; + sai->fw_ratio = 1; + /* match to register default */ + sai->is_master_mode = true; + dev_set_drvdata(&pdev->dev, sai); + + spin_lock_init(&sai->xfer_lock); + + sai->rst_h = devm_reset_control_get_optional_exclusive(&pdev->dev, "h"); + if (IS_ERR(sai->rst_h)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->rst_h), + "Error in 'h' reset control\n"); + + sai->rst_m = devm_reset_control_get_optional_exclusive(&pdev->dev, "m"); + if (IS_ERR(sai->rst_m)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->rst_m), + "Error in 'm' reset control\n"); + + regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(regs)) + return dev_err_probe(&pdev->dev, PTR_ERR(regs), + "Failed to get and ioremap resource\n"); + + sai->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &rockchip_sai_regmap_config); + if (IS_ERR(sai->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->regmap), + "Failed to initialize regmap\n"); + + irq = platform_get_irq_optional(pdev, 0); + if (irq > 0) { + ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr, + IRQF_SHARED, node->name, sai); + if (ret) { + return dev_err_probe(&pdev->dev, ret, + "Failed to request irq %d\n", irq); + } + } else { + dev_dbg(&pdev->dev, "Asked for an IRQ but got %d\n", irq); + } + + sai->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(sai->mclk)) { + return dev_err_probe(&pdev->dev, PTR_ERR(sai->mclk), + "Failed to get mclk\n"); + } + + sai->hclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(sai->hclk)) { + return dev_err_probe(&pdev->dev, PTR_ERR(sai->hclk), + "Failed to get hclk\n"); + } + + ret = clk_prepare_enable(sai->hclk); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to enable hclk\n"); + + regmap_read(sai->regmap, SAI_VERSION, &sai->version); + + ret = rockchip_sai_parse_quirks(sai); + if (ret) { + dev_err(&pdev->dev, "Failed to parse device quirks: %d\n", ret); + goto err_disable_hclk; + } + + ret = rockchip_sai_init_dai(sai, res, &dai); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize DAI: %d\n", ret); + goto err_disable_hclk; + } + + ret = rockchip_sai_parse_paths(sai, node); + if (ret) { + dev_err(&pdev->dev, "Failed to parse paths: %d\n", ret); + goto err_disable_hclk; + } + + /* + * From here on, all register accesses need to be wrapped in + * pm_runtime_get_sync/pm_runtime_put calls + * + * NB: we don't rely on _resume_and_get in case of !CONFIG_PM + */ + devm_pm_runtime_enable(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + ret = rockchip_sai_runtime_resume(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Failed to resume device: %pe\n", ERR_PTR(ret)); + goto err_disable_hclk; + } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) { + dev_err(&pdev->dev, "Failed to register PCM: %d\n", ret); + goto err_runtime_suspend; + } + + ret = devm_snd_soc_register_component(&pdev->dev, + &rockchip_sai_component, + dai, 1); + if (ret) { + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + goto err_runtime_suspend; + } + + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_put(&pdev->dev); + + /* + * runtime_resume already enabled our hclk again, so we need to also + * get rid of the manual enable we did earlier to balance the counts. + */ + clk_disable_unprepare(sai->hclk); + + return 0; + +err_runtime_suspend: + /* If we're !CONFIG_PM, we get -ENOSYS and disable manually */ + if (pm_runtime_put(&pdev->dev)) + rockchip_sai_runtime_suspend(&pdev->dev); +err_disable_hclk: + clk_disable_unprepare(sai->hclk); + + return ret; +} + +static void rockchip_sai_remove(struct platform_device *pdev) +{ +#ifndef CONFIG_PM + rockchip_sai_runtime_suspend(&pdev->dev); +#endif +} + +static const struct dev_pm_ops rockchip_sai_pm_ops = { + SET_RUNTIME_PM_OPS(rockchip_sai_runtime_suspend, rockchip_sai_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + +static struct platform_driver rockchip_sai_driver = { + .probe = rockchip_sai_probe, + .remove = rockchip_sai_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(rockchip_sai_match), + .pm = &rockchip_sai_pm_ops, + }, +}; +module_platform_driver(rockchip_sai_driver); + +MODULE_DESCRIPTION("Rockchip SAI ASoC Interface"); +MODULE_AUTHOR("Sugar Zhang "); +MODULE_AUTHOR("Nicolas Frattaroli "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, rockchip_sai_match); diff --git a/sound/soc/rockchip/rockchip_sai.h b/sound/soc/rockchip/rockchip_sai.h new file mode 100644 index 0000000000000000000000000000000000000000..c359c5d0311cae3ca4d45309e54c88d059bf254a --- /dev/null +++ b/sound/soc/rockchip/rockchip_sai.h @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * ALSA SoC Audio Layer - Rockchip SAI Controller driver + * + * Copyright (c) 2022 Rockchip Electronics Co. Ltd. + */ + +#ifndef _ROCKCHIP_SAI_H +#define _ROCKCHIP_SAI_H + +/* XCR Transmit / Receive Control Register */ +#define SAI_XCR_START_SEL_MASK BIT(23) +#define SAI_XCR_START_SEL_CHAINED BIT(23) +#define SAI_XCR_START_SEL_STANDALONE 0 +#define SAI_XCR_EDGE_SHIFT_MASK BIT(22) +#define SAI_XCR_EDGE_SHIFT_1 BIT(22) +#define SAI_XCR_EDGE_SHIFT_0 0 +#define SAI_XCR_CSR_MASK GENMASK(21, 20) +#define SAI_XCR_CSR(x) ((x - 1) << 20) +#define SAI_XCR_CSR_V(v) ((((v) & SAI_XCR_CSR_MASK) >> 20) + 1) +#define SAI_XCR_SJM_MASK BIT(19) +#define SAI_XCR_SJM_L BIT(19) +#define SAI_XCR_SJM_R 0 +#define SAI_XCR_FBM_MASK BIT(18) +#define SAI_XCR_FBM_LSB BIT(18) +#define SAI_XCR_FBM_MSB 0 +#define SAI_XCR_SNB_MASK GENMASK(17, 11) +#define SAI_XCR_SNB(x) ((x - 1) << 11) +#define SAI_XCR_VDJ_MASK BIT(10) +#define SAI_XCR_VDJ_L BIT(10) +#define SAI_XCR_VDJ_R 0 +#define SAI_XCR_SBW_MASK GENMASK(9, 5) +#define SAI_XCR_SBW(x) ((x - 1) << 5) +#define SAI_XCR_SBW_V(v) ((((v) & SAI_XCR_SBW_MASK) >> 5) + 1) +#define SAI_XCR_VDW_MASK GENMASK(4, 0) +#define SAI_XCR_VDW(x) ((x - 1) << 0) + +/* FSCR Frame Sync Control Register */ +#define SAI_FSCR_EDGE_MASK BIT(24) +#define SAI_FSCR_EDGE_DUAL BIT(24) +#define SAI_FSCR_EDGE_RISING 0 +#define SAI_FSCR_FPW_MASK GENMASK(23, 12) +#define SAI_FSCR_FPW(x) ((x - 1) << 12) +#define SAI_FSCR_FW_MASK GENMASK(11, 0) +#define SAI_FSCR_FW(x) ((x - 1) << 0) +#define SAI_FSCR_FW_V(v) ((((v) & SAI_FSCR_FW_MASK) >> 0) + 1) + +/* MONO_CR Mono Control Register */ +#define SAI_MCR_RX_MONO_SLOT_MASK GENMASK(8, 2) +#define SAI_MCR_RX_MONO_SLOT_SEL(x) ((x - 1) << 2) +#define SAI_MCR_RX_MONO_MASK BIT(1) +#define SAI_MCR_RX_MONO_EN BIT(1) +#define SAI_MCR_RX_MONO_DIS 0 +#define SAI_MCR_TX_MONO_MASK BIT(0) +#define SAI_MCR_TX_MONO_EN BIT(0) +#define SAI_MCR_TX_MONO_DIS 0 + +/* XFER Transfer Start Register */ +#define SAI_XFER_RX_IDLE BIT(8) +#define SAI_XFER_TX_IDLE BIT(7) +#define SAI_XFER_FS_IDLE BIT(6) +/* + * Used for TX only (VERSION >= SAI_VER_2311) + * + * SCLK/FSYNC auto gated when TX FIFO empty. + */ +#define SAI_XFER_TX_AUTO_MASK BIT(6) +#define SAI_XFER_TX_AUTO_EN BIT(6) +#define SAI_XFER_TX_AUTO_DIS 0 +#define SAI_XFER_RX_CNT_MASK BIT(5) +#define SAI_XFER_RX_CNT_EN BIT(5) +#define SAI_XFER_RX_CNT_DIS 0 +#define SAI_XFER_TX_CNT_MASK BIT(4) +#define SAI_XFER_TX_CNT_EN BIT(4) +#define SAI_XFER_TX_CNT_DIS 0 +#define SAI_XFER_RXS_MASK BIT(3) +#define SAI_XFER_RXS_EN BIT(3) +#define SAI_XFER_RXS_DIS 0 +#define SAI_XFER_TXS_MASK BIT(2) +#define SAI_XFER_TXS_EN BIT(2) +#define SAI_XFER_TXS_DIS 0 +#define SAI_XFER_FSS_MASK BIT(1) +#define SAI_XFER_FSS_EN BIT(1) +#define SAI_XFER_FSS_DIS 0 +#define SAI_XFER_CLK_MASK BIT(0) +#define SAI_XFER_CLK_EN BIT(0) +#define SAI_XFER_CLK_DIS 0 + +/* CLR Clear Logic Register */ +#define SAI_CLR_FCR BIT(3) /* TODO: what is this? */ +#define SAI_CLR_FSC BIT(2) +#define SAI_CLR_RXC BIT(1) +#define SAI_CLR_TXC BIT(0) + +/* CKR Clock Generation Register */ +#define SAI_CKR_MDIV_MASK GENMASK(14, 3) +#define SAI_CKR_MDIV(x) ((x - 1) << 3) +#define SAI_CKR_MSS_MASK BIT(2) +#define SAI_CKR_MSS_SLAVE BIT(2) +#define SAI_CKR_MSS_MASTER 0 +#define SAI_CKR_CKP_MASK BIT(1) +#define SAI_CKR_CKP_INVERTED BIT(1) +#define SAI_CKR_CKP_NORMAL 0 +#define SAI_CKR_FSP_MASK BIT(0) +#define SAI_CKR_FSP_INVERTED BIT(0) +#define SAI_CKR_FSP_NORMAL 0 + +/* DMACR DMA Control Register */ +#define SAI_DMACR_RDE_MASK BIT(24) +#define SAI_DMACR_RDE(x) ((x) << 24) +#define SAI_DMACR_RDL_MASK GENMASK(20, 16) +#define SAI_DMACR_RDL(x) ((x - 1) << 16) +#define SAI_DMACR_RDL_V(v) ((((v) & SAI_DMACR_RDL_MASK) >> 16) + 1) +#define SAI_DMACR_TDE_MASK BIT(8) +#define SAI_DMACR_TDE(x) ((x) << 8) +#define SAI_DMACR_TDL_MASK GENMASK(4, 0) +#define SAI_DMACR_TDL(x) ((x) << 0) +#define SAI_DMACR_TDL_V(v) (((v) & SAI_DMACR_TDL_MASK) >> 0) + +/* INTCR Interrupt Ctrl Register */ +#define SAI_INTCR_FSLOSTC BIT(28) +#define SAI_INTCR_FSLOST_MASK BIT(27) +#define SAI_INTCR_FSLOST(x) ((x) << 27) +#define SAI_INTCR_FSERRC BIT(26) +#define SAI_INTCR_FSERR_MASK BIT(25) +#define SAI_INTCR_FSERR(x) ((x) << 25) +#define SAI_INTCR_RXOIC BIT(18) +#define SAI_INTCR_RXOIE_MASK BIT(17) +#define SAI_INTCR_RXOIE(x) ((x) << 17) +#define SAI_INTCR_TXUIC BIT(2) +#define SAI_INTCR_TXUIE_MASK BIT(1) +#define SAI_INTCR_TXUIE(x) ((x) << 1) + +/* INTSR Interrupt Status Register */ +#define SAI_INTSR_FSLOSTI_INA 0 +#define SAI_INTSR_FSLOSTI_ACT BIT(19) +#define SAI_INTSR_FSERRI_INA 0 +#define SAI_INTSR_FSERRI_ACT BIT(18) +#define SAI_INTSR_RXOI_INA 0 +#define SAI_INTSR_RXOI_ACT BIT(17) +#define SAI_INTSR_TXUI_INA 0 +#define SAI_INTSR_TXUI_ACT BIT(1) + +/* PATH_SEL: Transfer / Receive Path Select Register */ +#define SAI_RX_PATH_SHIFT(x) (8 + (x) * 2) +#define SAI_RX_PATH_MASK(x) (0x3 << SAI_RX_PATH_SHIFT(x)) +#define SAI_RX_PATH(x, v) ((v) << SAI_RX_PATH_SHIFT(x)) +#define SAI_TX_PATH_SHIFT(x) (0 + (x) * 2) +#define SAI_TX_PATH_MASK(x) (0x3 << SAI_TX_PATH_SHIFT(x)) +#define SAI_TX_PATH(x, v) ((v) << SAI_TX_PATH_SHIFT(x)) + +/* XSHIFT: Transfer / Receive Frame Sync Shift Register */ + +/* + * TX-ONLY: LEFT Direction Feature + * +------------------------------------------------+ + * | DATA LEFTx (step: 0.5 cycle) | FSYNC Edge | + * +------------------------------------------------+ + */ +#define SAI_XSHIFT_LEFT_MASK GENMASK(25, 24) +#define SAI_XSHIFT_LEFT(x) ((x) << 24) +/* + * +------------------------------------------------+ + * | FSYNC Edge | DATA RIGHTx (step: 0.5 cycle) | + * +------------------------------------------------+ + */ +#define SAI_XSHIFT_RIGHT_MASK GENMASK(23, 0) +#define SAI_XSHIFT_RIGHT(x) (x) + +/* XFIFOLR: Transfer / Receive FIFO Level Register */ +#define SAI_FIFOLR_XFL3_SHIFT 18 +#define SAI_FIFOLR_XFL3_MASK GENMASK(23, 18) +#define SAI_FIFOLR_XFL2_SHIFT 12 +#define SAI_FIFOLR_XFL2_MASK GENMASK(17, 12) +#define SAI_FIFOLR_XFL1_SHIFT 6 +#define SAI_FIFOLR_XFL1_MASK GENMASK(11, 6) +#define SAI_FIFOLR_XFL0_SHIFT 0 +#define SAI_FIFOLR_XFL0_MASK GENMASK(5, 0) + +/* STATUS Status Register (VERSION >= SAI_VER_2307) */ +#define SAI_STATUS_RX_IDLE BIT(3) +#define SAI_STATUS_TX_IDLE BIT(2) +#define SAI_STATUS_FS_IDLE BIT(1) + +/* VERSION */ +/* + * Updates: + * + * VERSION >= SAI_VER_2311 + * + * Support Frame Sync xN (FSXN) + * Support Frame Sync Error Detect (FSE) + * Support Frame Sync Lost Detect (FSLOST) + * Support Force Clear (FCR) + * Support SAIn-Chained (e.g. SAI0-CLK-DATA + SAI3-DATA +...) + * Support Transmit Auto Gate Mode + * Support Timing Shift Left for TX + * + * Optimize SCLK/FSYNC Timing Alignment + * + * VERSION >= SAI_VER_2403 + * + * Support Loopback LR Select (e.g. L:MIC R:LP) + * + */ +#define SAI_VER_2307 0x23073576 +#define SAI_VER_2311 0x23112118 +#define SAI_VER_2401 0x24013506 +#define SAI_VER_2403 0x24031103 + +/* FS_TIMEOUT: Frame Sync Timeout Register */ +#define SAI_FS_TIMEOUT_VAL_MASK GENMASK(31, 1) +#define SAI_FS_TIMEOUT_VAL(x) ((x) << 1) +#define SAI_FS_TIMEOUT_EN_MASK BIT(0) +#define SAI_FS_TIMEOUT_EN(x) ((x) << 0) + +/* SAI Registers */ +#define SAI_TXCR (0x0000) +#define SAI_FSCR (0x0004) +#define SAI_RXCR (0x0008) +#define SAI_MONO_CR (0x000c) +#define SAI_XFER (0x0010) +#define SAI_CLR (0x0014) +#define SAI_CKR (0x0018) +#define SAI_TXFIFOLR (0x001c) +#define SAI_RXFIFOLR (0x0020) +#define SAI_DMACR (0x0024) +#define SAI_INTCR (0x0028) +#define SAI_INTSR (0x002c) +#define SAI_TXDR (0x0030) +#define SAI_RXDR (0x0034) +#define SAI_PATH_SEL (0x0038) +#define SAI_TX_SLOT_MASK0 (0x003c) +#define SAI_TX_SLOT_MASK1 (0x0040) +#define SAI_TX_SLOT_MASK2 (0x0044) +#define SAI_TX_SLOT_MASK3 (0x0048) +#define SAI_RX_SLOT_MASK0 (0x004c) +#define SAI_RX_SLOT_MASK1 (0x0050) +#define SAI_RX_SLOT_MASK2 (0x0054) +#define SAI_RX_SLOT_MASK3 (0x0058) +#define SAI_TX_DATA_CNT (0x005c) +#define SAI_RX_DATA_CNT (0x0060) +#define SAI_TX_SHIFT (0x0064) +#define SAI_RX_SHIFT (0x0068) +#define SAI_STATUS (0x006c) +#define SAI_VERSION (0x0070) +#define SAI_FSXN (0x0074) +#define SAI_FS_TIMEOUT (0x0078) +#define SAI_LOOPBACK_LR (0x007c) + +#endif /* _ROCKCHIP_SAI_H */ From patchwork Wed Mar 5 21:24:25 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Frattaroli X-Patchwork-Id: 14003571 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 5ECA4C28B26 for ; Wed, 5 Mar 2025 22:48:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=3Jlaf/j3jeSFOP0bpJXJWNf3t/93dYT2IY3rNJ9zTBg=; b=Jf/ItSLbIySDtdgFpdAtfkO2mZ 4Zt/UBjlwZsW9dDV9G9uHDzptf2pHJeGdQ+0KTcOtE20le0XBThHPIT1WfBquw+BWFLB2ZBLZC8mV MJo7F/9DKVeGMahbKPZhWy9JLOBJ0URhZiTEgyxxestJbdRby/Z02JcI8HGvU2rTZy/MVcT+oNoSn 3Qsm3/mH4Te72WLKRzGk4Qrdkwh9KVKaUL5vdJQPqzsRp6I4b2cmB3zMBULMWYB1XvMU09budYCuj +uqg9w/W3fiQtE9b/Ylj6C/DbOppuwAnIkskp0RBQt6IeAuqYO9a4mamUOK2jWD/ohIYlvnTZLeb9 9EyWIMGg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1tpxXm-00000009WUL-2vOF; Wed, 05 Mar 2025 22:48:50 +0000 Received: from sender4-pp-f112.zoho.com ([136.143.188.112]) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1tpwFC-00000009LDB-0teG; Wed, 05 Mar 2025 21:25:35 +0000 ARC-Seal: i=1; a=rsa-sha256; t=1741209923; cv=none; d=zohomail.com; s=zohoarc; b=OEelxNAApT7vS6/Kz/HpU+OruTv1o+SEspOSAHXYeb+k+M8FtMIrnJseIuWePt7Ww3zgTMJiPsNImxQxIvXpPtXrLhNcfBELn+e7NfC6vqC4PBBbhMn2PQib2lwZGUxRg6izhz5V3DwwJMITw2Ehr6iZxh20jjkyjheAzgaXjcg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1741209923; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=3Jlaf/j3jeSFOP0bpJXJWNf3t/93dYT2IY3rNJ9zTBg=; b=NjiQwR3nqHSAGjONVa5oBcMek5op0frTVizzP+zzlRSh30bLNmYDAbY3NNRKOrJw3vK1Vnb4VEcY/gZjBbESwv/5MVznBCeY0UTjh3fl5OZE9NAdNGG5c9fl5uHh89BU0kUj6/+OfdgQeHVxWnODTjDOF0j7s03gPspJDY6Uslo= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=nicolas.frattaroli@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1741209923; s=zohomail; d=collabora.com; i=nicolas.frattaroli@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=3Jlaf/j3jeSFOP0bpJXJWNf3t/93dYT2IY3rNJ9zTBg=; b=ad24r2D9OWGQGChRJqlugVz6q0q4YBndCylEOz7E90Dvu/wKY76zIRNbuvl8YJhR sb4awhQY1wVvKsvsN5NMKl8Wbm8uxa0pwdMMP9Tuar5EQ95L2xKMFoVkr8dSFQ1SI01 96I7vy1/ZlaOBvp6xklDcPsBa1QTZMkvaAkFbYE8= Received: by mx.zohomail.com with SMTPS id 174120992258066.36353357994255; Wed, 5 Mar 2025 13:25:22 -0800 (PST) From: Nicolas Frattaroli Date: Wed, 05 Mar 2025 22:24:25 +0100 Subject: [PATCH 5/7] arm64: dts: rockchip: Add RK3576 SAI nodes MIME-Version: 1.0 Message-Id: <20250305-rk3576-sai-v1-5-64e6cf863e9a@collabora.com> References: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> In-Reply-To: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> To: Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiko Stuebner , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , Philipp Zabel , Catalin Marinas , Will Deacon , Sugar Zhang Cc: Luca Ceresoli , Sebastian Reichel , kernel@collabora.com, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-sound@vger.kernel.org, Nicolas Frattaroli X-Mailer: b4 0.14.2 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20250305_132534_301250_E467CD11 X-CRM114-Status: GOOD ( 11.07 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org The RK3576 SoC has 10 SAI controllers in total. Five of them are in the video output power domains, and are used for digital audio output along with the video signal of those, e.g. HDMI audio. The other five, SAI0 through SAI4, are exposed externally. SAI0 and SAI1 are capable of 8-channel audio, whereas SAI2, SAI3 and SAI4 are limited to two channels. These five are in the audio power domain. Signed-off-by: Nicolas Frattaroli --- arch/arm64/boot/dts/rockchip/rk3576.dtsi | 194 +++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi index 8591065b575223a5eb2da70f723f16969aa2ecf7..2691efea02a3852babfdeb25c490f9309356c3b7 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi @@ -1010,6 +1010,38 @@ vop_mmu: iommu@27d07e00 { status = "disabled"; }; + sai5: sai@27d40000 { + compatible = "rockchip,rk3576-sai"; + reg = <0x0 0x27d40000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SAI5_8CH>, <&cru HCLK_SAI5_8CH>; + clock-names = "mclk", "hclk"; + dmas = <&dmac2 3>; + dma-names = "rx"; + power-domains = <&power RK3576_PD_VO0>; + resets = <&cru SRST_M_SAI5_8CH>, <&cru SRST_H_SAI5_8CH>; + reset-names = "m", "h"; + #sound-dai-cells = <0>; + sound-name-prefix = "SAI5"; + status = "disabled"; + }; + + sai6: sai@27d50000 { + compatible = "rockchip,rk3576-sai"; + reg = <0x0 0x27d50000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SAI6_8CH>, <&cru HCLK_SAI6_8CH>; + clock-names = "mclk", "hclk"; + dmas = <&dmac2 4>, <&dmac2 5>; + dma-names = "tx", "rx"; + power-domains = <&power RK3576_PD_VO0>; + resets = <&cru SRST_M_SAI6_8CH>, <&cru SRST_H_SAI6_8CH>; + reset-names = "m", "h"; + #sound-dai-cells = <0>; + sound-name-prefix = "SAI6"; + status = "disabled"; + }; + hdmi: hdmi@27da0000 { compatible = "rockchip,rk3576-dw-hdmi-qp"; reg = <0x0 0x27da0000 0x0 0x20000>; @@ -1050,6 +1082,54 @@ hdmi_out: port@1 { }; }; + sai7: sai@27ed0000 { + compatible = "rockchip,rk3576-sai"; + reg = <0x0 0x27ed0000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SAI7_8CH>, <&cru HCLK_SAI7_8CH>; + clock-names = "mclk", "hclk"; + dmas = <&dmac2 19>; + dma-names = "tx"; + power-domains = <&power RK3576_PD_VO1>; + resets = <&cru SRST_M_SAI7_8CH>, <&cru SRST_H_SAI7_8CH>; + reset-names = "m", "h"; + #sound-dai-cells = <0>; + sound-name-prefix = "SAI7"; + status = "disabled"; + }; + + sai8: sai@27ee0000 { + compatible = "rockchip,rk3576-sai"; + reg = <0x0 0x27ee0000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SAI8_8CH>, <&cru HCLK_SAI8_8CH>; + clock-names = "mclk", "hclk"; + dmas = <&dmac1 7>; + dma-names = "tx"; + power-domains = <&power RK3576_PD_VO1>; + resets = <&cru SRST_M_SAI8_8CH>, <&cru SRST_H_SAI8_8CH>; + reset-names = "m", "h"; + #sound-dai-cells = <0>; + sound-name-prefix = "SAI8"; + status = "disabled"; + }; + + sai9: sai@27ef0000 { + compatible = "rockchip,rk3576-sai"; + reg = <0x0 0x27ef0000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SAI9_8CH>, <&cru HCLK_SAI9_8CH>; + clock-names = "mclk", "hclk"; + dmas = <&dmac0 26>; + dma-names = "tx"; + power-domains = <&power RK3576_PD_VO1>; + resets = <&cru SRST_M_SAI9_8CH>, <&cru SRST_H_SAI9_8CH>; + reset-names = "m", "h"; + #sound-dai-cells = <0>; + sound-name-prefix = "SAI9"; + status = "disabled"; + }; + qos_hdcp1: qos@27f02000 { compatible = "rockchip,rk3576-qos", "syscon"; reg = <0x0 0x27f02000 0x0 0x20>; @@ -1436,6 +1516,120 @@ log_leakage: log-leakage@22 { }; }; + sai0: sai@2a600000 { + compatible = "rockchip,rk3576-sai"; + reg = <0x0 0x2a600000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SAI0_8CH>, <&cru HCLK_SAI0_8CH>; + clock-names = "mclk", "hclk"; + dmas = <&dmac0 0>, <&dmac0 1>; + dma-names = "tx", "rx"; + power-domains = <&power RK3576_PD_AUDIO>; + resets = <&cru SRST_M_SAI0_8CH>, <&cru SRST_H_SAI0_8CH>; + reset-names = "m", "h"; + pinctrl-names = "default"; + pinctrl-0 = <&sai0m0_lrck + &sai0m0_sclk + &sai0m0_sdi0 + &sai0m0_sdi1 + &sai0m0_sdi2 + &sai0m0_sdi3 + &sai0m0_sdo0 + &sai0m0_sdo1 + &sai0m0_sdo2 + &sai0m0_sdo3>; + #sound-dai-cells = <0>; + sound-name-prefix = "SAI0"; + status = "disabled"; + }; + + sai1: sai@2a610000 { + compatible = "rockchip,rk3576-sai"; + reg = <0x0 0x2a610000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SAI1_8CH>, <&cru HCLK_SAI1_8CH>; + clock-names = "mclk", "hclk"; + dmas = <&dmac0 2>, <&dmac0 3>; + dma-names = "tx", "rx"; + power-domains = <&power RK3576_PD_AUDIO>; + resets = <&cru SRST_M_SAI1_8CH>, <&cru SRST_H_SAI1_8CH>; + reset-names = "m", "h"; + pinctrl-names = "default"; + pinctrl-0 = <&sai1m0_lrck + &sai1m0_sclk + &sai1m0_sdi0 + &sai1m0_sdo0 + &sai1m0_sdo1 + &sai1m0_sdo2 + &sai1m0_sdo3>; + #sound-dai-cells = <0>; + sound-name-prefix = "SAI1"; + status = "disabled"; + }; + + sai2: sai@2a620000 { + compatible = "rockchip,rk3576-sai"; + reg = <0x0 0x2a620000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SAI2_2CH>, <&cru HCLK_SAI2_2CH>; + clock-names = "mclk", "hclk"; + dmas = <&dmac1 0>, <&dmac1 1>; + dma-names = "tx", "rx"; + power-domains = <&power RK3576_PD_AUDIO>; + resets = <&cru SRST_M_SAI2_2CH>, <&cru SRST_H_SAI2_2CH>; + reset-names = "m", "h"; + pinctrl-names = "default"; + pinctrl-0 = <&sai2m0_lrck + &sai2m0_sclk + &sai2m0_sdi + &sai2m0_sdo>; + #sound-dai-cells = <0>; + sound-name-prefix = "SAI2"; + status = "disabled"; + }; + + sai3: sai@2a630000 { + compatible = "rockchip,rk3576-sai"; + reg = <0x0 0x2a630000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SAI3_2CH>, <&cru HCLK_SAI3_2CH>; + clock-names = "mclk", "hclk"; + dmas = <&dmac1 2>, <&dmac1 3>; + dma-names = "tx", "rx"; + power-domains = <&power RK3576_PD_AUDIO>; + resets = <&cru SRST_M_SAI3_2CH>, <&cru SRST_H_SAI3_2CH>; + reset-names = "m", "h"; + pinctrl-names = "default"; + pinctrl-0 = <&sai3m0_lrck + &sai3m0_sclk + &sai3m0_sdi + &sai3m0_sdo>; + #sound-dai-cells = <0>; + sound-name-prefix = "SAI3"; + status = "disabled"; + }; + + sai4: sai@2a640000 { + compatible = "rockchip,rk3576-sai"; + reg = <0x0 0x2a640000 0x0 0x1000>; + interrupts = ; + clocks = <&cru MCLK_SAI4_2CH>, <&cru HCLK_SAI4_2CH>; + clock-names = "mclk", "hclk"; + dmas = <&dmac2 0>, <&dmac2 1>; + dma-names = "tx", "rx"; + power-domains = <&power RK3576_PD_AUDIO>; + resets = <&cru SRST_M_SAI4_2CH>, <&cru SRST_H_SAI4_2CH>; + reset-names = "m", "h"; + pinctrl-names = "default"; + pinctrl-0 = <&sai4m0_lrck + &sai4m0_sclk + &sai4m0_sdi + &sai4m0_sdo>; + #sound-dai-cells = <0>; + sound-name-prefix = "SAI4"; + status = "disabled"; + }; + gic: interrupt-controller@2a701000 { compatible = "arm,gic-400"; reg = <0x0 0x2a701000 0 0x10000>, From patchwork Wed Mar 5 21:24:26 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Frattaroli X-Patchwork-Id: 14003573 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id E632AC28B22 for ; Wed, 5 Mar 2025 22:48:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=zVAuxaI2woSNcR4BvXfhI+0VCh69YoH27nMHM/pehLg=; b=VmJEyiV1dou8P+N3d1ftM553R1 UB54lZHmJhme/l0/gk2S0KCxgJ/be7zMJcgTJY0jLMcQYVganG2QH1K7BF3lao9kOaeMbYSeJP8/J BMqOSuekxwAknXjOvIcnbCr6XyYGDfrcr+3XH5/tFt6+GY6UCkYZAE9U0JLrBTISFljXZiYbHHgoA G20FGcvMnnh2JcjDC1PWPzOCjyd+WE9/I/G7J1F7tBPdm165lfW8KvMySPl7eiUzR/1g58ffA76OV C4uBXDPlFLKm4y05iApJLPYGr1cKfV0+S7P3D++p5qJHJIDHxQ3ZLKdGD3vBhiQtvHnCAEp6smJsL li/UG4yg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1tpxXn-00000009WVz-37Fp; Wed, 05 Mar 2025 22:48:51 +0000 Received: from sender3-pp-f112.zoho.com ([136.143.184.112]) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1tpwFJ-00000009LF4-06Nx; Wed, 05 Mar 2025 21:25:42 +0000 ARC-Seal: i=1; a=rsa-sha256; t=1741209929; cv=none; d=zohomail.com; s=zohoarc; b=Tb4JYTnGTWGabKE8zAPEpXSrKRjo2OSjhIM9hbn9OUt7znAGqsAR7n0mOF4Fw4YElbggfIsdkwPwrSFhKhTqeUvYuLyvUeeD7FHlBbC+/2izMeIl3qpQCwzKOeRcUjSAhSjDRZQDIw0bcfRPPHiwhAl2VHWWanqZCAHV/dor7Mc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1741209929; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=zVAuxaI2woSNcR4BvXfhI+0VCh69YoH27nMHM/pehLg=; b=O/gYqZxp3mseeAu/X6E3WmaiRXvkA47oJduXIDio3JJoNPUPlUMaxejPzDqkt3uy7LPFE4i6kpYs0bHTkBd3YP0r6Wurjunz5T62SCh1fxtyyjYf4N0jQKal8Nq734DqYIGBLI18phuhZYnXCbT2p/gJD8rkzXbbXIBj+DvfN2E= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=nicolas.frattaroli@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1741209929; s=zohomail; d=collabora.com; i=nicolas.frattaroli@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=zVAuxaI2woSNcR4BvXfhI+0VCh69YoH27nMHM/pehLg=; b=G5lTezhgiTpOSXchWba+TjFCfCfHCT56WiPNkQcycbfRd+aF+l8Gw5kBubrR7fQC tZndF50J/qycA9mAP4qjIQ16/9WIhZDIHMOXOTiEafSfCfwEeXjR0H8GqzFwhgMly3b FHml8IciTHpSyQHy/zoA7AIBGFt7QRz4grBTSVCA= Received: by mx.zohomail.com with SMTPS id 1741209928167987.0554432648572; Wed, 5 Mar 2025 13:25:28 -0800 (PST) From: Nicolas Frattaroli Date: Wed, 05 Mar 2025 22:24:26 +0100 Subject: [PATCH 6/7] arm64: dts: rockchip: Add analog audio on RK3576 Sige5 MIME-Version: 1.0 Message-Id: <20250305-rk3576-sai-v1-6-64e6cf863e9a@collabora.com> References: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> In-Reply-To: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> To: Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiko Stuebner , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , Philipp Zabel , Catalin Marinas , Will Deacon , Sugar Zhang Cc: Luca Ceresoli , Sebastian Reichel , kernel@collabora.com, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-sound@vger.kernel.org, Nicolas Frattaroli X-Mailer: b4 0.14.2 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20250305_132541_147949_6523C514 X-CRM114-Status: GOOD ( 15.56 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org The ArmSoM Sige5 board features an Everest ES8388 codec to provide analog stereo audio output, as well as analog audio input. The codec hangs off the i2c2 bus and responds to address 0x10. It is connected to the SAI1 audio controller of the RK3576, with one SDO (output) lane and one SDI (input) lane. The codec has two sets of outputs. One set, LOUT1/ROUT1, is connected through a set of 22uF non-polarised coupling capacitors to a 3-position connector that appears to be a clone of the JST BM03B-SURS-TF header, and is capable of mating with a JST 03SUR-32S (or JST 03SUR-36L if you prefer lemon-lime) or compatible clone connector. The right headphone output is the one closest to the Type-C DC input connector, the left headphone output is the one in the middle, and the third position, the one closest to the USB3 Type-A host connector, is puzzingly labelled as "HP_GND" in the schematic but is in fact connected to the codecs RIN1 input through a 1uF non-plarised coupling capacitor. LOUT2 and ROUT2 are routed to 1mm test pads T36 and T37 respectively. These are located on the bottom of the board, and do not go through any coupling capacitor. For use as line out, the ES8388 datasheet recommends adding 1uF coupling capacitor if one wishes to use it as a line-level output. There is also a pair of inputs for a stereo microphone, going from two 1mm testpads T34 and T35, which are decoupled with a 100pF capacitor and pulled to 3.3v and ground respectively. These inputs then go through 1uF capacitors each and end up in the LINPUT2 and RINPUT2 pins of the ES8388 codec. The codec's power inputs are routed to receive 3.3V for both its analog and digital inputs, though from different supplies. Signed-off-by: Nicolas Frattaroli --- .../boot/dts/rockchip/rk3576-armsom-sige5.dts | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5.dts b/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5.dts index 78798b0722a3f36831b1d2c9f29acb83910631cf..4970a7de70cd33040b2761122c04c7b8b20c99d5 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5.dts +++ b/arch/arm64/boot/dts/rockchip/rk3576-armsom-sige5.dts @@ -56,6 +56,34 @@ red_led: red-led { }; }; + es8388_sound: es8388-sound { + compatible = "simple-audio-card"; + simple-audio-card,format = "i2s"; + simple-audio-card,mclk-fs = <256>; + simple-audio-card,name = "On-board Analog ES8388"; + simple-audio-card,widgets = "Microphone", "Headphone Mic", + "Microphone", "Mic Pads", + "Headphone", "Headphone", + "Line Out", "Line Out"; + simple-audio-card,routing = "Headphone", "LOUT1", + "Headphone", "ROUT1", + "Line Out", "LOUT2", + "Line Out", "ROUT2", + "RINPUT1", "Headphone Mic", + "LINPUT2", "Mic Pads", + "RINPUT2", "Mic Pads"; + simple-audio-card,pin-switches = "Headphone", "Line Out"; + + simple-audio-card,cpu { + sound-dai = <&sai1>; + }; + + simple-audio-card,codec { + sound-dai = <&es8388>; + system-clock-frequency = <12288000>; + }; + }; + vcc_12v0_dcin: regulator-vcc-12v0-dcin { compatible = "regulator-fixed"; regulator-name = "vcc_12v0_dcin"; @@ -619,6 +647,25 @@ hym8563: rtc@51 { }; }; +&i2c3 { + status = "okay"; + + es8388: audio-codec@10 { + compatible = "everest,es8388", "everest,es8328"; + reg = <0x10>; + clocks = <&cru CLK_SAI1_MCLKOUT_TO_IO>; + AVDD-supply = <&vcca_3v3_s0>; + DVDD-supply = <&vcc_3v3_s0>; + HPVDD-supply = <&vcca_3v3_s0>; + PVDD-supply = <&vcc_3v3_s0>; + assigned-clocks = <&cru CLK_SAI1_MCLKOUT_TO_IO>; + assigned-clock-rates = <12288000>; + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&sai1m0_mclk>; + }; +}; + &mdio0 { rgmii_phy0: phy@1 { compatible = "ethernet-phy-ieee802.3-c22"; @@ -658,6 +705,15 @@ led_rgb_g: led-green-en { }; }; +&sai1 { + pinctrl-names = "default"; + pinctrl-0 = <&sai1m0_lrck + &sai1m0_sclk + &sai1m0_sdi0 + &sai1m0_sdo0>; + status = "okay"; +}; + &sdhci { bus-width = <8>; full-pwr-cycle-in-suspend; From patchwork Wed Mar 5 21:24:27 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Frattaroli X-Patchwork-Id: 14003572 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 5FC45C28B23 for ; Wed, 5 Mar 2025 22:49:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=S2UIo1syohylbzOnft0Fm8/vENKskSYrX6KC1r45rPk=; b=mN1O4EkE3WyN+n7S59Uybj6fQW ttT1IvpdDhGogqC8RHMVS6/2jH1B/3J5DQK/QkMqpHdHW4zvhrgRsTF4ObgwziCozQEXI4YWf+3Vj ngjFBkegYuZZgf7EmQ4oLy0fsOTitWQyLq/WYt76LPOVhrojOw3OdXSICl21M4qG9M+YhM9ZllCUv X7MiJDGhxHtiqJJWNcBzEO2dao0OMCO0kTZFJiJMdYG6CGy6f9Q8JIf2hkiilkgGivWPfkeFBqS2K Oj8JCGzKmz2eIIciUMDGHeESlXLF+tlvsFBAK/OskEwTpl/9Ymi5YDa20HzkyEu2rXE8QnYnm1f4o aeByHvsw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1tpxXp-00000009WXQ-03Fk; Wed, 05 Mar 2025 22:48:53 +0000 Received: from sender4-pp-f112.zoho.com ([136.143.188.112]) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1tpwFO-00000009LHS-2TgE; Wed, 05 Mar 2025 21:25:47 +0000 ARC-Seal: i=1; a=rsa-sha256; t=1741209935; cv=none; d=zohomail.com; s=zohoarc; b=GL2SGXZ8q9mgFZcQeVJ59+xbCI18dGLBsFrz5HBtxii7m7macFyQPIUFGDWMKSn0XFEfCZRiTDrFH21ulZmT3cLupRZU+bH9mF8BfV9hgkTjAFMU/D2lsVhr0CViY2+26C8SkKE2oTJLbMhEEOGSpSI1Syl87oDlHQ7VH5m9yvk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1741209935; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=S2UIo1syohylbzOnft0Fm8/vENKskSYrX6KC1r45rPk=; b=F2GPIQb/iE/9eFe2KCfoiF9GunvIhP3kjlfm9Doen5WOZdoq3cjjCNu46RpffEVd+VoCUcug0vjjqAIQL1LQIK+gAcZSFyRpYy3zB6/zh3rpzQJFWs5Atyx/artQj7BJQbr0Nh0AloB1ODx/KtOKoeBAUQgpJ2DOYlNEoKgcxUk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=nicolas.frattaroli@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1741209935; s=zohomail; d=collabora.com; i=nicolas.frattaroli@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=S2UIo1syohylbzOnft0Fm8/vENKskSYrX6KC1r45rPk=; b=AULhPudeZhhRknxfMNgckFThfnNsjSw8gbhG3n3n2lSQvMOoFvIrYcJsb6f4Irdv F7kKZ/M/YZiGX6sIXvm1UDkYj5rSO7uRXKTuKEs/gvdRXqcpo9tDk8OvXgzL369364w PTTGQ1KFxZKTZ/JutPDYkG6xNUAEDE76aedVlRvU= Received: by mx.zohomail.com with SMTPS id 1741209933733237.76483107649017; Wed, 5 Mar 2025 13:25:33 -0800 (PST) From: Nicolas Frattaroli Date: Wed, 05 Mar 2025 22:24:27 +0100 Subject: [PATCH 7/7] arm64: defconfig: Enable Rockchip SAI MIME-Version: 1.0 Message-Id: <20250305-rk3576-sai-v1-7-64e6cf863e9a@collabora.com> References: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> In-Reply-To: <20250305-rk3576-sai-v1-0-64e6cf863e9a@collabora.com> To: Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiko Stuebner , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , Philipp Zabel , Catalin Marinas , Will Deacon , Sugar Zhang Cc: Luca Ceresoli , Sebastian Reichel , kernel@collabora.com, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-sound@vger.kernel.org, Nicolas Frattaroli X-Mailer: b4 0.14.2 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20250305_132546_699560_AD3E538D X-CRM114-Status: UNSURE ( 8.84 ) X-CRM114-Notice: Please train this message. X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org The RK3576 uses Rockchip SAI for audio output. Enable it in the defconfig. Signed-off-by: Nicolas Frattaroli --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 3a3706db29822036d25a7228f8936e2ad613b208..d503b5de2d2efb6d66d7fb8d7bafc10c0cf37b36 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1002,6 +1002,7 @@ CONFIG_SND_SOC_SC7280=m CONFIG_SND_SOC_X1E80100=m CONFIG_SND_SOC_ROCKCHIP=m CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=m +CONFIG_SND_SOC_ROCKCHIP_SAI=m CONFIG_SND_SOC_ROCKCHIP_SPDIF=m CONFIG_SND_SOC_ROCKCHIP_RT5645=m CONFIG_SND_SOC_RK3399_GRU_SOUND=m