From patchwork Wed Sep 7 14:21:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 12969041 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 alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (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 4E68AC54EE9 for ; Wed, 7 Sep 2022 14:23:05 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 2611E1660; Wed, 7 Sep 2022 16:22:13 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 2611E1660 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1662560583; bh=BtM15gBL+GVRCEnhdXlQBB1EtTXmn51j7I06jUmL1Po=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=fMtKxjp5OOlEdXst8Xgrg85ZFooUnH0AOx0IsalcjxSzGe9RdIVvYhgZ5H7aOKkBi bxvOYLKQD0Vr9P3Ws6kEYil8DQy6IhaJPRP72yGGljE5KaQvAZVpPCm3jHW8wk/khX EfgvZDGeFeMF1f8qrS85/74GmFQKH2wINdlSGCxI= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id BD8D6F804FC; Wed, 7 Sep 2022 16:22:12 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id F2FA5F804FC; Wed, 7 Sep 2022 16:22:10 +0200 (CEST) Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [IPv6:2001:4b98:dc4:8::232]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id B5D07F80118 for ; Wed, 7 Sep 2022 16:22:04 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz B5D07F80118 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="As1t+bF7" Received: from booty.fritz.box (unknown [77.244.183.192]) (Authenticated sender: luca.ceresoli@bootlin.com) by mail.gandi.net (Postfix) with ESMTPA id 7C1F8200014; Wed, 7 Sep 2022 14:22:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1662560523; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=XJgSJiCghhYz5ofqe9JG5z8U+htYvi8Oo0PRyZFIxmM=; b=As1t+bF7xHy0TiDvWlV9I9GIzxD73MRcATUh4KDYTxfNgSV1LpOVmTJwWvoEkOTEwhPiyj b/+PiATaksXk957zdxnUjUwtbbNr0gYsEDnrh4FCnF10q/wTfV+dpWJV46JfjyBxrc96fi v2yGwL4eMO1qMlFBf01zbYwb7sOyDsLAmlt+qjCxBG1N2JEcdJwIMSvhTCNZ76cWuMoeSQ t3oqPWrQyEbfaXi1vmAHC+llMteHb559Okjj7mFZ6bGBPjR563ZAxH37ZKc21CS5b5LHTS qtvFUCnfvUQc7lLv/7DD/VP1HvxdhbaEnwxPWLUTMAKJ7Ou7lAI/zMEoLvxrIg== From: luca.ceresoli@bootlin.com To: alsa-devel@alsa-project.org, linux-rockchip@lists.infradead.org Subject: [PATCH 1/8] ASoC: rockchip: rk3308: add internal audio codec bindings Date: Wed, 7 Sep 2022 16:21:17 +0200 Message-Id: <20220907142124.2532620-2-luca.ceresoli@bootlin.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> References: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, Heiko Stuebner , Takashi Iwai , Chris Morgan , linux-kernel@vger.kernel.org, Rob Herring , Liam Girdwood , Nicolas Frattaroli , Mark Brown , Krzysztof Kozlowski , Philipp Zabel , Johan Jonker , Luca Ceresoli , linux-arm-kernel@lists.infradead.org X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Luca Ceresoli Add device tree bindings document for the internal audio codec of the Rockchip RK3308 SoC. Signed-off-by: Luca Ceresoli --- .../bindings/sound/rockchip,rk3308-codec.yaml | 102 ++++++++++++++++++ MAINTAINERS | 6 ++ .../dt-bindings/sound/rockchip,rk3308-codec.h | 15 +++ 3 files changed, 123 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.yaml create mode 100644 include/dt-bindings/sound/rockchip,rk3308-codec.h diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.yaml b/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.yaml new file mode 100644 index 000000000000..f3458f86ef06 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/rockchip,rk3308-codec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip RK3308 Internal Codec + +description: | + This is the audio codec embedded in the Rockchip RK3308 + SoC. It has 8 24-bit ADCs and 2 24-bit DACs. The maximum supported + sampling rate is 192 kHz. + + It is connected internally to one out of a selection of the internal I2S + controllers. + + The RK3308 audio codec has 8 independent capture channels, but some + features work on stereo pairs called groups: + * grp 0 -- MIC1 / MIC2 + * grp 1 -- MIC3 / MIC4 + * grp 2 -- MIC5 / MIC6 + * grp 3 -- MIC7 / MIC8 + +maintainers: + - Luca Ceresoli + +properties: + compatible: + const: rockchip,rk3308-codec + + reg: + maxItems: 1 + + rockchip,grf: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the General Register Files (GRF) + + clocks: + items: + - description: clock for TX + - description: clock for RX + - description: AHB clock driving the interface + + clock-names: + items: + - const: mclk_tx + - const: mclk_rx + - const: hclk + + resets: true + + reset-names: + items: + - const: "acodec" + + "#sound-dai-cells": + const: 0 + + rockchip,micbias-avdd-multiplier: + description: | + Voltage setting for the MICBIAS pins expressed as a multiplier of + AVDD. + + E.g. if rockchip,micbias-avdd-multiplier = 7 (x0.85) and AVDD = 3v3, + then MIC BIAS voltage will be 3.3 V * 0.85 = 2.805 V. + + Value 0: multiplier = 0.50 + Value N: multiplier = 0.50 + 0.05 * N + Value 7: multiplier = 0.85 + + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 7 + +required: + - compatible + - reg + - rockchip,grf + - clocks + - resets + - "#sound-dai-cells" + +additionalProperties: false + +examples: + - | + #include + + acodec: acodec@ff560000 { + compatible = "rockchip,rk3308-codec"; + reg = <0xff560000 0x10000>; + rockchip,grf = <&grf>; + clock-names = "mclk_tx", "mclk_rx", "hclk"; + clocks = <&cru SCLK_I2S2_8CH_TX_OUT>, + <&cru SCLK_I2S2_8CH_RX_OUT>, + <&cru PCLK_ACODEC>; + reset-names = "acodec"; + resets = <&cru SRST_ACODEC_P>; + #sound-dai-cells = <0>; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index 895e8ace80dd..d53a8e74cb1e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17588,6 +17588,12 @@ S: Maintained F: Documentation/devicetree/bindings/media/rockchip-rga.yaml F: drivers/media/platform/rockchip/rga/ +ROCKCHIP RK3308 INTERNAL AUDIO CODEC +M: Luca Ceresoli +S: Maintained +F: Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.yaml +F: include/dt-bindings/sound/rockchip,rk3308-codec.h + ROCKCHIP VIDEO DECODER DRIVER M: Ezequiel Garcia L: linux-media@vger.kernel.org diff --git a/include/dt-bindings/sound/rockchip,rk3308-codec.h b/include/dt-bindings/sound/rockchip,rk3308-codec.h new file mode 100644 index 000000000000..9f1b210a048e --- /dev/null +++ b/include/dt-bindings/sound/rockchip,rk3308-codec.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __DT_BINDINGS_ROCKCHIP_RK3308_CODEC_H__ +#define __DT_BINDINGS_ROCKCHIP_RK3308_CODEC_H__ + +#define RK3308_CODEC_MICBIAS_AVDD_x_0_50 0 +#define RK3308_CODEC_MICBIAS_AVDD_x_0_55 1 +#define RK3308_CODEC_MICBIAS_AVDD_x_0_60 2 +#define RK3308_CODEC_MICBIAS_AVDD_x_0_65 3 +#define RK3308_CODEC_MICBIAS_AVDD_x_0_70 4 +#define RK3308_CODEC_MICBIAS_AVDD_x_0_75 5 +#define RK3308_CODEC_MICBIAS_AVDD_x_0_80 6 +#define RK3308_CODEC_MICBIAS_AVDD_x_0_85 7 +#define RK3308_CODEC_MICBIAS_NUM 8 + +#endif /* __DT_BINDINGS_ROCKCHIP_RK3308_CODEC_H__ */ From patchwork Wed Sep 7 14:21:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 12969044 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 alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (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 26903C54EE9 for ; Wed, 7 Sep 2022 14:23:49 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 5AF071689; Wed, 7 Sep 2022 16:22:57 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 5AF071689 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1662560627; bh=0AJRaTR99WJiEoPvsDyYYoAlPqKXiSb7ub67S9ixuzM=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=nHAJVD9I3MR5ujFktIr0YHTrxr141x4UjVogR3R0JxMg02+L9ZT6YSEUpHO4l8O33 fzM/vWf0/CEo1sXuK7Zr1HLCmVsg/eZKUgOtkc+DXuZnnPUsaU53zyzeE0DBkWi//7 mLdOHXSz9mVTJ6UkFI2D1Y5ujNbM45CKag79Gzpk= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id BF256F804CA; Wed, 7 Sep 2022 16:22:21 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 449B7F80552; Wed, 7 Sep 2022 16:22:20 +0200 (CEST) Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 3B388F80423 for ; Wed, 7 Sep 2022 16:22:06 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 3B388F80423 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="K3xa18hD" Received: from booty.fritz.box (unknown [77.244.183.192]) (Authenticated sender: luca.ceresoli@bootlin.com) by mail.gandi.net (Postfix) with ESMTPA id F2C2920000F; Wed, 7 Sep 2022 14:22:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1662560526; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=VgbxwdFt5PQppcOV7X5ar9OBFO85n+/rhDp625ejemw=; b=K3xa18hD5THvri4wHGXS1ynPnBzxFd5Zor2sm9QM6SMroLfr12HiVYVsbewqW89p0iqgV4 IlnnpUFS8seVJT3Mtv0E7rprRb3KqgTrVixWJN+fNMl6fibdHrWSEwwduzd3sEN9D7C5Kf 7R+Hh0+gkO0A4KbbemjizLE5quL2C8M20AJgKZJhM1WaQ1sNXjU+2lpBbooX5O5HfXNQAx q/o/FegSFyelVAKO3y8zlWkcJtcN+6eGCdGElS9d+wfZg76iBu4oazIuQ7Bww9Ry0x2qvA rIYgWb+yghfJ2U2bCudSsu/kIAyKbz6kVQr6rHL9gI+PC6OAaCk3UAm1q4QbRg== From: luca.ceresoli@bootlin.com To: alsa-devel@alsa-project.org, linux-rockchip@lists.infradead.org Subject: [PATCH 2/8] ASoC: rockchip: rk3308: add audio card bindings Date: Wed, 7 Sep 2022 16:21:18 +0200 Message-Id: <20220907142124.2532620-3-luca.ceresoli@bootlin.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> References: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, Heiko Stuebner , Takashi Iwai , Chris Morgan , linux-kernel@vger.kernel.org, Rob Herring , Liam Girdwood , Nicolas Frattaroli , Mark Brown , Krzysztof Kozlowski , Philipp Zabel , Johan Jonker , Luca Ceresoli , linux-arm-kernel@lists.infradead.org X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Luca Ceresoli Add device tree bindings document for the audio card based on the internal I2S of the Rockchip RK3308 SoC. Signed-off-by: Luca Ceresoli --- .../rockchip,rk3308-audio-graph-card.yaml | 50 +++++++++++++++++++ MAINTAINERS | 5 ++ 2 files changed, 55 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/rockchip,rk3308-audio-graph-card.yaml diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3308-audio-graph-card.yaml b/Documentation/devicetree/bindings/sound/rockchip,rk3308-audio-graph-card.yaml new file mode 100644 index 000000000000..8445a69dcdbb --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rockchip,rk3308-audio-graph-card.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/rockchip,rk3308-audio-graph-card.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip RK3308 Audio card based on internal I2S + +maintainers: + - Luca Ceresoli + +allOf: + - $ref: /schemas/sound/audio-graph.yaml# + +properties: + compatible: + const: rockchip,rk3308-audio-graph-card + +required: + - compatible + +unevaluatedProperties: false + +examples: + - | + sound { + compatible = "rockchip,rk3308-audio-graph-card"; + dais = <&i2s_8ch_2_port>; + }; + + i2s_8ch_2 { + i2s_8ch_2_port: port { + i2s_8ch_2_endpoint: endpoint { + remote-endpoint = <&acodec_endpoint>; + dai-format = "i2s"; + + /* The RK3308 acodec has no clock dividers, use the CPU */ + bitclock-master = <&i2s_8ch_2_endpoint>; + frame-master = <&i2s_8ch_2_endpoint>; + }; + }; + }; + + acodec { + port { + acodec_endpoint: endpoint { + remote-endpoint = <&i2s_8ch_2_endpoint>; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index d53a8e74cb1e..079bdd95dc49 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17594,6 +17594,11 @@ S: Maintained F: Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.yaml F: include/dt-bindings/sound/rockchip,rk3308-codec.h +ROCKCHIP RK3308 SOUND CARD DRIVER +M: Luca Ceresoli +S: Maintained +F: Documentation/devicetree/bindings/sound/rockchip,rk3308-audio-graph-card.yaml + ROCKCHIP VIDEO DECODER DRIVER M: Ezequiel Garcia L: linux-media@vger.kernel.org From patchwork Wed Sep 7 14:21:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 12969042 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 alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (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 01085C38145 for ; Wed, 7 Sep 2022 14:23:25 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 409B61672; Wed, 7 Sep 2022 16:22:34 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 409B61672 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1662560604; bh=v/zedDVQfoYzaHd+XKFJHxPu4a2sZ7QvxLsPpxc2y2A=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=kV2ahgtL0Mu8vr4Vmy57FiX9BNT0n4EFy9RwTmR6017TVz1R2/nkzhUQ2ZFKwGPol qniG4ONd5Kg/jB01+JKi88fZn7xrv56FzVyO6IIIRgANawzWyYYm0mTiwCBomP+Vi1 kgdr5Gn0+edOjkJBBkL6lYzmRb2eqrV27xpHYMTE= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 59F9AF80535; Wed, 7 Sep 2022 16:22:17 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 1D5C2F80536; Wed, 7 Sep 2022 16:22:16 +0200 (CEST) Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [IPv6:2001:4b98:dc4:8::232]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id C5915F804B2 for ; Wed, 7 Sep 2022 16:22:08 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz C5915F804B2 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="LSdOFgZf" Received: from booty.fritz.box (unknown [77.244.183.192]) (Authenticated sender: luca.ceresoli@bootlin.com) by mail.gandi.net (Postfix) with ESMTPA id 4695B20000D; Wed, 7 Sep 2022 14:22:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1662560527; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=d12YAjeraqwxRD2IqjgZc6aJaYWV3WXQkVFDeRXfBTA=; b=LSdOFgZfPTa/D1VumgCk6xEvKgX/7vfoT/oRTJ6fjbcCp0KIlM2iMoFtlZqgfNcVGW33/9 oR1WVXzp6lZl78tIxjrey5/180SsjzMs0j95C83GbnU2XQvOB2vUWNVeH+M6UqZO0K+AIZ u4rVWAXY3rp6Ubn1hoUwRapgyWPvbFMRymdPeC7ITbsw+K75+rfcwtNHcz3x2IuRu/uRF0 g443E9DIOjYXDKBbRmhaOSDl8vq+CESYUjQhL3pSMlJn/cHZw30ttO3vRN8i67WjOvUSUI dDEsxGXCg8IrZTOBezF6EnEvqs9kUouoZfeVnGcOqZWP5iw9371KlePqH2pgtg== From: luca.ceresoli@bootlin.com To: alsa-devel@alsa-project.org, linux-rockchip@lists.infradead.org Subject: [PATCH 3/8] arm64: dts: rockchip: add i2s_8ch_2 and i2s_8ch_3 Date: Wed, 7 Sep 2022 16:21:19 +0200 Message-Id: <20220907142124.2532620-4-luca.ceresoli@bootlin.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> References: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, Heiko Stuebner , Takashi Iwai , Chris Morgan , linux-kernel@vger.kernel.org, Rob Herring , Liam Girdwood , Nicolas Frattaroli , Mark Brown , Krzysztof Kozlowski , Philipp Zabel , Johan Jonker , Luca Ceresoli , linux-arm-kernel@lists.infradead.org X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Luca Ceresoli These are I2S engines internally connected to the built-in audio codec. Signed-off-by: Luca Ceresoli --- arch/arm64/boot/dts/rockchip/rk3308.dtsi | 54 ++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3308.dtsi b/arch/arm64/boot/dts/rockchip/rk3308.dtsi index 2dfa67f1cd67..093b70563b23 100644 --- a/arch/arm64/boot/dts/rockchip/rk3308.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3308.dtsi @@ -571,6 +571,60 @@ dmac1: dma-controller@ff2d0000 { #dma-cells = <1>; }; + /* + * - can be clock producer or consumer + * - up to 8 capture channels and 2 playback channels + * - connected internally to audio codec + */ + i2s_8ch_2: i2s@ff320000 { + compatible = "rockchip,rk3308-i2s-tdm"; + reg = <0x0 0xff320000 0x0 0x1000>; + interrupts = ; + clock-names = "mclk_tx", "mclk_rx", "hclk", + "mclk_tx_src", "mclk_rx_src", + "mclk_root0", "mclk_root1"; + clocks = <&cru SCLK_I2S2_8CH_TX>, + <&cru SCLK_I2S2_8CH_RX>, + <&cru HCLK_I2S2_8CH>, + <&cru SCLK_I2S2_8CH_TX_SRC>, + <&cru SCLK_I2S2_8CH_RX_SRC>, + <&cru PLL_VPLL0>, + <&cru PLL_VPLL1>; + dmas = <&dmac1 5>, <&dmac1 4>; + dma-names = "rx", "tx"; + resets = <&cru SRST_I2S2_8CH_TX_M>, <&cru SRST_I2S2_8CH_RX_M>; + reset-names = "tx-m", "rx-m"; + rockchip,grf = <&grf>; + status = "disabled"; + }; + + /* + * - can be clock consumer only + * - up to 4 capture channels, no playback + * - connected internally to audio codec + */ + i2s_8ch_3: i2s@ff330000 { + compatible = "rockchip,rk3308-i2s-tdm"; + reg = <0x0 0xff330000 0x0 0x1000>; + interrupts = ; + clock-names = "mclk_tx", "mclk_rx", "hclk", + "mclk_tx_src", "mclk_rx_src", + "mclk_root0", "mclk_root1"; + clocks = <&cru SCLK_I2S3_8CH_TX>, + <&cru SCLK_I2S3_8CH_RX>, + <&cru HCLK_I2S3_8CH>, + <&cru SCLK_I2S3_8CH_TX_SRC>, + <&cru SCLK_I2S3_8CH_RX_SRC>, + <&cru PLL_VPLL0>, + <&cru PLL_VPLL1>; + dmas = <&dmac1 7>; + dma-names = "rx"; + resets = <&cru SRST_I2S3_8CH_TX_M>, <&cru SRST_I2S3_8CH_RX_M>; + reset-names = "tx-m", "rx-m"; + rockchip,grf = <&grf>; + status = "disabled"; + }; + i2s_2ch_0: i2s@ff350000 { compatible = "rockchip,rk3308-i2s", "rockchip,rk3066-i2s"; reg = <0x0 0xff350000 0x0 0x1000>; From patchwork Wed Sep 7 14:21:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 12969043 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 alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (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 D3707C38145 for ; Wed, 7 Sep 2022 14:23:36 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 0807F1684; Wed, 7 Sep 2022 16:22:45 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 0807F1684 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1662560615; bh=bnhQgRBl3f5glvXBUCZGeUc6uLOekc/CxTl6xs76s6U=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=MMis79jgNNIb68Gb/72BuXRAN6VTFie9d+dpmyOWsdZ1DMAFkXguy0iPyepjSAA4T 2+L6xIMMwyglNlOfghin1U6qv+z3EigVSB7d6BYxJFzX6ty89JLgxWgaN547lIIYkG FJh/yBv6PthmyeWDa5pBwm78eP8rerr/GPxM8afM= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 07121F80549; Wed, 7 Sep 2022 16:22:20 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id D3EF5F80535; Wed, 7 Sep 2022 16:22:16 +0200 (CEST) Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 75D6EF804B1 for ; Wed, 7 Sep 2022 16:22:09 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 75D6EF804B1 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="ipnFSla9" Received: from booty.fritz.box (unknown [77.244.183.192]) (Authenticated sender: luca.ceresoli@bootlin.com) by mail.gandi.net (Postfix) with ESMTPA id 0F1EC200011; Wed, 7 Sep 2022 14:22:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1662560529; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=H7rApNsDwnaiu6MG3hhivs2ZkGDwqxF3VZC6R02WDNw=; b=ipnFSla9jeMfitB27ywlIo/bpDmP92NiUsF+hn3Fzn3jcCIAs1qozgpLFSWa95z3SOS/G2 mP0E6XtJBj17nE/m4P1L0EDsBM8QEmuPyFyjkX1hPNFSKO6c9ict3RLuo+kUI5KhZYLirw 6x/s+ItC9bDldSy5sCKwNsYzMO96ntkli2TXFop4rnjDVkoyWRYbAYeqRLcwFoIUj9KUz9 rpi9CanlYQnxHhhjV6BB7do9tp7SzgAI4UkikKmyMwoktiCxm2odK78norQc44MbYNOgBV tkgdYYMAhg0FWVN904llHJ03j0h73wCdJas/l8CMYvyUNuHjzCUVUbs4fEsTfA== From: luca.ceresoli@bootlin.com To: alsa-devel@alsa-project.org, linux-rockchip@lists.infradead.org Subject: [PATCH 4/8] arm64: dts: rockchip: add the internal audio codec Date: Wed, 7 Sep 2022 16:21:20 +0200 Message-Id: <20220907142124.2532620-5-luca.ceresoli@bootlin.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> References: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, Heiko Stuebner , Takashi Iwai , Chris Morgan , linux-kernel@vger.kernel.org, Rob Herring , Liam Girdwood , Nicolas Frattaroli , Mark Brown , Krzysztof Kozlowski , Philipp Zabel , Johan Jonker , Luca Ceresoli , linux-arm-kernel@lists.infradead.org X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Luca Ceresoli The RK3308 has a built-in audio codec that connects internally to i2s_8ch_2 or i2s_8ch_3. Signed-off-by: Luca Ceresoli --- arch/arm64/boot/dts/rockchip/rk3308.dtsi | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3308.dtsi b/arch/arm64/boot/dts/rockchip/rk3308.dtsi index 093b70563b23..221cde49dc98 100644 --- a/arch/arm64/boot/dts/rockchip/rk3308.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3308.dtsi @@ -808,6 +808,20 @@ cru: clock-controller@ff500000 { assigned-clock-rates = <32768>; }; + acodec: acodec@ff560000 { + compatible = "rockchip,rk3308-codec"; + reg = <0x0 0xff560000 0x0 0x10000>; + rockchip,grf = <&grf>; + clock-names = "mclk_tx", "mclk_rx", "hclk"; + clocks = <&cru SCLK_I2S2_8CH_TX_OUT>, + <&cru SCLK_I2S2_8CH_RX_OUT>, + <&cru PCLK_ACODEC>; + reset-names = "acodec-reset"; + resets = <&cru SRST_ACODEC_P>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + gic: interrupt-controller@ff580000 { compatible = "arm,gic-400"; reg = <0x0 0xff581000 0x0 0x1000>, From patchwork Wed Sep 7 14:21:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 12969046 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 alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (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 B422AC6FA89 for ; Wed, 7 Sep 2022 14:24:25 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id E03CD15C2; Wed, 7 Sep 2022 16:23:33 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz E03CD15C2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1662560664; bh=y56zi/60F5ITxynDT2hlctMP4NfN49y+o3mIImmH3Dg=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=ShBrUksajXjdydpOJq5cn3RazrB05eo/Fbm3C8DTgdAD1PUN9UJ1VWk+wiFcI0ZLL +REiISBxReiFWQA35CrXH2UTfy9vcMSAYZMsr4DOBU4sQtU0pdCvQmPhWkIDer8mte nxXI7zR0ZB6knNtNT2RNG89Wloa8T/CMjC8FQ20E= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 21640F8055B; Wed, 7 Sep 2022 16:22:24 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id F1DBBF80558; Wed, 7 Sep 2022 16:22:21 +0200 (CEST) Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [IPv6:2001:4b98:dc4:8::232]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 4C853F80533 for ; Wed, 7 Sep 2022 16:22:12 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 4C853F80533 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="AhbVgluw" Received: from booty.fritz.box (unknown [77.244.183.192]) (Authenticated sender: luca.ceresoli@bootlin.com) by mail.gandi.net (Postfix) with ESMTPA id E8775200007; Wed, 7 Sep 2022 14:22:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1662560531; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Hkh90Imkp9wad8yz7UhVbHvGQ7u2lRuqcdvVsAUHVf8=; b=AhbVgluwrJrNd+ZcBjQHaMqV4FSxpzqM601IbJNxKfAAmIIxy0f7OssfjbEkcOVea1pjUp x9+sKrwzlELO7L2QhPqkQUch8GB+iaLgzb2anM0ZWHYKcZvvlVXl0BswKpbCHHdAdRVZKj eeJSIh3wpoQSYe2zUOJ5pFiPxRzziImWfq/Lrmp7q250f4fjQ27YcAQXNEc9NV1YTQHgSc znSI++2sdpd3VvbPLBGfzGoLHHPPZaL74RMmlCHFlqzVg0cGQvYu9PoBu+eC03ViSVvMWp RgL1VH4yIBqgkvwHlTxzGMgOkA2GqBen4Y6L8q2ls4dMgJM98UA+78b/DHmvxw== From: luca.ceresoli@bootlin.com To: alsa-devel@alsa-project.org, linux-rockchip@lists.infradead.org Subject: [PATCH 5/8] ASoC: rockchip: i2s-tdm: Fix clk_id usage in .set_sysclk() Date: Wed, 7 Sep 2022 16:21:21 +0200 Message-Id: <20220907142124.2532620-6-luca.ceresoli@bootlin.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> References: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, Heiko Stuebner , Takashi Iwai , Chris Morgan , linux-kernel@vger.kernel.org, Rob Herring , Liam Girdwood , Nicolas Frattaroli , Mark Brown , Krzysztof Kozlowski , Philipp Zabel , Johan Jonker , Luca Ceresoli , linux-arm-kernel@lists.infradead.org X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Luca Ceresoli There are two problems with the second parameter of rockchip_i2s_tdm_set_sysclk(): 1. The second argument to a .set_sysclk() op is a clk_id, not a stream index, so it is incorrect to compare it with SNDRV_PCM_STREAM_PLAYBACK. Technically this code works correctly anyway because SNDRV_PCM_STREAM_PLAYBACK is defined as 0, which is also the clk_id for the mclk_tx as enforced by the device tree bindings. So this is a formal error, not triggering incorrect behaviour. 2. The else branch will consider any nonzero value as "rx", while only value 1 should be allowed for the mclk_rx clock. This does trigger incorrect behaviour if passing clk_id not equal to 0 or 1. Fix problem 1 by adding a new enum for the clock indexes as enforced in device tree and replace accordingly: * stream -> clk_id * SNDRV_PCM_STREAM_PLAYBACK -> CLK_MCLK_TX (value 0) Fix problem 2 by adding an 'else if' and returning error if clk_id is not 0 or 1. Signed-off-by: Luca Ceresoli --- sound/soc/rockchip/rockchip_i2s_tdm.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 2550bd2a5e78..4aa80fedb996 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -34,6 +34,9 @@ #define TRCM_TX 1 #define TRCM_RX 2 +/* Clock indexes as enforced by the DT bindings */ +enum { CLK_IDX_MCLK_TX, CLK_IDX_MCLK_RX }; + struct txrx_config { u32 addr; u32 reg; @@ -969,7 +972,7 @@ static int rockchip_i2s_tdm_trigger(struct snd_pcm_substream *substream, return 0; } -static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int stream, +static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai); @@ -978,15 +981,18 @@ static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int stream, if (i2s_tdm->clk_trcm) { i2s_tdm->mclk_tx_freq = freq; i2s_tdm->mclk_rx_freq = freq; + + dev_dbg(i2s_tdm->dev, "mclk freq: %u", freq); } else { - if (stream == SNDRV_PCM_STREAM_PLAYBACK) + if (clk_id == CLK_IDX_MCLK_TX) i2s_tdm->mclk_tx_freq = freq; - else + else if (clk_id == CLK_IDX_MCLK_RX) i2s_tdm->mclk_rx_freq = freq; - } + else + return -ENOTSUPP; - dev_dbg(i2s_tdm->dev, "The target mclk_%s freq is: %d\n", - stream ? "rx" : "tx", freq); + dev_dbg(i2s_tdm->dev, "mclk[%d] freq: %u", clk_id, freq); + } return 0; } From patchwork Wed Sep 7 14:21:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 12969045 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 alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (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 17297C38145 for ; Wed, 7 Sep 2022 14:24:10 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id BE9C2166F; Wed, 7 Sep 2022 16:23:17 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz BE9C2166F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1662560647; bh=KRASiHquA6p3sWtJ/NUy4nyW7vA3uR9sXVrEpwF0zmM=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=qEQXG9ybjiMcMvDyKhM9mL8hddQpr5hrcHoKMw5KA3dEPH5jj7kfK7hFWsVyVCu4o HjsoO0zA77ZVhLiPRYP+dpqxqAQeycBh9j/EHTY2IhDdXO4lGq2yGiRJYeGE7TEHSa F9TzO55+No3r0uY90wCj7IS4bAanCC3yTe+d/9OU= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 77A76F80552; Wed, 7 Sep 2022 16:22:23 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id AC7A1F80552; Wed, 7 Sep 2022 16:22:21 +0200 (CEST) Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id E6B95F804CA for ; Wed, 7 Sep 2022 16:22:13 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz E6B95F804CA Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="CCM0A87K" Received: from booty.fritz.box (unknown [77.244.183.192]) (Authenticated sender: luca.ceresoli@bootlin.com) by mail.gandi.net (Postfix) with ESMTPA id 0E4D3200019; Wed, 7 Sep 2022 14:22:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1662560533; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0x0N8xdASNNGJim+/6TTRqaoE75eCBfHEEUyRn2uUTs=; b=CCM0A87KExaQlKFFsYRXly065FI86fsGob/0RhItQzNDnScAjh7sOUNNcoL5Adu/N/ZzgI N/5BYEhHZkPJy/sMG6DhmtT/qWCVKqd4CUMg/HihWZbDmguNpAQssSgPfMQdld0kC3ObrO B16fCiUAayyyd9DMA3D1obcNhpb59k0sKKzsoBQDnPZgfDXwzfDaFu2a/djAo6l0i948er Mv5ZTZNVYQXYg4DMpGb/itY7Zcf81HWGALFdVvZANTuw20isR5VsyPoEgfVwtwOr8j43J2 iIKLpa/jg/hGDAB0G1iw/ijnXPVfHdQTnYQd27Lu+p5QwMApGb9PvZKrqgP5Iw== From: luca.ceresoli@bootlin.com To: alsa-devel@alsa-project.org, linux-rockchip@lists.infradead.org Subject: [PATCH 6/8] ASoC: audio-graph: let dai_link->init be overridable Date: Wed, 7 Sep 2022 16:21:22 +0200 Message-Id: <20220907142124.2532620-7-luca.ceresoli@bootlin.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> References: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, Heiko Stuebner , Takashi Iwai , Chris Morgan , linux-kernel@vger.kernel.org, Rob Herring , Liam Girdwood , Nicolas Frattaroli , Mark Brown , Krzysztof Kozlowski , Philipp Zabel , Johan Jonker , Luca Ceresoli , linux-arm-kernel@lists.infradead.org X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Luca Ceresoli We can already override dai_link->ops via a custom pointer in asoc_simple_priv. Do the same for dai_link->init. This is needed for a card that need to call .set_sysclk multiple times to initialize more than one clock. The current code does not allow to do it cleanly. Signed-off-by: Luca Ceresoli --- include/sound/simple_card_utils.h | 1 + sound/soc/generic/audio-graph-card.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index a0b827f0c2f6..60dab5f68f5e 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -75,6 +75,7 @@ struct asoc_simple_priv { struct snd_soc_dai_link_component dummy; struct snd_soc_codec_conf *codec_conf; struct gpio_desc *pa_gpio; + int (*init)(struct snd_soc_pcm_runtime *rtd); const struct snd_soc_ops *ops; unsigned int dpcm_selectable:1; unsigned int force_dpcm:1; diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index b327372f2e4a..38c05eb1c650 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -263,6 +263,8 @@ static int graph_link_init(struct asoc_simple_priv *priv, dai_link->init = asoc_simple_dai_init; dai_link->ops = &graph_ops; + if (priv->init) + dai_link->init = priv->init; if (priv->ops) dai_link->ops = priv->ops; From patchwork Wed Sep 7 14:21:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 12969048 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 alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (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 E2363C38145 for ; Wed, 7 Sep 2022 14:25:02 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 24416164E; Wed, 7 Sep 2022 16:24:11 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 24416164E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1662560701; bh=Bp1gLDukJh8sqKXDtzcTlyfIzs3Uuuvl6VNjcwuWHwg=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=I94gcXM4LsK/Y8cWgRsucsGy3X+uVTcbYSseg3BwlN7GrRd6QIwNecc2xIbYl/ptM +LuxFpWrl8qj4j9K7zjEDB4wg7ippuhD7jq0LaQ3vpwGAItqJ3ppS76OXru2dSOPqT Cc2uR6f++j7scJw/6qUOfmbAcVbYhu2rzK3xGuxA= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 49EFAF80579; Wed, 7 Sep 2022 16:22:28 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id D3FF3F80564; Wed, 7 Sep 2022 16:22:25 +0200 (CEST) Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id E2FBDF804B1 for ; Wed, 7 Sep 2022 16:22:15 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz E2FBDF804B1 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="DN8Rg/nb" Received: from booty.fritz.box (unknown [77.244.183.192]) (Authenticated sender: luca.ceresoli@bootlin.com) by mail.gandi.net (Postfix) with ESMTPA id E1F6920000A; Wed, 7 Sep 2022 14:22:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1662560535; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=xuLgzYpw92Z5skYvCzdhlOMlfP8qmscKLZEAb70AKTs=; b=DN8Rg/nbHNnTFIRz68juFamKfuaBvwQ0gkCeYBgkQODd8YW8yFYCkr4sc1Tjonht0NcunK RFEqWHlXeU4W87ocOy4oVphuNrkhq8Ok0EtxOSskU/EPKcdB3xjj7G0haxzppU5wD7fkmQ xAPu5AXICCS2sDKmVqVkzccGKNejd0iPrVvxz5ElqSIqpYNlSV5gDvScioW7+hYPGb+5RW CqaxecofWcPHPa4ChShtNtEtbzx23BwMHgxs8hy5RT80j1+8YmH91f8FEe4erMgsIM1Xgz xqprRuWoFj8siCzEmAjCmnBIl2kkSzzmTzbkYmLik6qJjWcZRlFw5OvdF4d6Dg== From: luca.ceresoli@bootlin.com To: alsa-devel@alsa-project.org, linux-rockchip@lists.infradead.org Subject: [PATCH 7/8] ASoC: codecs: Add RK3308 internal audio codec driver Date: Wed, 7 Sep 2022 16:21:23 +0200 Message-Id: <20220907142124.2532620-8-luca.ceresoli@bootlin.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> References: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, Heiko Stuebner , Takashi Iwai , Chris Morgan , linux-kernel@vger.kernel.org, Rob Herring , Liam Girdwood , Nicolas Frattaroli , Mark Brown , Krzysztof Kozlowski , Philipp Zabel , Johan Jonker , Luca Ceresoli , linux-arm-kernel@lists.infradead.org X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Luca Ceresoli Add driver for the internal audio codec of the Rockchip RK3308 SoC. Initially based on the vendor kernel driver [0], with lots of cleanups, fixes, improvements and removal of some features. [0] https://github.com/rockchip-linux/kernel/blob/develop-4.19/sound/soc/codecs/rk3308_codec.c Signed-off-by: Luca Ceresoli Reported-by: kernel test robot Reported-by: kernel test robot Reported-by: kernel test robot Reported-by: Dan Carpenter Reported-by: kernel test robot --- MAINTAINERS | 2 + sound/soc/codecs/Kconfig | 11 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rk3308_codec.c | 2122 +++++++++++++++++++++++++++++++ sound/soc/codecs/rk3308_codec.h | 648 ++++++++++ 5 files changed, 2785 insertions(+) create mode 100644 sound/soc/codecs/rk3308_codec.c create mode 100644 sound/soc/codecs/rk3308_codec.h diff --git a/MAINTAINERS b/MAINTAINERS index 079bdd95dc49..fb2a0a6e3c1f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17593,6 +17593,8 @@ M: Luca Ceresoli S: Maintained F: Documentation/devicetree/bindings/sound/rockchip,rk3308-codec.yaml F: include/dt-bindings/sound/rockchip,rk3308-codec.h +F: sound/soc/codecs/rk3308_codec.c +F: sound/soc/codecs/rk3308_codec.h ROCKCHIP RK3308 SOUND CARD DRIVER M: Luca Ceresoli diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c7d83fe999e9..e05b084308f4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -164,6 +164,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_PCM5102A imply SND_SOC_PCM512x_I2C imply SND_SOC_PCM512x_SPI + imply SND_SOC_RK3308 imply SND_SOC_RK3328 imply SND_SOC_RK817 imply SND_SOC_RT274 @@ -1191,6 +1192,16 @@ config SND_SOC_PCM512x_SPI select SND_SOC_PCM512x select REGMAP_SPI +config SND_SOC_RK3308 + tristate "Rockchip RK3308 audio CODEC" + select REGMAP_MMIO + help + This is a device driver for the audio codec embedded in the + Rockchip RK3308 SoC. + + It has 8 24-bit ADCs and 2 24-bit DACs. The maximum supported + sampling rate is 192 kHz. + config SND_SOC_RK3328 tristate "Rockchip RK3328 audio CODEC" select REGMAP_MMIO diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 16a01635dd04..8f6a473c2467 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -181,6 +181,7 @@ snd-soc-pcm5102a-objs := pcm5102a.o snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o +snd-soc-rk3308-objs := rk3308_codec.o snd-soc-rk3328-objs := rk3328_codec.o snd-soc-rk817-objs := rk817_codec.o snd-soc-rl6231-objs := rl6231.o @@ -534,6 +535,7 @@ obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o +obj-$(CONFIG_SND_SOC_RK3308) += snd-soc-rk3308.o obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o obj-$(CONFIG_SND_SOC_RK817) += snd-soc-rk817.o obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o diff --git a/sound/soc/codecs/rk3308_codec.c b/sound/soc/codecs/rk3308_codec.c new file mode 100644 index 000000000000..2c001689dce7 --- /dev/null +++ b/sound/soc/codecs/rk3308_codec.c @@ -0,0 +1,2122 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Rockchip RK3308 internal audio codec driver + * + * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. + * Copyright (c) 2022, Vivax-Metrotech Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rk3308_codec.h" + +#define ADC_LR_GROUP_MAX 4 +#define RK3308_N_MICBIAS_MAX 2 + +#define GRF_CHIP_ID 0x800 + +#define ACODEC_VERSION_A 0xa +#define ACODEC_VERSION_B 0xb + +enum { + ADC_GRP0_MICIN = 0, + ADC_GRP0_LINEIN +}; + +enum { + DAC_LINEOUT = 0, + DAC_HPOUT = 1, + DAC_LINEOUT_HPOUT = 11, +}; + +struct rk3308_codec_priv { + const struct device *dev; + struct regmap *regmap; + struct regmap *grf; + struct reset_control *reset; + struct clk *pclk; + struct clk *mclk_rx; + struct clk *mclk_tx; + struct snd_soc_component *component; + u32 codec_ver; + + /* + * To select ADCs for groups: + * + * grp 0 -- select ADC1 / ADC2 + * grp 1 -- select ADC3 / ADC4 + * grp 2 -- select ADC5 / ADC6 + * grp 3 -- select ADC7 / ADC8 + */ + u32 used_adc_grps; + int adc_grp0_using_linein; + /* 0: line out, 1: hp out, 11: lineout and hpout */ + int dac_output; + + /* AGC L/R Off/on */ + unsigned int agc_l[ADC_LR_GROUP_MAX]; + unsigned int agc_r[ADC_LR_GROUP_MAX]; + + /* Only hpout do fade-in and fade-out */ + unsigned int hpout_l_dgain; + unsigned int hpout_r_dgain; + + bool micbias_enabled[RK3308_N_MICBIAS_MAX]; + u32 micbias_avdd_mult; +}; + +static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_grp_gain_tlv, -1800, 150, 0); +static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_grp_max_gain_tlv, -1350, 600, 0); +static const DECLARE_TLV_DB_SCALE(rk3308_codec_alc_agc_grp_min_gain_tlv, -1800, 600, 0); +static const DECLARE_TLV_DB_SCALE(rk3308_codec_adc_alc_gain_tlv, -1800, 150, 0); +static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpout_gain_tlv, -3900, 150, 0); +static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpmix_gain_tlv, -600, 600, 0); + +static const DECLARE_TLV_DB_RANGE(rk3308_codec_dac_lineout_gain_tlv, + 0, 0, TLV_DB_SCALE_ITEM(-600, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(-300, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(-150, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(0, 0, 0), +); + +static int rk3308_codec_hpout_l_get_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_hpout_l_put_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_hpout_r_get_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_hpout_r_put_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_agc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_agc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_micbias_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rk3308_codec_micbias_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static const char *offon_text[2] = { + [0] = "Off", + [1] = "On", +}; + +static const struct soc_enum rk3308_micbias_enum[] = { + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(1, 0, ARRAY_SIZE(offon_text), offon_text), +}; + +/* ALC AGC Switch */ +static const struct soc_enum rk3308_agc_enum_array[] = { + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(1, 0, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(1, 1, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(2, 0, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(2, 1, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(3, 0, ARRAY_SIZE(offon_text), offon_text), + SOC_ENUM_SINGLE(3, 1, ARRAY_SIZE(offon_text), offon_text), +}; + +static const unsigned int agc_approx_rate[] = { + 96000, 48000, 44100, 32000, 24000, 16000, 12000, 8000 +}; + +static const struct snd_kcontrol_new rk3308_codec_controls[] = { + /* Despite the register names, these set the gain when AGC is OFF */ + SOC_SINGLE_RANGE_TLV("MIC1 Capture Volume", + RK3308_ADC_ANA_CON03(0), + RK3308_ADC_CH1_ALC_GAIN_SFT, + RK3308_ADC_CH1_ALC_GAIN_MIN, + RK3308_ADC_CH1_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC2 Capture Volume", + RK3308_ADC_ANA_CON04(0), + RK3308_ADC_CH2_ALC_GAIN_SFT, + RK3308_ADC_CH2_ALC_GAIN_MIN, + RK3308_ADC_CH2_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC3 Capture Volume", + RK3308_ADC_ANA_CON03(1), + RK3308_ADC_CH1_ALC_GAIN_SFT, + RK3308_ADC_CH1_ALC_GAIN_MIN, + RK3308_ADC_CH1_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC4 Capture Volume", + RK3308_ADC_ANA_CON04(1), + RK3308_ADC_CH2_ALC_GAIN_SFT, + RK3308_ADC_CH2_ALC_GAIN_MIN, + RK3308_ADC_CH2_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC5 Capture Volume", + RK3308_ADC_ANA_CON03(2), + RK3308_ADC_CH1_ALC_GAIN_SFT, + RK3308_ADC_CH1_ALC_GAIN_MIN, + RK3308_ADC_CH1_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC6 Capture Volume", + RK3308_ADC_ANA_CON04(2), + RK3308_ADC_CH2_ALC_GAIN_SFT, + RK3308_ADC_CH2_ALC_GAIN_MIN, + RK3308_ADC_CH2_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC7 Capture Volume", + RK3308_ADC_ANA_CON03(3), + RK3308_ADC_CH1_ALC_GAIN_SFT, + RK3308_ADC_CH1_ALC_GAIN_MIN, + RK3308_ADC_CH1_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC8 Capture Volume", + RK3308_ADC_ANA_CON04(3), + RK3308_ADC_CH2_ALC_GAIN_SFT, + RK3308_ADC_CH2_ALC_GAIN_MIN, + RK3308_ADC_CH2_ALC_GAIN_MAX, + 0, rk3308_codec_adc_alc_gain_tlv), + SOC_SINGLE("MIC1 Capture Switch", RK3308_ADC_DIG_CON03(0), RK3308_ADC_L_CH_BIST_SFT, 1, 1), + SOC_SINGLE("MIC2 Capture Switch", RK3308_ADC_DIG_CON03(0), RK3308_ADC_R_CH_BIST_SFT, 1, 1), + SOC_SINGLE("MIC3 Capture Switch", RK3308_ADC_DIG_CON03(1), RK3308_ADC_L_CH_BIST_SFT, 1, 1), + SOC_SINGLE("MIC4 Capture Switch", RK3308_ADC_DIG_CON03(1), RK3308_ADC_R_CH_BIST_SFT, 1, 1), + SOC_SINGLE("MIC5 Capture Switch", RK3308_ADC_DIG_CON03(2), RK3308_ADC_L_CH_BIST_SFT, 1, 1), + SOC_SINGLE("MIC6 Capture Switch", RK3308_ADC_DIG_CON03(2), RK3308_ADC_R_CH_BIST_SFT, 1, 1), + SOC_SINGLE("MIC7 Capture Switch", RK3308_ADC_DIG_CON03(3), RK3308_ADC_L_CH_BIST_SFT, 1, 1), + SOC_SINGLE("MIC8 Capture Switch", RK3308_ADC_DIG_CON03(3), RK3308_ADC_R_CH_BIST_SFT, 1, 1), + + SOC_ENUM_EXT("MIC1 AGC Capture Switch", rk3308_agc_enum_array[0], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("MIC2 AGC Capture Switch", rk3308_agc_enum_array[1], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("MIC3 AGC Capture Switch", rk3308_agc_enum_array[2], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("MIC4 AGC Capture Switch", rk3308_agc_enum_array[3], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("MIC5 AGC Capture Switch", rk3308_agc_enum_array[4], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("MIC6 AGC Capture Switch", rk3308_agc_enum_array[5], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("MIC7 AGC Capture Switch", rk3308_agc_enum_array[6], + rk3308_codec_agc_get, rk3308_codec_agc_put), + SOC_ENUM_EXT("MIC8 AGC Capture Switch", rk3308_agc_enum_array[7], + rk3308_codec_agc_get, rk3308_codec_agc_put), + + SOC_SINGLE_RANGE_TLV("MIC1 AGC Max Capture Volume", + RK3308_ALC_L_DIG_CON09(0), + RK3308_AGC_MAX_GAIN_PGA_SFT, + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC2 AGC Max Capture Volume", + RK3308_ALC_R_DIG_CON09(0), + RK3308_AGC_MAX_GAIN_PGA_SFT, + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC3 AGC Max Capture Volume", + RK3308_ALC_L_DIG_CON09(1), + RK3308_AGC_MAX_GAIN_PGA_SFT, + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC4 AGC Max Capture Volume", + RK3308_ALC_R_DIG_CON09(1), + RK3308_AGC_MAX_GAIN_PGA_SFT, + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC5 AGC Max Capture Volume", + RK3308_ALC_L_DIG_CON09(2), + RK3308_AGC_MAX_GAIN_PGA_SFT, + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC6 AGC Max Capture Volume", + RK3308_ALC_R_DIG_CON09(2), + RK3308_AGC_MAX_GAIN_PGA_SFT, + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC7 AGC Max Capture Volume", + RK3308_ALC_L_DIG_CON09(3), + RK3308_AGC_MAX_GAIN_PGA_SFT, + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC8 AGC Max Capture Volume", + RK3308_ALC_R_DIG_CON09(3), + RK3308_AGC_MAX_GAIN_PGA_SFT, + RK3308_AGC_MAX_GAIN_PGA_MIN, + RK3308_AGC_MAX_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_max_gain_tlv), + + SOC_SINGLE_RANGE_TLV("MIC1 AGC Min Capture Volume", + RK3308_ALC_L_DIG_CON09(0), + RK3308_AGC_MIN_GAIN_PGA_SFT, + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC2 AGC Min Capture Volume", + RK3308_ALC_R_DIG_CON09(0), + RK3308_AGC_MIN_GAIN_PGA_SFT, + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC3 AGC Min Capture Volume", + RK3308_ALC_L_DIG_CON09(1), + RK3308_AGC_MIN_GAIN_PGA_SFT, + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC4 AGC Min Capture Volume", + RK3308_ALC_R_DIG_CON09(1), + RK3308_AGC_MIN_GAIN_PGA_SFT, + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC5 AGC Min Capture Volume", + RK3308_ALC_L_DIG_CON09(2), + RK3308_AGC_MIN_GAIN_PGA_SFT, + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC6 AGC Min Capture Volume", + RK3308_ALC_R_DIG_CON09(2), + RK3308_AGC_MIN_GAIN_PGA_SFT, + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC7 AGC Min Capture Volume", + RK3308_ALC_L_DIG_CON09(3), + RK3308_AGC_MIN_GAIN_PGA_SFT, + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC8 AGC Min Capture Volume", + RK3308_ALC_R_DIG_CON09(3), + RK3308_AGC_MIN_GAIN_PGA_SFT, + RK3308_AGC_MIN_GAIN_PGA_MIN, + RK3308_AGC_MIN_GAIN_PGA_MAX, + 0, rk3308_codec_alc_agc_grp_min_gain_tlv), + + SOC_SINGLE_RANGE_TLV("MIC1 PGA Gain Capture Volume", + RK3308_ALC_L_DIG_CON03(0), + RK3308_AGC_PGA_GAIN_SFT, + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC2 PGA Gain Capture Volume", + RK3308_ALC_R_DIG_CON03(0), + RK3308_AGC_PGA_GAIN_SFT, + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC3 PGA Gain Capture Volume", + RK3308_ALC_L_DIG_CON03(1), + RK3308_AGC_PGA_GAIN_SFT, + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC4 PGA Gain Capture Volume", + RK3308_ALC_R_DIG_CON03(1), + RK3308_AGC_PGA_GAIN_SFT, + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC5 PGA Gain Capture Volume", + RK3308_ALC_L_DIG_CON03(2), + RK3308_AGC_PGA_GAIN_SFT, + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC6 PGA Gain Capture Volume", + RK3308_ALC_R_DIG_CON03(2), + RK3308_AGC_PGA_GAIN_SFT, + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC7 PGA Gain Capture Volume", + RK3308_ALC_L_DIG_CON03(3), + RK3308_AGC_PGA_GAIN_SFT, + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + SOC_SINGLE_RANGE_TLV("MIC8 PGA Gain Capture Volume", + RK3308_ALC_R_DIG_CON03(3), + RK3308_AGC_PGA_GAIN_SFT, + RK3308_AGC_PGA_GAIN_MIN, + RK3308_AGC_PGA_GAIN_MAX, + 0, rk3308_codec_alc_agc_grp_gain_tlv), + + SOC_ENUM_EXT("MICBIAS1 Capture Switch", rk3308_micbias_enum[0], + rk3308_codec_micbias_get, rk3308_codec_micbias_put), + SOC_ENUM_EXT("MICBIAS2 Capture Switch", rk3308_micbias_enum[1], + rk3308_codec_micbias_get, rk3308_codec_micbias_put), + + SOC_SINGLE_TLV("Line Out Left Playback Volume", + RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_GAIN_SFT, + RK3308_DAC_L_LINEOUT_GAIN_MAX, + 0, rk3308_codec_dac_lineout_gain_tlv), + SOC_SINGLE_TLV("Line Out Right Playback Volume", + RK3308_DAC_ANA_CON04, + RK3308_DAC_R_LINEOUT_GAIN_SFT, + RK3308_DAC_R_LINEOUT_GAIN_MAX, + 0, rk3308_codec_dac_lineout_gain_tlv), + + SOC_SINGLE_EXT_TLV("Headphone Left Playback Volume", + RK3308_DAC_ANA_CON05, + RK3308_DAC_L_HPOUT_GAIN_SFT, + RK3308_DAC_L_HPOUT_GAIN_MAX, + 0, + rk3308_codec_hpout_l_get_tlv, + rk3308_codec_hpout_l_put_tlv, + rk3308_codec_dac_hpout_gain_tlv), + SOC_SINGLE_EXT_TLV("Headphone Right Playback Volume", + RK3308_DAC_ANA_CON06, + RK3308_DAC_R_HPOUT_GAIN_SFT, + RK3308_DAC_R_HPOUT_GAIN_MAX, + 0, + rk3308_codec_hpout_r_get_tlv, + rk3308_codec_hpout_r_put_tlv, + rk3308_codec_dac_hpout_gain_tlv), + + SOC_SINGLE_RANGE_TLV("Headphone Mix Left Playback Volume", + RK3308_DAC_ANA_CON12, + RK3308_DAC_L_HPMIX_GAIN_SFT, + RK3308_DAC_L_HPMIX_GAIN_MIN, + RK3308_DAC_L_HPMIX_GAIN_MAX, + 0, rk3308_codec_dac_hpmix_gain_tlv), + SOC_SINGLE_RANGE_TLV("Headphone Mix Right Playback Volume", + RK3308_DAC_ANA_CON12, + RK3308_DAC_R_HPMIX_GAIN_SFT, + RK3308_DAC_R_HPMIX_GAIN_MIN, + RK3308_DAC_R_HPMIX_GAIN_MAX, + 0, rk3308_codec_dac_hpmix_gain_tlv), +}; + +static void rk3308_codec_update_zerocross(struct rk3308_codec_priv *rk3308) +{ + unsigned int agc_on, value; + int grp; + + /* 14: enable zero-cross detection if AGC enabled, else AGC won't work */ + for (grp = 0; grp < rk3308->used_adc_grps; grp++) { + regmap_read(rk3308->regmap, RK3308_ALC_L_DIG_CON09(grp), &agc_on); + value = (agc_on & RK3308_AGC_FUNC_SEL_MSK) ? RK3308_ADC_CH1_ZEROCROSS_DET_EN : 0; + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ZEROCROSS_DET_EN, value); + + regmap_read(rk3308->regmap, RK3308_ALC_R_DIG_CON09(grp), &agc_on); + value = (agc_on & RK3308_AGC_FUNC_SEL_MSK) ? RK3308_ADC_CH2_ZEROCROSS_DET_EN : 0; + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH2_ZEROCROSS_DET_EN, value); + } +} + +static int rk3308_codec_agc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + snd_BUG_ON(e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1); + + if (e->shift_l) + ucontrol->value.integer.value[0] = rk3308->agc_r[e->reg]; + else + ucontrol->value.integer.value[0] = rk3308->agc_l[e->reg]; + + return 0; +} + +static int rk3308_codec_agc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int value = ucontrol->value.integer.value[0]; + int grp = e->reg; + + snd_BUG_ON(e->reg < 0 || e->reg > ADC_LR_GROUP_MAX - 1); + + if (value) { + /* ALC AGC On */ + if (e->shift_l) { + /* ALC AGC Right On */ + regmap_set_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON09(grp), + RK3308_AGC_FUNC_SEL_MSK); + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(grp), + RK3308_ADC_ALCR_CON_GAIN_PGAR_EN); + + rk3308->agc_r[e->reg] = 1; + } else { + /* ALC AGC Left On */ + regmap_set_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON09(grp), + RK3308_AGC_FUNC_SEL_MSK); + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(grp), + RK3308_ADC_ALCL_CON_GAIN_PGAL_EN); + + rk3308->agc_l[e->reg] = 1; + } + } else { + /* ALC AGC Off */ + if (e->shift_l) { + /* ALC AGC Right Off */ + regmap_clear_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON09(grp), + RK3308_AGC_FUNC_SEL_MSK); + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(grp), + RK3308_ADC_ALCR_CON_GAIN_PGAR_EN); + + rk3308->agc_r[e->reg] = 0; + } else { + /* ALC AGC Left Off */ + regmap_clear_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON09(grp), + RK3308_AGC_FUNC_SEL_MSK); + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON11(grp), + RK3308_ADC_ALCL_CON_GAIN_PGAL_EN); + + rk3308->agc_l[e->reg] = 0; + } + } + + rk3308_codec_update_zerocross(rk3308); + + return 0; +} + +/* + * Disable all BIAS circuits, but do not change the driver status (rk3308->micbias_*). + * Useful e.g. for suspend. + */ +static void rk3308_codec_micbias_disable(struct rk3308_codec_priv *rk3308) +{ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(1), RK3308_ADC_MIC_BIAS_BUF_EN); + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(2), RK3308_ADC_MIC_BIAS_BUF_EN); + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(0), RK3308_ADC_MICBIAS_CURRENT_EN); +} + +/* + * Enable or disable MIC BIAS HW components based on the status (rk3308->micbias_*). + */ +static void rk3308_codec_micbias_apply(struct rk3308_codec_priv *rk3308) +{ + bool any_micbias_enabled = (rk3308->micbias_enabled[0] || rk3308->micbias_enabled[1]); + unsigned int i; + + /* + * MICBIAS programming sequence from TRM: + * + * - Enable MICBIAS: + * 1. Configure ACODEC_ADC_ANA_CON7[2:0] (AVDD voltage multiplier) + * 2. Wait until the VCMH stable + * 3. Configure ACODEC_ADC_ANA_CON8[4] to 1 (enable current source) + * 4. Configure the (ADC_ANA_CON7+{0x40|0x80})[3] (enable MICBIAS{1|2}) + * + * - Disable MICBIAS: + * 1. Clear (ADC_ANA_CON7+{0x40|0x80})[3] (disable MICBIAS{1|2}) + * 2. Clear ACODEC_ADC_ANA_CON8[4] (disenable current source) + */ + + if (any_micbias_enabled) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0), + RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK, + rk3308->micbias_avdd_mult); + + /* Wait until the VCMH keep stable */ + msleep(20); /* estimated value */ + + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(0), + RK3308_ADC_MICBIAS_CURRENT_EN); + } + + for (i = 0; i < RK3308_N_MICBIAS_MAX; i++) + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(i + 1), + RK3308_ADC_MIC_BIAS_BUF_EN, + rk3308->micbias_enabled[i] ? RK3308_ADC_MIC_BIAS_BUF_EN : 0); + + if (!any_micbias_enabled) + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON08(0), + RK3308_ADC_MICBIAS_CURRENT_EN); +} + +static int rk3308_codec_micbias_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + struct soc_enum *priv = (struct soc_enum *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = rk3308->micbias_enabled[priv->reg]; + + return 0; +} + +static int rk3308_codec_micbias_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + struct soc_enum *priv = (struct soc_enum *)kcontrol->private_value; + unsigned int on = ucontrol->value.integer.value[0]; + + rk3308->micbias_enabled[priv->reg] = on; + + rk3308_codec_micbias_apply(rk3308); + + return 0; +} + +static int rk3308_codec_hpout_l_get_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return snd_soc_get_volsw_range(kcontrol, ucontrol); +} + +static int rk3308_codec_hpout_l_put_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + unsigned int dgain = ucontrol->value.integer.value[0]; + + rk3308->hpout_l_dgain = dgain; + + return snd_soc_put_volsw_range(kcontrol, ucontrol); +} + +static int rk3308_codec_hpout_r_get_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return snd_soc_get_volsw_range(kcontrol, ucontrol); +} + +static int rk3308_codec_hpout_r_put_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + unsigned int dgain = ucontrol->value.integer.value[0]; + + rk3308->hpout_r_dgain = dgain; + + return snd_soc_put_volsw_range(kcontrol, ucontrol); +} + +static int rk3308_codec_reset(struct snd_soc_component *component) +{ + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + reset_control_assert(rk3308->reset); + usleep_range(10000, 11000); /* estimated value */ + reset_control_deassert(rk3308->reset); + + regmap_write(rk3308->regmap, RK3308_GLB_CON, 0x00); + usleep_range(10000, 11000); /* estimated value */ + regmap_write(rk3308->regmap, RK3308_GLB_CON, + RK3308_SYS_WORK | + RK3308_DAC_DIG_WORK | + RK3308_ADC_DIG_WORK); + + return 0; +} + +static int rk3308_codec_adc_dig_reset(struct rk3308_codec_priv *rk3308) +{ + regmap_clear_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_ADC_DIG_WORK); + usleep_range(50, 100); + regmap_set_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_ADC_DIG_WORK); + + return 0; +} + +static int rk3308_codec_dac_dig_reset(struct rk3308_codec_priv *rk3308) +{ + regmap_clear_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_DAC_DIG_WORK); + usleep_range(10000, 11000); + regmap_set_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_DAC_DIG_WORK); + + return 0; +} + +static int rk3308_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + rk3308_codec_micbias_apply(rk3308); + break; + case SND_SOC_BIAS_STANDBY: + rk3308_codec_micbias_disable(rk3308); + break; + case SND_SOC_BIAS_OFF: + break; + } + + return 0; +} + +static int rk3308_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + const unsigned int inv_bits = fmt & SND_SOC_DAIFMT_INV_MASK; + const bool inv_bitclk = + (inv_bits & SND_SOC_DAIFMT_IB_IF) || + (inv_bits & SND_SOC_DAIFMT_IB_NF); + const bool inv_frmclk = + (inv_bits & SND_SOC_DAIFMT_IB_IF) || + (inv_bits & SND_SOC_DAIFMT_NB_IF); + + unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0; + int grp, is_master; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + case SND_SOC_DAIFMT_CBP_CFP: + adc_aif2 |= RK3308_ADC_IO_MODE_MASTER; + adc_aif2 |= RK3308_ADC_MODE_MASTER; + dac_aif2 |= RK3308_DAC_IO_MODE_MASTER; + dac_aif2 |= RK3308_DAC_MODE_MASTER; + is_master = 1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + adc_aif1 |= RK3308_ADC_I2S_MODE_PCM; + dac_aif1 |= RK3308_DAC_I2S_MODE_PCM; + break; + case SND_SOC_DAIFMT_I2S: + adc_aif1 |= RK3308_ADC_I2S_MODE_I2S; + dac_aif1 |= RK3308_DAC_I2S_MODE_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + adc_aif1 |= RK3308_ADC_I2S_MODE_RJ; + dac_aif1 |= RK3308_DAC_I2S_MODE_RJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + adc_aif1 |= RK3308_ADC_I2S_MODE_LJ; + dac_aif1 |= RK3308_DAC_I2S_MODE_LJ; + break; + default: + return -EINVAL; + } + + if (inv_bitclk) { + adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL; + dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL; + } + + if (inv_frmclk) { + adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL; + dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL; + } + + /* + * Hold ADC Digital registers start at master mode + * + * There are 8 ADCs and use the same SCLK and LRCK internal for master + * mode, We need to make sure that they are in effect at the same time, + * otherwise they will cause the abnormal clocks. + */ + if (is_master) + regmap_clear_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_ADC_DIG_WORK); + + for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(grp), + RK3308_ADC_I2S_LRC_POL_REVERSAL | + RK3308_ADC_I2S_MODE_MSK, + adc_aif1); + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(grp), + RK3308_ADC_IO_MODE_MASTER | + RK3308_ADC_MODE_MASTER | + RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL, + adc_aif2); + } + + /* Hold ADC Digital registers end at master mode */ + if (is_master) + regmap_set_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_ADC_DIG_WORK); + + regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01, + RK3308_DAC_I2S_LRC_POL_REVERSAL | + RK3308_DAC_I2S_MODE_MSK, + dac_aif1); + regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, + RK3308_DAC_IO_MODE_MASTER | + RK3308_DAC_MODE_MASTER | + RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL, + dac_aif2); + + return 0; +} + +static int rk3308_codec_dac_dig_config(struct rk3308_codec_priv *rk3308, + struct snd_pcm_hw_params *params) +{ + unsigned int dac_aif1 = 0; + + /* Clear the status of DAC DIG Digital reigisters */ + rk3308_codec_dac_dig_reset(rk3308); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_16BITS; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_20BITS; + break; + case SNDRV_PCM_FORMAT_S24_LE: + dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_24BITS; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_32BITS; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01, + RK3308_DAC_I2S_VALID_LEN_MSK, dac_aif1); + regmap_set_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, RK3308_DAC_I2S_WORK); + + return 0; +} + +static int rk3308_codec_adc_dig_config(struct rk3308_codec_priv *rk3308, + struct snd_pcm_hw_params *params) +{ + unsigned int adc_aif1 = 0; + int grp; + + /* Clear the status of ADC DIG Digital reigisters */ + rk3308_codec_adc_dig_reset(rk3308); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_16BITS; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_20BITS; + break; + case SNDRV_PCM_FORMAT_S24_LE: + adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_24BITS; + break; + case SNDRV_PCM_FORMAT_S32_LE: + adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_32BITS; + break; + default: + return -EINVAL; + } + + switch (params_channels(params)) { + case 1: + adc_aif1 |= RK3308_ADC_I2S_MONO; + break; + case 2: + case 4: + case 6: + case 8: + break; + default: + return -EINVAL; + } + + for (grp = 0; grp < rk3308->used_adc_grps; grp++) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(grp), + RK3308_ADC_I2S_VALID_LEN_MSK | RK3308_ADC_I2S_MONO, adc_aif1); + regmap_set_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(grp), RK3308_ADC_I2S_WORK); + } + + return 0; +} + +static int rk3308_codec_agc_dig_config(struct rk3308_codec_priv *rk3308, + struct snd_pcm_hw_params *params) +{ + int grp; + + unsigned int value = find_closest_descending(params_rate(params), + agc_approx_rate, ARRAY_SIZE(agc_approx_rate)); + + for (grp = 0; grp < rk3308->used_adc_grps; grp++) { + regmap_update_bits(rk3308->regmap, RK3308_ALC_L_DIG_CON04(grp), + RK3308_AGC_APPROX_RATE_MSK, value); + regmap_update_bits(rk3308->regmap, RK3308_ALC_R_DIG_CON04(grp), + RK3308_AGC_APPROX_RATE_MSK, value); + } + + return 0; +} + +static int rk3308_codec_update_adc_grps(struct rk3308_codec_priv *rk3308, + struct snd_pcm_hw_params *params) +{ + switch (params_channels(params)) { + case 1: + rk3308->used_adc_grps = 1; + break; + case 2: + case 4: + case 6: + case 8: + rk3308->used_adc_grps = params_channels(params) / 2; + break; + default: + dev_err(rk3308->dev, "Invalid channels: %d\n", + params_channels(params)); + return -EINVAL; + } + + return 0; +} + +static int rk3308_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + int dgain; + + if (mute) { + for (dgain = 0x2; dgain <= 0x7; dgain++) { + /* + * Keep the max -> min digital CIC interpolation + * filter gain step by step. + * + * loud: 0x2; whisper: 0x7 + */ + regmap_update_bits(rk3308->regmap, + RK3308_DAC_DIG_CON04, + RK3308_DAC_CIC_IF_GAIN_MSK, + dgain); + usleep_range(200, 300); /* estimated value */ + } + } else { + for (dgain = 0x7; dgain >= 0x2; dgain--) { + /* + * Keep the min -> max digital CIC interpolation + * filter gain step by step + * + * loud: 0x2; whisper: 0x7 + */ + regmap_update_bits(rk3308->regmap, + RK3308_DAC_DIG_CON04, + RK3308_DAC_CIC_IF_GAIN_MSK, + dgain); + usleep_range(200, 300); /* estimated value */ + } + } + } + + return 0; +} + +static int rk3308_codec_digital_fadein(struct rk3308_codec_priv *rk3308) +{ + unsigned int dgain, dgain_ref; + + if (rk3308->hpout_l_dgain != rk3308->hpout_r_dgain) { + pr_warn("HPOUT l_dgain: 0x%x != r_dgain: 0x%x\n", + rk3308->hpout_l_dgain, rk3308->hpout_r_dgain); + dgain_ref = min(rk3308->hpout_l_dgain, rk3308->hpout_r_dgain); + } else { + dgain_ref = rk3308->hpout_l_dgain; + } + + /* + * We'd better change the gain of the left and right channels + * at the same time to avoid different listening + */ + for (dgain = RK3308_DAC_L_HPOUT_GAIN_NDB_39; + dgain <= dgain_ref; dgain++) { + /* Step 02 decrease dgains for de-pop */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, + RK3308_DAC_L_HPOUT_GAIN_MSK, + dgain); + + /* Step 02 decrease dgains for de-pop */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, + RK3308_DAC_R_HPOUT_GAIN_MSK, + dgain); + } + + return 0; +} + +static int rk3308_codec_digital_fadeout(struct rk3308_codec_priv *rk3308) +{ + unsigned int l_dgain, r_dgain; + + /* + * Note. In the step2, adjusting the register step by step to + * the appropriate value and taking 20ms as time step + */ + regmap_read(rk3308->regmap, RK3308_DAC_ANA_CON05, &l_dgain); + l_dgain &= RK3308_DAC_L_HPOUT_GAIN_MSK; + + regmap_read(rk3308->regmap, RK3308_DAC_ANA_CON06, &r_dgain); + r_dgain &= RK3308_DAC_R_HPOUT_GAIN_MSK; + + if (l_dgain != r_dgain) { + pr_warn("HPOUT l_dgain: 0x%x != r_dgain: 0x%x\n", + l_dgain, r_dgain); + l_dgain = min(l_dgain, r_dgain); + } + + /* + * We'd better change the gain of the left and right channels + * at the same time to avoid different listening + */ + while (l_dgain >= RK3308_DAC_L_HPOUT_GAIN_NDB_39) { + /* Step 02 decrease dgains for de-pop */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, + RK3308_DAC_L_HPOUT_GAIN_MSK, + l_dgain); + + /* Step 02 decrease dgains for de-pop */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, + RK3308_DAC_R_HPOUT_GAIN_MSK, + l_dgain); + + usleep_range(200, 300); /* estimated value */ + + if (l_dgain == RK3308_DAC_L_HPOUT_GAIN_NDB_39) + break; + + l_dgain--; + } + + return 0; +} + +static int rk3308_codec_dac_enable(struct rk3308_codec_priv *rk3308) +{ + /* + * Note1. If the ACODEC_DAC_ANA_CON12[6] or ACODEC_DAC_ANA_CON12[2] + * is set to 0x1, ignoring the step9~12. + */ + + /* + * Note2. If the ACODEC_ DAC_ANA_CON12[7] or ACODEC_DAC_ANA_CON12[3] + * is set to 0x1, the ADC0 or ADC1 should be enabled firstly, and + * please refer to Enable ADC Configuration Standard Usage Flow(expect + * step7~step9,step14). + */ + + /* + * Note3. If no opening the line out, ignoring the step6, step17 and + * step19. + */ + + /* + * Note4. If no opening the headphone out, ignoring the step3,step7~8, + * step16 and step18. + */ + + /* + * Note5. In the step18, adjust the register step by step to the + * appropriate value and taking 10ms as one time step + */ + + /* + * 1. Set the ACODEC_DAC_ANA_CON0[0] to 0x1, to enable the current + * source of DAC + */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, + RK3308_DAC_CURRENT_EN); + + usleep_range(20, 40); + + /* + * 2. Set the ACODEC_DAC_ANA_CON1[6] and ACODEC_DAC_ANA_CON1[2] to 0x1, + * to enable the reference voltage buffer + */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_BUF_REF_L_EN | + RK3308_DAC_BUF_REF_R_EN); + + /* Waiting the stable reference voltage */ + mdelay(1); + + /* Step 03 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_HPOUT_POP_SOUND_L_MSK | + RK3308_DAC_HPOUT_POP_SOUND_R_MSK, + RK3308_DAC_HPOUT_POP_SOUND_L_WORK | + RK3308_DAC_HPOUT_POP_SOUND_R_WORK); + + usleep_range(20, 40); + + if (rk3308->codec_ver == ACODEC_VERSION_B && + (rk3308->dac_output == DAC_LINEOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT)) { + /* Step 04 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, + RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | + RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, + RK3308_DAC_L_SEL_DC_FROM_INTERNAL | + RK3308_DAC_R_SEL_DC_FROM_INTERNAL); + + usleep_range(20, 40); + } + + /* Step 05 */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_EN | + RK3308_DAC_R_HPMIX_EN); + + /* Waiting the stable HPMIX */ + mdelay(1); + + /* Step 06. Reset HPMIX and recover HPMIX gains */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_WORK | + RK3308_DAC_R_HPMIX_WORK); + usleep_range(50, 100); + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_WORK | + RK3308_DAC_R_HPMIX_WORK); + + usleep_range(20, 40); + + if (rk3308->dac_output == DAC_LINEOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Step 07 */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_EN | + RK3308_DAC_R_LINEOUT_EN); + + usleep_range(20, 40); + } + + if (rk3308->dac_output == DAC_HPOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Step 08 */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_EN | + RK3308_DAC_R_HPOUT_EN); + + usleep_range(20, 40); + + /* Step 09 */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_WORK | + RK3308_DAC_R_HPOUT_WORK); + + usleep_range(20, 40); + } + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* Step 10 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, + RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | + RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, + RK3308_DAC_L_SEL_LINEOUT_FROM_INTERNAL | + RK3308_DAC_R_SEL_LINEOUT_FROM_INTERNAL); + + usleep_range(20, 40); + } + + /* Step 11 */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_REF_EN | RK3308_DAC_R_REF_EN); + + usleep_range(20, 40); + + /* Step 12 */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_CLK_EN | RK3308_DAC_R_CLK_EN); + + usleep_range(20, 40); + + /* Step 13 */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN); + + usleep_range(20, 40); + + /* Step 14 */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_DAC_WORK | RK3308_DAC_R_DAC_WORK); + + usleep_range(20, 40); + + /* Step 15 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, + RK3308_DAC_L_HPMIX_SEL_MSK | + RK3308_DAC_R_HPMIX_SEL_MSK, + RK3308_DAC_L_HPMIX_I2S | + RK3308_DAC_R_HPMIX_I2S); + + usleep_range(20, 40); + + /* Step 16 */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_UNMUTE | RK3308_DAC_R_HPMIX_UNMUTE); + + usleep_range(20, 40); + + /* Step 17: Put configuration HPMIX Gain */ + + if (rk3308->dac_output == DAC_HPOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Step 18 */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_UNMUTE | RK3308_DAC_R_HPOUT_UNMUTE); + + usleep_range(20, 40); + } + + if (rk3308->dac_output == DAC_LINEOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Step 19 */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_UNMUTE | RK3308_DAC_R_LINEOUT_UNMUTE); + usleep_range(20, 40); + } + + /* Step 20, put configuration HPOUT gain control */ + /* Step 21, put configuration LINEOUT gain control */ + + if (rk3308->dac_output == DAC_HPOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Just for HPOUT */ + rk3308_codec_digital_fadein(rk3308); + } + + /* TODO: TRY TO TEST DRIVE STRENGTH */ + + return 0; +} + +static int rk3308_codec_dac_disable(struct rk3308_codec_priv *rk3308) +{ + /* + * Step 00 skipped. Keep the DAC channel work and input the mute signal. + */ + + /* Step 01 skipped. May set the min gain for LINEOUT. */ + + /* Step 02 skipped. May set the min gain for HPOUT. */ + + if (rk3308->dac_output == DAC_HPOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT) { + /* Just for HPOUT */ + rk3308_codec_digital_fadeout(rk3308); + } + + /* Step 03 */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_UNMUTE | RK3308_DAC_R_HPMIX_UNMUTE); + + /* Step 04 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, + RK3308_DAC_L_HPMIX_SEL_MSK | + RK3308_DAC_R_HPMIX_SEL_MSK, + RK3308_DAC_L_HPMIX_NONE | + RK3308_DAC_R_HPMIX_NONE); + /* Step 05 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_UNMUTE | + RK3308_DAC_R_HPOUT_UNMUTE); + + /* Step 06 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_DAC_WORK | + RK3308_DAC_R_DAC_WORK); + + /* Step 07 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_EN | + RK3308_DAC_R_HPOUT_EN); + + /* Step 08 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_UNMUTE | + RK3308_DAC_R_LINEOUT_UNMUTE); + + /* Step 09 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_EN | + RK3308_DAC_R_LINEOUT_EN); + + /* Step 10 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_EN | + RK3308_DAC_R_HPMIX_EN); + + /* Step 11 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_DAC_EN | + RK3308_DAC_R_DAC_EN); + + /* Step 12 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_CLK_EN | + RK3308_DAC_R_CLK_EN); + + /* Step 13 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON02, + RK3308_DAC_L_REF_EN | + RK3308_DAC_R_REF_EN); + + /* Step 14 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_HPOUT_POP_SOUND_L_MSK | + RK3308_DAC_HPOUT_POP_SOUND_R_MSK, + RK3308_DAC_HPOUT_POP_SOUND_L_INIT | + RK3308_DAC_HPOUT_POP_SOUND_R_INIT); + + /* Step 15 */ + if (rk3308->codec_ver == ACODEC_VERSION_B && + (rk3308->dac_output == DAC_LINEOUT || + rk3308->dac_output == DAC_LINEOUT_HPOUT)) { + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, + RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | + RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, + RK3308_DAC_L_SEL_DC_FROM_VCM | + RK3308_DAC_R_SEL_DC_FROM_VCM); + } + + /* Step 16 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_BUF_REF_L_EN | + RK3308_DAC_BUF_REF_R_EN); + + /* Step 17 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON00, + RK3308_DAC_CURRENT_EN); + + /* Step 18 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON03, + RK3308_DAC_L_HPOUT_WORK | + RK3308_DAC_R_HPOUT_WORK); + + /* Step 19 */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON13, + RK3308_DAC_L_HPMIX_WORK | + RK3308_DAC_R_HPMIX_WORK, + RK3308_DAC_L_HPMIX_WORK | + RK3308_DAC_R_HPMIX_WORK); + + /* Step 20 skipped, may set the min gain for HPOUT. */ + + /* + * Note2. If the ACODEC_DAC_ANA_CON12[7] or ACODEC_DAC_ANA_CON12[3] + * is set to 0x1, add the steps from the section Disable ADC + * Configuration Standard Usage Flow after complete the step 19 + * + * IF USING LINE-IN + * rk3308_codec_adc_ana_disable(rk3308, type); + */ + + return 0; +} + +static int rk3308_codec_power_on(struct rk3308_codec_priv *rk3308) +{ + unsigned int v; + + /* TRM Section 8.6.3 Power Up */ + + /* 0. Supply the power of digital part and reset the Audio Codec */ + + /* + * 1. Configure ACODEC_DAC_ANA_CON1[1:0] and ACODEC_DAC_ANA_CON1[5:4] + * to 0x1, to setup dc voltage of the DAC channel output. + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01, + RK3308_DAC_HPOUT_POP_SOUND_L_MSK | + RK3308_DAC_HPOUT_POP_SOUND_R_MSK, + RK3308_DAC_HPOUT_POP_SOUND_L_INIT | + RK3308_DAC_HPOUT_POP_SOUND_R_INIT); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 2. Configure ACODEC_DAC_ANA_CON15[1:0] and + * ACODEC_DAC_ANA_CON15[5:4] to 0x1, to setup dc voltage of + * the DAC channel output. + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON15, + RK3308_DAC_LINEOUT_POP_SOUND_L_MSK | + RK3308_DAC_LINEOUT_POP_SOUND_R_MSK, + RK3308_DAC_L_SEL_DC_FROM_VCM | + RK3308_DAC_R_SEL_DC_FROM_VCM); + } + + /* + * 3. Configure the register ACODEC_ADC_ANA_CON10[6:0] to 7’b000_0001. + */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), + RK3308_ADC_CURRENT_CHARGE_MSK, + RK3308_ADC_SEL_I(0x1)); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 4. Configure the register ACODEC_ADC_ANA_CON14[3:0] to + * 4’b0001. + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, + RK3308_DAC_CURRENT_CHARGE_MSK, + RK3308_DAC_SEL_I(0x1)); + } + + /* 5. Supply the power of the analog part(AVDD,AVDDRV) */ + + /* + * 6. Configure the register ACODEC_ADC_ANA_CON10[7] to 0x1 to setup + * reference voltage + */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), RK3308_ADC_REF_EN); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 7. Configure the register ACODEC_ADC_ANA_CON14[4] to 0x1 to + * setup reference voltage + */ + regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, + RK3308_DAC_VCM_LINEOUT_EN); + } + + /* + * 8. Change the register ACODEC_ADC_ANA_CON10[6:0] from the 0x1 to + * 0x7f step by step or configure the ACODEC_ADC_ANA_CON10[6:0] to + * 0x7f directly. Here the slot time of the step is 200us. + */ + for (v = 0x1; v <= 0x7f; v++) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), + RK3308_ADC_CURRENT_CHARGE_MSK, v); + usleep_range(200, 400); + } + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 9. Change the register ACODEC_ADC_ANA_CON14[3:0] from the 0x1 + * to 0xf step by step or configure the + * ACODEC_ADC_ANA_CON14[3:0] to 0xf directly. Here the slot + * time of the step is 200us. + */ + for (v = 0x1; v <= 0xf; v++) { + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, + RK3308_DAC_CURRENT_CHARGE_MSK, v); + usleep_range(200, 400); + } + } + + /* 10. Wait until the voltage of VCM keeps stable at the AVDD/2 */ + msleep(20); /* estimated value */ + + /* + * 11. Configure the register ACODEC_ADC_ANA_CON10[6:0] to the + * appropriate value (expect 0x0) for reducing power. + */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), + RK3308_ADC_CURRENT_CHARGE_MSK, 0x7c); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 12. Configure the register ACODEC_DAC_ANA_CON14[6:0] to the + * appropriate value(expect 0x0) for reducing power. + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, + RK3308_DAC_CURRENT_CHARGE_MSK, 0xf); + } + + return 0; +} + +static int rk3308_codec_power_off(struct rk3308_codec_priv *rk3308) +{ + unsigned int v; + + /* + * 0. Keep the power on and disable the DAC and ADC path according to + * the section power on configuration standard usage flow. + */ + + /* + * 1. Configure the register ACODEC_ADC_ANA_CON10[6:0] to 7’b000_0001. + */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), + RK3308_ADC_CURRENT_CHARGE_MSK, + RK3308_ADC_SEL_I(0x1)); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 2. Configure the register ACODEC_DAC_ANA_CON14[3:0] to + * 4’b0001. + */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, + RK3308_DAC_CURRENT_CHARGE_MSK, + RK3308_DAC_SEL_I(0x1)); + } + + /* 3. Configure the register ACODEC_ADC_ANA_CON10[7] to 0x0 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), RK3308_ADC_REF_EN); + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* 4. Configure the register ACODEC_DAC_ANA_CON14[7] to 0x0 */ + regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON14, + RK3308_DAC_VCM_LINEOUT_EN); + } + + /* + * 5. Change the register ACODEC_ADC_ANA_CON10[6:0] from the 0x1 to 0x7f + * step by step or configure the ACODEC_ADC_ANA_CON10[6:0] to 0x7f + * directly. Here the slot time of the step is 200us. + */ + for (v = 0x1; v <= 0x7f; v++) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0), + RK3308_ADC_CURRENT_CHARGE_MSK, v); + usleep_range(200, 400); + } + + if (rk3308->codec_ver == ACODEC_VERSION_B) { + /* + * 6. Change the register ACODEC_DAC_ANA_CON14[3:0] from the 0x1 + * to 0xf step by step or configure the + * ACODEC_DAC_ANA_CON14[3:0] to 0xf directly. Here the slot + * time of the step is 200us. + */ + for (v = 0x1; v <= 0x7f; v++) { + regmap_update_bits(rk3308->regmap, + RK3308_ADC_ANA_CON10(0), + RK3308_ADC_CURRENT_CHARGE_MSK, v); + usleep_range(200, 400); + } + } + + /* 7. Wait until the voltage of VCM keeps stable at the AGND */ + msleep(20); /* estimated value */ + + /* 8. Power off the analog power supply */ + /* 9. Power off the digital power supply */ + + /* Do something via hardware */ + + return 0; +} + +static int rk3308_codec_adc_reinit_mics(struct rk3308_codec_priv *rk3308) +{ + int grp; + + for (grp = 0; grp < rk3308->used_adc_grps; grp++) { + /* vendor step 1 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_ADC_WORK | + RK3308_ADC_CH2_ADC_WORK); + + /* vendor step 2 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ALC_WORK | + RK3308_ADC_CH2_ALC_WORK); + + /* vendor step 3 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_WORK | + RK3308_ADC_CH2_MIC_WORK); + } + + usleep_range(200, 250); /* estimated value */ + + for (grp = 0; grp < rk3308->used_adc_grps; grp++) { + /* vendor step 1 */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_ADC_WORK | + RK3308_ADC_CH2_ADC_WORK); + + /* vendor step 2 */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ALC_WORK | + RK3308_ADC_CH2_ALC_WORK); + + /* vendor step 3 */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_WORK | + RK3308_ADC_CH2_MIC_WORK); + } + + return 0; +} + +static int rk3308_codec_adc_ana_enable(struct rk3308_codec_priv *rk3308) +{ + int grp = 0; + + /* + * 1. Set the ACODEC_ADC_ANA_CON7[7:6] and ACODEC_ADC_ANA_CON7[5:4], + * to select the line-in or microphone as input of ADC + * + * Note1. Please ignore the step1 for enabling ADC3, ADC4, ADC5, + * ADC6, ADC7, and ADC8 + */ + if (rk3308->adc_grp0_using_linein) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0), + RK3308_ADC_CH1_IN_SEL_MSK | + RK3308_ADC_CH2_IN_SEL_MSK, + RK3308_ADC_CH1_IN_LINEIN | + RK3308_ADC_CH2_IN_LINEIN); + grp++; + } + + for (; grp < rk3308->used_adc_grps; grp++) { + regmap_update_bits(rk3308->regmap, + RK3308_ADC_ANA_CON07(grp), + RK3308_ADC_CH1_IN_SEL_MSK | + RK3308_ADC_CH2_IN_SEL_MSK, + RK3308_ADC_CH1_IN_MIC | + RK3308_ADC_CH2_IN_MIC); + } + + for (grp = 0; grp < rk3308->used_adc_grps; grp++) { + /* + * 2. Set ACODEC_ADC_ANA_CON0[7] and [3] to 0x1, to end the mute station + * of ADC, to enable the MIC module, to enable the reference voltage + * buffer, and to end the initialization of MIC + */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_UNMUTE | + RK3308_ADC_CH2_MIC_UNMUTE); + + /* + * 3. Set ACODEC_ADC_ANA_CON6[0] to 0x1, to enable the current source + * of audio + */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON06(grp), + RK3308_ADC_CURRENT_EN); + } + + /* + * This is mainly used for BIST mode that wait ADCs are stable. + * + * By tested results, the type delay is >40us, but we need to leave + * enough delay margin. + */ + usleep_range(400, 500); + + for (grp = 0; grp < rk3308->used_adc_grps; grp++) { + /* vendor step 4 */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_BUF_REF_EN | + RK3308_ADC_CH2_BUF_REF_EN); + + /* vendor step 5 */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_EN | + RK3308_ADC_CH2_MIC_EN); + + /* vendor step 6 */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ALC_EN | + RK3308_ADC_CH2_ALC_EN); + + /* vendor step 7 */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_CLK_EN | + RK3308_ADC_CH2_CLK_EN); + + /* vendor step 8 */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_ADC_EN | + RK3308_ADC_CH2_ADC_EN); + + /* vendor step 9 */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_ADC_WORK | + RK3308_ADC_CH2_ADC_WORK); + + /* vendor step 10 */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ALC_WORK | + RK3308_ADC_CH2_ALC_WORK); + + /* vendor step 11 */ + regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_WORK | + RK3308_ADC_CH2_MIC_WORK); + } + + rk3308_codec_update_zerocross(rk3308); + + return 0; +} + +static int rk3308_codec_adc_ana_disable(struct rk3308_codec_priv *rk3308, + unsigned int num_grps) +{ + int grp; + + for (grp = 0; grp < num_grps; grp++) { + /* vendor step 1 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ZEROCROSS_DET_EN | + RK3308_ADC_CH2_ZEROCROSS_DET_EN); + + /* vendor step 2 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_ADC_EN | + RK3308_ADC_CH2_ADC_EN); + + /* vendor step 3 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_CLK_EN | + RK3308_ADC_CH2_CLK_EN); + + /* vendor step 4 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ALC_EN | + RK3308_ADC_CH2_ALC_EN); + + /* vendor step 5 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_EN | + RK3308_ADC_CH2_MIC_EN); + + /* vendor step 6 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_BUF_REF_EN | + RK3308_ADC_CH2_BUF_REF_EN); + + /* vendor step 7 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON06(grp), + RK3308_ADC_CURRENT_EN); + + /* vendor step 8 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON05(grp), + RK3308_ADC_CH1_ADC_WORK | + RK3308_ADC_CH2_ADC_WORK); + + /* vendor step 9 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp), + RK3308_ADC_CH1_ALC_WORK | + RK3308_ADC_CH2_ALC_WORK); + + /* vendor step 10 */ + regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON00(grp), + RK3308_ADC_CH1_MIC_WORK | + RK3308_ADC_CH2_MIC_WORK); + } + + return 0; +} + +static int rk3308_codec_open_capture(struct rk3308_codec_priv *rk3308) +{ + int grp = 0; + + rk3308_codec_adc_ana_enable(rk3308); + rk3308_codec_adc_reinit_mics(rk3308); + + if (rk3308->adc_grp0_using_linein) { + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON03(0), + RK3308_ADC_L_CH_BIST_MSK, + RK3308_ADC_L_CH_NORMAL_LEFT); + regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON03(0), + RK3308_ADC_R_CH_BIST_MSK, + RK3308_ADC_R_CH_NORMAL_RIGHT); + } else { + for (grp = 0; grp < rk3308->used_adc_grps; grp++) { + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_L_CH_BIST_MSK, + RK3308_ADC_L_CH_NORMAL_LEFT); + regmap_update_bits(rk3308->regmap, + RK3308_ADC_DIG_CON03(grp), + RK3308_ADC_R_CH_BIST_MSK, + RK3308_ADC_R_CH_NORMAL_RIGHT); + } + } + + return 0; +} + +static void rk3308_codec_adc_mclk_disable(struct rk3308_codec_priv *rk3308) +{ + regmap_set_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_ADC_MCLK_GATING_MSK); +} + +static void rk3308_codec_adc_mclk_enable(struct rk3308_codec_priv *rk3308) +{ + regmap_clear_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_ADC_MCLK_GATING_MSK); + usleep_range(20, 40); +} + +static void rk3308_codec_dac_mclk_disable(struct rk3308_codec_priv *rk3308) +{ + regmap_set_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_DAC_MCLK_GATING_MSK); +} + +static void rk3308_codec_dac_mclk_enable(struct rk3308_codec_priv *rk3308) +{ + regmap_clear_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_DAC_MCLK_GATING_MSK); + usleep_range(20, 40); +} + +static int rk3308_codec_llp_down(struct rk3308_codec_priv *rk3308) +{ + rk3308_codec_adc_mclk_disable(rk3308); + rk3308_codec_dac_mclk_disable(rk3308); + + return 0; +} + +static int rk3308_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* DAC only supports 2 channels */ + rk3308_codec_dac_mclk_enable(rk3308); + rk3308_codec_dac_enable(rk3308); + rk3308_codec_dac_dig_config(rk3308, params); + } else { + rk3308_codec_micbias_apply(rk3308); + rk3308_codec_adc_mclk_enable(rk3308); + ret = rk3308_codec_update_adc_grps(rk3308, params); + if (ret < 0) + return ret; + + rk3308_codec_open_capture(rk3308); + rk3308_codec_agc_dig_config(rk3308, params); + rk3308_codec_adc_dig_config(rk3308, params); + } + + return 0; +} + +static void rk3308_pcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rk3308_codec_dac_disable(rk3308); + rk3308_codec_dac_mclk_disable(rk3308); + } else { + rk3308_codec_adc_ana_disable(rk3308, rk3308->used_adc_grps); + rk3308_codec_adc_mclk_disable(rk3308); + rk3308_codec_micbias_disable(rk3308); + } +} + +static const struct snd_soc_dai_ops rk3308_dai_ops = { + .hw_params = rk3308_hw_params, + .set_fmt = rk3308_set_dai_fmt, + .mute_stream = rk3308_mute_stream, + .shutdown = rk3308_pcm_shutdown, +}; + +static struct snd_soc_dai_driver rk3308_dai[] = { + { + .name = "rk3308-hifi", + .id = RK3308_HIFI, + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + }, + .ops = &rk3308_dai_ops, + }, +}; + +static int rk3308_codec_default_gains(struct rk3308_codec_priv *rk3308) +{ + int grp; + + for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) { + /* 12: set ADC gain to 20 dB (recommended by TRM when using MIC input) */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON01(grp), + RK3308_ADC_CH1_MIC_GAIN_MSK | + RK3308_ADC_CH2_MIC_GAIN_MSK, + RK3308_ADC_CH1_MIC_GAIN_20DB | + RK3308_ADC_CH2_MIC_GAIN_20DB); + + /* vendor step 13, set ALC default gains */ + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON03(grp), + RK3308_ADC_CH1_ALC_GAIN_MSK, + RK3308_ADC_CH1_ALC_GAIN_0DB); + regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON04(grp), + RK3308_ADC_CH2_ALC_GAIN_MSK, + RK3308_ADC_CH2_ALC_GAIN_0DB); + } + + /* Prepare DAC gains */ + /* Step 15, set HPMIX default gains */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12, + RK3308_DAC_L_HPMIX_GAIN_MSK | + RK3308_DAC_R_HPMIX_GAIN_MSK, + RK3308_DAC_L_HPMIX_GAIN_NDB_6 | + RK3308_DAC_R_HPMIX_GAIN_NDB_6); + + /* Step 18, set HPOUT default gains */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON05, + RK3308_DAC_L_HPOUT_GAIN_MSK, + RK3308_DAC_L_HPOUT_GAIN_NDB_39); + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON06, + RK3308_DAC_R_HPOUT_GAIN_MSK, + RK3308_DAC_R_HPOUT_GAIN_NDB_39); + + /* Using the same gain to HPOUT LR channels */ + rk3308->hpout_l_dgain = RK3308_DAC_L_HPOUT_GAIN_NDB_39; + + /* Step 19, set LINEOUT default gains */ + regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON04, + RK3308_DAC_L_LINEOUT_GAIN_MSK | + RK3308_DAC_R_LINEOUT_GAIN_MSK, + RK3308_DAC_L_LINEOUT_GAIN_NDB_6 | + RK3308_DAC_R_LINEOUT_GAIN_NDB_6); + + return 0; +} + +static int rk3308_codec_controls_prepare(struct rk3308_codec_priv *rk3308) +{ + int grp; + + for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) { + rk3308->agc_l[grp] = 0; + rk3308->agc_r[grp] = 0; + } + + return 0; +} + +static int rk3308_codec_prepare(struct rk3308_codec_priv *rk3308) +{ + /* Clear registers for ADC and DAC */ + rk3308_codec_dac_disable(rk3308); + rk3308_codec_adc_ana_disable(rk3308, ADC_LR_GROUP_MAX); + rk3308_codec_default_gains(rk3308); + rk3308_codec_llp_down(rk3308); + rk3308_codec_controls_prepare(rk3308); + + return 0; +} + +static int rk3308_probe(struct snd_soc_component *component) +{ + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + rk3308->component = component; + + rk3308_codec_reset(component); + rk3308_codec_power_on(rk3308); + + rk3308_codec_micbias_disable(rk3308); + + rk3308_codec_prepare(rk3308); + + return 0; +} + +static void rk3308_remove(struct snd_soc_component *component) +{ + struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component); + + rk3308_codec_micbias_disable(rk3308); + rk3308_codec_power_off(rk3308); +} + +static const struct snd_soc_component_driver soc_codec_dev_rk3308 = { + .probe = rk3308_probe, + .remove = rk3308_remove, + .set_bias_level = rk3308_set_bias_level, + .controls = rk3308_codec_controls, + .num_controls = ARRAY_SIZE(rk3308_codec_controls), +}; + +static const struct regmap_config rk3308_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = RK3308_DAC_ANA_CON15, +}; + +static int rk3308_codec_get_version(struct rk3308_codec_priv *rk3308) +{ + unsigned int chip_id; + + regmap_read(rk3308->grf, GRF_CHIP_ID, &chip_id); + switch (chip_id) { + case 3306: + rk3308->codec_ver = ACODEC_VERSION_A; + break; + case 0x3308: + rk3308->codec_ver = ACODEC_VERSION_B; + break; + default: + return dev_err_probe(rk3308->dev, -EINVAL, "Unknown chip_id: 0x%x\n", chip_id); + } + + dev_info(rk3308->dev, "Found codec version %X\n", rk3308->codec_ver); + return 0; +} + +static int rk3308_codec_parse_dt(struct rk3308_codec_priv *rk3308) +{ + struct device_node *np = rk3308->dev->of_node; + int err; + + /* Default value is 0 */ + err = of_property_read_u32(np, "rockchip,micbias-avdd-multiplier", + &rk3308->micbias_avdd_mult); + if (rk3308->micbias_avdd_mult >= RK3308_CODEC_MICBIAS_NUM) + return dev_err_probe(rk3308->dev, -EINVAL, + "Invalid value for 'rockchip,micbias-avdd-multiplier'\n"); + + return 0; +} + +static int rk3308_platform_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct rk3308_codec_priv *rk3308; + struct resource *res; + void __iomem *base; + int err; + + rk3308 = devm_kzalloc(&pdev->dev, sizeof(*rk3308), GFP_KERNEL); + if (!rk3308) + return -ENOMEM; + + rk3308->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(rk3308->grf)) + return dev_err_probe(&pdev->dev, PTR_ERR(rk3308->grf), + "Missing 'rockchip,grf' property\n"); + + rk3308->dev = &pdev->dev; + + err = rk3308_codec_parse_dt(rk3308); + if (err) + return err; + + rk3308->reset = devm_reset_control_get(&pdev->dev, "acodec"); + if (IS_ERR(rk3308->reset)) { + err = PTR_ERR(rk3308->reset); + if (err != -ENOENT) + return err; + + dev_dbg(&pdev->dev, "No reset control found\n"); + rk3308->reset = NULL; + } + + rk3308->pclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(rk3308->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(rk3308->pclk), "Can't get hclk\n"); + + rk3308->mclk_rx = devm_clk_get(&pdev->dev, "mclk_rx"); + if (IS_ERR(rk3308->mclk_rx)) + return dev_err_probe(&pdev->dev, PTR_ERR(rk3308->mclk_rx), "Can't get mclk_rx\n"); + + rk3308->mclk_tx = devm_clk_get(&pdev->dev, "mclk_tx"); + if (IS_ERR(rk3308->mclk_tx)) + return dev_err_probe(&pdev->dev, PTR_ERR(rk3308->mclk_tx), "Can't get mclk_tx\n"); + + err = clk_prepare_enable(rk3308->pclk); + if (err) + return dev_err_probe(&pdev->dev, err, "Failed to enable acodec pclk\n"); + + err = clk_prepare_enable(rk3308->mclk_rx); + if (err) + return dev_err_probe(&pdev->dev, err, "Failed to enable i2s mclk_rx\n"); + + err = clk_prepare_enable(rk3308->mclk_tx); + if (err) + return dev_err_probe(&pdev->dev, err, "Failed to enable i2s mclk_tx\n"); + + err = rk3308_codec_get_version(rk3308); + if (err) + return dev_err_probe(&pdev->dev, err, "Failed to get acodec version\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return dev_err_probe(&pdev->dev, PTR_ERR(base), "Failed to ioremap resource\n"); + + rk3308->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &rk3308_codec_regmap_config); + if (IS_ERR(rk3308->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(rk3308->regmap), + "Failed to init regmap\n"); + + rk3308->adc_grp0_using_linein = ADC_GRP0_MICIN; + rk3308->dac_output = DAC_LINEOUT; + + platform_set_drvdata(pdev, rk3308); + + err = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk3308, + rk3308_dai, ARRAY_SIZE(rk3308_dai)); + if (err) + return dev_err_probe(&pdev->dev, err, "Failed to register codec\n"); + + return 0; +} + +static const struct of_device_id rk3308codec_of_match[] = { + { .compatible = "rockchip,rk3308-codec", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rk3308codec_of_match); + +static struct platform_driver rk3308_codec_driver = { + .driver = { + .name = "rk3308-acodec", + .of_match_table = of_match_ptr(rk3308codec_of_match), + }, + .probe = rk3308_platform_probe, +}; +module_platform_driver(rk3308_codec_driver); + +MODULE_AUTHOR("Xing Zheng "); +MODULE_AUTHOR("Luca Ceresoli "); +MODULE_DESCRIPTION("ASoC RK3308 Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rk3308_codec.h b/sound/soc/codecs/rk3308_codec.h new file mode 100644 index 000000000000..7ab08972141d --- /dev/null +++ b/sound/soc/codecs/rk3308_codec.h @@ -0,0 +1,648 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Rockchip RK3308 internal audio codec driver -- register definitions + * + * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. + * Copyright (c) 2022, Vivax-Metrotech Ltd + */ + +#ifndef __RK3308_CODEC_H__ +#define __RK3308_CODEC_H__ + +#define RK3308_GLB_CON 0x00 + +/* ADC DIGITAL REGISTERS */ + +/* + * The ADC group are 0 ~ 3, that control: + * + * CH0: left_0(ADC1) and right_0(ADC2) + * CH1: left_1(ADC3) and right_1(ADC4) + * CH2: left_2(ADC5) and right_2(ADC6) + * CH3: left_3(ADC7) and right_3(ADC8) + */ +#define RK3308_ADC_DIG_OFFSET(ch) (((ch) & 0x3) * 0xc0 + 0x0) + +#define RK3308_ADC_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x04) +#define RK3308_ADC_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x08) +#define RK3308_ADC_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x0c) +#define RK3308_ADC_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x10) +#define RK3308_ADC_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x1c) + +#define RK3308_ALC_L_DIG_CON00(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x40) +#define RK3308_ALC_L_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x44) +#define RK3308_ALC_L_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x48) +#define RK3308_ALC_L_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x4c) +#define RK3308_ALC_L_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x50) +#define RK3308_ALC_L_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x54) +#define RK3308_ALC_L_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x58) +#define RK3308_ALC_L_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x5c) +#define RK3308_ALC_L_DIG_CON08(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x60) +#define RK3308_ALC_L_DIG_CON09(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x64) +#define RK3308_ALC_L_DIG_CON12(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x70) + +#define RK3308_ALC_R_DIG_CON00(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x80) +#define RK3308_ALC_R_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x84) +#define RK3308_ALC_R_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x88) +#define RK3308_ALC_R_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x8c) +#define RK3308_ALC_R_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x90) +#define RK3308_ALC_R_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x94) +#define RK3308_ALC_R_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x98) +#define RK3308_ALC_R_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x9c) +#define RK3308_ALC_R_DIG_CON08(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0xa0) +#define RK3308_ALC_R_DIG_CON09(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0xa4) +#define RK3308_ALC_R_DIG_CON12(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0xb0) + +/* DAC DIGITAL REGISTERS */ +#define RK3308_DAC_DIG_OFFSET 0x300 +#define RK3308_DAC_DIG_CON01 (RK3308_DAC_DIG_OFFSET + 0x04) +#define RK3308_DAC_DIG_CON02 (RK3308_DAC_DIG_OFFSET + 0x08) +#define RK3308_DAC_DIG_CON03 (RK3308_DAC_DIG_OFFSET + 0x0c) +#define RK3308_DAC_DIG_CON04 (RK3308_DAC_DIG_OFFSET + 0x10) +#define RK3308_DAC_DIG_CON05 (RK3308_DAC_DIG_OFFSET + 0x14) +#define RK3308_DAC_DIG_CON10 (RK3308_DAC_DIG_OFFSET + 0x28) +#define RK3308_DAC_DIG_CON11 (RK3308_DAC_DIG_OFFSET + 0x2c) +#define RK3308_DAC_DIG_CON13 (RK3308_DAC_DIG_OFFSET + 0x34) +#define RK3308_DAC_DIG_CON14 (RK3308_DAC_DIG_OFFSET + 0x38) + +/* ADC ANALOG REGISTERS */ +/* + * The ADC group are 0 ~ 3, that control: + * + * CH0: left_0(ADC1) and right_0(ADC2) + * CH1: left_1(ADC3) and right_1(ADC4) + * CH2: left_2(ADC5) and right_2(ADC6) + * CH3: left_3(ADC7) and right_3(ADC8) + */ +#define RK3308_ADC_ANA_OFFSET(ch) (((ch) & 0x3) * 0x40 + 0x340) +#define RK3308_ADC_ANA_CON00(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x00) +#define RK3308_ADC_ANA_CON01(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x04) +#define RK3308_ADC_ANA_CON02(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x08) +#define RK3308_ADC_ANA_CON03(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x0c) +#define RK3308_ADC_ANA_CON04(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x10) +#define RK3308_ADC_ANA_CON05(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x14) +#define RK3308_ADC_ANA_CON06(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x18) +#define RK3308_ADC_ANA_CON07(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x1c) +#define RK3308_ADC_ANA_CON08(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x20) +#define RK3308_ADC_ANA_CON10(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x28) +#define RK3308_ADC_ANA_CON11(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x2c) + +/* DAC ANALOG REGISTERS */ +#define RK3308_DAC_ANA_OFFSET 0x440 +#define RK3308_DAC_ANA_CON00 (RK3308_DAC_ANA_OFFSET + 0x00) +#define RK3308_DAC_ANA_CON01 (RK3308_DAC_ANA_OFFSET + 0x04) +#define RK3308_DAC_ANA_CON02 (RK3308_DAC_ANA_OFFSET + 0x08) +#define RK3308_DAC_ANA_CON03 (RK3308_DAC_ANA_OFFSET + 0x0c) +#define RK3308_DAC_ANA_CON04 (RK3308_DAC_ANA_OFFSET + 0x10) +#define RK3308_DAC_ANA_CON05 (RK3308_DAC_ANA_OFFSET + 0x14) +#define RK3308_DAC_ANA_CON06 (RK3308_DAC_ANA_OFFSET + 0x18) +#define RK3308_DAC_ANA_CON07 (RK3308_DAC_ANA_OFFSET + 0x1c) +#define RK3308_DAC_ANA_CON08 (RK3308_DAC_ANA_OFFSET + 0x20) +#define RK3308_DAC_ANA_CON12 (RK3308_DAC_ANA_OFFSET + 0x30) +#define RK3308_DAC_ANA_CON13 (RK3308_DAC_ANA_OFFSET + 0x34) +#define RK3308_DAC_ANA_CON14 (RK3308_DAC_ANA_OFFSET + 0x38) +#define RK3308_DAC_ANA_CON15 (RK3308_DAC_ANA_OFFSET + 0x3c) + +/* + * These are the bits for registers + */ + +/* RK3308_GLB_CON - REG: 0x0000 */ +#define RK3308_ADC_BIST_WORK BIT(7) +#define RK3308_DAC_BIST_WORK BIT(6) +#define RK3308_ADC_MCLK_GATING_MSK BIT(5) +#define RK3308_DAC_MCLK_GATING_MSK BIT(4) +#define RK3308_CODEC_RST_MSK (0x7 << 0) +#define RK3308_ADC_DIG_WORK BIT(2) +#define RK3308_DAC_DIG_WORK BIT(1) +#define RK3308_SYS_WORK BIT(0) + +/* RK3308_ADC_DIG_CON01 - REG: 0x0004 */ +#define RK3308_ADC_I2S_LRC_POL_REVERSAL BIT(7) +#define RK3308_ADC_I2S_VALID_LEN_SFT 5 +#define RK3308_ADC_I2S_VALID_LEN_MSK (0x3 << RK3308_ADC_I2S_VALID_LEN_SFT) +#define RK3308_ADC_I2S_VALID_LEN_32BITS (0x3 << RK3308_ADC_I2S_VALID_LEN_SFT) +#define RK3308_ADC_I2S_VALID_LEN_24BITS (0x2 << RK3308_ADC_I2S_VALID_LEN_SFT) +#define RK3308_ADC_I2S_VALID_LEN_20BITS (0x1 << RK3308_ADC_I2S_VALID_LEN_SFT) +#define RK3308_ADC_I2S_VALID_LEN_16BITS (0x0 << RK3308_ADC_I2S_VALID_LEN_SFT) +#define RK3308_ADC_I2S_MODE_SFT 3 +#define RK3308_ADC_I2S_MODE_MSK (0x3 << RK3308_ADC_I2S_MODE_SFT) +#define RK3308_ADC_I2S_MODE_PCM (0x3 << RK3308_ADC_I2S_MODE_SFT) +#define RK3308_ADC_I2S_MODE_I2S (0x2 << RK3308_ADC_I2S_MODE_SFT) +#define RK3308_ADC_I2S_MODE_LJ (0x1 << RK3308_ADC_I2S_MODE_SFT) +#define RK3308_ADC_I2S_MODE_RJ (0x0 << RK3308_ADC_I2S_MODE_SFT) +#define RK3308_ADC_I2S_LR_SWAP BIT(1) +#define RK3308_ADC_I2S_MONO BIT(0) + +/* RK3308_ADC_DIG_CON02 - REG: 0x0008 */ +#define RK3308_ADC_IO_MODE_MASTER BIT(5) +#define RK3308_ADC_MODE_MASTER BIT(4) +#define RK3308_ADC_I2S_FRAME_LEN_SFT 2 +#define RK3308_ADC_I2S_FRAME_LEN_MSK (0x3 << RK3308_ADC_I2S_FRAME_LEN_SFT) +#define RK3308_ADC_I2S_FRAME_32BITS (0x3 << RK3308_ADC_I2S_FRAME_LEN_SFT) +#define RK3308_ADC_I2S_FRAME_24BITS (0x2 << RK3308_ADC_I2S_FRAME_LEN_SFT) +#define RK3308_ADC_I2S_FRAME_20BITS (0x1 << RK3308_ADC_I2S_FRAME_LEN_SFT) +#define RK3308_ADC_I2S_FRAME_16BITS (0x0 << RK3308_ADC_I2S_FRAME_LEN_SFT) +#define RK3308_ADC_I2S_WORK BIT(1) +#define RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL BIT(0) + +/* RK3308_ADC_DIG_CON03 - REG: 0x000c */ +#define RK3308_ADC_L_CH_BIST_SFT 2 +#define RK3308_ADC_L_CH_BIST_MSK (0x3 << RK3308_ADC_L_CH_BIST_SFT) +#define RK3308_ADC_L_CH_NORMAL_RIGHT (0x3 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */ +#define RK3308_ADC_L_CH_BIST_CUBE (0x2 << RK3308_ADC_L_CH_BIST_SFT) +#define RK3308_ADC_L_CH_BIST_SINE (0x1 << RK3308_ADC_L_CH_BIST_SFT) +#define RK3308_ADC_L_CH_NORMAL_LEFT (0x0 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */ +#define RK3308_ADC_R_CH_BIST_SFT 0 +#define RK3308_ADC_R_CH_BIST_MSK (0x3 << RK3308_ADC_R_CH_BIST_SFT) +#define RK3308_ADC_R_CH_NORMAL_LEFT (0x3 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */ +#define RK3308_ADC_R_CH_BIST_CUBE (0x2 << RK3308_ADC_R_CH_BIST_SFT) +#define RK3308_ADC_R_CH_BIST_SINE (0x1 << RK3308_ADC_R_CH_BIST_SFT) +#define RK3308_ADC_R_CH_NORMAL_RIGHT (0x0 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */ + +/* RK3308_ADC_DIG_CON04 - REG: 0x0010 */ +#define RK3308_ADC_HPF_PATH_DIS BIT(2) +#define RK3308_ADC_HPF_CUTOFF_SFT 0 +#define RK3308_ADC_HPF_CUTOFF_MSK (0x3 << RK3308_ADC_HPF_CUTOFF_SFT) +#define RK3308_ADC_HPF_CUTOFF_612HZ (0x2 << RK3308_ADC_HPF_CUTOFF_SFT) +#define RK3308_ADC_HPF_CUTOFF_245HZ (0x1 << RK3308_ADC_HPF_CUTOFF_SFT) +#define RK3308_ADC_HPF_CUTOFF_20HZ (0x0 << RK3308_ADC_HPF_CUTOFF_SFT) + +/* RK3308_ADC_DIG_CON07 - REG: 0x001c */ +#define RK3308_ADCL_DATA_SFT 4 +#define RK3308_ADCR_DATA_SFT 2 +#define RK3308_ADCL_DATA_SEL_ADCL BIT(1) +#define RK3308_ADCR_DATA_SEL_ADCR BIT(0) + +/* + * RK3308_ALC_L_DIG_CON00 - REG: 0x0040 + ch * 0xc0 + * RK3308_ALC_R_DIG_CON00 - REG: 0x0080 + ch * 0xc0 + */ +#define RK3308_GAIN_ATTACK_JACK BIT(6) +#define RK3308_CTRL_GEN_SFT 4 +#define RK3308_CTRL_GEN_MSK (0x3 << RK3308_ALC_CTRL_GEN_SFT) +#define RK3308_CTRL_GEN_JACK3 (0x3 << RK3308_ALC_CTRL_GEN_SFT) +#define RK3308_CTRL_GEN_JACK2 (0x2 << RK3308_ALC_CTRL_GEN_SFT) +#define RK3308_CTRL_GEN_JACK1 (0x1 << RK3308_ALC_CTRL_GEN_SFT) +#define RK3308_CTRL_GEN_NORMAL (0x0 << RK3308_ALC_CTRL_GEN_SFT) +#define RK3308_AGC_HOLD_TIME_SFT 0 +#define RK3308_AGC_HOLD_TIME_MSK (0xf << RK3308_AGC_HOLD_TIME_SFT) +#define RK3308_AGC_HOLD_TIME_1S (0xa << RK3308_AGC_HOLD_TIME_SFT) +#define RK3308_AGC_HOLD_TIME_512MS (0x9 << RK3308_AGC_HOLD_TIME_SFT) +#define RK3308_AGC_HOLD_TIME_256MS (0x8 << RK3308_AGC_HOLD_TIME_SFT) +#define RK3308_AGC_HOLD_TIME_128MS (0x7 << RK3308_AGC_HOLD_TIME_SFT) +#define RK3308_AGC_HOLD_TIME_64MS (0x6 << RK3308_AGC_HOLD_TIME_SFT) +#define RK3308_AGC_HOLD_TIME_32MS (0x5 << RK3308_AGC_HOLD_TIME_SFT) +#define RK3308_AGC_HOLD_TIME_16MS (0x4 << RK3308_AGC_HOLD_TIME_SFT) +#define RK3308_AGC_HOLD_TIME_8MS (0x3 << RK3308_AGC_HOLD_TIME_SFT) +#define RK3308_AGC_HOLD_TIME_4MS (0x2 << RK3308_AGC_HOLD_TIME_SFT) +#define RK3308_AGC_HOLD_TIME_2MS (0x1 << RK3308_AGC_HOLD_TIME_SFT) +#define RK3308_AGC_HOLD_TIME_0MS (0x0 << RK3308_AGC_HOLD_TIME_SFT) + +/* + * RK3308_ALC_L_DIG_CON01 - REG: 0x0044 + ch * 0xc0 + * RK3308_ALC_R_DIG_CON01 - REG: 0x0084 + ch * 0xc0 + */ +#define RK3308_AGC_DECAY_TIME_SFT 4 +#define RK3308_AGC_ATTACK_TIME_SFT 0 + +/* + * RK3308_ALC_L_DIG_CON02 - REG: 0x0048 + ch * 0xc0 + * RK3308_ALC_R_DIG_CON02 - REG: 0x0088 + ch * 0xc0 + */ +#define RK3308_AGC_MODE_LIMITER BIT(7) +#define RK3308_AGC_ZERO_CRO_EN BIT(6) +#define RK3308_AGC_AMP_RECOVER_GAIN BIT(5) +#define RK3308_AGC_FAST_DEC_EN BIT(4) +#define RK3308_AGC_NOISE_GATE_EN BIT(3) +#define RK3308_AGC_NOISE_GATE_THRESH_SFT 0 +#define RK3308_AGC_NOISE_GATE_THRESH_MSK (0x7 << RK3308_AGC_NOISE_GATE_THRESH_SFT) + +/* + * RK3308_ALC_L_DIG_CON03 - REG: 0x004c + ch * 0xc0 + * RK3308_ALC_R_DIG_CON03 - REG: 0x008c + ch * 0xc0 + */ +#define RK3308_AGC_PGA_ZERO_CRO_EN BIT(5) +#define RK3308_AGC_PGA_GAIN_MAX 0x1f +#define RK3308_AGC_PGA_GAIN_MIN 0 +#define RK3308_AGC_PGA_GAIN_SFT 0 + +/* + * RK3308_ALC_L_DIG_CON04 - REG: 0x0050 + ch * 0xc0 + * RK3308_ALC_R_DIG_CON04 - REG: 0x0090 + ch * 0xc0 + */ +#define RK3308_AGC_SLOW_CLK_EN BIT(3) +#define RK3308_AGC_APPROX_RATE_SFT 0 +#define RK3308_AGC_APPROX_RATE_MSK (0x7 << RK3308_AGC_APPROX_RATE_SFT) + +/* + * RK3308_ALC_L_DIG_CON05 - REG: 0x0054 + ch * 0xc0 + * RK3308_ALC_R_DIG_CON05 - REG: 0x0094 + ch * 0xc0 + */ +#define RK3308_AGC_LO_8BITS_AGC_MAX_MSK 0xff + +/* + * RK3308_ALC_L_DIG_CON06 - REG: 0x0058 + ch * 0xc0 + * RK3308_ALC_R_DIG_CON06 - REG: 0x0098 + ch * 0xc0 + */ +#define RK3308_AGC_HI_8BITS_AGC_MAX_MSK 0xff + +/* + * RK3308_ALC_L_DIG_CON07 - REG: 0x005c + ch * 0xc0 + * RK3308_ALC_R_DIG_CON07 - REG: 0x009c + ch * 0xc0 + */ +#define RK3308_AGC_LO_8BITS_AGC_MIN_MSK 0xff + +/* + * RK3308_ALC_L_DIG_CON08 - REG: 0x0060 + ch * 0xc0 + * RK3308_ALC_R_DIG_CON08 - REG: 0x00a0 + ch * 0xc0 + */ +#define RK3308_AGC_HI_8BITS_AGC_MIN_MSK 0xff + +/* + * RK3308_ALC_L_DIG_CON09 - REG: 0x0064 + ch * 0xc0 + * RK3308_ALC_R_DIG_CON09 - REG: 0x00a4 + ch * 0xc0 + */ +#define RK3308_AGC_FUNC_SEL_MSK BIT(6) +#define RK3308_AGC_MAX_GAIN_PGA_MAX 0x7 +#define RK3308_AGC_MAX_GAIN_PGA_MIN 0 +#define RK3308_AGC_MAX_GAIN_PGA_SFT 3 +#define RK3308_AGC_MAX_GAIN_PGA_MSK (0x7 << RK3308_AGC_MAX_GAIN_PGA_SFT) +#define RK3308_AGC_MIN_GAIN_PGA_MAX 0x7 +#define RK3308_AGC_MIN_GAIN_PGA_MIN 0 +#define RK3308_AGC_MIN_GAIN_PGA_SFT 0 +#define RK3308_AGC_MIN_GAIN_PGA_MSK (0x7 << RK3308_AGC_MIN_GAIN_PGA_SFT) + +/* + * RK3308_ALC_L_DIG_CON12 - REG: 0x0068 + ch * 0xc0 + * RK3308_ALC_R_DIG_CON12 - REG: 0x00a8 + ch * 0xc0 + */ +#define RK3308_AGC_GAIN_MSK 0x1f + +/* RK3308_DAC_DIG_CON01 - REG: 0x0304 */ +#define RK3308_DAC_I2S_LRC_POL_REVERSAL BIT(7) +#define RK3308_DAC_I2S_VALID_LEN_SFT 5 +#define RK3308_DAC_I2S_VALID_LEN_MSK (0x3 << RK3308_DAC_I2S_VALID_LEN_SFT) +#define RK3308_DAC_I2S_VALID_LEN_32BITS (0x3 << RK3308_DAC_I2S_VALID_LEN_SFT) +#define RK3308_DAC_I2S_VALID_LEN_24BITS (0x2 << RK3308_DAC_I2S_VALID_LEN_SFT) +#define RK3308_DAC_I2S_VALID_LEN_20BITS (0x1 << RK3308_DAC_I2S_VALID_LEN_SFT) +#define RK3308_DAC_I2S_VALID_LEN_16BITS (0x0 << RK3308_DAC_I2S_VALID_LEN_SFT) +#define RK3308_DAC_I2S_MODE_SFT 3 +#define RK3308_DAC_I2S_MODE_MSK (0x3 << RK3308_DAC_I2S_MODE_SFT) +#define RK3308_DAC_I2S_MODE_PCM (0x3 << RK3308_DAC_I2S_MODE_SFT) +#define RK3308_DAC_I2S_MODE_I2S (0x2 << RK3308_DAC_I2S_MODE_SFT) +#define RK3308_DAC_I2S_MODE_LJ (0x1 << RK3308_DAC_I2S_MODE_SFT) +#define RK3308_DAC_I2S_MODE_RJ (0x0 << RK3308_DAC_I2S_MODE_SFT) +#define RK3308_DAC_I2S_LR_SWAP BIT(2) + +/* RK3308_DAC_DIG_CON02 - REG: 0x0308 */ +#define RK3308_DAC_IO_MODE_MASTER BIT(5) +#define RK3308_DAC_MODE_MASTER BIT(4) +#define RK3308_DAC_I2S_FRAME_LEN_SFT 2 +#define RK3308_DAC_I2S_FRAME_LEN_MSK (0x3 << RK3308_DAC_I2S_FRAME_LEN_SFT) +#define RK3308_DAC_I2S_FRAME_32BITS (0x3 << RK3308_DAC_I2S_FRAME_LEN_SFT) +#define RK3308_DAC_I2S_FRAME_24BITS (0x2 << RK3308_DAC_I2S_FRAME_LEN_SFT) +#define RK3308_DAC_I2S_FRAME_20BITS (0x1 << RK3308_DAC_I2S_FRAME_LEN_SFT) +#define RK3308_DAC_I2S_FRAME_16BITS (0x0 << RK3308_DAC_I2S_FRAME_LEN_SFT) +#define RK3308_DAC_I2S_WORK BIT(1) +#define RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL BIT(0) + +/* RK3308_DAC_DIG_CON03 - REG: 0x030C */ +#define RK3308_DAC_L_CH_BIST_SFT 2 +#define RK3308_DAC_L_CH_BIST_MSK (0x3 << RK3308_DAC_L_CH_BIST_SFT) +#define RK3308_DAC_L_CH_BIST_LEFT (0x3 << RK3308_DAC_L_CH_BIST_SFT) /* normal mode */ +#define RK3308_DAC_L_CH_BIST_CUBE (0x2 << RK3308_DAC_L_CH_BIST_SFT) +#define RK3308_DAC_L_CH_BIST_SINE (0x1 << RK3308_DAC_L_CH_BIST_SFT) +#define RK3308_DAC_L_CH_BIST_RIGHT (0x0 << RK3308_DAC_L_CH_BIST_SFT) /* normal mode */ +#define RK3308_DAC_R_CH_BIST_SFT 0 +#define RK3308_DAC_R_CH_BIST_MSK (0x3 << RK3308_DAC_R_CH_BIST_SFT) +#define RK3308_DAC_R_CH_BIST_LEFT (0x3 << RK3308_DAC_R_CH_BIST_SFT) /* normal mode */ +#define RK3308_DAC_R_CH_BIST_CUBE (0x2 << RK3308_DAC_R_CH_BIST_SFT) +#define RK3308_DAC_R_CH_BIST_SINE (0x1 << RK3308_DAC_R_CH_BIST_SFT) +#define RK3308_DAC_R_CH_BIST_RIGHT (0x0 << RK3308_DAC_R_CH_BIST_SFT) /* normal mode */ + +/* RK3308_DAC_DIG_CON04 - REG: 0x0310 */ +#define RK3308_DAC_MODULATOR_GAIN_SFT 4 +#define RK3308_DAC_MODULATOR_GAIN_MSK (0x7 << RK3308_DAC_MODULATOR_GAIN_SFT) +#define RK3308_DAC_CIC_IF_GAIN_SFT 0 +#define RK3308_DAC_CIC_IF_GAIN_MSK (0x7 << RK3308_DAC_CIC_IF_GAIN_SFT) + +/* RK3308_DAC_DIG_CON05 - REG: 0x0314 */ +#define RK3308_DAC_L_REG_CTL_INDATA BIT(2) +#define RK3308_DAC_R_REG_CTL_INDATA BIT(1) + +/* RK3308_DAC_DIG_CON10 - REG: 0x0328 */ +#define RK3308_DAC_DATA_HI4(x) ((x) & 0xf) + +/* RK3308_DAC_DIG_CON11 - REG: 0x032c */ +#define RK3308_DAC_DATA_LO8(x) ((x) & 0xff) + +/* RK3308_ADC_ANA_CON00 - REG: 0x0340 */ +#define RK3308_ADC_CH1_CH2_MIC_ALL_MSK (0xff << 0) +#define RK3308_ADC_CH1_CH2_MIC_ALL 0xff +#define RK3308_ADC_CH2_MIC_UNMUTE BIT(7) +#define RK3308_ADC_CH2_MIC_WORK BIT(6) +#define RK3308_ADC_CH2_MIC_EN BIT(5) +#define RK3308_ADC_CH2_BUF_REF_EN BIT(4) +#define RK3308_ADC_CH1_MIC_UNMUTE BIT(3) +#define RK3308_ADC_CH1_MIC_WORK BIT(2) +#define RK3308_ADC_CH1_MIC_EN BIT(1) +#define RK3308_ADC_CH1_BUF_REF_EN BIT(0) + +/* RK3308_ADC_ANA_CON01 - REG: 0x0344 + * + * The PGA of MIC-INs: + * - HW version A: + * 0x0 - MIC1~MIC8 0 dB (recommended when ADC used as loopback) + * 0x3 - MIC1~MIC8 20 dB (recommended when ADC used as MIC input) + * - HW version B: + * 0x0 - MIC1~MIC8 0 dB + * 0x1 - MIC1~MIC8 6.6 dB + * 0x2 - MIC1~MIC8 13 dB + * 0x3 - MIC1~MIC8 20 dB + */ +#define RK3308_ADC_CH2_MIC_GAIN_MAX 0x3 +#define RK3308_ADC_CH2_MIC_GAIN_MIN 0 +#define RK3308_ADC_CH2_MIC_GAIN_SFT 4 +#define RK3308_ADC_CH2_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT) +#define RK3308_ADC_CH2_MIC_GAIN_20DB (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT) +#define RK3308_ADC_CH2_MIC_GAIN_13DB (0x2 << RK3308_ADC_CH2_MIC_GAIN_SFT) +#define RK3308_ADC_CH2_MIC_GAIN_6_6DB (0x1 << RK3308_ADC_CH2_MIC_GAIN_SFT) +#define RK3308_ADC_CH2_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH2_MIC_GAIN_SFT) + +#define RK3308_ADC_CH1_MIC_GAIN_MAX 0x3 +#define RK3308_ADC_CH1_MIC_GAIN_MIN 0 +#define RK3308_ADC_CH1_MIC_GAIN_SFT 0 +#define RK3308_ADC_CH1_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT) +#define RK3308_ADC_CH1_MIC_GAIN_20DB (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT) +#define RK3308_ADC_CH1_MIC_GAIN_13DB (0x2 << RK3308_ADC_CH1_MIC_GAIN_SFT) +#define RK3308_ADC_CH1_MIC_GAIN_6_6DB (0x1 << RK3308_ADC_CH1_MIC_GAIN_SFT) +#define RK3308_ADC_CH1_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH1_MIC_GAIN_SFT) + +/* RK3308_ADC_ANA_CON02 - REG: 0x0348 */ +#define RK3308_ADC_CH2_ZEROCROSS_DET_EN BIT(6) +#define RK3308_ADC_CH2_ALC_WORK BIT(5) +#define RK3308_ADC_CH2_ALC_EN BIT(4) +#define RK3308_ADC_CH1_ZEROCROSS_DET_EN BIT(2) +#define RK3308_ADC_CH1_ALC_WORK BIT(1) +#define RK3308_ADC_CH1_ALC_EN BIT(0) + +/* RK3308_ADC_ANA_CON03 - REG: 0x034c */ +#define RK3308_ADC_CH1_ALC_GAIN_MAX 0x1f +#define RK3308_ADC_CH1_ALC_GAIN_MIN 0 +#define RK3308_ADC_CH1_ALC_GAIN_SFT 0 +#define RK3308_ADC_CH1_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH1_ALC_GAIN_SFT) +#define RK3308_ADC_CH1_ALC_GAIN_0DB (0x0c << RK3308_ADC_CH1_ALC_GAIN_SFT) + +/* RK3308_ADC_ANA_CON04 - REG: 0x0350 */ +#define RK3308_ADC_CH2_ALC_GAIN_MAX 0x1f +#define RK3308_ADC_CH2_ALC_GAIN_MIN 0 +#define RK3308_ADC_CH2_ALC_GAIN_SFT 0 +#define RK3308_ADC_CH2_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH2_ALC_GAIN_SFT) +#define RK3308_ADC_CH2_ALC_GAIN_0DB (0x0c << RK3308_ADC_CH2_ALC_GAIN_SFT) + +/* RK3308_ADC_ANA_CON05 - REG: 0x0354 */ +#define RK3308_ADC_CH2_ADC_WORK BIT(6) +#define RK3308_ADC_CH2_ADC_EN BIT(5) +#define RK3308_ADC_CH2_CLK_EN BIT(4) +#define RK3308_ADC_CH1_ADC_WORK BIT(2) +#define RK3308_ADC_CH1_ADC_EN BIT(1) +#define RK3308_ADC_CH1_CLK_EN BIT(0) + +/* RK3308_ADC_ANA_CON06 - REG: 0x0358 */ +#define RK3308_ADC_CURRENT_EN BIT(0) + +/* RK3308_ADC_ANA_CON07 - REG: 0x035c */ +/* Note: The register configuration is only valid for ADC2 */ +#define RK3308_ADC_CH2_IN_SEL_SFT 6 +#define RK3308_ADC_CH2_IN_SEL_MSK (0x3 << RK3308_ADC_CH2_IN_SEL_SFT) +#define RK3308_ADC_CH2_IN_LINEIN_MIC (0x3 << RK3308_ADC_CH2_IN_SEL_SFT) +#define RK3308_ADC_CH2_IN_LINEIN (0x2 << RK3308_ADC_CH2_IN_SEL_SFT) +#define RK3308_ADC_CH2_IN_MIC (0x1 << RK3308_ADC_CH2_IN_SEL_SFT) +#define RK3308_ADC_CH2_IN_NONE (0x0 << RK3308_ADC_CH2_IN_SEL_SFT) +/* Note: The register configuration is only valid for ADC1 */ +#define RK3308_ADC_CH1_IN_SEL_SFT 4 +#define RK3308_ADC_CH1_IN_SEL_MSK (0x3 << RK3308_ADC_CH1_IN_SEL_SFT) +#define RK3308_ADC_CH1_IN_LINEIN_MIC (0x3 << RK3308_ADC_CH1_IN_SEL_SFT) +#define RK3308_ADC_CH1_IN_LINEIN (0x2 << RK3308_ADC_CH1_IN_SEL_SFT) +#define RK3308_ADC_CH1_IN_MIC (0x1 << RK3308_ADC_CH1_IN_SEL_SFT) +#define RK3308_ADC_CH1_IN_NONE (0x0 << RK3308_ADC_CH1_IN_SEL_SFT) +#define RK3308_ADC_MIC_BIAS_BUF_EN BIT(3) +#define RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT 0 +#define RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK (0x7 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT) + +/* RK3308_ADC_ANA_CON08 - REG: 0x0360 */ +#define RK3308_ADC_MICBIAS_CURRENT_EN BIT(4) + +/* RK3308_ADC_ANA_CON10 - REG: 0x0368 */ +#define RK3308_ADC_REF_EN BIT(7) +#define RK3308_ADC_CURRENT_CHARGE_SFT 0 +#define RK3308_ADC_CURRENT_CHARGE_MSK (0x7f << RK3308_ADC_CURRENT_CHARGE_SFT) +/* + * 1: Choose the current I + * 0: Don't choose the current I + */ +#define RK3308_ADC_SEL_I(x) ((x) & 0x7f) + +/* RK3308_ADC_ANA_CON11 - REG: 0x036c */ +#define RK3308_ADC_ALCR_CON_GAIN_PGAR_EN BIT(1) +#define RK3308_ADC_ALCL_CON_GAIN_PGAL_EN BIT(0) + +/* RK3308_DAC_ANA_CON00 - REG: 0x0440 */ +#define RK3308_DAC_HEADPHONE_DET_EN BIT(1) +#define RK3308_DAC_CURRENT_EN BIT(0) + +/* RK3308_DAC_ANA_CON01 - REG: 0x0444 */ +#define RK3308_DAC_BUF_REF_R_EN BIT(6) +#define RK3308_DAC_HPOUT_POP_SOUND_R_SFT 4 +#define RK3308_DAC_HPOUT_POP_SOUND_R_MSK (0x3 << RK3308_DAC_HPOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_HPOUT_POP_SOUND_R_WORK (0x2 << RK3308_DAC_HPOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_HPOUT_POP_SOUND_R_INIT (0x1 << RK3308_DAC_HPOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_BUF_REF_L_EN BIT(2) +#define RK3308_DAC_HPOUT_POP_SOUND_L_SFT 0 +#define RK3308_DAC_HPOUT_POP_SOUND_L_MSK (0x3 << RK3308_DAC_HPOUT_POP_SOUND_L_SFT) +#define RK3308_DAC_HPOUT_POP_SOUND_L_WORK (0x2 << RK3308_DAC_HPOUT_POP_SOUND_L_SFT) +#define RK3308_DAC_HPOUT_POP_SOUND_L_INIT (0x1 << RK3308_DAC_HPOUT_POP_SOUND_L_SFT) + +/* RK3308_DAC_ANA_CON02 - REG: 0x0448 */ +#define RK3308_DAC_R_DAC_WORK BIT(7) +#define RK3308_DAC_R_DAC_EN BIT(6) +#define RK3308_DAC_R_CLK_EN BIT(5) +#define RK3308_DAC_R_REF_EN BIT(4) +#define RK3308_DAC_L_DAC_WORK BIT(3) +#define RK3308_DAC_L_DAC_EN BIT(2) +#define RK3308_DAC_L_CLK_EN BIT(1) +#define RK3308_DAC_L_REF_EN BIT(0) + +/* RK3308_DAC_ANA_CON03 - REG: 0x044c */ +#define RK3308_DAC_R_HPOUT_WORK BIT(6) +#define RK3308_DAC_R_HPOUT_EN BIT(5) +#define RK3308_DAC_R_HPOUT_UNMUTE BIT(4) +#define RK3308_DAC_L_HPOUT_WORK BIT(2) +#define RK3308_DAC_L_HPOUT_EN BIT(1) +#define RK3308_DAC_L_HPOUT_UNMUTE BIT(0) + +/* RK3308_DAC_ANA_CON04 - REG: 0x0450 */ +#define RK3308_DAC_R_LINEOUT_GAIN_MAX 0x3 +#define RK3308_DAC_R_LINEOUT_GAIN_SFT 6 +#define RK3308_DAC_R_LINEOUT_GAIN_MSK (0x3 << RK3308_DAC_R_LINEOUT_GAIN_SFT) +#define RK3308_DAC_R_LINEOUT_GAIN_0DB (0x3 << RK3308_DAC_R_LINEOUT_GAIN_SFT) +#define RK3308_DAC_R_LINEOUT_GAIN_NDB_1_5 (0x2 << RK3308_DAC_R_LINEOUT_GAIN_SFT) +#define RK3308_DAC_R_LINEOUT_GAIN_NDB_3 (0x1 << RK3308_DAC_R_LINEOUT_GAIN_SFT) +#define RK3308_DAC_R_LINEOUT_GAIN_NDB_6 (0x0 << RK3308_DAC_R_LINEOUT_GAIN_SFT) +#define RK3308_DAC_R_LINEOUT_UNMUTE BIT(5) +#define RK3308_DAC_R_LINEOUT_EN BIT(4) +#define RK3308_DAC_L_LINEOUT_GAIN_MAX 0x3 +#define RK3308_DAC_L_LINEOUT_GAIN_SFT 2 +#define RK3308_DAC_L_LINEOUT_GAIN_MSK (0x3 << RK3308_DAC_L_LINEOUT_GAIN_SFT) +#define RK3308_DAC_L_LINEOUT_GAIN_0DB (0x3 << RK3308_DAC_L_LINEOUT_GAIN_SFT) +#define RK3308_DAC_L_LINEOUT_GAIN_NDB_1_5 (0x2 << RK3308_DAC_L_LINEOUT_GAIN_SFT) +#define RK3308_DAC_L_LINEOUT_GAIN_NDB_3 (0x1 << RK3308_DAC_L_LINEOUT_GAIN_SFT) +#define RK3308_DAC_L_LINEOUT_GAIN_NDB_6 (0x0 << RK3308_DAC_L_LINEOUT_GAIN_SFT) +#define RK3308_DAC_L_LINEOUT_UNMUTE BIT(1) +#define RK3308_DAC_L_LINEOUT_EN BIT(0) + +/* RK3308_DAC_ANA_CON05 - REG: 0x0454, step is 1.5db */ +#define RK3308_DAC_L_HPOUT_GAIN_MAX 0x1e +#define RK3308_DAC_L_HPOUT_GAIN_SFT 0 +#define RK3308_DAC_L_HPOUT_GAIN_MSK (0x1f << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_PDB_6 (0x1e << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_PDB_4_5 (0x1d << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_PDB_3 (0x1c << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_PDB_1_5 (0x1b << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_0DB (0x1a << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_1_5 (0x19 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_3 (0x18 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_4_5 (0x17 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_6 (0x16 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_7_5 (0x15 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_9 (0x14 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_10_5 (0x13 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_12 (0x12 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_13_5 (0x11 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_15 (0x10 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_16_5 (0x0f << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_18 (0x0e << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_19_5 (0x0d << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_21 (0x0c << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_22_5 (0x0b << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_24 (0x0a << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_25_5 (0x09 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_27 (0x08 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_28_5 (0x07 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_30 (0x06 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_31_5 (0x05 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_33 (0x04 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_34_5 (0x03 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_36 (0x02 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_37_5 (0x01 << RK3308_DAC_L_HPOUT_GAIN_SFT) +#define RK3308_DAC_L_HPOUT_GAIN_NDB_39 (0x00 << RK3308_DAC_L_HPOUT_GAIN_SFT) + +/* RK3308_DAC_ANA_CON06 - REG: 0x0458, step is 1.5db */ +#define RK3308_DAC_R_HPOUT_GAIN_MAX 0x1e +#define RK3308_DAC_R_HPOUT_GAIN_SFT 0 +#define RK3308_DAC_R_HPOUT_GAIN_MSK (0x1f << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_PDB_6 (0x1e << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_PDB_4_5 (0x1d << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_PDB_3 (0x1c << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_PDB_1_5 (0x1b << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_0DB (0x1a << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_1_5 (0x19 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_3 (0x18 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_4_5 (0x17 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_6 (0x16 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_7_5 (0x15 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_9 (0x14 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_10_5 (0x13 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_12 (0x12 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_13_5 (0x11 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_15 (0x10 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_16_5 (0x0f << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_18 (0x0e << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_19_5 (0x0d << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_21 (0x0c << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_22_5 (0x0b << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_24 (0x0a << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_25_5 (0x09 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_27 (0x08 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_28_5 (0x07 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_30 (0x06 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_31_5 (0x05 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_33 (0x04 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_34_5 (0x03 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_36 (0x02 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_37_5 (0x01 << RK3308_DAC_R_HPOUT_GAIN_SFT) +#define RK3308_DAC_R_HPOUT_GAIN_NDB_39 (0x00 << RK3308_DAC_R_HPOUT_GAIN_SFT) + +/* RK3308_DAC_ANA_CON07 - REG: 0x045c */ +#define RK3308_DAC_R_HPOUT_DRV_SFT 4 +#define RK3308_DAC_R_HPOUT_DRV_MSK (0xf << RK3308_DAC_R_HPOUT_DRV_SFT) +#define RK3308_DAC_L_HPOUT_DRV_SFT 0 +#define RK3308_DAC_L_HPOUT_DRV_MSK (0xf << RK3308_DAC_L_HPOUT_DRV_SFT) + +/* RK3308_DAC_ANA_CON08 - REG: 0x0460 */ +#define RK3308_DAC_R_LINEOUT_DRV_SFT 4 +#define RK3308_DAC_R_LINEOUT_DRV_MSK (0xf << RK3308_DAC_R_LINEOUT_DRV_SFT) +#define RK3308_DAC_L_LINEOUT_DRV_SFT 0 +#define RK3308_DAC_L_LINEOUT_DRV_MSK (0xf << RK3308_DAC_L_LINEOUT_DRV_SFT) + +/* RK3308_DAC_ANA_CON12 - REG: 0x0470 */ +#define RK3308_DAC_R_HPMIX_SEL_SFT 6 +#define RK3308_DAC_R_HPMIX_SEL_MSK (0x3 << RK3308_DAC_R_HPMIX_SEL_SFT) +#define RK3308_DAC_R_HPMIX_LINEIN_I2S (0x3 << RK3308_DAC_R_HPMIX_SEL_SFT) +#define RK3308_DAC_R_HPMIX_LINEIN (0x2 << RK3308_DAC_R_HPMIX_SEL_SFT) +#define RK3308_DAC_R_HPMIX_I2S (0x1 << RK3308_DAC_R_HPMIX_SEL_SFT) +#define RK3308_DAC_R_HPMIX_NONE (0x0 << RK3308_DAC_R_HPMIX_SEL_SFT) +#define RK3308_DAC_R_HPMIX_GAIN_MIN 0x1 /* 0xx0 and 0x3 are reserved */ +#define RK3308_DAC_R_HPMIX_GAIN_MAX 0x2 +#define RK3308_DAC_R_HPMIX_GAIN_SFT 4 +#define RK3308_DAC_R_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_R_HPMIX_GAIN_SFT) +#define RK3308_DAC_R_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_R_HPMIX_GAIN_SFT) +#define RK3308_DAC_R_HPMIX_GAIN_NDB_6 (0x1 << RK3308_DAC_R_HPMIX_GAIN_SFT) +#define RK3308_DAC_L_HPMIX_SEL_SFT 2 +#define RK3308_DAC_L_HPMIX_SEL_MSK (0x3 << RK3308_DAC_L_HPMIX_SEL_SFT) +#define RK3308_DAC_L_HPMIX_LINEIN_I2S (0x3 << RK3308_DAC_L_HPMIX_SEL_SFT) +#define RK3308_DAC_L_HPMIX_LINEIN (0x2 << RK3308_DAC_L_HPMIX_SEL_SFT) +#define RK3308_DAC_L_HPMIX_I2S (0x1 << RK3308_DAC_L_HPMIX_SEL_SFT) +#define RK3308_DAC_L_HPMIX_NONE (0x0 << RK3308_DAC_L_HPMIX_SEL_SFT) +#define RK3308_DAC_L_HPMIX_GAIN_MIN 0x1 /* 0xx0 and 0x3 are reserved */ +#define RK3308_DAC_L_HPMIX_GAIN_MAX 0x2 +#define RK3308_DAC_L_HPMIX_GAIN_SFT 0 +#define RK3308_DAC_L_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_L_HPMIX_GAIN_SFT) +#define RK3308_DAC_L_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_L_HPMIX_GAIN_SFT) +#define RK3308_DAC_L_HPMIX_GAIN_NDB_6 (0x1 << RK3308_DAC_L_HPMIX_GAIN_SFT) + +/* RK3308_DAC_ANA_CON13 - REG: 0x0474 */ +#define RK3308_DAC_R_HPMIX_UNMUTE BIT(6) +#define RK3308_DAC_R_HPMIX_WORK BIT(5) +#define RK3308_DAC_R_HPMIX_EN BIT(4) +#define RK3308_DAC_L_HPMIX_UNMUTE BIT(2) +#define RK3308_DAC_L_HPMIX_WORK BIT(1) +#define RK3308_DAC_L_HPMIX_EN BIT(0) + +/* RK3308_DAC_ANA_CON14 - REG: 0x0478 */ +#define RK3308_DAC_VCM_LINEOUT_EN (0x1 << 4) +#define RK3308_DAC_CURRENT_CHARGE_SFT 0 +#define RK3308_DAC_CURRENT_CHARGE_MSK (0xf << RK3308_DAC_CURRENT_CHARGE_SFT) + +/* + * 1: Choose the current I + * 0: Don't choose the current I + */ +#define RK3308_DAC_SEL_I(x) ((x) & 0xf) + +/* RK3308_DAC_ANA_CON15 - REG: 0x047C */ +#define RK3308_DAC_LINEOUT_POP_SOUND_R_SFT 4 +#define RK3308_DAC_LINEOUT_POP_SOUND_R_MSK (0x3 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_R_SEL_DC_FROM_INTERNAL (0x2 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_R_SEL_DC_FROM_VCM (0x1 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_R_SEL_LINEOUT_FROM_INTERNAL (0x0 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT) +#define RK3308_DAC_LINEOUT_POP_SOUND_L_SFT 0 +#define RK3308_DAC_LINEOUT_POP_SOUND_L_MSK (0x3 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT) +#define RK3308_DAC_L_SEL_DC_FROM_INTERNAL (0x2 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT) +#define RK3308_DAC_L_SEL_DC_FROM_VCM (0x1 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT) +#define RK3308_DAC_L_SEL_LINEOUT_FROM_INTERNAL (0x0 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT) + +#define RK3308_HIFI 0x0 + +#endif /* __RK3308_CODEC_H__ */ From patchwork Wed Sep 7 14:21:24 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luca Ceresoli X-Patchwork-Id: 12969047 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 alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (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 2ABDCC54EE9 for ; Wed, 7 Sep 2022 14:24:38 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 5FBAC1696; Wed, 7 Sep 2022 16:23:46 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 5FBAC1696 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1662560676; bh=XoygatHT4Li32rCgO8k43XIL6g7WCfZNoyPKsWFgees=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=gBfEhiCxxWMTbb1AbK/F2wPSQ2Kx28bUZ8ybvznOIBRMM1d3NEwpJWnP4WaOS7fTu rFGZeS0LteZbSvoMHBuz/DyilGAbUWiVqGAuCOein5GRH7eKRRIPd0f84mS5rsDa4o OLNu69BPd8VDgh7kOoKui+jSB4qjjkuuFvcSx04o= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 4604CF80563; Wed, 7 Sep 2022 16:22:26 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id A417FF80564; Wed, 7 Sep 2022 16:22:24 +0200 (CEST) Received: from relay12.mail.gandi.net (relay12.mail.gandi.net [217.70.178.232]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 0DC82F80542 for ; Wed, 7 Sep 2022 16:22:18 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 0DC82F80542 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="AOWGd9Mn" Received: from booty.fritz.box (unknown [77.244.183.192]) (Authenticated sender: luca.ceresoli@bootlin.com) by mail.gandi.net (Postfix) with ESMTPA id D228B20000F; Wed, 7 Sep 2022 14:22:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1662560537; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=SRJhK68KAnFJfkryAuHaNW+7tiZrtjjidVkx4uYL5XU=; b=AOWGd9MnnWKkE7Kmh2RGiJ/tBsTNQAQlKX7ROe9c/tvImUL9+OaD5W96WHNG9mvr6LmjGl PUmSTtKfPtvyt88zsz1P9TKi8vzYE+Kx5tiTBSCYjU1wfbYNX6TDJ7TsuW+n7fkGEWEHa9 yauHRc7Ov7tlYpoW6WRPbL0dxV0vf7gQbenMqjFAPJQTazZpap8MXWGBI7oixstVXRTdD9 tSbp3QWfblJLKxm0SvRGUDGdSt36nQ0YrUwrQdrV+ayViu491G9x195k9S4gqsjvBJkA7k hdXkSoElmUCLXK6Kw7IwGFwPb1WX2xY8pMUz3K8TVuAVKNqL240woQFIA31GaA== From: luca.ceresoli@bootlin.com To: alsa-devel@alsa-project.org, linux-rockchip@lists.infradead.org Subject: [PATCH 8/8] ASoC: rockchip: add new RK3308 sound card Date: Wed, 7 Sep 2022 16:21:24 +0200 Message-Id: <20220907142124.2532620-9-luca.ceresoli@bootlin.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> References: <20220907142124.2532620-1-luca.ceresoli@bootlin.com> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, Heiko Stuebner , Takashi Iwai , Chris Morgan , linux-kernel@vger.kernel.org, Rob Herring , Liam Girdwood , Nicolas Frattaroli , Mark Brown , Krzysztof Kozlowski , Philipp Zabel , Johan Jonker , Luca Ceresoli , linux-arm-kernel@lists.infradead.org X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Luca Ceresoli The Rockchip RK3308 8-channel I2S adapter has a mainline driver that can work fine with an audio-graph-card or simple-audio-card. Those card drivers call the .set_sysclk op once, and this is usually enough for applications using an external codec. But in reality the I2S adapter has two clock inputs (TX and RX), and the preferred way to use the RK3308 internal codec is enabling both clocks, potentially with different rates. The existing simple-card code does not implement this possibility. To allow setting both clocks, add a new minimal driver that builds on top of audio-graph-card and changes the dai_link->init callback with a modified version of asoc_simple_init_dai(). This ultimately calls the set_sysclk() callback as many times as the number of clocks defined in device tree. With this implementation, the same rate is set to all the sysclks. Setting different rates can be added later. Signed-off-by: Luca Ceresoli --- MAINTAINERS | 1 + sound/soc/rockchip/Kconfig | 14 ++++ sound/soc/rockchip/Makefile | 1 + sound/soc/rockchip/rockchip_rk3308_card.c | 97 +++++++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 sound/soc/rockchip/rockchip_rk3308_card.c diff --git a/MAINTAINERS b/MAINTAINERS index fb2a0a6e3c1f..96ccda9625f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17600,6 +17600,7 @@ ROCKCHIP RK3308 SOUND CARD DRIVER M: Luca Ceresoli S: Maintained F: Documentation/devicetree/bindings/sound/rockchip,rk3308-audio-graph-card.yaml +F: sound/soc/rockchip/rockchip_rk3308_card.c ROCKCHIP VIDEO DECODER DRIVER M: Ezequiel Garcia diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 42f76bc0fb02..b00dc04f8fd0 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -45,6 +45,20 @@ config SND_SOC_ROCKCHIP_SPDIF Say Y or M if you want to add support for SPDIF driver for Rockchip SPDIF transceiver device. +config SND_SOC_ROCKCHIP_RK3308_INTERNAL_CODEC + tristate "ASoC sound card based on the internal RK3308 codec" + depends on HAVE_CLK && SND_SOC_ROCKCHIP + depends on SND_AUDIO_GRAPH_CARD + select SND_SOC_ROCKCHIP_I2S_TDM + select SND_SOC_GENERIC_DMAENGINE_PCM + help + ASoC sound card driver for the RK3308 internal audio codec. + + The Rockchip RK3308 SoC has a built-in audio codec that is + connected internally to one out of a selection of the internal + I2S controllers. This driver implements an audio card using these + components. + config SND_SOC_ROCKCHIP_MAX98090 tristate "ASoC support for Rockchip boards using a MAX98090 codec" depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 30c57c0d7660..680decae0c02 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -15,6 +15,7 @@ snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o snd-soc-rk3288-hdmi-analog-objs := rk3288_hdmi_analog.o snd-soc-rk3399-gru-sound-objs := rk3399_gru_sound.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_RK3308_INTERNAL_CODEC) += rockchip_rk3308_card.o obj-$(CONFIG_SND_SOC_ROCKCHIP_MAX98090) += snd-soc-rockchip-max98090.o obj-$(CONFIG_SND_SOC_ROCKCHIP_RT5645) += snd-soc-rockchip-rt5645.o obj-$(CONFIG_SND_SOC_RK3288_HDMI_ANALOG) += snd-soc-rk3288-hdmi-analog.o diff --git a/sound/soc/rockchip/rockchip_rk3308_card.c b/sound/soc/rockchip/rockchip_rk3308_card.c new file mode 100644 index 000000000000..3cfc751993fe --- /dev/null +++ b/sound/soc/rockchip/rockchip_rk3308_card.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Audio card using the RK3308 internal I2S +// +// Allows driving the I2S peripheral with both the RX and TX clocks. This +// is useful to use the RK3308 internal audio codec. +// +// Copyright (c) 2022, Vivax-Metrotech Ltd +// +// Based on sound/soc/generic/audio-graph-card.c + +#include +#include +#include +#include +#include + +static int rk3308_audio_asoc_simple_init_dai(struct snd_soc_dai *dai, + struct asoc_simple_dai *simple_dai) +{ + const int nclks = 2; /* The sysclks are clk_id 0 and 1 for the RK3308 driver */ + int err; + int i; + + if (!simple_dai || !simple_dai->sysclk) + return 0; + + /* Can be extended to get two different sysclk values via device tree */ + for (i = 0; i < nclks; i++) { + err = snd_soc_dai_set_sysclk(dai, i, simple_dai->sysclk, + simple_dai->clk_direction); + if (err && err != -ENOTSUPP) + return dev_err_probe(dai->dev, err, "simple-card: set_sysclk error\n"); + } + + return 0; +} + +static int rk3308_audio_asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); + struct asoc_simple_dai *dai; + int i, ret; + + for_each_prop_dai_codec(props, i, dai) { + ret = rk3308_audio_asoc_simple_init_dai(asoc_rtd_to_codec(rtd, i), dai); + if (ret < 0) + return ret; + } + for_each_prop_dai_cpu(props, i, dai) { + ret = rk3308_audio_asoc_simple_init_dai(asoc_rtd_to_cpu(rtd, i), dai); + if (ret < 0) + return ret; + } + + return 0; +} + +static int rk3308_audio_graph_probe(struct platform_device *pdev) +{ + struct asoc_simple_priv *priv; + struct device *dev = &pdev->dev; + struct snd_soc_card *card; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + card = simple_priv_to_card(priv); + card->driver_name = "rk3308-audio-graph-card"; + card->probe = asoc_graph_card_probe; + priv->init = rk3308_audio_asoc_simple_dai_init; + + return audio_graph_parse_of(priv, dev); +} + +static const struct of_device_id graph_of_rk3308_card_match[] = { + { .compatible = "rockchip,rk3308-audio-graph-card" }, + {}, +}; +MODULE_DEVICE_TABLE(of, graph_of_rk3308_card_match); + +static struct platform_driver rk3308_audio_graph_card = { + .driver = { + .name = "rk3308-audio-graph-card", + .pm = &snd_soc_pm_ops, + .of_match_table = graph_of_rk3308_card_match, + }, + .probe = rk3308_audio_graph_probe, + .remove = asoc_simple_remove, +}; +module_platform_driver(rk3308_audio_graph_card); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ASoC Audio Graph Card for Rockchip RK3308"); +MODULE_AUTHOR("Luca Ceresoli ");