From patchwork Wed Nov 9 16:53:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 623810 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 CA120C433FE for ; Wed, 9 Nov 2022 16:55: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 9CD2CAE8; Wed, 9 Nov 2022 17:54:10 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 9CD2CAE8 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1668012900; bh=paXFV8XF+8v2oOoM42EgPOcGHJIAZouNH7KXny/LTRE=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=Z/XgajtuH+uzuj1I10GchTYJyDspvvdKIAusZvYuq4W8uxN2Ea1yxlsJxX2/Tl55x NSmEnJLjFee1mF+jzPjt7qwtWIN1S6Ml/aAhjG/nOKeGec5miVRfZKKy215+3mFX53 +ZyGEeIy19KHn5v//VmRs8H6m8pq2DEFOWTBqQg8= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 5327EF80563; Wed, 9 Nov 2022 17:53:45 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 5194EF80563; Wed, 9 Nov 2022 17:53:43 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0b-001ae601.pphosted.com [67.231.152.168]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 93584F80121 for ; Wed, 9 Nov 2022 17:53:36 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 93584F80121 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="bsRyIi+O" Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 2A9EDNjQ027347; Wed, 9 Nov 2022 10:53:34 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=fN0zW4Rl3sUJbDXHFHW7XlWiF0bZFHQc8IMVz5ltwaQ=; b=bsRyIi+Oo0NcYr6kexzqQk1ecx8P7He+JFIeKDyUIixgLgmMXeZV06mWAfwf5VAzESKy aqvRTeWngdZ9jIIEKZoD8vLcZBwcAqkQHd1PJ1BM8DuWgmvGp5JHMZEXLsDczXMgUU5s UJw3ycStSlV6a8KqOMuTf855EZL4NJhZluVVwKWRs3z/18QyGwZK3Wgja5QhSAoeuD5Z zHR1Qj0JE0eCUp5YGrPpyv/+xOfoWLN3iiA+cfRFYK3CQGdb3jRkMrhYWwdzBGtwmPm4 nQdpGUg5TtoPfI3df4E37neydloMo/S0vPnkoVBLevkC0iATLnlWuPVpEnAyiDx+lUtI rw== Received: from ediex02.ad.cirrus.com ([84.19.233.68]) by mx0b-001ae601.pphosted.com (PPS) with ESMTPS id 3knm8pde7v-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Nov 2022 10:53:34 -0600 Received: from ediex01.ad.cirrus.com (198.61.84.80) by ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.15; Wed, 9 Nov 2022 10:53:32 -0600 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by ediex01.ad.cirrus.com (198.61.84.80) with Microsoft SMTP Server id 15.2.1118.15 via Frontend Transport; Wed, 9 Nov 2022 10:53:32 -0600 Received: from debianA11184.ad.cirrus.com (AUSNPC0LSNW1.ad.cirrus.com [198.61.65.92]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id 1DD87B06; Wed, 9 Nov 2022 16:53:32 +0000 (UTC) From: Richard Fitzgerald To: , , , , , , Subject: [PATCH 01/12] dt-bindings: mfd: Add Cirrus Logic CS48L32 audio codec Date: Wed, 9 Nov 2022 16:53:20 +0000 Message-ID: <20221109165331.29332-2-rf@opensource.cirrus.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221109165331.29332-1-rf@opensource.cirrus.com> References: <20221109165331.29332-1-rf@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: 1tRRsN5me2kxuKVQN6G2wOaLmMkAzOi0 X-Proofpoint-GUID: 1tRRsN5me2kxuKVQN6G2wOaLmMkAzOi0 X-Proofpoint-Spam-Reason: safe Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, patches@opensource.cirrus.com, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Richard Fitzgerald 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" The CS48L32 has multiple digital and analog audio I/O, a high-performance low-power programmable audio DSP, and a variety of power-efficient fixed-function audio processors, with digital mixing and routing. Signed-off-by: Richard Fitzgerald --- .../bindings/mfd/cirrus,cs48l32.yaml | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/cirrus,cs48l32.yaml diff --git a/Documentation/devicetree/bindings/mfd/cirrus,cs48l32.yaml b/Documentation/devicetree/bindings/mfd/cirrus,cs48l32.yaml new file mode 100644 index 000000000000..d128600c0b72 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/cirrus,cs48l32.yaml @@ -0,0 +1,166 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/cirrus,cs48l32.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS48L32 audio CODECs Multi-Functional Device + +maintainers: + - patches@opensource.cirrus.com + +description: | + The CS48L32 is an audio SoC with extensive digital capabilities + and a range of digital and analogue I/O. + + See also the child driver bindings in: + + bindings/pinctrl/cirrus,cs48l32.yaml + bindings/regulator/wlf,arizona.yaml + bindings/sound/cirrus,cs48l32.yaml + +allOf: + - $ref: /schemas/pinctrl/cirrus,cs48l32.yaml# + - $ref: /schemas/regulator/wlf,arizona.yaml# + - $ref: /schemas/sound/cirrus,cs48l32.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + enum: + - cirrus,cs48l31 + - cirrus,cs48l32 + - cirrus,cs48l33 + + reg: + maxItems: 1 + + gpio-controller: true + + '#gpio-cells': + description: + The first cell is the pin number. The second cell is reserved for + future use and must be zero + const: 2 + + interrupt-controller: true + + '#interrupt-cells': + description: + The first cell is the IRQ number. + The second cell is the flags, encoded as the trigger masks from + bindings/interrupt-controller/interrupts.txt + const: 2 + + interrupts: + maxItems: 1 + + reset-gpios: + description: + One entry specifying the GPIO controlling /RESET. As defined in + bindings/gpio.txt. Although optional, it is strongly recommended + to use a hardware reset. + maxItems: 1 + + clocks: + description: + Should reference the clocks supplied on MCLK1 + minItems: 1 + maxItems: 1 + + clock-names: + description: | + Must be "mclk1" + const: mclk1 + + VDD_A-supply: + description: + Analogue power supply. + + VDD_D-supply: + description: + Digital core power supply. + + VDD_IO-supply: + description: + Digital buffer (I/O) supply. + + VDD_CP-supply: + description: + Charge pump power supply. + + VOUT_MIC-supply: + description: + Microphone power supply, normally supplied internally. + + pinctrl-0: + description: + A phandle to the node containing the subnodes containing pinctrl + configurations. + + pinctrl-1: + description: + A phandle to the node containing the subnodes containing pinctrl + configurations. + + pinctrl-names: + items: + - const: init + - const: default + +required: + - compatible + - interrupt-parent + - interrupts + - VDD_A-supply + - VDD_D-supply + - VDD_IO-supply + - VDD_CP-supply + +unevaluatedProperties: false + +examples: + - | + #include + &spi1 { + cs48l32: cs48l32@1 { + compatible = "cirrus,cs48l32"; + reg = <0x1>; + + spi-max-frequency = <25000000>; + + interrupts = <56 8>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio0>; + gpio-controller; + #gpio-cells = <2>; + #sound-dai-cells = <1>; + + VDD_A-supply = <®ulator_1v8>; + VDD_D-supply = <&lochnagar_1v2>; + VDD_IO-supply = <®ulator_1v8>; + VDD_CP-supply = <®ulator_1v8>; + + reset-gpios = <&gpio 0 0>; + + pinctrl-names = "default"; + pinctrl-0 = <&cs48l32_defaults>; + + cs48l32_pinctrl: pinctrl { + compatible = "cirrus,cs48l32-pinctrl"; + + cs48l32_defaults: defaults { + asp1 { + groups = "asp1"; + function = "asp1"; + bias-bus-hold; + }; + asp2 { + groups = "asp2"; + function = "asp2"; + bias-bus-hold; + }; + }; + }; + }; From patchwork Wed Nov 9 16:53:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 622951 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 21630C4332F for ; Wed, 9 Nov 2022 16:55:09 +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 167B615E5; Wed, 9 Nov 2022 17:54:17 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 167B615E5 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1668012907; bh=1dKSGKexcL2JYzyGvp4SWQwCBQr6QtFzVP0XiGJSNCg=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=rb0kWB4B3LoGPX57nBAuhsM6rEP1UgvJ3HUQIxJ6zg5OExH8nP4xt46GO3Dysc2Ov 9GtEOVmPJ8fqnBf7Yn/XtuIt33Pm1BjsH5xxAZJ2CnsztQ9YaHp9yq5A0HIU3VZZMT Zwp/XH/4S0aJ7zBtcvccVYu1/Uw47NiRvE/lL0Ks= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 7687EF8056F; Wed, 9 Nov 2022 17:53:48 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 3D518F80570; Wed, 9 Nov 2022 17:53:46 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0b-001ae601.pphosted.com [67.231.152.168]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 019F1F80166 for ; Wed, 9 Nov 2022 17:53:36 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 019F1F80166 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="nPmhvIaX" Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 2A9EDNjR027347; Wed, 9 Nov 2022 10:53:35 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=lxCGl1a6zaCL7VKNPPTYmoyWBCI5ub/E2lqYjtmMCeY=; b=nPmhvIaX5vb/SXUEP0/QKw1SplUStzlRNznXRR9axJD7/FgN3UXHjOXzmcdbYxxqkdXz XR7NNMWiXXmvXQsC21o3WBCWNtZjS6al6yhTbSXfcA+sK/rWcOIwRRFmP+nmTEj+yQqo v69WpIfipKrL07h3a+f+8z+iZ/LOIbXJ0S98j2vEiC02KA1ZnThjX6DeIawJjGKUKtdm ExTInYMOgSgkWXrCMYNPY+uOHEVnXeRUoO9a+mMDWt5q2l8v6xb/msW7RXY8tMILDKKt yYVotSYzum2A9kA1CJa7QPRL0MYy46PHKa+hwFl+6ueTWKBqn50muA60W6RuvlSbZ21p RQ== Received: from ediex02.ad.cirrus.com ([84.19.233.68]) by mx0b-001ae601.pphosted.com (PPS) with ESMTPS id 3knm8pde7v-3 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Nov 2022 10:53:35 -0600 Received: from ediex01.ad.cirrus.com (198.61.84.80) by ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.15; Wed, 9 Nov 2022 10:53:33 -0600 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by ediex01.ad.cirrus.com (198.61.84.80) with Microsoft SMTP Server id 15.2.1118.15 via Frontend Transport; Wed, 9 Nov 2022 10:53:33 -0600 Received: from debianA11184.ad.cirrus.com (AUSNPC0LSNW1.ad.cirrus.com [198.61.65.92]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id AA088468; Wed, 9 Nov 2022 16:53:32 +0000 (UTC) From: Richard Fitzgerald To: , , , , , , Subject: [PATCH 02/12] mfd: cs48l32: Add register definitions for Cirrus Logic CS48L31/32/33 Date: Wed, 9 Nov 2022 16:53:21 +0000 Message-ID: <20221109165331.29332-3-rf@opensource.cirrus.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221109165331.29332-1-rf@opensource.cirrus.com> References: <20221109165331.29332-1-rf@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: wLw80btBBqjG-oO8VRAQdprxYLo4Ym5D X-Proofpoint-GUID: wLw80btBBqjG-oO8VRAQdprxYLo4Ym5D X-Proofpoint-Spam-Reason: safe Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, patches@opensource.cirrus.com, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Richard Fitzgerald 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" This adds the register definitions for the CS48L31/32/33. The CS48L31/32/33 audio codecs are multi-function devices containing gpios, irq controller and regulators in addition to the core audio functionality. Signed-off-by: Piotr Stankiewicz Signed-off-by: Stuart Henderson Signed-off-by: Richard Fitzgerald --- include/linux/mfd/cs48l32/registers.h | 509 ++++++++++++++++++++++++++ 1 file changed, 509 insertions(+) create mode 100644 include/linux/mfd/cs48l32/registers.h diff --git a/include/linux/mfd/cs48l32/registers.h b/include/linux/mfd/cs48l32/registers.h new file mode 100644 index 000000000000..b8125950d74c --- /dev/null +++ b/include/linux/mfd/cs48l32/registers.h @@ -0,0 +1,509 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Register definitions for Cirrus Logic CS48L32 + * + * Copyright (C) 2017-2018, 2020, 2022 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef CS48L32_REGISTERS_H +#define CS48L32_REGISTERS_H + +/* Register Addresses. */ +#define CS48L32_DEVID 0x0 +#define CS48L32_REVID 0x4 +#define CS48L32_OTPID 0x10 +#define CS48L32_SFT_RESET 0x20 +#define CS48L32_CTRL_IF_DEBUG3 0xA8 +#define CS48L32_MCU_CTRL1 0x804 +#define CS48L32_GPIO1_CTRL1 0xc08 +#define CS48L32_GPIO16_CTRL1 0xc44 +#define CS48L32_OUTPUT_SYS_CLK 0x1020 +#define CS48L32_AUXPDM_CTRL 0x1044 +#define CS48L32_AUXPDM_CTRL2 0x105c +#define CS48L32_CLOCK32K 0x1400 +#define CS48L32_SYSTEM_CLOCK1 0x1404 +#define CS48L32_SYSTEM_CLOCK2 0x1408 +#define CS48L32_SAMPLE_RATE1 0x1420 +#define CS48L32_SAMPLE_RATE2 0x1424 +#define CS48L32_SAMPLE_RATE3 0x1428 +#define CS48L32_SAMPLE_RATE4 0x142c +#define CS48L32_DSP_CLOCK1 0x1510 +#define CS48L32_FLL1_CONTROL1 0x1c00 +#define CS48L32_FLL1_CONTROL5 0x1c10 +#define CS48L32_FLL1_CONTROL6 0x1c14 +#define CS48L32_FLL1_GPIO_CLOCK 0x1ca0 +#define CS48L32_CHARGE_PUMP1 0x2000 +#define CS48L32_LDO2_CTRL1 0x2408 +#define CS48L32_MICBIAS_CTRL1 0x2410 +#define CS48L32_MICBIAS_CTRL5 0x2418 +#define CS48L32_IRQ1_CTRL_AOD 0x2710 +#define CS48L32_AOD_PAD_CTRL 0x2718 +#define CS48L32_INPUT_CONTROL 0x4000 +#define CS48L32_INPUT_STATUS 0x4004 +#define CS48L32_INPUT_RATE_CONTROL 0x4008 +#define CS48L32_INPUT_CONTROL2 0x400c +#define CS48L32_INPUT_CONTROL3 0x4014 +#define CS48L32_INPUT1_CONTROL1 0x4020 +#define CS48L32_IN1L_CONTROL1 0x4024 +#define CS48L32_IN1L_CONTROL2 0x4028 +#define CS48L32_IN1R_CONTROL1 0x4044 +#define CS48L32_IN1R_CONTROL2 0x4048 +#define CS48L32_INPUT2_CONTROL1 0x4060 +#define CS48L32_IN2L_CONTROL1 0x4064 +#define CS48L32_IN2L_CONTROL2 0x4068 +#define CS48L32_IN2R_CONTROL1 0x4084 +#define CS48L32_IN2R_CONTROL2 0x4088 +#define CS48L32_INPUT_HPF_CONTROL 0x4244 +#define CS48L32_INPUT_VOL_CONTROL 0x4248 +#define CS48L32_AUXPDM_CONTROL1 0x4300 +#define CS48L32_AUXPDM_CONTROL2 0x4304 +#define CS48L32_AUXPDM1_CONTROL1 0x4308 +#define CS48L32_AUXPDM2_CONTROL1 0x4310 +#define CS48L32_ADC1L_ANA_CONTROL1 0x4688 +#define CS48L32_ADC1R_ANA_CONTROL1 0x468c +#define CS48L32_ASP1_ENABLES1 0x6000 +#define CS48L32_ASP1_DATA_CONTROL5 0x6040 +#define CS48L32_ASP2_ENABLES1 0x6080 +#define CS48L32_ASP2_DATA_CONTROL5 0x60c0 +#define CS48L32_ASP1TX1_INPUT1 0x8200 +#define CS48L32_ASP1TX2_INPUT1 0x8210 +#define CS48L32_ASP1TX3_INPUT1 0x8220 +#define CS48L32_ASP1TX4_INPUT1 0x8230 +#define CS48L32_ASP1TX5_INPUT1 0x8240 +#define CS48L32_ASP1TX6_INPUT1 0x8250 +#define CS48L32_ASP1TX7_INPUT1 0x8260 +#define CS48L32_ASP1TX8_INPUT1 0x8270 +#define CS48L32_ASP1TX8_INPUT4 0x827c +#define CS48L32_ASP2TX1_INPUT1 0x8300 +#define CS48L32_ASP2TX2_INPUT1 0x8310 +#define CS48L32_ASP2TX3_INPUT1 0x8320 +#define CS48L32_ASP2TX4_INPUT1 0x8330 +#define CS48L32_ASP2TX4_INPUT4 0x833c +#define CS48L32_ISRC1INT1_INPUT1 0x8980 +#define CS48L32_ISRC1INT2_INPUT1 0x8990 +#define CS48L32_ISRC1INT3_INPUT1 0x89a0 +#define CS48L32_ISRC1INT4_INPUT1 0x89b0 +#define CS48L32_ISRC1DEC1_INPUT1 0x89c0 +#define CS48L32_ISRC1DEC2_INPUT1 0x89d0 +#define CS48L32_ISRC1DEC3_INPUT1 0x89e0 +#define CS48L32_ISRC1DEC4_INPUT1 0x89f0 +#define CS48L32_ISRC2INT1_INPUT1 0x8a00 +#define CS48L32_ISRC2INT2_INPUT1 0x8a10 +#define CS48L32_ISRC2DEC1_INPUT1 0x8a40 +#define CS48L32_ISRC2DEC2_INPUT1 0x8a50 +#define CS48L32_ISRC3INT1_INPUT1 0x8a80 +#define CS48L32_ISRC3INT2_INPUT1 0x8a90 +#define CS48L32_ISRC3DEC1_INPUT1 0x8ac0 +#define CS48L32_ISRC3DEC2_INPUT1 0x8ad0 +#define CS48L32_EQ1_INPUT1 0x8b80 +#define CS48L32_EQ2_INPUT1 0x8b90 +#define CS48L32_EQ3_INPUT1 0x8ba0 +#define CS48L32_EQ4_INPUT1 0x8bb0 +#define CS48L32_EQ4_INPUT4 0x8bbc +#define CS48L32_DRC1L_INPUT1 0x8c00 +#define CS48L32_DRC1R_INPUT1 0x8c10 +#define CS48L32_DRC1R_INPUT4 0x8c1c +#define CS48L32_DRC2L_INPUT1 0x8c20 +#define CS48L32_DRC2R_INPUT1 0x8c30 +#define CS48L32_DRC2R_INPUT4 0x8c3c +#define CS48L32_LHPF1_INPUT1 0x8c80 +#define CS48L32_LHPF1_INPUT4 0x8c8c +#define CS48L32_LHPF2_INPUT1 0x8c90 +#define CS48L32_LHPF2_INPUT4 0x8c9c +#define CS48L32_LHPF3_INPUT1 0x8ca0 +#define CS48L32_LHPF3_INPUT4 0x8cac +#define CS48L32_LHPF4_INPUT1 0x8cb0 +#define CS48L32_LHPF4_INPUT4 0x8cbc +#define CS48L32_DSP1RX1_INPUT1 0x9000 +#define CS48L32_DSP1RX2_INPUT1 0x9010 +#define CS48L32_DSP1RX3_INPUT1 0x9020 +#define CS48L32_DSP1RX4_INPUT1 0x9030 +#define CS48L32_DSP1RX5_INPUT1 0x9040 +#define CS48L32_DSP1RX6_INPUT1 0x9050 +#define CS48L32_DSP1RX7_INPUT1 0x9060 +#define CS48L32_DSP1RX8_INPUT1 0x9070 +#define CS48L32_DSP1RX8_INPUT4 0x907c +#define CS48L32_ISRC1_CONTROL1 0xa400 +#define CS48L32_ISRC1_CONTROL2 0xa404 +#define CS48L32_ISRC2_CONTROL1 0xa510 +#define CS48L32_ISRC2_CONTROL2 0xa514 +#define CS48L32_ISRC3_CONTROL1 0xa620 +#define CS48L32_ISRC3_CONTROL2 0xa624 +#define CS48L32_FX_SAMPLE_RATE 0xa800 +#define CS48L32_EQ_CONTROL1 0xa808 +#define CS48L32_EQ_CONTROL2 0xa80c +#define CS48L32_EQ1_GAIN1 0xa810 +#define CS48L32_EQ1_GAIN2 0xa814 +#define CS48L32_EQ1_BAND1_COEFF1 0xa818 +#define CS48L32_EQ1_BAND1_COEFF2 0xa81c +#define CS48L32_EQ1_BAND1_PG 0xa820 +#define CS48L32_EQ1_BAND2_COEFF1 0xa824 +#define CS48L32_EQ1_BAND2_COEFF2 0xa828 +#define CS48L32_EQ1_BAND2_PG 0xa82c +#define CS48L32_EQ1_BAND3_COEFF1 0xa830 +#define CS48L32_EQ1_BAND3_COEFF2 0xa834 +#define CS48L32_EQ1_BAND3_PG 0xa838 +#define CS48L32_EQ1_BAND4_COEFF1 0xa83c +#define CS48L32_EQ1_BAND4_COEFF2 0xa840 +#define CS48L32_EQ1_BAND4_PG 0xa844 +#define CS48L32_EQ1_BAND5_COEFF1 0xa848 +#define CS48L32_EQ1_BAND5_PG 0xa850 +#define CS48L32_EQ2_GAIN1 0xa854 +#define CS48L32_EQ2_GAIN2 0xa858 +#define CS48L32_EQ2_BAND1_COEFF1 0xa85c +#define CS48L32_EQ2_BAND1_COEFF2 0xa860 +#define CS48L32_EQ2_BAND1_PG 0xa864 +#define CS48L32_EQ2_BAND2_COEFF1 0xa868 +#define CS48L32_EQ2_BAND2_COEFF2 0xa86c +#define CS48L32_EQ2_BAND2_PG 0xa870 +#define CS48L32_EQ2_BAND3_COEFF1 0xa874 +#define CS48L32_EQ2_BAND3_COEFF2 0xa878 +#define CS48L32_EQ2_BAND3_PG 0xa87c +#define CS48L32_EQ2_BAND4_COEFF1 0xa880 +#define CS48L32_EQ2_BAND4_COEFF2 0xa884 +#define CS48L32_EQ2_BAND4_PG 0xa888 +#define CS48L32_EQ2_BAND5_COEFF1 0xa88c +#define CS48L32_EQ2_BAND5_PG 0xa894 +#define CS48L32_EQ3_GAIN1 0xa898 +#define CS48L32_EQ3_GAIN2 0xa89c +#define CS48L32_EQ3_BAND1_COEFF1 0xa8a0 +#define CS48L32_EQ3_BAND1_COEFF2 0xa8a4 +#define CS48L32_EQ3_BAND1_PG 0xa8a8 +#define CS48L32_EQ3_BAND2_COEFF1 0xa8ac +#define CS48L32_EQ3_BAND2_COEFF2 0xa8b0 +#define CS48L32_EQ3_BAND2_PG 0xa8b4 +#define CS48L32_EQ3_BAND3_COEFF1 0xa8b8 +#define CS48L32_EQ3_BAND3_COEFF2 0xa8bc +#define CS48L32_EQ3_BAND3_PG 0xa8c0 +#define CS48L32_EQ3_BAND4_COEFF1 0xa8c4 +#define CS48L32_EQ3_BAND4_COEFF2 0xa8c8 +#define CS48L32_EQ3_BAND4_PG 0xa8cc +#define CS48L32_EQ3_BAND5_COEFF1 0xa8d0 +#define CS48L32_EQ3_BAND5_PG 0xa8d8 +#define CS48L32_EQ4_GAIN1 0xa8dc +#define CS48L32_EQ4_GAIN2 0xa8e0 +#define CS48L32_EQ4_BAND1_COEFF1 0xa8e4 +#define CS48L32_EQ4_BAND1_COEFF2 0xa8e8 +#define CS48L32_EQ4_BAND1_PG 0xa8ec +#define CS48L32_EQ4_BAND2_COEFF1 0xa8f0 +#define CS48L32_EQ4_BAND2_COEFF2 0xa8f4 +#define CS48L32_EQ4_BAND2_PG 0xa8f8 +#define CS48L32_EQ4_BAND3_COEFF1 0xa8fc +#define CS48L32_EQ4_BAND3_COEFF2 0xa900 +#define CS48L32_EQ4_BAND3_PG 0xa904 +#define CS48L32_EQ4_BAND4_COEFF1 0xa908 +#define CS48L32_EQ4_BAND4_COEFF2 0xa90c +#define CS48L32_EQ4_BAND4_PG 0xa910 +#define CS48L32_EQ4_BAND5_COEFF1 0xa914 +#define CS48L32_EQ4_BAND5_PG 0xa91c +#define CS48L32_LHPF_CONTROL1 0xaa30 +#define CS48L32_LHPF_CONTROL2 0xaa34 +#define CS48L32_LHPF1_COEFF 0xaa38 +#define CS48L32_LHPF2_COEFF 0xaa3c +#define CS48L32_LHPF3_COEFF 0xaa40 +#define CS48L32_LHPF4_COEFF 0xaa44 +#define CS48L32_DRC1_CONTROL1 0xab00 +#define CS48L32_DRC1_CONTROL4 0xab0c +#define CS48L32_DRC2_CONTROL1 0xab14 +#define CS48L32_DRC2_CONTROL4 0xab20 +#define CS48L32_TONE_GENERATOR1 0xb000 +#define CS48L32_TONE_GENERATOR2 0xb004 +#define CS48L32_COMFORT_NOISE_GENERATOR 0xb400 +#define CS48L32_US_CONTROL 0xb800 +#define CS48L32_US1_CONTROL 0xb804 +#define CS48L32_US1_DET_CONTROL 0xb808 +#define CS48L32_US2_CONTROL 0xb814 +#define CS48L32_US2_DET_CONTROL 0xb818 +#define CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 0x1700c +#define CS48L32_DSP1_XM_SRAM_IBUS_SETUP_1 0x17010 +#define CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24 0x1706c +#define CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 0x17070 +#define CS48L32_DSP1_YM_SRAM_IBUS_SETUP_1 0x17074 +#define CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8 0x17090 +#define CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 0x17094 +#define CS48L32_DSP1_PM_SRAM_IBUS_SETUP_1 0x17098 +#define CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7 0x170b0 +#define CS48L32_IRQ1_STATUS 0x18004 +#define CS48L32_IRQ1_EINT_1 0x18010 +#define CS48L32_IRQ1_EINT_2 0x18014 +#define CS48L32_IRQ1_EINT_5 0x18020 +#define CS48L32_IRQ1_EINT_6 0x18024 +#define CS48L32_IRQ1_EINT_7 0x18028 +#define CS48L32_IRQ1_EINT_9 0x18030 +#define CS48L32_IRQ1_EINT_11 0x18038 +#define CS48L32_IRQ1_STS_1 0x18090 +#define CS48L32_IRQ1_STS_6 0x180a4 +#define CS48L32_IRQ1_STS_11 0x180b8 +#define CS48L32_IRQ1_MASK_1 0x18110 +#define CS48L32_IRQ1_MASK_11 0x18138 +#define CS48L32_DSP1_XMEM_PACKED_0 0x2000000 +#define CS48L32_DSP1_XMEM_PACKED_147455 0x208fffc +#define CS48L32_DSP1_SYS_INFO_ID 0x25e0000 +#define CS48L32_DSP1_AHBM_WINDOW_DEBUG_1 0x25e2044 +#define CS48L32_DSP1_XMEM_UNPACKED24_0 0x2800000 +#define CS48L32_DSP1_XMEM_UNPACKED24_196607 0x28bfffc +#define CS48L32_DSP1_CLOCK_FREQ 0x2b80000 +#define CS48L32_DSP1_SAMPLE_RATE_TX8 0x2b802b8 +#define CS48L32_DSP1_SCRATCH1 0x2b805c0 +#define CS48L32_DSP1_SCRATCH4 0x2b805d8 +#define CS48L32_DSP1_CCM_CORE_CONTROL 0x2bc1000 +#define CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1 0x2bc5a00 +#define CS48L32_DSP1_YMEM_PACKED_0 0x2c00000 +#define CS48L32_DSP1_YMEM_PACKED_49151 0x2c2fffc +#define CS48L32_DSP1_YMEM_UNPACKED24_0 0x3400000 +#define CS48L32_DSP1_YMEM_UNPACKED24_65535 0x343fffc +#define CS48L32_DSP1_PMEM_0 0x3800000 +#define CS48L32_DSP1_PMEM_71679 0x3845ffc + +/* (0x0) DEVID */ +#define CS48L32_DEVID_MASK 0x00ffffff +#define CS48L32_DEVID_SHIFT 0 + +/* (0x4) REVID */ +#define CS48L32_AREVID_MASK 0x000000f0 +#define CS48L32_AREVID_SHIFT 4 +#define CS48L32_MTLREVID_MASK 0x0000000f +#define CS48L32_MTLREVID_SHIFT 0 + +/* (0x10) OTPID */ +#define CS48L32_OTPID_MASK 0x0000000f + +/* (0x0804) MCU_CTRL1 */ +#define CS48L32_MCU_STS_MASK 0x0000ff00 +#define CS48L32_MCU_STS_SHIFT 8 + +/* (0x1020) OUTPUT_SYS_CLK */ +#define CS48L32_OPCLK_EN_SHIFT 15 +#define CS48L32_OPCLK_DIV_MASK 0x000000f8 +#define CS48L32_OPCLK_DIV_SHIFT 3 +#define CS48L32_OPCLK_SEL_MASK 0x00000007 + +/* (0x105c) AUXPDM_CTRL2 */ +#define CS48L32_AUXPDMDAT2_SRC_SHIFT 4 +#define CS48L32_AUXPDMDAT1_SRC_SHIFT 0 + +/* (0x1400) CLOCK32K */ +#define CS48L32_CLK_32K_EN_MASK 0x00000040 +#define CS48L32_CLK_32K_SRC_MASK 0x00000003 + +/* (0x1404) SYSTEM_CLOCK1 */ +#define CS48L32_SYSCLK_FRAC_MASK 0x00008000 +#define CS48L32_SYSCLK_FREQ_MASK 0x00000700 +#define CS48L32_SYSCLK_FREQ_SHIFT 8 +#define CS48L32_SYSCLK_EN_SHIFT 6 +#define CS48L32_SYSCLK_SRC_MASK 0x0000001f +#define CS48L32_SYSCLK_SRC_SHIFT 0 + +/* (0x1408) SYSTEM_CLOCK2 */ +#define CS48L32_SYSCLK_FREQ_STS_MASK 0x00000700 +#define CS48L32_SYSCLK_FREQ_STS_SHIFT 8 + +/* (0x1420) SAMPLE_RATE1 */ +#define CS48L32_SAMPLE_RATE_1_MASK 0x0000001f +#define CS48L32_SAMPLE_RATE_1_SHIFT 0 + +/* (0x1510) DSP_CLOCK1 */ +#define CS48L32_DSP_CLK_FREQ_MASK 0xffff0000 +#define CS48L32_DSP_CLK_FREQ_SHIFT 16 + +/* (0x1c00) FLL_CONTROL1 */ +#define CS48L32_FLL_CTRL_UPD_MASK 0x00000004 +#define CS48L32_FLL_HOLD_MASK 0x00000002 +#define CS48L32_FLL_EN_MASK 0x00000001 + +/* (0x1c04) FLL_CONTROL2 */ +#define CS48L32_FLL_LOCKDET_THR_MASK 0xf0000000 +#define CS48L32_FLL_LOCKDET_THR_SHIFT 28 +#define CS48L32_FLL_LOCKDET_MASK 0x08000000 +#define CS48L32_FLL_PHASEDET_MASK 0x00400000 +#define CS48L32_FLL_PHASEDET_SHIFT 22 +#define CS48L32_FLL_REFCLK_DIV_MASK 0x00030000 +#define CS48L32_FLL_REFCLK_DIV_SHIFT 16 +#define CS48L32_FLL_REFCLK_SRC_MASK 0x0000f000 +#define CS48L32_FLL_REFCLK_SRC_SHIFT 12 +#define CS48L32_FLL_N_MASK 0x000003ff +#define CS48L32_FLL_N_SHIFT 0 + +/* (0x1c08) FLL_CONTROL3 */ +#define CS48L32_FLL_LAMBDA_MASK 0xffff0000 +#define CS48L32_FLL_LAMBDA_SHIFT 16 +#define CS48L32_FLL_THETA_MASK 0x0000ffff +#define CS48L32_FLL_THETA_SHIFT 0 + +/* (0x1c0c) FLL_CONTROL4 */ +#define CS48L32_FLL_FD_GAIN_COARSE_SHIFT 16 +#define CS48L32_FLL_HP_MASK 0x00003000 +#define CS48L32_FLL_HP_SHIFT 12 +#define CS48L32_FLL_FB_DIV_MASK 0x000003ff +#define CS48L32_FLL_FB_DIV_SHIFT 0 + +/* (0x1c10) FLL_CONTROL5 */ +#define CS48L32_FLL_FRC_INTEG_UPD_MASK 0x00008000 + +/* (0x2000) CHARGE_PUMP1 */ +#define CS48L32_CP2_BYPASS_MASK 0x00000002 +#define CS48L32_CP2_EN_MASK 0x00000001 + +/* (0x2408) LDO2_CTRL1 */ +#define CS48L32_LDO2_VSEL_MASK 0x000007e0 + +/* (0x2410) MICBIAS_CTRL1 */ +#define CS48L32_MICB1_EN_SHIFT 0 + +/* (0x2418) MICBIAS_CTRL5 */ +#define CS48L32_MICB1C_EN_SHIFT 8 +#define CS48L32_MICB1B_EN_SHIFT 4 +#define CS48L32_MICB1A_EN_SHIFT 0 + +/* (0x2710) IRQ1_CTRL_AOD */ +#define CS48L32_IRQ_POL_MASK 0x00000400 + +/* (0x4000) INPUT_CONTROL */ +#define CS48L32_IN2L_EN_SHIFT 3 +#define CS48L32_IN2R_EN_SHIFT 2 +#define CS48L32_IN1L_EN_SHIFT 1 +#define CS48L32_IN1R_EN_SHIFT 0 + +/* (0x400c) INPUT_CONTROL2 */ +#define CS48L32_PDM_FLLCLK_SRC_MASK 0x0000000f +#define CS48L32_PDM_FLLCLK_SRC_SHIFT 0 + +/* (0x4014) INPUT_CONTROL3 */ +#define CS48L32_IN_VU 0x20000000 +#define CS48L32_IN_VU_MASK 0x20000000 +#define CS48L32_IN_VU_SHIFT 29 +#define CS48L32_IN_VU_WIDTH 1 + +/* (0x4020) INPUT1_CONTROL1 */ +#define CS48L32_IN1_OSR_SHIFT 16 +#define CS48L32_IN1_PDM_SUP_MASK 0x00000300 +#define CS48L32_IN1_PDM_SUP_SHIFT 8 +#define CS48L32_IN1_MODE_SHIFT 0 + +/* + * (0x4024) IN1L_CONTROL1 + * (0x4044) IN1R_CONTROL1 + */ +#define CS48L32_INx_SRC_MASK 0x30000000 +#define CS48L32_INx_SRC_SHIFT 28 +#define CS48L32_INx_RATE_MASK 0x0000f800 +#define CS48L32_INx_RATE_SHIFT 11 +#define CS48L32_INx_HPF_SHIFT 2 +#define CS48L32_INx_LP_MODE_SHIFT 0 + +/* + * (0x4028) IN1L_CONTROL2 + * (0x4048) IN1R_CONTROL2 + */ +#define CS48L32_INx_MUTE_MASK 0x10000000 +#define CS48L32_INx_VOL_SHIFT 16 +#define CS48L32_INx_PGA_VOL_SHIFT 1 + +/* (0x4244) INPUT_HPF_CONTROL */ +#define CS48L32_IN_HPF_CUT_SHIFT 0 + +/* (0x4248) INPUT_VOL_CONTROL */ +#define CS48L32_IN_VD_RAMP_SHIFT 4 +#define CS48L32_IN_VI_RAMP_SHIFT 0 + +/* (0x4308) AUXPDM1_CONTROL1 */ +#define CS48L32_AUXPDM1_FREQ_SHIFT 16 +#define CS48L32_AUXPDM1_SRC_MASK 0x00000f00 +#define CS48L32_AUXPDM1_SRC_SHIFT 8 + +/* (0x4688) ADC1L_ANA_CONTROL1 */ +/* (0x468c) ADC1R_ANA_CONTROL1 */ +#define CS48L32_ADC1x_INT_ENA_FRC_MASK 0x00000002 + +/* (0x6004) ASPn_CONTROL1 */ +#define CS48L32_ASP_RATE_MASK 0x00001f00 +#define CS48L32_ASP_RATE_SHIFT 8 +#define CS48L32_ASP_BCLK_FREQ_MASK 0x0000003f + +/* (0x6008) ASPn_CONTROL2 */ +#define CS48L32_ASP_RX_WIDTH_MASK 0xff000000 +#define CS48L32_ASP_RX_WIDTH_SHIFT 24 +#define CS48L32_ASP_TX_WIDTH_MASK 0x00ff0000 +#define CS48L32_ASP_TX_WIDTH_SHIFT 16 +#define CS48L32_ASP_FMT_MASK 0x00000700 +#define CS48L32_ASP_FMT_SHIFT 8 +#define CS48L32_ASP_BCLK_INV_MASK 0x00000040 +#define CS48L32_ASP_BCLK_MSTR_MASK 0x00000010 +#define CS48L32_ASP_FSYNC_INV_MASK 0x00000004 +#define CS48L32_ASP_FSYNC_MSTR_MASK 0x00000001 + +/* (0x6030) ASPn_DATA_CONTROL1 */ +#define CS48L32_ASP_TX_WL_MASK 0x0000003f + +/* (0x6040) ASPn_DATA_CONTROL5 */ +#define CS48L32_ASP_RX_WL_MASK 0x0000003f + +/* (0x82xx - 0x90xx) *_INPUT[1-4] */ +#define CS48L32_MIXER_VOL_MASK 0x00FE0000 +#define CS48L32_MIXER_VOL_SHIFT 17 +#define CS48L32_MIXER_VOL_WIDTH 7 +#define CS48L32_MIXER_SRC_MASK 0x000001ff +#define CS48L32_MIXER_SRC_SHIFT 0 +#define CS48L32_MIXER_SRC_WIDTH 9 + +/* (0xa400) ISRC1_CONTROL1 */ +#define CS48L32_ISRC1_FSL_MASK 0xf8000000 +#define CS48L32_ISRC1_FSL_SHIFT 27 +#define CS48L32_ISRC1_FSH_MASK 0x0000f800 +#define CS48L32_ISRC1_FSH_SHIFT 11 + +/* (0xa404) ISRC1_CONTROL2 */ +#define CS48L32_ISRC1_INT4_EN_SHIFT 11 +#define CS48L32_ISRC1_INT3_EN_SHIFT 10 +#define CS48L32_ISRC1_INT2_EN_SHIFT 9 +#define CS48L32_ISRC1_INT1_EN_SHIFT 8 +#define CS48L32_ISRC1_DEC4_EN_SHIFT 3 +#define CS48L32_ISRC1_DEC3_EN_SHIFT 2 +#define CS48L32_ISRC1_DEC2_EN_SHIFT 1 +#define CS48L32_ISRC1_DEC1_EN_SHIFT 0 + +/* (0xa800) FX_SAMPLE_RATE */ +#define CS48L32_FX_RATE_MASK 0x0000f800 +#define CS48L32_FX_RATE_SHIFT 11 + +/* (0xab00) DRC1_CONTROL1 */ +#define CS48L32_DRC1L_EN_SHIFT 1 +#define CS48L32_DRC1R_EN_SHIFT 0 + +/* (0xb400) Comfort_Noise_Generator */ +#define CS48L32_NOISE_GEN_RATE_MASK 0x0000f800 +#define CS48L32_NOISE_GEN_RATE_SHIFT 11 +#define CS48L32_NOISE_GEN_EN_SHIFT 5 +#define CS48L32_NOISE_GEN_GAIN_SHIFT 0 + +/* (0xb800) US_CONTROL */ +#define CS48L32_US1_DET_EN_SHIFT 8 + +/* (0xb804) US1_CONTROL */ +#define CS48L32_US1_RATE_MASK 0xf8000000 +#define CS48L32_US1_RATE_SHIFT 27 +#define CS48L32_US1_GAIN_SHIFT 12 +#define CS48L32_US1_SRC_MASK 0x00000f00 +#define CS48L32_US1_SRC_SHIFT 8 +#define CS48L32_US1_FREQ_MASK 0x00000070 +#define CS48L32_US1_FREQ_SHIFT 4 + +/* (0xb808) US1_DET_CONTROL */ +#define CS48L32_US1_DET_DCY_SHIFT 28 +#define CS48L32_US1_DET_HOLD_SHIFT 24 +#define CS48L32_US1_DET_NUM_SHIFT 20 +#define CS48L32_US1_DET_THR_SHIFT 16 +#define CS48L32_US1_DET_LPF_CUT_SHIFT 5 +#define CS48L32_US1_DET_LPF_SHIFT 4 + +/* (0x18004) IRQ1_STATUS */ +#define CS48L32_IRQ1_STS_MASK 0x00000001 + +/* (0x18014) IRQ1_EINT_2 */ +#define CS48L32_BOOT_DONE_EINT1_MASK 0x00000008 + +/* (0x180a4) IRQ1_STS_6 */ +#define CS48L32_FLL1_LOCK_STS1_MASK 0x00000001 + +#endif From patchwork Wed Nov 9 16:53:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 623809 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 9F57EC43219 for ; Wed, 9 Nov 2022 16:55:27 +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 B4BDE1631; Wed, 9 Nov 2022 17:54:35 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz B4BDE1631 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1668012925; bh=tCrYitjMNFN1ooKBsaWgTw2pTA9VegiEWfGhERiSFL0=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=ojuudMhvJbPpxy/UNL7jU2rZ6GDAuLRMHuequpvOknK6CIil5zD9rmJsJ4uCbgH9g e9Xkf2jUE2Qg/vGWqPVVCF9hSY9659fFVJuvvsRWm4asqHYkqmN1ssAeqoERBUaPWo ziFd7hmPM3/4byN54KkzI1MlPG2zZ9H6Y65HXBV0= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 1751FF8057D; Wed, 9 Nov 2022 17:53:53 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 73C8DF8057B; Wed, 9 Nov 2022 17:53:50 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0b-001ae601.pphosted.com [67.231.152.168]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 33440F80217 for ; Wed, 9 Nov 2022 17:53:37 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 33440F80217 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="LY28Pi0e" Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 2A9EDNjS027347; Wed, 9 Nov 2022 10:53:36 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=dJ5BiHi/4KsxHP0YWvFqK47+1pv9GNU8ZJtgpqFaKJ0=; b=LY28Pi0eJhh1jFkyLFRvMj2fERv4wRLRt9FFQ2LKyTpKilaoiDd7zy8A9eL/Yj4+AJ5i jlQfkt3C6L0zpDh7meCiJHJZA9lQm9+QwZovc5O+RM+2I8mqEvSdJZNJMywvvIOvmeGN JK6U0NkHJWuag0FNPRPFHFeYFvWDHs1S56iixgaqLcQnFekIgJ6T2flU5ghVZG9P7FwQ +9iiFleq/uzqN27rY0N1/CHp6priyo52AjlHFVz7w1q7uuqpvfukyEU6GIGVQVoc2UoH GeygAPtdU/oWxwVXziXDcT1OjbwBEwKnYlAsldeXqf3gLBvTn3ZRpTRnAuOYZ5xSCL5H Uw== Received: from ediex02.ad.cirrus.com ([84.19.233.68]) by mx0b-001ae601.pphosted.com (PPS) with ESMTPS id 3knm8pde7v-4 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Nov 2022 10:53:36 -0600 Received: from ediex01.ad.cirrus.com (198.61.84.80) by ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.15; Wed, 9 Nov 2022 10:53:33 -0600 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by ediex01.ad.cirrus.com (198.61.84.80) with Microsoft SMTP Server id 15.2.1118.15 via Frontend Transport; Wed, 9 Nov 2022 10:53:33 -0600 Received: from debianA11184.ad.cirrus.com (AUSNPC0LSNW1.ad.cirrus.com [198.61.65.92]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id 461F7478; Wed, 9 Nov 2022 16:53:33 +0000 (UTC) From: Richard Fitzgerald To: , , , , , , Subject: [PATCH 03/12] mfd: cs48l32: Add support for CS48L31/32/33 codecs Date: Wed, 9 Nov 2022 16:53:22 +0000 Message-ID: <20221109165331.29332-4-rf@opensource.cirrus.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221109165331.29332-1-rf@opensource.cirrus.com> References: <20221109165331.29332-1-rf@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: VuRZUj3DP1_OgkG2GM65W3o5jte__lzz X-Proofpoint-GUID: VuRZUj3DP1_OgkG2GM65W3o5jte__lzz X-Proofpoint-Spam-Reason: safe Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, patches@opensource.cirrus.com, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Richard Fitzgerald 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: Piotr Stankiewicz The CS48L31/32/33 audio codecs are multi-function devices containing gpios, irq controller and regulators in addition to the core audio functionality. Signed-off-by: Piotr Stankiewicz Signed-off-by: Qi Zhou Signed-off-by: Stuart Henderson Signed-off-by: Richard Fitzgerald --- MAINTAINERS | 5 +- drivers/mfd/Kconfig | 13 + drivers/mfd/Makefile | 2 + drivers/mfd/cs48l32-tables.c | 541 +++++++++++++++++++++++++++++++ drivers/mfd/cs48l32.c | 434 +++++++++++++++++++++++++ drivers/mfd/cs48l32.h | 28 ++ include/linux/mfd/cs48l32/core.h | 49 +++ 7 files changed, 1071 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/cs48l32-tables.c create mode 100644 drivers/mfd/cs48l32.c create mode 100644 drivers/mfd/cs48l32.h create mode 100644 include/linux/mfd/cs48l32/core.h diff --git a/MAINTAINERS b/MAINTAINERS index 3f94ed38089b..f1d696f29f11 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5001,7 +5001,7 @@ F: include/dt-bindings/pinctrl/lochnagar.h F: include/linux/mfd/lochnagar* F: sound/soc/codecs/lochnagar-sc.c -CIRRUS LOGIC MADERA CODEC DRIVERS +CIRRUS LOGIC MADERA/CS48L32 CODEC DRIVERS M: Charles Keepax M: Richard Fitzgerald L: alsa-devel@alsa-project.org (moderated for non-subscribers) @@ -5009,16 +5009,19 @@ L: patches@opensource.cirrus.com S: Supported W: https://github.com/CirrusLogic/linux-drivers/wiki T: git https://github.com/CirrusLogic/linux-drivers.git +F: Documentation/devicetree/bindings/mfd/cirrus,cs48l32.yaml F: Documentation/devicetree/bindings/mfd/cirrus,madera.yaml F: Documentation/devicetree/bindings/pinctrl/cirrus,madera.yaml F: Documentation/devicetree/bindings/sound/cirrus,madera.yaml F: drivers/gpio/gpio-madera* F: drivers/irqchip/irq-madera* F: drivers/mfd/cs47l* +F: drivers/mfd/cs48l* F: drivers/mfd/madera* F: drivers/pinctrl/cirrus/* F: include/dt-bindings/sound/madera* F: include/linux/irqchip/irq-madera* +F: include/linux/mfd/cs48l32/* F: include/linux/mfd/madera/* F: include/sound/madera* F: sound/soc/codecs/cs47l* diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6653d03e0fe3..2be52ba23c7a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -300,6 +300,19 @@ config MFD_CS47L92 help Support for Cirrus Logic CS42L92, CS47L92 and CS47L93 Smart Codecs +config MFD_CS48L32 + bool "Cirrus Logic CS48L31/32/33" + depends on SPI_MASTER + select MFD_CORE + select REGMAP + select REGMAP_SPI + select REGMAP_IRQ + select CIRRUS_CS48L32_IRQ + select PINCTRL + select PINCTRL_CS48L32 + help + Support for Cirrus Logic CS48L31, CS48L32 and CS48L33 Smart Codecs. + config MFD_ASIC3 bool "Compaq ASIC3" depends on GPIOLIB diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 4dd479212b3a..4edbeb9b7a31 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -91,6 +91,8 @@ obj-$(CONFIG_MFD_MADERA) += madera.o obj-$(CONFIG_MFD_MADERA_I2C) += madera-i2c.o obj-$(CONFIG_MFD_MADERA_SPI) += madera-spi.o +obj-$(CONFIG_MFD_CS48L32) += cs48l32.o cs48l32-tables.o + obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o diff --git a/drivers/mfd/cs48l32-tables.c b/drivers/mfd/cs48l32-tables.c new file mode 100644 index 000000000000..5dab9753deb0 --- /dev/null +++ b/drivers/mfd/cs48l32-tables.c @@ -0,0 +1,541 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Regmap tables for Cirrus Logic CS48L32 audio codec. + * + * Copyright (C) 2018, 2020, 2022 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include +#include +#include +#include + +#include "cs48l32.h" + +static const struct reg_sequence cs48l32_reva_patch[] = { + { 0x00001044, 0x0005000f }, + { 0x00001c34, 0x000037e8 }, + { 0x000046d8, 0x00000fe0 }, +}; + +int cs48l32_patch(struct cs48l32_mfd *cs48l32) +{ + int ret; + + ret = regmap_register_patch(cs48l32->regmap, cs48l32_reva_patch, + ARRAY_SIZE(cs48l32_reva_patch)); + if (ret < 0) + dev_err(cs48l32->dev, "Error applying patch: %d\n", ret); + + return ret; +} + +static const struct reg_default cs48l32_reg_default[] = { + { 0x00000c08, 0xe1000001 }, /* GPIO1_CTRL1 */ + { 0x00000c0c, 0xe1000001 }, /* GPIO2_CTRL1 */ + { 0x00000c10, 0xe1000001 }, /* GPIO3_CTRL1 */ + { 0x00000c14, 0xe1000001 }, /* GPIO4_CTRL1 */ + { 0x00000c18, 0xe1000001 }, /* GPIO5_CTRL1 */ + { 0x00000c1c, 0xe1000001 }, /* GPIO6_CTRL1 */ + { 0x00000c20, 0xe1000001 }, /* GPIO7_CTRL1 */ + { 0x00000c24, 0xe1000001 }, /* GPIO8_CTRL1 */ + { 0x00000c28, 0xe1000001 }, /* GPIO9_CTRL1 */ + { 0x00000c2c, 0xe1000001 }, /* GPIO10_CTRL1 */ + { 0x00000c30, 0xe1000001 }, /* GPIO11_CTRL1 */ + { 0x00000c34, 0xe1000001 }, /* GPIO12_CTRL1 */ + { 0x00000c38, 0xe1000001 }, /* GPIO13_CTRL1 */ + { 0x00000c3c, 0xe1000001 }, /* GPIO14_CTRL1 */ + { 0x00000c40, 0xe1000001 }, /* GPIO15_CTRL1 */ + { 0x00000c44, 0xe1000001 }, /* GPIO16_CTRL1 */ + { 0x00001020, 0x00000000 }, /* OUTPUT_SYS_CLK */ + { 0x00001044, 0x0005000f }, /* AUXPDM_CTRL */ + { 0x0000105c, 0x00000000 }, /* AUXPDM_CTRL2 */ + { 0x00001400, 0x00000002 }, /* CLOCK32K */ + { 0x00001404, 0x00000404 }, /* SYSTEM_CLOCK1 */ + { 0x00001420, 0x00000003 }, /* SAMPLE_RATE1 */ + { 0x00001424, 0x00000003 }, /* SAMPLE_RATE2 */ + { 0x00001428, 0x00000003 }, /* SAMPLE_RATE3 */ + { 0x0000142c, 0x00000003 }, /* SAMPLE_RATE4 */ + { 0x00001c00, 0x00000002 }, /* FLL1_CONTROL1 */ + { 0x00001c04, 0x88203004 }, /* FLL1_CONTROL2 */ + { 0x00001c08, 0x00000000 }, /* FLL1_CONTROL3 */ + { 0x00001c0c, 0x21f05001 }, /* FLL1_CONTROL4 */ + { 0x00001ca0, 0x00000c04 }, /* FLL1_GPIO_CLOCK */ + { 0x00002000, 0x00000006 }, /* CHARGE_PUMP1 */ + { 0x00002408, 0x000003e4 }, /* LDO2_CTRL1 */ + { 0x00002410, 0x000000e6 }, /* MICBIAS_CTRL1 */ + { 0x00002418, 0x00000222 }, /* MICBIAS_CTRL5 */ + { 0x00002710, 0x00004600 }, /* IRQ1_CTRL_AOD */ + { 0x00004000, 0x00000000 }, /* INPUT_CONTROL */ + { 0x00004008, 0x00000400 }, /* INPUT_RATE_CONTROL */ + { 0x0000400c, 0x00000000 }, /* INPUT_CONTROL2 */ + { 0x00004020, 0x00050020 }, /* INPUT1_CONTROL1 */ + { 0x00004024, 0x00000000 }, /* IN1L_CONTROL1 */ + { 0x00004028, 0x10800080 }, /* IN1L_CONTROL2 */ + { 0x00004044, 0x00000000 }, /* IN1R_CONTROL1 */ + { 0x00004048, 0x10800080 }, /* IN1R_CONTROL2 */ + { 0x00004060, 0x00050020 }, /* INPUT2_CONTROL1 */ + { 0x00004064, 0x00000000 }, /* IN2L_CONTROL1 */ + { 0x00004068, 0x10800000 }, /* IN2L_CONTROL2 */ + { 0x00004084, 0x00000000 }, /* IN2R_CONTROL1 */ + { 0x00004088, 0x10800000 }, /* IN2R_CONTROL2 */ + { 0x00004244, 0x00000002 }, /* INPUT_HPF_CONTROL */ + { 0x00004248, 0x00000022 }, /* INPUT_VOL_CONTROL */ + { 0x00004300, 0x00000000 }, /* AUXPDM_CONTROL1 */ + { 0x00004304, 0x00000000 }, /* AUXPDM_CONTROL2 */ + { 0x00004308, 0x00010008 }, /* AUXPDM1_CONTROL1 */ + { 0x00004310, 0x00010008 }, /* AUXPDM2_CONTROL1 */ + { 0x00004688, 0x00000000 }, /* ADC1L_ANA_CONTROL1 */ + { 0x0000468c, 0x00000000 }, /* ADC1R_ANA_CONTROL1 */ + { 0x00006000, 0x00000000 }, /* ASP1_ENABLES1 */ + { 0x00006004, 0x00000028 }, /* ASP1_CONTROL1 */ + { 0x00006008, 0x18180200 }, /* ASP1_CONTROL2 */ + { 0x0000600c, 0x00000002 }, /* ASP1_CONTROL3 */ + { 0x00006010, 0x03020100 }, /* ASP1_FRAME_CONTROL1 */ + { 0x00006014, 0x07060504 }, /* ASP1_FRAME_CONTROL2 */ + { 0x00006020, 0x03020100 }, /* ASP1_FRAME_CONTROL5 */ + { 0x00006024, 0x07060504 }, /* ASP1_FRAME_CONTROL6 */ + { 0x00006030, 0x00000020 }, /* ASP1_DATA_CONTROL1 */ + { 0x00006040, 0x00000020 }, /* ASP1_DATA_CONTROL5 */ + { 0x00006080, 0x00000000 }, /* ASP2_ENABLES1 */ + { 0x00006084, 0x00000028 }, /* ASP2_CONTROL1 */ + { 0x00006088, 0x18180200 }, /* ASP2_CONTROL2 */ + { 0x0000608c, 0x00000002 }, /* ASP2_CONTROL3 */ + { 0x00006090, 0x03020100 }, /* ASP2_FRAME_CONTROL1 */ + { 0x000060a0, 0x03020100 }, /* ASP2_FRAME_CONTROL5 */ + { 0x000060b0, 0x00000020 }, /* ASP2_DATA_CONTROL1 */ + { 0x000060c0, 0x00000020 }, /* ASP2_DATA_CONTROL5 */ + { 0x00008200, 0x00800000 }, /* ASP1TX1_INPUT1 */ + { 0x00008204, 0x00800000 }, /* ASP1TX1_INPUT2 */ + { 0x00008208, 0x00800000 }, /* ASP1TX1_INPUT3 */ + { 0x0000820c, 0x00800000 }, /* ASP1TX1_INPUT4 */ + { 0x00008210, 0x00800000 }, /* ASP1TX2_INPUT1 */ + { 0x00008214, 0x00800000 }, /* ASP1TX2_INPUT2 */ + { 0x00008218, 0x00800000 }, /* ASP1TX2_INPUT3 */ + { 0x0000821c, 0x00800000 }, /* ASP1TX2_INPUT4 */ + { 0x00008220, 0x00800000 }, /* ASP1TX3_INPUT1 */ + { 0x00008224, 0x00800000 }, /* ASP1TX3_INPUT2 */ + { 0x00008228, 0x00800000 }, /* ASP1TX3_INPUT3 */ + { 0x0000822c, 0x00800000 }, /* ASP1TX3_INPUT4 */ + { 0x00008230, 0x00800000 }, /* ASP1TX4_INPUT1 */ + { 0x00008234, 0x00800000 }, /* ASP1TX4_INPUT2 */ + { 0x00008238, 0x00800000 }, /* ASP1TX4_INPUT3 */ + { 0x0000823c, 0x00800000 }, /* ASP1TX4_INPUT4 */ + { 0x00008240, 0x00800000 }, /* ASP1TX5_INPUT1 */ + { 0x00008244, 0x00800000 }, /* ASP1TX5_INPUT2 */ + { 0x00008248, 0x00800000 }, /* ASP1TX5_INPUT3 */ + { 0x0000824c, 0x00800000 }, /* ASP1TX5_INPUT4 */ + { 0x00008250, 0x00800000 }, /* ASP1TX6_INPUT1 */ + { 0x00008254, 0x00800000 }, /* ASP1TX6_INPUT2 */ + { 0x00008258, 0x00800000 }, /* ASP1TX6_INPUT3 */ + { 0x0000825c, 0x00800000 }, /* ASP1TX6_INPUT4 */ + { 0x00008260, 0x00800000 }, /* ASP1TX7_INPUT1 */ + { 0x00008264, 0x00800000 }, /* ASP1TX7_INPUT2 */ + { 0x00008268, 0x00800000 }, /* ASP1TX7_INPUT3 */ + { 0x0000826c, 0x00800000 }, /* ASP1TX7_INPUT4 */ + { 0x00008270, 0x00800000 }, /* ASP1TX8_INPUT1 */ + { 0x00008274, 0x00800000 }, /* ASP1TX8_INPUT2 */ + { 0x00008278, 0x00800000 }, /* ASP1TX8_INPUT3 */ + { 0x0000827c, 0x00800000 }, /* ASP1TX8_INPUT4 */ + { 0x00008300, 0x00800000 }, /* ASP2TX1_INPUT1 */ + { 0x00008304, 0x00800000 }, /* ASP2TX1_INPUT2 */ + { 0x00008308, 0x00800000 }, /* ASP2TX1_INPUT3 */ + { 0x0000830c, 0x00800000 }, /* ASP2TX1_INPUT4 */ + { 0x00008310, 0x00800000 }, /* ASP2TX2_INPUT1 */ + { 0x00008314, 0x00800000 }, /* ASP2TX2_INPUT2 */ + { 0x00008318, 0x00800000 }, /* ASP2TX2_INPUT3 */ + { 0x0000831c, 0x00800000 }, /* ASP2TX2_INPUT4 */ + { 0x00008320, 0x00800000 }, /* ASP2TX3_INPUT1 */ + { 0x00008324, 0x00800000 }, /* ASP2TX3_INPUT2 */ + { 0x00008328, 0x00800000 }, /* ASP2TX3_INPUT3 */ + { 0x0000832c, 0x00800000 }, /* ASP2TX3_INPUT4 */ + { 0x00008330, 0x00800000 }, /* ASP2TX4_INPUT1 */ + { 0x00008334, 0x00800000 }, /* ASP2TX4_INPUT2 */ + { 0x00008338, 0x00800000 }, /* ASP2TX4_INPUT3 */ + { 0x0000833c, 0x00800000 }, /* ASP2TX4_INPUT4 */ + { 0x00008980, 0x00000000 }, /* ISRC1INT1_INPUT1 */ + { 0x00008990, 0x00000000 }, /* ISRC1INT2_INPUT1 */ + { 0x000089a0, 0x00000000 }, /* ISRC1INT3_INPUT1 */ + { 0x000089b0, 0x00000000 }, /* ISRC1INT4_INPUT1 */ + { 0x000089c0, 0x00000000 }, /* ISRC1DEC1_INPUT1 */ + { 0x000089d0, 0x00000000 }, /* ISRC1DEC2_INPUT1 */ + { 0x000089e0, 0x00000000 }, /* ISRC1DEC3_INPUT1 */ + { 0x000089f0, 0x00000000 }, /* ISRC1DEC4_INPUT1 */ + { 0x00008a00, 0x00000000 }, /* ISRC2INT1_INPUT1 */ + { 0x00008a10, 0x00000000 }, /* ISRC2INT2_INPUT1 */ + { 0x00008a40, 0x00000000 }, /* ISRC2DEC1_INPUT1 */ + { 0x00008a50, 0x00000000 }, /* ISRC2DEC2_INPUT1 */ + { 0x00008a80, 0x00000000 }, /* ISRC3INT1_INPUT1 */ + { 0x00008a90, 0x00000000 }, /* ISRC3INT2_INPUT1 */ + { 0x00008ac0, 0x00000000 }, /* ISRC3DEC1_INPUT1 */ + { 0x00008ad0, 0x00000000 }, /* ISRC3DEC2_INPUT1 */ + { 0x00008b80, 0x00800000 }, /* EQ1_INPUT1 */ + { 0x00008b84, 0x00800000 }, /* EQ1_INPUT2 */ + { 0x00008b88, 0x00800000 }, /* EQ1_INPUT3 */ + { 0x00008b8c, 0x00800000 }, /* EQ1_INPUT4 */ + { 0x00008b90, 0x00800000 }, /* EQ2_INPUT1 */ + { 0x00008b94, 0x00800000 }, /* EQ2_INPUT2 */ + { 0x00008b98, 0x00800000 }, /* EQ2_INPUT3 */ + { 0x00008b9c, 0x00800000 }, /* EQ2_INPUT4 */ + { 0x00008ba0, 0x00800000 }, /* EQ3_INPUT1 */ + { 0x00008ba4, 0x00800000 }, /* EQ3_INPUT2 */ + { 0x00008ba8, 0x00800000 }, /* EQ3_INPUT3 */ + { 0x00008bac, 0x00800000 }, /* EQ3_INPUT4 */ + { 0x00008bb0, 0x00800000 }, /* EQ4_INPUT1 */ + { 0x00008bb4, 0x00800000 }, /* EQ4_INPUT2 */ + { 0x00008bb8, 0x00800000 }, /* EQ4_INPUT3 */ + { 0x00008bbc, 0x00800000 }, /* EQ4_INPUT4 */ + { 0x00008c00, 0x00800000 }, /* DRC1L_INPUT1 */ + { 0x00008c04, 0x00800000 }, /* DRC1L_INPUT2 */ + { 0x00008c08, 0x00800000 }, /* DRC1L_INPUT3 */ + { 0x00008c0c, 0x00800000 }, /* DRC1L_INPUT4 */ + { 0x00008c10, 0x00800000 }, /* DRC1R_INPUT1 */ + { 0x00008c14, 0x00800000 }, /* DRC1R_INPUT2 */ + { 0x00008c18, 0x00800000 }, /* DRC1R_INPUT3 */ + { 0x00008c1c, 0x00800000 }, /* DRC1R_INPUT4 */ + { 0x00008c20, 0x00800000 }, /* DRC2L_INPUT1 */ + { 0x00008c24, 0x00800000 }, /* DRC2L_INPUT2 */ + { 0x00008c28, 0x00800000 }, /* DRC2L_INPUT3 */ + { 0x00008c2c, 0x00800000 }, /* DRC2L_INPUT4 */ + { 0x00008c30, 0x00800000 }, /* DRC2R_INPUT1 */ + { 0x00008c34, 0x00800000 }, /* DRC2R_INPUT2 */ + { 0x00008c38, 0x00800000 }, /* DRC2R_INPUT3 */ + { 0x00008c3c, 0x00800000 }, /* DRC2R_INPUT4 */ + { 0x00008c80, 0x00800000 }, /* LHPF1_INPUT1 */ + { 0x00008c84, 0x00800000 }, /* LHPF1_INPUT2 */ + { 0x00008c88, 0x00800000 }, /* LHPF1_INPUT3 */ + { 0x00008c8c, 0x00800000 }, /* LHPF1_INPUT4 */ + { 0x00008c90, 0x00800000 }, /* LHPF2_INPUT1 */ + { 0x00008c94, 0x00800000 }, /* LHPF2_INPUT2 */ + { 0x00008c98, 0x00800000 }, /* LHPF2_INPUT3 */ + { 0x00008c9c, 0x00800000 }, /* LHPF2_INPUT4 */ + { 0x00008ca0, 0x00800000 }, /* LHPF3_INPUT1 */ + { 0x00008ca4, 0x00800000 }, /* LHPF3_INPUT2 */ + { 0x00008ca8, 0x00800000 }, /* LHPF3_INPUT3 */ + { 0x00008cac, 0x00800000 }, /* LHPF3_INPUT4 */ + { 0x00008cb0, 0x00800000 }, /* LHPF4_INPUT1 */ + { 0x00008cb4, 0x00800000 }, /* LHPF4_INPUT2 */ + { 0x00008cb8, 0x00800000 }, /* LHPF4_INPUT3 */ + { 0x00008cbc, 0x00800000 }, /* LHPF4_INPUT4 */ + { 0x00009000, 0x00800000 }, /* DSP1RX1_INPUT1 */ + { 0x00009004, 0x00800000 }, /* DSP1RX1_INPUT2 */ + { 0x00009008, 0x00800000 }, /* DSP1RX1_INPUT3 */ + { 0x0000900c, 0x00800000 }, /* DSP1RX1_INPUT4 */ + { 0x00009010, 0x00800000 }, /* DSP1RX2_INPUT1 */ + { 0x00009014, 0x00800000 }, /* DSP1RX2_INPUT2 */ + { 0x00009018, 0x00800000 }, /* DSP1RX2_INPUT3 */ + { 0x0000901c, 0x00800000 }, /* DSP1RX2_INPUT4 */ + { 0x00009020, 0x00800000 }, /* DSP1RX3_INPUT1 */ + { 0x00009024, 0x00800000 }, /* DSP1RX3_INPUT2 */ + { 0x00009028, 0x00800000 }, /* DSP1RX3_INPUT3 */ + { 0x0000902c, 0x00800000 }, /* DSP1RX3_INPUT4 */ + { 0x00009030, 0x00800000 }, /* DSP1RX4_INPUT1 */ + { 0x00009034, 0x00800000 }, /* DSP1RX4_INPUT2 */ + { 0x00009038, 0x00800000 }, /* DSP1RX4_INPUT3 */ + { 0x0000903c, 0x00800000 }, /* DSP1RX4_INPUT4 */ + { 0x00009040, 0x00800000 }, /* DSP1RX5_INPUT1 */ + { 0x00009044, 0x00800000 }, /* DSP1RX5_INPUT2 */ + { 0x00009048, 0x00800000 }, /* DSP1RX5_INPUT3 */ + { 0x0000904c, 0x00800000 }, /* DSP1RX5_INPUT4 */ + { 0x00009050, 0x00800000 }, /* DSP1RX6_INPUT1 */ + { 0x00009054, 0x00800000 }, /* DSP1RX6_INPUT2 */ + { 0x00009058, 0x00800000 }, /* DSP1RX6_INPUT3 */ + { 0x0000905c, 0x00800000 }, /* DSP1RX6_INPUT4 */ + { 0x00009060, 0x00800000 }, /* DSP1RX7_INPUT1 */ + { 0x00009064, 0x00800000 }, /* DSP1RX7_INPUT2 */ + { 0x00009068, 0x00800000 }, /* DSP1RX7_INPUT3 */ + { 0x0000906c, 0x00800000 }, /* DSP1RX7_INPUT4 */ + { 0x00009070, 0x00800000 }, /* DSP1RX8_INPUT1 */ + { 0x00009074, 0x00800000 }, /* DSP1RX8_INPUT2 */ + { 0x00009078, 0x00800000 }, /* DSP1RX8_INPUT3 */ + { 0x0000907c, 0x00800000 }, /* DSP1RX8_INPUT4 */ + { 0x0000a400, 0x00000000 }, /* ISRC1_CONTROL1 */ + { 0x0000a404, 0x00000000 }, /* ISRC1_CONTROL2 */ + { 0x0000a510, 0x00000000 }, /* ISRC2_CONTROL1 */ + { 0x0000a514, 0x00000000 }, /* ISRC2_CONTROL2 */ + { 0x0000a620, 0x00000000 }, /* ISRC3_CONTROL1 */ + { 0x0000a624, 0x00000000 }, /* ISRC3_CONTROL2 */ + { 0x0000a800, 0x00000000 }, /* FX_SAMPLE_RATE */ + { 0x0000a808, 0x00000000 }, /* EQ_CONTROL1 */ + { 0x0000a80c, 0x00000000 }, /* EQ_CONTROL2 */ + { 0x0000a810, 0x0c0c0c0c }, /* EQ1_GAIN1 */ + { 0x0000a814, 0x0000000c }, /* EQ1_GAIN2 */ + { 0x0000a818, 0x03fe0fc8 }, /* EQ1_BAND1_COEFF1 */ + { 0x0000a81c, 0x00000b75 }, /* EQ1_BAND1_COEFF2 */ + { 0x0000a820, 0x000000e0 }, /* EQ1_BAND1_PG */ + { 0x0000a824, 0xf1361ec4 }, /* EQ1_BAND2_COEFF1 */ + { 0x0000a828, 0x00000409 }, /* EQ1_BAND2_COEFF2 */ + { 0x0000a82c, 0x000004cc }, /* EQ1_BAND2_PG */ + { 0x0000a830, 0xf3371c9b }, /* EQ1_BAND3_COEFF1 */ + { 0x0000a834, 0x0000040b }, /* EQ1_BAND3_COEFF2 */ + { 0x0000a838, 0x00000cbb }, /* EQ1_BAND3_PG */ + { 0x0000a83c, 0xf7d916f8 }, /* EQ1_BAND4_COEFF1 */ + { 0x0000a840, 0x0000040a }, /* EQ1_BAND4_COEFF2 */ + { 0x0000a844, 0x00001f14 }, /* EQ1_BAND4_PG */ + { 0x0000a848, 0x0563058c }, /* EQ1_BAND5_COEFF1 */ + { 0x0000a84c, 0x00000000 }, /* EQ1_BAND5_COEFF1 + 4 */ + { 0x0000a850, 0x00004000 }, /* EQ1_BAND5_PG */ + { 0x0000a854, 0x0c0c0c0c }, /* EQ2_GAIN1 */ + { 0x0000a858, 0x0000000c }, /* EQ2_GAIN2 */ + { 0x0000a85c, 0x03fe0fc8 }, /* EQ2_BAND1_COEFF1 */ + { 0x0000a860, 0x00000b75 }, /* EQ2_BAND1_COEFF2 */ + { 0x0000a864, 0x000000e0 }, /* EQ2_BAND1_PG */ + { 0x0000a868, 0xf1361ec4 }, /* EQ2_BAND2_COEFF1 */ + { 0x0000a86c, 0x00000409 }, /* EQ2_BAND2_COEFF2 */ + { 0x0000a870, 0x000004cc }, /* EQ2_BAND2_PG */ + { 0x0000a874, 0xf3371c9b }, /* EQ2_BAND3_COEFF1 */ + { 0x0000a878, 0x0000040b }, /* EQ2_BAND3_COEFF2 */ + { 0x0000a87c, 0x00000cbb }, /* EQ2_BAND3_PG */ + { 0x0000a880, 0xf7d916f8 }, /* EQ2_BAND4_COEFF1 */ + { 0x0000a884, 0x0000040a }, /* EQ2_BAND4_COEFF2 */ + { 0x0000a888, 0x00001f14 }, /* EQ2_BAND4_PG */ + { 0x0000a88c, 0x0563058c }, /* EQ2_BAND5_COEFF1 */ + { 0x0000a890, 0x00000000 }, /* EQ2_BAND5_COEFF1 + 4 */ + { 0x0000a894, 0x00004000 }, /* EQ2_BAND5_PG */ + { 0x0000a898, 0x0c0c0c0c }, /* EQ3_GAIN1 */ + { 0x0000a89c, 0x0000000c }, /* EQ3_GAIN2 */ + { 0x0000a8a0, 0x03fe0fc8 }, /* EQ3_BAND1_COEFF1 */ + { 0x0000a8a4, 0x00000b75 }, /* EQ3_BAND1_COEFF2 */ + { 0x0000a8a8, 0x000000e0 }, /* EQ3_BAND1_PG */ + { 0x0000a8ac, 0xf1361ec4 }, /* EQ3_BAND2_COEFF1 */ + { 0x0000a8b0, 0x00000409 }, /* EQ3_BAND2_COEFF2 */ + { 0x0000a8b4, 0x000004cc }, /* EQ3_BAND2_PG */ + { 0x0000a8b8, 0xf3371c9b }, /* EQ3_BAND3_COEFF1 */ + { 0x0000a8bc, 0x0000040b }, /* EQ3_BAND3_COEFF2 */ + { 0x0000a8c0, 0x00000cbb }, /* EQ3_BAND3_PG */ + { 0x0000a8c4, 0xf7d916f8 }, /* EQ3_BAND4_COEFF1 */ + { 0x0000a8c8, 0x0000040a }, /* EQ3_BAND4_COEFF2 */ + { 0x0000a8cc, 0x00001f14 }, /* EQ3_BAND4_PG */ + { 0x0000a8d0, 0x0563058c }, /* EQ3_BAND5_COEFF1 */ + { 0x0000a8d4, 0x00000000 }, /* EQ3_BAND5_COEFF1 + 4 */ + { 0x0000a8d8, 0x00004000 }, /* EQ3_BAND5_PG */ + { 0x0000a8dc, 0x0c0c0c0c }, /* EQ4_GAIN1 */ + { 0x0000a8e0, 0x0000000c }, /* EQ4_GAIN2 */ + { 0x0000a8e4, 0x03fe0fc8 }, /* EQ4_BAND1_COEFF1 */ + { 0x0000a8e8, 0x00000b75 }, /* EQ4_BAND1_COEFF2 */ + { 0x0000a8ec, 0x000000e0 }, /* EQ4_BAND1_PG */ + { 0x0000a8f0, 0xf1361ec4 }, /* EQ4_BAND2_COEFF1 */ + { 0x0000a8f4, 0x00000409 }, /* EQ4_BAND2_COEFF2 */ + { 0x0000a8f8, 0x000004cc }, /* EQ4_BAND2_PG */ + { 0x0000a8fc, 0xf3371c9b }, /* EQ4_BAND3_COEFF1 */ + { 0x0000a900, 0x0000040b }, /* EQ4_BAND3_COEFF2 */ + { 0x0000a904, 0x00000cbb }, /* EQ4_BAND3_PG */ + { 0x0000a908, 0xf7d916f8 }, /* EQ4_BAND4_COEFF1 */ + { 0x0000a90c, 0x0000040a }, /* EQ4_BAND4_COEFF2 */ + { 0x0000a910, 0x00001f14 }, /* EQ4_BAND4_PG */ + { 0x0000a914, 0x0563058c }, /* EQ4_BAND5_COEFF1 */ + { 0x0000a918, 0x00000000 }, /* EQ4_BAND5_COEFF1 + 4 */ + { 0x0000a91c, 0x00004000 }, /* EQ4_BAND5_PG */ + { 0x0000aa30, 0x00000000 }, /* LHPF_CONTROL1 */ + { 0x0000aa34, 0x00000000 }, /* LHPF_CONTROL2 */ + { 0x0000aa38, 0x00000000 }, /* LHPF1_COEFF */ + { 0x0000aa3c, 0x00000000 }, /* LHPF2_COEFF */ + { 0x0000aa40, 0x00000000 }, /* LHPF3_COEFF */ + { 0x0000aa44, 0x00000000 }, /* LHPF4_COEFF */ + { 0x0000ab00, 0x00000000 }, /* DRC1_CONTROL1 */ + { 0x0000ab04, 0x49130018 }, /* DRC1_CONTROL2 */ + { 0x0000ab08, 0x00000018 }, /* DRC1_CONTROL3 */ + { 0x0000ab0c, 0x00000000 }, /* DRC1_CONTROL4 */ + { 0x0000ab14, 0x00000000 }, /* DRC2_CONTROL1 */ + { 0x0000ab18, 0x49130018 }, /* DRC2_CONTROL2 */ + { 0x0000ab1c, 0x00000018 }, /* DRC2_CONTROL3 */ + { 0x0000ab20, 0x00000000 }, /* DRC2_CONTROL4 */ + { 0x0000b000, 0x00000000 }, /* TONE_GENERATOR1 */ + { 0x0000b004, 0x00100000 }, /* TONE_GENERATOR2 */ + { 0x0000b400, 0x00000000 }, /* COMFORT_NOISE_GENERATOR */ + { 0x0000b800, 0x00000000 }, /* US_CONTROL */ + { 0x0000b804, 0x00002020 }, /* US1_CONTROL */ + { 0x0000b808, 0x00000000 }, /* US1_DET_CONTROL */ + { 0x0000b814, 0x00002020 }, /* US2_CONTROL */ + { 0x0000b818, 0x00000000 }, /* US2_DET_CONTROL */ + { 0x00018110, 0xffffffff }, /* IRQ1_MASK_1 */ + { 0x00018114, 0xfffffff7 }, /* IRQ1_MASK_2 */ + { 0x00018118, 0xffffffff }, /* IRQ1_MASK_3 */ + { 0x0001811c, 0xffffffff }, /* IRQ1_MASK_4 */ + { 0x00018120, 0xffffffff }, /* IRQ1_MASK_5 */ + { 0x00018124, 0xffffffff }, /* IRQ1_MASK_6 */ + { 0x00018128, 0xffffffff }, /* IRQ1_MASK_7 */ + { 0x0001812c, 0xffffffff }, /* IRQ1_MASK_8 */ + { 0x00018130, 0xffffffff }, /* IRQ1_MASK_9 */ + { 0x00018134, 0xffffffff }, /* IRQ1_MASK_10 */ + { 0x00018138, 0xffffffff }, /* IRQ1_MASK_11 */ +}; + +static bool cs48l32_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS48L32_DEVID: + case CS48L32_REVID: + case CS48L32_OTPID: + case CS48L32_SFT_RESET: + case CS48L32_CTRL_IF_DEBUG3: + case CS48L32_MCU_CTRL1: + case CS48L32_GPIO1_CTRL1 ... CS48L32_GPIO16_CTRL1: + case CS48L32_OUTPUT_SYS_CLK: + case CS48L32_AUXPDM_CTRL: + case CS48L32_AUXPDM_CTRL2: + case CS48L32_CLOCK32K: + case CS48L32_SYSTEM_CLOCK1 ... CS48L32_SYSTEM_CLOCK2: + case CS48L32_SAMPLE_RATE1 ... CS48L32_SAMPLE_RATE4: + case CS48L32_FLL1_CONTROL1 ... CS48L32_FLL1_GPIO_CLOCK: + case CS48L32_CHARGE_PUMP1: + case CS48L32_LDO2_CTRL1: + case CS48L32_MICBIAS_CTRL1: + case CS48L32_MICBIAS_CTRL5: + case CS48L32_IRQ1_CTRL_AOD: + case CS48L32_INPUT_CONTROL: + case CS48L32_INPUT_STATUS: + case CS48L32_INPUT_RATE_CONTROL: + case CS48L32_INPUT_CONTROL2: + case CS48L32_INPUT_CONTROL3: + case CS48L32_INPUT1_CONTROL1: + case CS48L32_IN1L_CONTROL1 ... CS48L32_IN1L_CONTROL2: + case CS48L32_IN1R_CONTROL1 ... CS48L32_IN1R_CONTROL2: + case CS48L32_INPUT2_CONTROL1: + case CS48L32_IN2L_CONTROL1 ... CS48L32_IN2L_CONTROL2: + case CS48L32_IN2R_CONTROL1 ... CS48L32_IN2R_CONTROL2: + case CS48L32_INPUT_HPF_CONTROL: + case CS48L32_INPUT_VOL_CONTROL: + case CS48L32_AUXPDM_CONTROL1: + case CS48L32_AUXPDM_CONTROL2: + case CS48L32_AUXPDM1_CONTROL1: + case CS48L32_AUXPDM2_CONTROL1: + case CS48L32_ADC1L_ANA_CONTROL1: + case CS48L32_ADC1R_ANA_CONTROL1: + case CS48L32_ASP1_ENABLES1 ... CS48L32_ASP1_DATA_CONTROL5: + case CS48L32_ASP2_ENABLES1 ... CS48L32_ASP2_DATA_CONTROL5: + case CS48L32_ASP1TX1_INPUT1 ... CS48L32_ASP1TX8_INPUT4: + case CS48L32_ASP2TX1_INPUT1 ... CS48L32_ASP2TX4_INPUT4: + case CS48L32_ISRC1INT1_INPUT1 ... CS48L32_ISRC1DEC4_INPUT1: + case CS48L32_ISRC2INT1_INPUT1 ... CS48L32_ISRC2DEC2_INPUT1: + case CS48L32_ISRC3INT1_INPUT1 ... CS48L32_ISRC3DEC2_INPUT1: + case CS48L32_EQ1_INPUT1 ... CS48L32_EQ4_INPUT4: + case CS48L32_DRC1L_INPUT1 ... CS48L32_DRC1R_INPUT4: + case CS48L32_DRC2L_INPUT1 ... CS48L32_DRC2R_INPUT4: + case CS48L32_LHPF1_INPUT1 ... CS48L32_LHPF1_INPUT4: + case CS48L32_LHPF2_INPUT1 ... CS48L32_LHPF2_INPUT4: + case CS48L32_LHPF3_INPUT1 ... CS48L32_LHPF3_INPUT4: + case CS48L32_LHPF4_INPUT1 ... CS48L32_LHPF4_INPUT4: + case CS48L32_DSP1RX1_INPUT1 ... CS48L32_DSP1RX8_INPUT4: + case CS48L32_ISRC1_CONTROL1 ... CS48L32_ISRC1_CONTROL2: + case CS48L32_ISRC2_CONTROL1 ... CS48L32_ISRC2_CONTROL2: + case CS48L32_ISRC3_CONTROL1 ... CS48L32_ISRC3_CONTROL2: + case CS48L32_FX_SAMPLE_RATE: + case CS48L32_EQ_CONTROL1 ... CS48L32_EQ_CONTROL2: + case CS48L32_EQ1_GAIN1 ... CS48L32_EQ1_BAND5_PG: + case CS48L32_EQ2_GAIN1 ... CS48L32_EQ2_BAND5_PG: + case CS48L32_EQ3_GAIN1 ... CS48L32_EQ3_BAND5_PG: + case CS48L32_EQ4_GAIN1 ... CS48L32_EQ4_BAND5_PG: + case CS48L32_LHPF_CONTROL1 ... CS48L32_LHPF_CONTROL2: + case CS48L32_LHPF1_COEFF ... CS48L32_LHPF4_COEFF: + case CS48L32_DRC1_CONTROL1 ... CS48L32_DRC1_CONTROL4: + case CS48L32_DRC2_CONTROL1 ... CS48L32_DRC2_CONTROL4: + case CS48L32_TONE_GENERATOR1 ... CS48L32_TONE_GENERATOR2: + case CS48L32_COMFORT_NOISE_GENERATOR: + case CS48L32_US_CONTROL: + case CS48L32_US1_CONTROL: + case CS48L32_US1_DET_CONTROL: + case CS48L32_US2_CONTROL: + case CS48L32_US2_DET_CONTROL: + case CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24: + case CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8: + case CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7: + case CS48L32_IRQ1_STATUS: + case CS48L32_IRQ1_EINT_1 ... CS48L32_IRQ1_EINT_11: + case CS48L32_IRQ1_STS_1 ... CS48L32_IRQ1_STS_11: + case CS48L32_IRQ1_MASK_1 ... CS48L32_IRQ1_MASK_11: + case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_147455: + case CS48L32_DSP1_SYS_INFO_ID ... CS48L32_DSP1_AHBM_WINDOW_DEBUG_1: + case CS48L32_DSP1_XMEM_UNPACKED24_0 ... CS48L32_DSP1_XMEM_UNPACKED24_196607: + case CS48L32_DSP1_CLOCK_FREQ ... CS48L32_DSP1_SAMPLE_RATE_TX8: + case CS48L32_DSP1_SCRATCH1 ... CS48L32_DSP1_SCRATCH4: + case CS48L32_DSP1_CCM_CORE_CONTROL ... CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1: + case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_49151: + case CS48L32_DSP1_YMEM_UNPACKED24_0 ... CS48L32_DSP1_YMEM_UNPACKED24_65535: + case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_71679: + return true; + default: + return false; + } +} + +static bool cs48l32_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS48L32_DEVID: + case CS48L32_REVID: + case CS48L32_OTPID: + case CS48L32_SFT_RESET: + case CS48L32_CTRL_IF_DEBUG3: + case CS48L32_MCU_CTRL1: + case CS48L32_SYSTEM_CLOCK2: + case CS48L32_FLL1_CONTROL5: + case CS48L32_FLL1_CONTROL6: + case CS48L32_INPUT_STATUS: + case CS48L32_INPUT_CONTROL3: + case CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24: + case CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8: + case CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7: + case CS48L32_IRQ1_STATUS: + case CS48L32_IRQ1_EINT_1 ... CS48L32_IRQ1_EINT_11: + case CS48L32_IRQ1_STS_1 ... CS48L32_IRQ1_STS_11: + case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_147455: + case CS48L32_DSP1_SYS_INFO_ID ... CS48L32_DSP1_AHBM_WINDOW_DEBUG_1: + case CS48L32_DSP1_XMEM_UNPACKED24_0 ... CS48L32_DSP1_XMEM_UNPACKED24_196607: + case CS48L32_DSP1_CLOCK_FREQ ... CS48L32_DSP1_SAMPLE_RATE_TX8: + case CS48L32_DSP1_SCRATCH1 ... CS48L32_DSP1_SCRATCH4: + case CS48L32_DSP1_CCM_CORE_CONTROL ... CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1: + case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_49151: + case CS48L32_DSP1_YMEM_UNPACKED24_0 ... CS48L32_DSP1_YMEM_UNPACKED24_65535: + case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_71679: + return true; + default: + return false; + } +} + +/* + * The bus bridge requires DSP packed memory registers to be accessed in + * aligned block multiples. + * Mark precious to prevent regmap debugfs causing an illegal bus transaction. + */ +static bool cs48l32_precious_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_147455: + case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_49151: + case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_71679: + return true; + default: + return false; + } +} + +static const struct regmap_config cs48l32_spi_regmap = { + .name = "cs48l32", + .reg_bits = 32, + .reg_stride = 4, + .pad_bits = 32, + .val_bits = 32, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + + .max_register = CS48L32_DSP1_PMEM_71679, + .readable_reg = &cs48l32_readable_register, + .volatile_reg = &cs48l32_volatile_register, + .precious_reg = &cs48l32_precious_register, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = cs48l32_reg_default, + .num_reg_defaults = ARRAY_SIZE(cs48l32_reg_default), +}; + +int cs48l32_init_regmap(struct spi_device *spi, struct cs48l32_mfd *cs48l32) +{ + cs48l32->regmap = devm_regmap_init_spi(spi, &cs48l32_spi_regmap); + if (IS_ERR(cs48l32->regmap)) + return PTR_ERR(cs48l32->regmap); + + return 0; +} diff --git a/drivers/mfd/cs48l32.c b/drivers/mfd/cs48l32.c new file mode 100644 index 000000000000..9506a1e0560b --- /dev/null +++ b/drivers/mfd/cs48l32.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MFD for Cirrus Logic CS48L32 audio codec. + * + * Copyright (C) 2016-2018, 2020, 2022 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs48l32.h" + +static const char * const cs48l32_core_supplies[] = { + "VDD_A", + "VDD_IO", +}; + +static const char * const cs48l32_supplies[] = { + "VOUT_MIC", + "VDD_CP", +}; + +static const struct mfd_cell cs48l32_devs[] = { + { .name = "cs48l32-pinctrl", }, + { .name = "cs48l32-irq", }, + { .name = "cs48l32-micsupp", }, + { + .name = "cs48l32-codec", + .parent_supplies = cs48l32_supplies, + .num_parent_supplies = ARRAY_SIZE(cs48l32_supplies), + }, +}; + +static int cs48l32_wait_for_boot(struct cs48l32_mfd *cs48l32) +{ + unsigned int val; + int i, ret; + + /* regmap_read_poll_timeout would fail on read errors so roll our own */ + for (i = 0; i < CS48L32_BOOT_TIMEOUT_MS; ++i) { + val = 0; + regmap_read(cs48l32->regmap, CS48L32_IRQ1_EINT_2, &val); + if (val & CS48L32_BOOT_DONE_EINT1_MASK) + break; + + usleep_range(1000, 2000); + } + if (i == CS48L32_BOOT_TIMEOUT_MS) { + dev_err(cs48l32->dev, "BOOT_DONE timed out\n"); + return -ETIMEDOUT; + } + + ret = regmap_read(cs48l32->regmap, CS48L32_MCU_CTRL1, &val); + if (ret) { + dev_err(cs48l32->dev, "Failed to read MCU_CTRL1: %d\n", ret); + return ret; + } + + if (val & BIT(CS48L32_MCU_STS_SHIFT)) { + dev_err(cs48l32->dev, "MCU boot failed\n"); + return -EIO; + } + + ret = regmap_set_bits(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, CS48L32_SEEN_BOOT_DONE); + if (ret) { + dev_err(cs48l32->dev, "Failed to write CTRL_IF_DEBUG3: %d\n", ret); + return ret; + } + + pm_runtime_mark_last_busy(cs48l32->dev); + + return 0; +} + +static int cs48l32_soft_reset(struct cs48l32_mfd *cs48l32) +{ + int ret; + + ret = regmap_write(cs48l32->regmap, CS48L32_SFT_RESET, CS48L32_SFT_RESET_MAGIC); + if (ret != 0) { + dev_err(cs48l32->dev, "Failed to write soft reset: %d\n", ret); + return ret; + } + + usleep_range(2000, 3000); + + return 0; +} + +static void cs48l32_enable_hard_reset(struct cs48l32_mfd *cs48l32) +{ + if (cs48l32->reset_gpio) + gpiod_set_value_cansleep(cs48l32->reset_gpio, 0); +} + +static void cs48l32_disable_hard_reset(struct cs48l32_mfd *cs48l32) +{ + if (cs48l32->reset_gpio) { + gpiod_set_value_cansleep(cs48l32->reset_gpio, 1); + usleep_range(2000, 3000); + } +} + +static int __maybe_unused cs48l32_runtime_resume(struct device *dev) +{ + struct cs48l32_mfd *cs48l32 = dev_get_drvdata(dev); + unsigned int val; + int ret; + + dev_dbg(cs48l32->dev, "Leaving sleep mode\n"); + + ret = regulator_enable(cs48l32->vdd_d); + if (ret) { + dev_err(cs48l32->dev, "Failed to enable VDD_D: %d\n", ret); + return ret; + } + + usleep_range(2000, 3000); + + regcache_cache_only(cs48l32->regmap, false); + + /* Did it power down during suspend? */ + ret = regmap_read(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, &val); + if ((ret == 0) && (val & CS48L32_SEEN_BOOT_DONE)) { + dev_dbg(cs48l32->dev, "VDD_D didn't power off during suspend\n"); + } else { + ret = cs48l32_wait_for_boot(cs48l32); + if (ret) + goto err; + + /* Flag that the registers have reset to defaults */ + regcache_mark_dirty(cs48l32->regmap); + } + + ret = regcache_sync(cs48l32->regmap); + if (ret) { + dev_err(cs48l32->dev, "Failed to restore register cache\n"); + goto err; + } + + return 0; + +err: + regcache_cache_only(cs48l32->regmap, true); + regulator_disable(cs48l32->vdd_d); + return ret; +} + +static int __maybe_unused cs48l32_runtime_suspend(struct device *dev) +{ + struct cs48l32_mfd *cs48l32 = dev_get_drvdata(dev); + + dev_dbg(cs48l32->dev, "Entering sleep mode\n"); + + regcache_cache_only(cs48l32->regmap, true); + regulator_disable(cs48l32->vdd_d); + + return 0; +} + +static const struct dev_pm_ops cs48l32_pm_ops = { + SET_RUNTIME_PM_OPS(cs48l32_runtime_suspend, cs48l32_runtime_resume, NULL) +}; + +static const struct __maybe_unused of_device_id cs48l32_of_match[] = { + { .compatible = "cirrus,cs48l31", }, + { .compatible = "cirrus,cs48l32", }, + { .compatible = "cirrus,cs48l33", }, + {}, +}; + +static int cs48l32_configure_clk32k(struct cs48l32_mfd *cs48l32) +{ + int ret = 0; + + ret = clk_prepare_enable(cs48l32->mclk1); + if (ret) { + dev_err(cs48l32->dev, "Failed to enable 32k clock: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(cs48l32->regmap, CS48L32_CLOCK32K, + CS48L32_CLK_32K_EN_MASK | CS48L32_CLK_32K_SRC_MASK, + CS48L32_CLK_32K_EN_MASK | CS48L32_32K_MCLK1); + if (ret) { + dev_err(cs48l32->dev, "Failed to init 32k clock: %d\n", ret); + clk_disable_unprepare(cs48l32->mclk1); + return ret; + } + + return 0; +} + +static int cs48l32_get_clocks(struct cs48l32_mfd *cs48l32) +{ + cs48l32->mclk1 = devm_clk_get_optional(cs48l32->dev, "mclk1"); + if (IS_ERR(cs48l32->mclk1)) + return dev_err_probe(cs48l32->dev, PTR_ERR(cs48l32->mclk1), + "Failed to get mclk1\n"); + + return 0; +} + +static int cs48l32_get_reset_gpio(struct cs48l32_mfd *cs48l32) +{ + struct gpio_desc *reset; + + reset = devm_gpiod_get_optional(cs48l32->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset)) + return dev_err_probe(cs48l32->dev, PTR_ERR(reset), "Failed to request /RESET\n"); + + /* + * A hard reset is needed for full reset of the chip. We allow running + * without hard reset only because it can be useful for early + * prototyping and some debugging, but we need to warn it's not ideal. + */ + if (!reset) + dev_warn(cs48l32->dev, "Running without reset GPIO is not recommended\n"); + + cs48l32->reset_gpio = reset; + + return 0; +} + +static int cs48l32_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct cs48l32_mfd *cs48l32; + unsigned int hwid, rev, otp_rev; + int i, ret; + + cs48l32 = devm_kzalloc(&spi->dev, sizeof(*cs48l32), GFP_KERNEL); + if (!cs48l32) + return -ENOMEM; + + ret = cs48l32_init_regmap(spi, cs48l32); + if (ret) + return dev_err_probe(&spi->dev, ret, "Failed to allocate regmap\n"); + + cs48l32->dev = dev; + cs48l32->irq = spi->irq; + + dev_set_drvdata(cs48l32->dev, cs48l32); + + BLOCKING_INIT_NOTIFIER_HEAD(&cs48l32->notifier); + + regcache_cache_only(cs48l32->regmap, true); + + ret = cs48l32_get_reset_gpio(cs48l32); + if (ret) + return ret; + + ret = cs48l32_get_clocks(cs48l32); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(cs48l32_core_supplies); i++) + cs48l32->core_supplies[i].supply = cs48l32_core_supplies[i]; + + cs48l32->num_core_supplies = ARRAY_SIZE(cs48l32_core_supplies); + + ret = devm_regulator_bulk_get(dev, cs48l32->num_core_supplies, cs48l32->core_supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to request core supplies\n"); + + cs48l32->vdd_d = devm_regulator_get(cs48l32->dev, "VDD_D"); + if (IS_ERR(cs48l32->vdd_d)) + return dev_err_probe(dev, PTR_ERR(cs48l32->vdd_d), "Failed to request VDD_D\n"); + + ret = regulator_set_voltage(cs48l32->vdd_d, 1200000, 1200000); + if (ret) + return dev_err_probe(dev, ret, "Failed to request VDD_D=1.2v\n"); + + ret = regulator_bulk_enable(cs48l32->num_core_supplies, cs48l32->core_supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable core supplies\n"); + + ret = regulator_enable(cs48l32->vdd_d); + if (ret) { + dev_err(dev, "Failed to enable VDD_D: %d\n", ret); + goto err_enable; + } + + cs48l32_disable_hard_reset(cs48l32); + + regcache_cache_only(cs48l32->regmap, false); + + /* If we don't have a reset GPIO use a soft reset */ + if (!cs48l32->reset_gpio) { + ret = cs48l32_soft_reset(cs48l32); + if (ret) + goto err_reset; + } + + ret = cs48l32_wait_for_boot(cs48l32); + if (ret) { + dev_err(cs48l32->dev, "Device failed initial boot: %d\n", ret); + goto err_reset; + } + + ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &hwid); + if (ret) { + dev_err(dev, "Failed to read ID register: %d\n", ret); + goto err_reset; + } + hwid &= CS48L32_DEVID_MASK; + + switch (hwid) { + case CS48L31_SILICON_ID: + case CS48L32_SILICON_ID: + case CS48L33_SILICON_ID: + break; + default: + dev_err(cs48l32->dev, "Unknown device ID: %x\n", hwid); + ret = -EINVAL; + goto err_reset; + } + + ret = regmap_read(cs48l32->regmap, CS48L32_REVID, &rev); + if (ret) { + dev_err(dev, "Failed to read revision register: %d\n", ret); + goto err_reset; + } + rev &= CS48L32_AREVID_MASK | CS48L32_MTLREVID_MASK; + + ret = regmap_read(cs48l32->regmap, CS48L32_OTPID, &otp_rev); + if (ret) { + dev_err(dev, "Failed to read OTP revision register: %d\n", ret); + goto err_reset; + } + otp_rev &= CS48L32_OTPID_MASK; + + cs48l32->part = hwid & 0xff; + + dev_info(dev, "CS48L%x revision %X%u.%u\n", cs48l32->part, + rev >> CS48L32_AREVID_SHIFT, rev & CS48L32_MTLREVID_MASK, otp_rev); + + /* Apply hardware patch */ + ret = cs48l32_patch(cs48l32); + if (ret) { + dev_err(cs48l32->dev, "Failed to apply patch %d\n", ret); + goto err_reset; + } + + ret = cs48l32_configure_clk32k(cs48l32); + if (ret) + goto err_reset; + + pm_runtime_set_active(cs48l32->dev); + pm_runtime_enable(cs48l32->dev); + pm_runtime_set_autosuspend_delay(cs48l32->dev, 100); + pm_runtime_use_autosuspend(cs48l32->dev); + + ret = mfd_add_devices(cs48l32->dev, PLATFORM_DEVID_NONE, + cs48l32_devs, ARRAY_SIZE(cs48l32_devs), NULL, 0, NULL); + if (ret) { + dev_err(cs48l32->dev, "Failed to add subdevices: %d\n", ret); + goto err_clk32k; + } + + return 0; + +err_clk32k: + clk_disable_unprepare(cs48l32->mclk1); +err_reset: + cs48l32_enable_hard_reset(cs48l32); + regulator_disable(cs48l32->vdd_d); +err_enable: + regulator_bulk_disable(cs48l32->num_core_supplies, cs48l32->core_supplies); + + return ret; +} + +static void cs48l32_spi_remove(struct spi_device *spi) +{ + struct cs48l32_mfd *cs48l32 = spi_get_drvdata(spi); + + /* Prevent any IRQs being serviced while we clean up */ + disable_irq(cs48l32->irq); + + mfd_remove_devices(cs48l32->dev); + + pm_runtime_disable(cs48l32->dev); + regulator_disable(cs48l32->vdd_d); + clk_disable_unprepare(cs48l32->mclk1); + cs48l32_enable_hard_reset(cs48l32); + regulator_bulk_disable(cs48l32->num_core_supplies, cs48l32->core_supplies); +} + +static const struct spi_device_id cs48l32_spi_ids[] = { + { "cs48l31", }, + { "cs48l32", }, + { "cs48l33", }, + { }, +}; +MODULE_DEVICE_TABLE(spi, cs48l32_spi_ids); + +static struct spi_driver cs48l32_spi_driver = { + .driver = { + .name = "cs48l32", + .owner = THIS_MODULE, + .pm = &cs48l32_pm_ops, + .of_match_table = of_match_ptr(cs48l32_of_match), + }, + .probe = &cs48l32_spi_probe, + .remove = &cs48l32_spi_remove, + .id_table = cs48l32_spi_ids, +}; + +module_spi_driver(cs48l32_spi_driver); + +MODULE_SOFTDEP("pre: pinctrl-cs48l32 irq-cirrus-cs48l32"); +MODULE_DESCRIPTION("CS48L32 MFD driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/cs48l32.h b/drivers/mfd/cs48l32.h new file mode 100644 index 000000000000..36020c0cd686 --- /dev/null +++ b/drivers/mfd/cs48l32.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * MFD for Cirrus Logic CS48L32 audio codec. + * + * Copyright (C) 2016-2018, 2020, 2022 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef CS48L32_MFD_H +#define CS48L32_MFD_H + +#include + +#define CS48L31_SILICON_ID 0x48a31 +#define CS48L32_SILICON_ID 0x48a32 +#define CS48L33_SILICON_ID 0x48a33 + +#define CS48L32_32K_MCLK1 0 + +#define CS48L32_SFT_RESET_MAGIC 0x5a000000 +#define CS48L32_SEEN_BOOT_DONE BIT(0) + +#define CS48L32_BOOT_TIMEOUT_MS 25 + +int cs48l32_init_regmap(struct spi_device *spi, struct cs48l32_mfd *cs48l32); +int cs48l32_patch(struct cs48l32_mfd *cs48l32); + +#endif diff --git a/include/linux/mfd/cs48l32/core.h b/include/linux/mfd/cs48l32/core.h new file mode 100644 index 000000000000..1c4269d70c8c --- /dev/null +++ b/include/linux/mfd/cs48l32/core.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * MFD for Cirrus Logic CS48L32 audio codec. + * + * Copyright (C) 2016-2020, 2022 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef CS48L32_CORE_H +#define CS48L32_CORE_H + +#include +#include +#include +#include +#include +#include + +#define CS48L32_MAX_CORE_SUPPLIES 2 + +struct snd_soc_dapm_context; + +struct cs48l32_mfd { + struct regmap *regmap; + struct device *dev; + struct gpio_desc *reset_gpio; + struct clk *mclk1; + struct snd_soc_dapm_context *dapm; + + int num_core_supplies; + struct regulator_bulk_data core_supplies[CS48L32_MAX_CORE_SUPPLIES]; + struct regulator *vdd_d; + + struct device *irq_dev; + struct regmap_irq_chip_data *irq_data; + int irq; + + u8 part; + + struct blocking_notifier_head notifier; +}; + +static inline int cs48l32_call_notifiers(struct cs48l32_mfd *mfd, + unsigned long event, + void *data) +{ + return blocking_notifier_call_chain(&mfd->notifier, event, data); +} +#endif From patchwork Wed Nov 9 16:53:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 622950 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 C40C5C4332F for ; Wed, 9 Nov 2022 16:55:46 +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 CC15C850; Wed, 9 Nov 2022 17:54:54 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz CC15C850 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1668012944; bh=ydqhri5kvtDQYrqv3KbRYtDEfekt/JRdeQFr5wXg7ak=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=RR2J8uuZHgCFz1a3wKWBIAYisVSEDpNGVqb5eoDV3o0jq5Me3qT7WU9fWT5LiaTjH ESwMsABTYWQcyEetbPKIfkN++aZqGig+e2wsWQwI3rh5g2E4lNSfJvuNQPFHtmcBIw 7UxDuaHpewKe3i2u9O8aSaaR/WgwnchOJdbjxRwM= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 3B5F6F80580; Wed, 9 Nov 2022 17:53:55 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 31C24F8057A; Wed, 9 Nov 2022 17:53:52 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0a-001ae601.pphosted.com [67.231.149.25]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 873A6F802A0 for ; Wed, 9 Nov 2022 17:53:38 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 873A6F802A0 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="ZKr156ho" Received: from pps.filterd (m0077473.ppops.net [127.0.0.1]) by mx0a-001ae601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 2A9EEsmo027592; Wed, 9 Nov 2022 10:53:36 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=cqQO47P2lcMdmDdGNz1/UmZ7HXFI+NKT10pg7/xczD0=; b=ZKr156hoeW/l7m+9K8hKpgGihOsnR6cjsNB8OtRje2skR46opdIR1HyBgVXnBClVFKpu wRggrD8afx8LYlNVWJQ8t7gYmpGl76Oimrdd5KxW5Lkpcn8P+LbQp1elQYVgLJJAv21W O1SFDMgzm8GudtmN1Oz3vVdyLvOX4wNhLvazhy10VwD4v/WVXyB7Nk7f50geOqNBkLcY lEm7Jv381AzIq5DkVl8FZ0Lw7pwurOuxNAYHOn/pC++zdnY/x6L423lAfCkQP9+njyG+ N603M5wRayIzZtVo0hqtrSOll1FqOR6PZqsxsijBAmR8KzG7ka+lGUuNvchIYumoD/HG Lg== Received: from ediex01.ad.cirrus.com ([84.19.233.68]) by mx0a-001ae601.pphosted.com (PPS) with ESMTPS id 3knn81nk99-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Nov 2022 10:53:36 -0600 Received: from ediex02.ad.cirrus.com (198.61.84.81) by ediex01.ad.cirrus.com (198.61.84.80) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.15; Wed, 9 Nov 2022 10:53:34 -0600 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by anon-ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server id 15.2.1118.15 via Frontend Transport; Wed, 9 Nov 2022 10:53:34 -0600 Received: from debianA11184.ad.cirrus.com (AUSNPC0LSNW1.ad.cirrus.com [198.61.65.92]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id D7F29468; Wed, 9 Nov 2022 16:53:33 +0000 (UTC) From: Richard Fitzgerald To: , , , , , , Subject: [PATCH 04/12] dt-bindings: pinctrl: Add Cirrus Logic CS48L31/32/33 Date: Wed, 9 Nov 2022 16:53:23 +0000 Message-ID: <20221109165331.29332-5-rf@opensource.cirrus.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221109165331.29332-1-rf@opensource.cirrus.com> References: <20221109165331.29332-1-rf@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: VTjH1W65hT9eMedc0D1BYTzdkwt1QPAO X-Proofpoint-GUID: VTjH1W65hT9eMedc0D1BYTzdkwt1QPAO X-Proofpoint-Spam-Reason: safe Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, patches@opensource.cirrus.com, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Richard Fitzgerald 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" Codecs in this family have multiple digital I/O functions for audio, DSP subsystem, GPIO and various special functions. All muxable pins are selectable as either a GPIO or one of the available alternate functions. Signed-off-by: Richard Fitzgerald --- .../bindings/pinctrl/cirrus,cs48l32.yaml | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/cirrus,cs48l32.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/cirrus,cs48l32.yaml b/Documentation/devicetree/bindings/pinctrl/cirrus,cs48l32.yaml new file mode 100644 index 000000000000..b24fbae6a8f8 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/cirrus,cs48l32.yaml @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/cirrus,cs48l32.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS48L32 audio codec pinctrl driver + +maintainers: + - patches@opensource.cirrus.com + +description: | +The Cirrus Logic CS48L32 codec has a number of GPIO functions for +interfacing to external hardware. Certain groups of GPIO pins also +have an alternate function. + +The properties for this driver exist within the parent MFD driver node. +See the core bindings for the parent MFD driver for an example: + + Documentation/devicetree/bindings/mfd/cirrus,cs48l32.yaml + +And the generic pinctrl bindings: + + Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt + +properties: + pin-settings: + description: + One subnode is required to contain the default settings. It + contains an arbitrary number of configuration subnodes, one for + each group or pin configuration you want to apply as a default. + type: object + patternProperties: + '-pins$': + type: object + allOf: + - $ref: "pincfg-node.yaml#" + - $ref: "pinmux-node.yaml#" + properties: + groups: + description: + Name of one pin group to configure. + enum: [ asp1, asp2, in1-pdm, in2-pdm, + gpio1, gpio2, gpio3, gpio4, gpio5, gpio6, gpio7, + gpio8, gpio9, gpio10, gpio11, gpio12, gpio13, + gpio14, gpio15, gpio16 ] + + function: + description: + Name of function to assign to this group. + enum: [ asp1, asp2, in1-pdm, in2-pdm, spi2, io, dsp-gpio, + irq1, fll1-clk, fll1-lock, opclk, opclk-dsp, uart, + input-path-signal-detect, + ultrasonic-in1-activity-detect, ultrasonic-in2-activity-detect, + dma-ch0-programmable-transfer-complete, + dma-ch1-programmable-transfer-complete, + dma-ch2-programmable-transfer-complete, + dma-ch3-programmable-transfer-complete, + dma-ch4-programmable-transfer-complete, + dma-ch5-programmable-transfer-complete, + dma-ch6-programmable-transfer-complete, + dma-ch7-programmable-transfer-complete, + sample-rate-change-trigger-a, sample-rate-change-trigger-b, + sample-rate-change-trigger-c, sample-rate-change-trigger-d, + timer1-irq-ch1, timer1-irq-ch2, timer1-irq-ch3, timer1-irq-ch4, + timer2-irq-ch1, timer2-irq-ch2, timer2-irq-ch3, timer2-irq-ch4, + timer3-irq-ch1, timer3-irq-ch2, timer3-irq-ch3, timer3-irq-ch4, + timer4-irq-ch1, timer4-irq-ch2, timer4-irq-ch3, timer4-irq-ch4, + timer5-irq-ch1, timer5-irq-ch2, timer5-irq-ch3, timer5-irq-ch4, + timer-1, timer-2, timer-3, timer-4, timer-5 ] + + bias-disable: true + + bias-bus-hold: true + + bias-pull-up: true + + bias-pull-down: true + + drive-push-pull: true + + drive-open-drain: true + + drive-strength: + description: + Drive strength in mA. + enum: [ 4, 8 ] + + output-low: true + + output-high: true + + additionalProperties: false + + required: + - groups + + additionalProperties: false From patchwork Wed Nov 9 16:53:24 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 623807 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 31726C4332F for ; Wed, 9 Nov 2022 16:56:33 +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 4B7BE166D; Wed, 9 Nov 2022 17:55:41 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 4B7BE166D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1668012991; bh=Co4zjDr3Lf0lLQro+t4umtbJZzm6sSc2xKyTHg6Dl1o=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=pRRKyNkJ3qwyJriEqsunWbVh6B92sv8DobO8EbP6LKyYoDDekOPLm2WqDEB/vEQrL mCIaR4RinKqpnJ0onJgpIyY9+ZMHevY6YNY5GVIvW3MR/p+PLeZ2JL/BlhH/QWebVH WH09EjCP8YEdSH5+J85OM6vwgIggAXmw3aeoG7Bo= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 41B5AF805AF; Wed, 9 Nov 2022 17:54:01 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 5B28CF80587; Wed, 9 Nov 2022 17:53:54 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0b-001ae601.pphosted.com [67.231.152.168]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 4BDDFF800EC for ; Wed, 9 Nov 2022 17:53:38 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 4BDDFF800EC Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="j39otrAG" Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 2A9EDNjT027347; Wed, 9 Nov 2022 10:53:37 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=Q/WbI1em8k4vxyaQ6hKXQak8tzf3uNIASHJiJMLKsQY=; b=j39otrAGNXZgf3fAvfFKOx60cGVYOkea4mp4eCncVPZonmSASXT8/7d+wki3dCMm+iEs mOXaspeGx2BaKMlKW/Qsn9jTTLqVyaABVBXTl3sOh3szB9TROYZvG00hcezl4TuTBry2 1m8XyL5m7mZ3/ajvbNsCjW3lKiTui5RA/yeoAWnt3lZFW6bhAwW+WqcIqzCMD1w1oR2d DE+eHZKiFhkKkczv0mORYl7wkjWGQS8h4hAvxaiwj1UUiYu9CvzA5EsZCosz6QZqPFn1 e8MF1+KTczwq/1XdEwZcr0jwWqcjbXj2MR2bnBjcQ6yijdBEC5Dcfhg+/4HhKpNK+p/h SA== Received: from ediex02.ad.cirrus.com ([84.19.233.68]) by mx0b-001ae601.pphosted.com (PPS) with ESMTPS id 3knm8pde7v-5 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Nov 2022 10:53:36 -0600 Received: from ediex02.ad.cirrus.com (198.61.84.81) by ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.15; Wed, 9 Nov 2022 10:53:34 -0600 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by anon-ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server id 15.2.1118.15 via Frontend Transport; Wed, 9 Nov 2022 10:53:34 -0600 Received: from debianA11184.ad.cirrus.com (AUSNPC0LSNW1.ad.cirrus.com [198.61.65.92]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id 6727A478; Wed, 9 Nov 2022 16:53:34 +0000 (UTC) From: Richard Fitzgerald To: , , , , , , Subject: [PATCH 05/12] pinctrl: cirrus: Add support for CS48L31/32/33 codecs Date: Wed, 9 Nov 2022 16:53:24 +0000 Message-ID: <20221109165331.29332-6-rf@opensource.cirrus.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221109165331.29332-1-rf@opensource.cirrus.com> References: <20221109165331.29332-1-rf@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: n76uAZArC6TEZecmrLGIImjob4a_Zq3C X-Proofpoint-GUID: n76uAZArC6TEZecmrLGIImjob4a_Zq3C X-Proofpoint-Spam-Reason: safe Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, patches@opensource.cirrus.com, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Richard Fitzgerald 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: Piotr Stankiewicz Codecs in this family have multiple digital I/O functions for audio, DSP subsystem, GPIO and various special functions. All muxable pins are selectable as either a GPIO or an alternate function. Signed-off-by: Piotr Stankiewicz Signed-off-by: Qi Zhou Signed-off-by: Stuart Henderson Signed-off-by: Richard Fitzgerald --- MAINTAINERS | 1 + drivers/pinctrl/cirrus/Kconfig | 5 + drivers/pinctrl/cirrus/Makefile | 2 + drivers/pinctrl/cirrus/pinctrl-cs48l32.c | 932 +++++++++++++++++++++++ drivers/pinctrl/cirrus/pinctrl-cs48l32.h | 62 ++ 5 files changed, 1002 insertions(+) create mode 100644 drivers/pinctrl/cirrus/pinctrl-cs48l32.c create mode 100644 drivers/pinctrl/cirrus/pinctrl-cs48l32.h diff --git a/MAINTAINERS b/MAINTAINERS index f1d696f29f11..cd1773d39dd8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5011,6 +5011,7 @@ W: https://github.com/CirrusLogic/linux-drivers/wiki T: git https://github.com/CirrusLogic/linux-drivers.git F: Documentation/devicetree/bindings/mfd/cirrus,cs48l32.yaml F: Documentation/devicetree/bindings/mfd/cirrus,madera.yaml +F: Documentation/devicetree/bindings/pinctrl/cirrus,cs48l32.yaml F: Documentation/devicetree/bindings/pinctrl/cirrus,madera.yaml F: Documentation/devicetree/bindings/sound/cirrus,madera.yaml F: drivers/gpio/gpio-madera* diff --git a/drivers/pinctrl/cirrus/Kconfig b/drivers/pinctrl/cirrus/Kconfig index 530426a74f75..c51192bde87a 100644 --- a/drivers/pinctrl/cirrus/Kconfig +++ b/drivers/pinctrl/cirrus/Kconfig @@ -30,3 +30,8 @@ config PINCTRL_CS47L90 config PINCTRL_CS47L92 bool + +config PINCTRL_CS48L32 + tristate + select PINMUX + select GENERIC_PINCONF diff --git a/drivers/pinctrl/cirrus/Makefile b/drivers/pinctrl/cirrus/Makefile index a484518c840e..18290f6be00c 100644 --- a/drivers/pinctrl/cirrus/Makefile +++ b/drivers/pinctrl/cirrus/Makefile @@ -19,4 +19,6 @@ ifeq ($(CONFIG_PINCTRL_CS47L92),y) pinctrl-madera-objs += pinctrl-cs47l92.o endif +obj-$(CONFIG_PINCTRL_CS48L32) += pinctrl-cs48l32.o + obj-$(CONFIG_PINCTRL_MADERA) += pinctrl-madera.o diff --git a/drivers/pinctrl/cirrus/pinctrl-cs48l32.c b/drivers/pinctrl/cirrus/pinctrl-cs48l32.c new file mode 100644 index 000000000000..cb5031d6d0ce --- /dev/null +++ b/drivers/pinctrl/cirrus/pinctrl-cs48l32.c @@ -0,0 +1,932 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Pinctrl for Cirrus Logic CS48L32 + * + * Copyright (C) 2017-2018, 2020, 2022 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pinctrl-utils.h" +#include "pinctrl-cs48l32.h" + +/* + * Pins are named after their GPIO number + * NOTE: IDs are zero-indexed for coding convenience + */ +static const struct pinctrl_pin_desc cs48l32_pins[] = { + PINCTRL_PIN(0, "gpio1"), + PINCTRL_PIN(1, "gpio2"), + PINCTRL_PIN(2, "gpio3"), + PINCTRL_PIN(3, "gpio4"), + PINCTRL_PIN(4, "gpio5"), + PINCTRL_PIN(5, "gpio6"), + PINCTRL_PIN(6, "gpio7"), + PINCTRL_PIN(7, "gpio8"), + PINCTRL_PIN(8, "gpio9"), + PINCTRL_PIN(9, "gpio10"), + PINCTRL_PIN(10, "gpio11"), + PINCTRL_PIN(11, "gpio12"), + PINCTRL_PIN(12, "gpio13"), + PINCTRL_PIN(13, "gpio14"), + PINCTRL_PIN(14, "gpio15"), + PINCTRL_PIN(15, "gpio16"), +}; + +/* + * All single-pin functions can be mapped to any GPIO, however pinmux applies + * functions to pin groups and only those groups declared as supporting that + * function. To make this work we must put each pin in its own dummy group so + * that the functions can be described as applying to all pins. + * Since these do not correspond to anything in the actual hardware - they are + * merely an adaptation to pinctrl's view of the world - we use the same name + * as the pin to avoid confusion when comparing with datasheet instructions + */ +static const char * const cs48l32_pin_single_group_names[] = { + "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", +}; + +/* set of pin numbers for single-pin groups */ +static const unsigned int cs48l32_pin_single_group_pins[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, +}; + +static const char * const cs48l32_asp1_group_names[] = { "asp1" }; +static const char * const cs48l32_asp2_group_names[] = { "asp2" }; +static const char * const cs48l32_in1pdm_group_names[] = { "in1-pdm" }; +static const char * const cs48l32_in2pdm_group_names[] = { "in2-pdm" }; +static const char * const cs48l32_spi2_group_names[] = { "spi2" }; + +/* + * alt-functions always apply to only one group, other functions always + * apply to all pins + */ +static const struct { + const char *name; + const char * const *group_names; + u32 func; +} cs48l32_mux_funcs[] = { + { + .name = "asp1", + .group_names = cs48l32_asp1_group_names, + .func = 0x000 + }, + { + .name = "asp2", + .group_names = cs48l32_asp2_group_names, + .func = 0x000 + }, + { + .name = "in1-pdm", + .group_names = cs48l32_in1pdm_group_names, + .func = 0x000 + }, + { + .name = "in2-pdm", + .group_names = cs48l32_in2pdm_group_names, + .func = 0x000, + }, + { + .name = "spi2", + .group_names = cs48l32_spi2_group_names, + .func = 0x000 + }, + { + .name = "io", + .group_names = cs48l32_pin_single_group_names, + .func = 0x001 + }, + { + .name = "dsp-gpio", + .group_names = cs48l32_pin_single_group_names, + .func = 0x002 + }, + { + .name = "irq1", + .group_names = cs48l32_pin_single_group_names, + .func = 0x003 + }, + { + .name = "fll1-clk", + .group_names = cs48l32_pin_single_group_names, + .func = 0x010 + }, + { + .name = "fll1-lock", + .group_names = cs48l32_pin_single_group_names, + .func = 0x018 + }, + { + .name = "opclk", + .group_names = cs48l32_pin_single_group_names, + .func = 0x048 + }, + { + .name = "opclk-dsp", + .group_names = cs48l32_pin_single_group_names, + .func = 0x04a + }, + { + .name = "uart", + .group_names = cs48l32_pin_single_group_names, + .func = 0x04c + }, + { + .name = "input-path-signal-detect", + .group_names = cs48l32_pin_single_group_names, + .func = 0x08c + }, + { + .name = "ultrasonic-in1-activity-detect", + .group_names = cs48l32_pin_single_group_names, + .func = 0x090 + }, + { + .name = "ultrasonic-in2-activity-detect", + .group_names = cs48l32_pin_single_group_names, + .func = 0x092 + }, + { + .name = "dma-ch0-programmable-transfer-complete", + .group_names = cs48l32_pin_single_group_names, + .func = 0x190 + }, + { + .name = "dma-ch1-programmable-transfer-complete", + .group_names = cs48l32_pin_single_group_names, + .func = 0x191 + }, + { + .name = "dma-ch2-programmable-transfer-complete", + .group_names = cs48l32_pin_single_group_names, + .func = 0x192 + }, + { + .name = "dma-ch3-programmable-transfer-complete", + .group_names = cs48l32_pin_single_group_names, + .func = 0x193 + }, + { + .name = "dma-ch4-programmable-transfer-complete", + .group_names = cs48l32_pin_single_group_names, + .func = 0x194 + }, + { + .name = "dma-ch5-programmable-transfer-complete", + .group_names = cs48l32_pin_single_group_names, + .func = 0x195 + }, + { + .name = "dma-ch6-programmable-transfer-complete", + .group_names = cs48l32_pin_single_group_names, + .func = 0x196 + }, + { + .name = "dma-ch7-programmable-transfer-complete", + .group_names = cs48l32_pin_single_group_names, + .func = 0x197 + }, + { + .name = "sample-rate-change-trigger-a", + .group_names = cs48l32_pin_single_group_names, + .func = 0x214 + }, + { + .name = "sample-rate-change-trigger-b", + .group_names = cs48l32_pin_single_group_names, + .func = 0x215 + }, + { + .name = "sample-rate-change-trigger-c", + .group_names = cs48l32_pin_single_group_names, + .func = 0x216 + }, + { + .name = "sample-rate-change-trigger-d", + .group_names = cs48l32_pin_single_group_names, + .func = 0x217 + }, + { + .name = "timer1-irq-ch1", + .group_names = cs48l32_pin_single_group_names, + .func = 0x230 + }, + { + .name = "timer1-irq-ch2", + .group_names = cs48l32_pin_single_group_names, + .func = 0x231 + }, + { + .name = "timer1-irq-ch3", + .group_names = cs48l32_pin_single_group_names, + .func = 0x232 + }, + { + .name = "timer1-irq-ch4", + .group_names = cs48l32_pin_single_group_names, + .func = 0x233 + }, + { + .name = "timer2-irq-ch1", + .group_names = cs48l32_pin_single_group_names, + .func = 0x234 + }, + { + .name = "timer2-irq-ch2", + .group_names = cs48l32_pin_single_group_names, + .func = 0x235 + }, + { + .name = "timer2-irq-ch3", + .group_names = cs48l32_pin_single_group_names, + .func = 0x236 + }, + { + .name = "timer2-irq-ch4", + .group_names = cs48l32_pin_single_group_names, + .func = 0x237 + }, + { + .name = "timer3-irq-ch1", + .group_names = cs48l32_pin_single_group_names, + .func = 0x238 + }, + { + .name = "timer3-irq-ch2", + .group_names = cs48l32_pin_single_group_names, + .func = 0x239 + }, + { + .name = "timer3-irq-ch3", + .group_names = cs48l32_pin_single_group_names, + .func = 0x23a + }, + { + .name = "timer3-irq-ch4", + .group_names = cs48l32_pin_single_group_names, + .func = 0x23b + }, + { + .name = "timer4-irq-ch1", + .group_names = cs48l32_pin_single_group_names, + .func = 0x23c + }, + { + .name = "timer4-irq-ch2", + .group_names = cs48l32_pin_single_group_names, + .func = 0x23d + }, + { + .name = "timer4-irq-ch3", + .group_names = cs48l32_pin_single_group_names, + .func = 0x23e + }, + { + .name = "timer4-irq-ch4", + .group_names = cs48l32_pin_single_group_names, + .func = 0x23f + }, + { + .name = "timer5-irq-ch1", + .group_names = cs48l32_pin_single_group_names, + .func = 0x240 + }, + { + .name = "timer5-irq-ch2", + .group_names = cs48l32_pin_single_group_names, + .func = 0x241 + }, + { + .name = "timer5-irq-ch3", + .group_names = cs48l32_pin_single_group_names, + .func = 0x242 + }, + { + .name = "timer5-irq-ch4", + .group_names = cs48l32_pin_single_group_names, + .func = 0x243 + }, + { + .name = "timer-1", + .group_names = cs48l32_pin_single_group_names, + .func = 0x250 + }, + { + .name = "timer-2", + .group_names = cs48l32_pin_single_group_names, + .func = 0x251 + }, + { + .name = "timer-3", + .group_names = cs48l32_pin_single_group_names, + .func = 0x252 + }, + { + .name = "timer-4", + .group_names = cs48l32_pin_single_group_names, + .func = 0x253 + }, + { + .name = "timer-5", + .group_names = cs48l32_pin_single_group_names, + .func = 0x254 + }, +}; + +/* Note - all 1 less than in datasheet because these are zero-indexed */ +static const unsigned int cs48l32_asp1_pins[] = { 2, 3, 4, 5 }; +static const unsigned int cs48l32_asp2_pins[] = { 6, 7, 8, 9 }; +static const unsigned int cs48l32_spi2_pins[] = { 10, 11, 12, 13, 14, 15 }; + +static const struct cs48l32_pin_groups cs48l32_pin_groups[] = { + { "asp1", cs48l32_asp1_pins, ARRAY_SIZE(cs48l32_asp1_pins) }, + { "asp2", cs48l32_asp2_pins, ARRAY_SIZE(cs48l32_asp2_pins) }, + { "spi2", cs48l32_spi2_pins, ARRAY_SIZE(cs48l32_spi2_pins) }, +}; + +static const struct cs48l32_pin_chip cs48l32_pin_chip = { + .n_pins = CS48L32_NUM_GPIOS, + .pin_groups = cs48l32_pin_groups, + .n_pin_groups = ARRAY_SIZE(cs48l32_pin_groups), +}; + +static unsigned int cs48l32_pin_make_drv_str(struct cs48l32_pin_private *priv, + unsigned int milliamps) +{ + switch (milliamps) { + case 4: + return 0; + case 8: + return 1 << CS48L32_GP_DRV_STR_SHIFT; + default: + break; + } + + dev_warn(priv->dev, "%u mA is not a valid drive strength\n", milliamps); + + return 0; +} + +static unsigned int cs48l32_pin_unmake_drv_str(struct cs48l32_pin_private *priv, + unsigned int regval) +{ + regval = (regval & CS48L32_GP_DRV_STR_MASK) >> CS48L32_GP_DRV_STR_SHIFT; + + switch (regval) { + case 0: + return 4; + case 1: + return 8; + default: + break; + } + + return 0; +} + +static int cs48l32_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct cs48l32_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + + /* Number of alt function groups plus number of single-pin groups */ + return priv->chip->n_pin_groups + priv->chip->n_pins; +} + +static const char *cs48l32_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct cs48l32_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + + if (selector < priv->chip->n_pin_groups) + return priv->chip->pin_groups[selector].name; + + selector -= priv->chip->n_pin_groups; + + return cs48l32_pin_single_group_names[selector]; +} + +static int cs48l32_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + const unsigned int **pins, + unsigned int *num_pins) +{ + struct cs48l32_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + + if (selector < priv->chip->n_pin_groups) { + *pins = priv->chip->pin_groups[selector].pins; + *num_pins = priv->chip->pin_groups[selector].n_pins; + } else { + /* return the dummy group for a single pin */ + selector -= priv->chip->n_pin_groups; + *pins = &cs48l32_pin_single_group_pins[selector]; + *num_pins = 1; + } + + return 0; +} + +static void cs48l32_pin_dbg_show_fn(struct cs48l32_pin_private *priv, + struct seq_file *s, + unsigned int pin, unsigned int fn) +{ + const struct cs48l32_pin_chip *chip = priv->chip; + int i, g_pin; + + if (fn != 0) { + for (i = 0; i < ARRAY_SIZE(cs48l32_mux_funcs); ++i) { + if (cs48l32_mux_funcs[i].func == fn) { + seq_printf(s, " FN=%s", cs48l32_mux_funcs[i].name); + return; + } + } + return; /* ignore unknown function values */ + } + + /* alt function */ + for (i = 0; i < chip->n_pin_groups; ++i) { + for (g_pin = 0; g_pin < chip->pin_groups[i].n_pins; ++g_pin) { + if (chip->pin_groups[i].pins[g_pin] == pin) { + seq_printf(s, " FN=%s", chip->pin_groups[i].name); + return; + } + } + } +} + +static void cs48l32_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned int pin) +{ + struct cs48l32_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + unsigned int reg = CS48L32_GPIO1_CTRL1 + (4 * pin); + unsigned int conf, fn; + int ret; + + ret = regmap_read(priv->mfd->regmap, reg, &conf); + if (ret) + return; + + seq_printf(s, "%08x", conf); + + fn = (conf & CS48L32_GP_FN_MASK) >> CS48L32_GP_FN_SHIFT; + cs48l32_pin_dbg_show_fn(priv, s, pin, fn); + + /* State of direction bit is only relevant if function==1 */ + if (fn == 1) { + if (conf & CS48L32_GP_DIR_MASK) + seq_puts(s, " IN"); + else + seq_puts(s, " OUT"); + } + + if (conf & CS48L32_GP_PU_MASK) + seq_puts(s, " PU"); + + if (conf & CS48L32_GP_PD_MASK) + seq_puts(s, " PD"); + + if (conf & CS48L32_GP_DB_MASK) + seq_puts(s, " DB"); + + if (conf & CS48L32_GP_OP_CFG_MASK) + seq_puts(s, " OD"); + else + seq_puts(s, " CMOS"); + + seq_printf(s, " DRV=%umA", cs48l32_pin_unmake_drv_str(priv, conf)); +} + + +static const struct pinctrl_ops cs48l32_pin_group_ops = { + .get_groups_count = &cs48l32_get_groups_count, + .get_group_name = &cs48l32_get_group_name, + .get_group_pins = &cs48l32_get_group_pins, + .pin_dbg_show = &cs48l32_pin_dbg_show, + .dt_node_to_map = &pinconf_generic_dt_node_to_map_all, + .dt_free_map = &pinctrl_utils_free_map, +}; + +static int cs48l32_mux_get_funcs_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(cs48l32_mux_funcs); +} + +static const char *cs48l32_mux_get_func_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + return cs48l32_mux_funcs[selector].name; +} + +static int cs48l32_mux_get_groups(struct pinctrl_dev *pctldev, + unsigned int selector, + const char * const **groups, + unsigned int * const num_groups) +{ + struct cs48l32_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + + *groups = cs48l32_mux_funcs[selector].group_names; + + if (cs48l32_mux_funcs[selector].func == 0) { + /* alt func always maps to a single group */ + *num_groups = 1; + } else { + /* other funcs map to all available gpio pins */ + *num_groups = priv->chip->n_pins; + } + + return 0; +} + +static int cs48l32_mux_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, + unsigned int group) +{ + struct cs48l32_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + struct cs48l32_mfd *mfd = priv->mfd; + const struct cs48l32_pin_groups *pin_group = priv->chip->pin_groups; + unsigned int n_chip_groups = priv->chip->n_pin_groups; + const char *func_name = cs48l32_mux_funcs[selector].name; + unsigned int reg; + int i, ret; + + dev_dbg(priv->dev, "%s selecting %u (%s) for group %u (%s)\n", + __func__, selector, func_name, group, + cs48l32_get_group_name(pctldev, group)); + + if (cs48l32_mux_funcs[selector].func == 0) { + /* alt func pin assignments are codec-specific */ + for (i = 0; i < n_chip_groups; ++i) { + if (strcmp(func_name, pin_group->name) == 0) + break; + + ++pin_group; + } + + if (i == n_chip_groups) + return -EINVAL; + + for (i = 0; i < pin_group->n_pins; ++i) { + reg = CS48L32_GPIO1_CTRL1 + (4 * pin_group->pins[i]); + + dev_dbg(priv->dev, "%s setting 0x%x func bits to 0\n", __func__, reg); + + ret = regmap_update_bits(mfd->regmap, reg, CS48L32_GP_FN_MASK, 0); + if (ret) + break; + + } + } else { + /* + * for other funcs the group will be the gpio number and will + * be offset by the number of chip-specific functions at the + * start of the group list + */ + group -= n_chip_groups; + reg = CS48L32_GPIO1_CTRL1 + (4 * group); + + dev_dbg(priv->dev, "%s setting 0x%x func bits to 0x%x\n", + __func__, reg, cs48l32_mux_funcs[selector].func); + + ret = regmap_update_bits(mfd->regmap, + reg, + CS48L32_GP_FN_MASK, + cs48l32_mux_funcs[selector].func); + } + + if (ret) + dev_err(priv->dev, "Failed to write to 0x%x (%d)\n", reg, ret); + + return ret; +} + +static int cs48l32_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin, + bool input) +{ + struct cs48l32_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + struct cs48l32_mfd *mfd = priv->mfd; + unsigned int reg = CS48L32_GPIO1_CTRL1 + (4 * pin); + unsigned int val; + int ret; + + if (input) + val = CS48L32_GP_DIR_MASK; + else + val = 0; + + ret = regmap_update_bits(mfd->regmap, reg, CS48L32_GP_DIR_MASK, val); + if (ret) + dev_err(priv->dev, "Failed to write to 0x%x (%d)\n", reg, ret); + + return ret; +} + +static int cs48l32_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct cs48l32_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + struct cs48l32_mfd *mfd = priv->mfd; + unsigned int reg = CS48L32_GPIO1_CTRL1 + (4 * pin); + int ret; + + /* put the pin into GPIO mode */ + ret = regmap_update_bits(mfd->regmap, reg, CS48L32_GP_FN_MASK, 1); + if (ret) + dev_err(priv->dev, "Failed to write to 0x%x (%d)\n", reg, ret); + + return ret; +} + +static void cs48l32_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct cs48l32_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + struct cs48l32_mfd *mfd = priv->mfd; + unsigned int reg = CS48L32_GPIO1_CTRL1 + (4 * pin); + int ret; + + /* disable GPIO by setting to GPIO IN */ + cs48l32_gpio_set_direction(pctldev, range, pin, true); + + ret = regmap_update_bits(mfd->regmap, reg, CS48L32_GP_FN_MASK, 1); + if (ret) + dev_err(priv->dev, "Failed to write to 0x%x (%d)\n", reg, ret); +} +static const struct pinmux_ops cs48l32_pin_mux_ops = { + .get_functions_count = &cs48l32_mux_get_funcs_count, + .get_function_name = &cs48l32_mux_get_func_name, + .get_function_groups = &cs48l32_mux_get_groups, + .set_mux = &cs48l32_mux_set_mux, + .gpio_request_enable = &cs48l32_gpio_request_enable, + .gpio_disable_free = &cs48l32_gpio_disable_free, + .gpio_set_direction = &cs48l32_gpio_set_direction, + .strict = true, /* GPIO and other functions are exclusive */ +}; + +static int cs48l32_pin_conf_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct cs48l32_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + unsigned int param = pinconf_to_config_param(*config); + unsigned int result = 0; + unsigned int conf; + int ret; + + ret = regmap_read(priv->mfd->regmap, CS48L32_GPIO1_CTRL1 + (4 * pin), &conf); + if (ret) { + dev_err(priv->dev, "Failed to read GP%d conf (%d)\n", pin + 1, ret); + return ret; + } + + switch (param) { + case PIN_CONFIG_BIAS_BUS_HOLD: + conf &= CS48L32_GP_PU_MASK | CS48L32_GP_PD_MASK; + if (conf == (CS48L32_GP_PU_MASK | CS48L32_GP_PD_MASK)) + result = 1; + break; + case PIN_CONFIG_BIAS_DISABLE: + conf &= CS48L32_GP_PU_MASK | CS48L32_GP_PD_MASK; + if (!conf) + result = 1; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + conf &= CS48L32_GP_PU_MASK | CS48L32_GP_PD_MASK; + if (conf == CS48L32_GP_PD_MASK) + result = 1; + break; + case PIN_CONFIG_BIAS_PULL_UP: + conf &= CS48L32_GP_PU_MASK | CS48L32_GP_PD_MASK; + if (conf == CS48L32_GP_PU_MASK) + result = 1; + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + if (conf & CS48L32_GP_OP_CFG_MASK) + result = 1; + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + if (!(conf & CS48L32_GP_OP_CFG_MASK)) + result = 1; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + result = cs48l32_pin_unmake_drv_str(priv, conf); + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + dev_dbg(priv->dev, "Input debounce time not supported."); + break; + case PIN_CONFIG_INPUT_ENABLE: + if (conf & CS48L32_GP_DIR_MASK) + result = 1; + break; + case PIN_CONFIG_OUTPUT: + if ((conf & CS48L32_GP_DIR_MASK) && (conf & CS48L32_GP_LVL_MASK)) + result = 1; + break; + default: + return -EINVAL; + } + + *config = pinconf_to_config_packed(param, result); + + return 0; +} + +static int cs48l32_pin_conf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct cs48l32_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + unsigned int conf = 0; + unsigned int mask = 0; + unsigned int reg = CS48L32_GPIO1_CTRL1 + (4 * pin); + unsigned int val; + int ret; + + while (num_configs) { + dev_dbg(priv->dev, "%s config 0x%lx\n", __func__, *configs); + + switch (pinconf_to_config_param(*configs)) { + case PIN_CONFIG_BIAS_BUS_HOLD: + mask |= CS48L32_GP_PU_MASK | CS48L32_GP_PD_MASK; + conf |= CS48L32_GP_PU_MASK | CS48L32_GP_PD_MASK; + break; + case PIN_CONFIG_BIAS_DISABLE: + mask |= CS48L32_GP_PU_MASK | CS48L32_GP_PD_MASK; + conf &= ~(CS48L32_GP_PU_MASK | CS48L32_GP_PD_MASK); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + mask |= CS48L32_GP_PU_MASK | CS48L32_GP_PD_MASK; + conf |= CS48L32_GP_PD_MASK; + conf &= ~CS48L32_GP_PU_MASK; + break; + case PIN_CONFIG_BIAS_PULL_UP: + mask |= CS48L32_GP_PU_MASK | CS48L32_GP_PD_MASK; + conf |= CS48L32_GP_PU_MASK; + conf &= ~CS48L32_GP_PD_MASK; + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + mask |= CS48L32_GP_OP_CFG_MASK; + conf |= CS48L32_GP_OP_CFG_MASK; + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + mask |= CS48L32_GP_OP_CFG_MASK; + conf &= ~CS48L32_GP_OP_CFG_MASK; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + val = pinconf_to_config_argument(*configs); + mask |= CS48L32_GP_DRV_STR_MASK; + conf &= ~CS48L32_GP_DRV_STR_MASK; + conf |= cs48l32_pin_make_drv_str(priv, val); + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + dev_dbg(priv->dev, "Input debounce time not supported."); + break; + case PIN_CONFIG_INPUT_ENABLE: + val = pinconf_to_config_argument(*configs); + mask |= CS48L32_GP_DIR_MASK; + if (val) + conf |= CS48L32_GP_DIR_MASK; + else + conf &= ~CS48L32_GP_DIR_MASK; + break; + case PIN_CONFIG_OUTPUT: + val = pinconf_to_config_argument(*configs); + mask |= CS48L32_GP_LVL_MASK; + if (val) + conf |= CS48L32_GP_LVL_MASK; + else + conf &= ~CS48L32_GP_LVL_MASK; + + mask |= CS48L32_GP_DIR_MASK; + conf &= ~CS48L32_GP_DIR_MASK; + break; + default: + return -EINVAL; + } + + ++configs; + --num_configs; + } + + dev_dbg(priv->dev, "%s gpio%d 0x%x:0x%x\n", __func__, pin + 1, reg, conf); + + ret = regmap_update_bits(priv->mfd->regmap, reg, mask, conf); + if (ret) + dev_err(priv->dev, "Failed to write GPIO%d conf (%d) reg 0x%x\n", + pin + 1, ret, reg); + + return ret; +} + +static int cs48l32_pin_conf_group_set(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned long *configs, + unsigned int num_configs) +{ + struct cs48l32_pin_private *priv = pinctrl_dev_get_drvdata(pctldev); + const struct cs48l32_pin_groups *pin_group; + unsigned int n_groups = priv->chip->n_pin_groups; + int i, ret; + + dev_dbg(priv->dev, "%s setting group %s\n", __func__, + cs48l32_get_group_name(pctldev, selector)); + + if (selector >= n_groups) { + /* group is a single pin, convert to pin number and set */ + return cs48l32_pin_conf_set(pctldev, + selector - n_groups, + configs, + num_configs); + } else { + pin_group = &priv->chip->pin_groups[selector]; + + for (i = 0; i < pin_group->n_pins; ++i) { + ret = cs48l32_pin_conf_set(pctldev, + pin_group->pins[i], + configs, + num_configs); + if (ret) + return ret; + } + } + + return 0; +} + +static const struct pinconf_ops cs48l32_pin_conf_ops = { + .is_generic = true, + .pin_config_get = &cs48l32_pin_conf_get, + .pin_config_set = &cs48l32_pin_conf_set, + .pin_config_group_set = &cs48l32_pin_conf_group_set, + +}; + +static struct pinctrl_desc cs48l32_pin_desc = { + .name = "cs48l32-pinctrl", + .pins = cs48l32_pins, + .pctlops = &cs48l32_pin_group_ops, + .pmxops = &cs48l32_pin_mux_ops, + .confops = &cs48l32_pin_conf_ops, + .owner = THIS_MODULE, +}; + +static int cs48l32_pin_probe(struct platform_device *pdev) +{ + struct cs48l32_mfd *mfd = dev_get_drvdata(pdev->dev.parent); + struct cs48l32_pin_private *priv; + int ret; + + BUILD_BUG_ON(ARRAY_SIZE(cs48l32_pin_single_group_names) != + ARRAY_SIZE(cs48l32_pin_single_group_pins)); + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + priv->mfd = mfd; + /* Composite MFD device so shares the parent OF node. */ + pdev->dev.of_node = mfd->dev->of_node; + + priv->chip = &cs48l32_pin_chip; + cs48l32_pin_desc.npins = priv->chip->n_pins; + + ret = devm_pinctrl_register_and_init(&pdev->dev, &cs48l32_pin_desc, priv, &priv->pctl); + if (ret) + return dev_err_probe(priv->dev, ret, "Failed pinctrl register\n"); + + ret = pinctrl_enable(priv->pctl); + if (ret) + return dev_err_probe(priv->dev, ret, "Failed to enable pinctrl\n"); + + platform_set_drvdata(pdev, priv); + + dev_dbg(priv->dev, "pinctrl registered\n"); + + return 0; +} + +static struct platform_driver cs48l32_pin_driver = { + .probe = &cs48l32_pin_probe, + .driver = { + .name = "cs48l32-pinctrl", + }, +}; + +module_platform_driver(cs48l32_pin_driver); + +MODULE_DESCRIPTION("CS48L32 pinctrl driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Piotr Stankiewicz "); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/cirrus/pinctrl-cs48l32.h b/drivers/pinctrl/cirrus/pinctrl-cs48l32.h new file mode 100644 index 000000000000..2193c7558dd3 --- /dev/null +++ b/drivers/pinctrl/cirrus/pinctrl-cs48l32.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Pinctrl for Cirrus Logic CS48L32 + * + * Copyright (C) 2020, 2022 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef PINCTRL_CS48L32_H +#define PINCTRL_CS48L32_H + +#include +#include + +struct pinctrl_dev; + +#define CS48L32_GP_DIR_MASK 0x80000000 +#define CS48L32_GP_DIR_SHIFT 31 +#define CS48L32_GP_PU_MASK 0x40000000 +#define CS48L32_GP_PU_SHIFT 30 +#define CS48L32_GP_PD_MASK 0x20000000 +#define CS48L32_GP_PD_SHIFT 29 +#define CS48L32_GP_DRV_STR_MASK 0x03000000 +#define CS48L32_GP_DRV_STR_SHIFT 24 +#define CS48L32_GP_DBTIME_MASK 0x000f0000 +#define CS48L32_GP_DBTIME_SHIFT 16 +#define CS48L32_GP_LVL_MASK 0x00008000 +#define CS48L32_GP_LVL_SHIFT 15 +#define CS48L32_GP_OP_CFG_MASK 0x00004000 +#define CS48L32_GP_OP_CFG_SHIFT 14 +#define CS48L32_GP_DB_MASK 0x00002000 +#define CS48L32_GP_DB_SHIFT 13 +#define CS48L32_GP_POL_MASK 0x00001000 +#define CS48L32_GP_POL_SHIFT 12 +#define CS48L32_GP_FN_MASK 0x000007ff +#define CS48L32_GP_FN_SHIFT 0 + +#define CS48L32_NUM_GPIOS 16 + +struct cs48l32_pin_groups { + const char *name; + const unsigned int *pins; + unsigned int n_pins; +}; + +struct cs48l32_pin_chip { + unsigned int n_pins; + + const struct cs48l32_pin_groups *pin_groups; + unsigned int n_pin_groups; +}; + +struct cs48l32_pin_private { + struct cs48l32_mfd *mfd; + + const struct cs48l32_pin_chip *chip; /* chip-specific groups */ + + struct device *dev; + struct pinctrl_dev *pctl; +}; + +#endif From patchwork Wed Nov 9 16:53:25 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 623805 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 71B68C4332F for ; Wed, 9 Nov 2022 16:57: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 9E00A1676; Wed, 9 Nov 2022 17:56:46 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 9E00A1676 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1668013056; bh=ZzuCfoCbFPXbzKGq1Rveuf+spLmGUiWABYpdL4yi9Ow=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=YPqg1rpuzvz11fpQGy5s7BALuIsm9pX3+0DUiN+XqdN67wHG1l8f4k4cTZdfwBuDP Ek8pcA0nsHdvHsK8QV/rhghqlq521AnObS2H4sWaACK3XvEw8pyuw6SVY76HwXhdsp UYnic9Lw2WGLeEzeQN4+oFDBL6hUckgEIP0fA00g= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id B83C2F805C7; Wed, 9 Nov 2022 17:54:05 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 3F0EBF805BB; Wed, 9 Nov 2022 17:54:03 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0a-001ae601.pphosted.com [67.231.149.25]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 8C4FEF80567 for ; Wed, 9 Nov 2022 17:53:38 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 8C4FEF80567 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="dUnEOdFD" Received: from pps.filterd (m0077473.ppops.net [127.0.0.1]) by mx0a-001ae601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 2A9EEsmp027592; Wed, 9 Nov 2022 10:53:37 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=J51tDe4n/P70JEywu3N4H/UfOKsxpHMpiGSiyCiQ/4I=; b=dUnEOdFDDkZcRKd5VRb2CjaARxqLtp1Zo1haeVegqgJyjBBch47GKy4D/OsvXqstG1Dl Sfp3BIWo5MgpT46BmRhxVY9CEnIxAaqFLk3SlTI9kzcZtMS4zDkiOGiuOo00ChRUQ7kq 59/ZuIJVISyKZvZ1fFo3Oy/Cn169lBf5k527Hwkc3I5dq7XB5oPnet0tnPKaC8utDjOb 7oCcCH6p2BJrEQ9sFDd0n1l+JZkgw8zkjWHSRG7CxkhTF7eaMKqH2WAiUzifGS/vi7Jl DJxQ36foWjnYNhQWp9LqyUiAPlMGpaFjebocikNxI3IPrElu6fOi0X7oJZziXlvzMXVe TQ== Received: from ediex01.ad.cirrus.com ([84.19.233.68]) by mx0a-001ae601.pphosted.com (PPS) with ESMTPS id 3knn81nk99-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Nov 2022 10:53:37 -0600 Received: from ediex02.ad.cirrus.com (198.61.84.81) by ediex01.ad.cirrus.com (198.61.84.80) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.15; Wed, 9 Nov 2022 10:53:35 -0600 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by anon-ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server id 15.2.1118.15 via Frontend Transport; Wed, 9 Nov 2022 10:53:35 -0600 Received: from debianA11184.ad.cirrus.com (AUSNPC0LSNW1.ad.cirrus.com [198.61.65.92]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id 0205E468; Wed, 9 Nov 2022 16:53:34 +0000 (UTC) From: Richard Fitzgerald To: , , , , , , Subject: [PATCH 06/12] regulator: arizona-micsupp: Don't hardcode use of ARIZONA defines Date: Wed, 9 Nov 2022 16:53:25 +0000 Message-ID: <20221109165331.29332-7-rf@opensource.cirrus.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221109165331.29332-1-rf@opensource.cirrus.com> References: <20221109165331.29332-1-rf@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: jLRy66NxW7yMTh04myuZ0lqcWa6l1Zol X-Proofpoint-GUID: jLRy66NxW7yMTh04myuZ0lqcWa6l1Zol X-Proofpoint-Spam-Reason: safe Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, patches@opensource.cirrus.com, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Richard Fitzgerald 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" When Madera support was added to this driver the code was left using ARIZONA_* defines. This wasn't causing any problem because those defines just happened to have the same value as the equivalent MADERA_* defines. But it is not ideal to assume this, and future devices that can share this driver do not have the same register map. Fix the code to refer to the register data in struct regulator_desc. Signed-off-by: Richard Fitzgerald --- drivers/regulator/arizona-micsupp.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index f6cfd3f6f0dd..21c36972a8e9 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -34,7 +34,7 @@ struct arizona_micsupp { struct regulator_dev *regulator; struct regmap *regmap; struct snd_soc_dapm_context **dapm; - unsigned int enable_reg; + const struct regulator_desc *desc; struct device *dev; struct regulator_consumer_supply supply; @@ -49,10 +49,11 @@ static void arizona_micsupp_check_cp(struct work_struct *work) container_of(work, struct arizona_micsupp, check_cp_work); struct snd_soc_dapm_context *dapm = *micsupp->dapm; struct snd_soc_component *component; + const struct regulator_desc *desc = micsupp->desc; unsigned int val; int ret; - ret = regmap_read(micsupp->regmap, micsupp->enable_reg, &val); + ret = regmap_read(micsupp->regmap, desc->enable_reg, &val); if (ret != 0) { dev_err(micsupp->dev, "Failed to read CP state: %d\n", ret); @@ -62,8 +63,8 @@ static void arizona_micsupp_check_cp(struct work_struct *work) if (dapm) { component = snd_soc_dapm_to_component(dapm); - if ((val & (ARIZONA_CPMIC_ENA | ARIZONA_CPMIC_BYPASS)) == - ARIZONA_CPMIC_ENA) + if ((val & (desc->enable_mask | desc->bypass_mask)) == + desc->enable_mask) snd_soc_component_force_enable_pin(component, "MICSUPP"); else @@ -209,7 +210,6 @@ static const struct regulator_desc madera_micsupp = { .type = REGULATOR_VOLTAGE, .n_voltages = 40, .ops = &arizona_micsupp_ops, - .vsel_reg = MADERA_LDO2_CONTROL_1, .vsel_mask = MADERA_LDO2_VSEL_MASK, .enable_reg = MADERA_MIC_CHARGE_PUMP_1, @@ -264,7 +264,7 @@ static int arizona_micsupp_common_init(struct platform_device *pdev, micsupp->init_data.consumer_supplies = &micsupp->supply; micsupp->supply.supply = "MICVDD"; micsupp->supply.dev_name = dev_name(micsupp->dev); - micsupp->enable_reg = desc->enable_reg; + micsupp->desc = desc; config.dev = micsupp->dev; config.driver_data = micsupp; @@ -285,8 +285,7 @@ static int arizona_micsupp_common_init(struct platform_device *pdev, config.init_data = &micsupp->init_data; /* Default to regulated mode */ - regmap_update_bits(micsupp->regmap, micsupp->enable_reg, - ARIZONA_CPMIC_BYPASS, 0); + regmap_update_bits(micsupp->regmap, desc->enable_reg, desc->bypass_mask, 0); micsupp->regulator = devm_regulator_register(&pdev->dev, desc, From patchwork Wed Nov 9 16:53:26 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 623808 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 7345AC4332F for ; Wed, 9 Nov 2022 16:55:59 +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 9176A163F; Wed, 9 Nov 2022 17:55:07 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 9176A163F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1668012957; bh=MzHl7pk9viWNyXhVOAFEGqxLZpOZJ7GvDwZhl8Ao//U=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=dpXnFoMuHh+hMoQIOXnBy8Ew0VSm16sFo4AHxLElhtEWZlgOwH+UsZ5p9QBRGn8XR tRNS0JGjfkjolFT1euMNnCrJMSsWNC5M6k9NruwsFCfil6TE8J0SWcWCvw2dnJvmmU S/sQwGMysGOHKf5zWRhiMwXQ6as3GZavpUicgGKE= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 3BA38F8058C; Wed, 9 Nov 2022 17:53:56 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 69DCFF8057C; Wed, 9 Nov 2022 17:53:52 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0b-001ae601.pphosted.com [67.231.152.168]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id B5378F804FB for ; Wed, 9 Nov 2022 17:53:39 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz B5378F804FB Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="aHl5/FYs" Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 2A9EDNjU027347; Wed, 9 Nov 2022 10:53:38 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=PbgpNffsH6yRXzQNXNDtOnf8nTURcBJoWScHT08Ed6Q=; b=aHl5/FYsB6Lo4X/iwT7k8cJTRaduWd/jZddWKU9nRmjjDnKc7g0m5WMHlQOEv7muuk+M sfIvpc7QPLYRlEKsqLdlDhYhH7d9BEBjcx9S/nfIM05UZyWNvKMrt3sZKZ2VLAzUYkEY jZz9mC58L72/EH8GdggD+hK3YMnFVllUHnhoU8sk0ttlLy57JcsaOwd5jUmy2NOCOCLh hhq80cS0bEvvAyTcbt8wswld+wjBbPnSJICQ9j8bS4J5C+E9e3X3g0tpeV9zm5+ukF8L zneGM2ry1Q9uW1oBX9bdGG5BUimr7XlTWH2GnxDhEFdQ4HWmosPdtwl+kAITnBhRrYoo 4w== Received: from ediex02.ad.cirrus.com ([84.19.233.68]) by mx0b-001ae601.pphosted.com (PPS) with ESMTPS id 3knm8pde7v-6 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Nov 2022 10:53:37 -0600 Received: from ediex02.ad.cirrus.com (198.61.84.81) by ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.15; Wed, 9 Nov 2022 10:53:36 -0600 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by anon-ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server id 15.2.1118.15 via Frontend Transport; Wed, 9 Nov 2022 10:53:36 -0600 Received: from debianA11184.ad.cirrus.com (AUSNPC0LSNW1.ad.cirrus.com [198.61.65.92]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id 881E0478; Wed, 9 Nov 2022 16:53:35 +0000 (UTC) From: Richard Fitzgerald To: , , , , , , Subject: [PATCH 07/12] regulator: arizona-micsupp: Don't use a common regulator name Date: Wed, 9 Nov 2022 16:53:26 +0000 Message-ID: <20221109165331.29332-8-rf@opensource.cirrus.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221109165331.29332-1-rf@opensource.cirrus.com> References: <20221109165331.29332-1-rf@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: JnpQQAGo045iAXegME2a1rfJrvmbhA6H X-Proofpoint-GUID: JnpQQAGo045iAXegME2a1rfJrvmbhA6H X-Proofpoint-Spam-Reason: safe Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, patches@opensource.cirrus.com, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Richard Fitzgerald 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" The Arizona and Madera codecs all have a datasheet name of "MICVDD" for the regulator output. But future codecs with a regulator that can be controlled by this driver have different naming convention for the output of the regulator. Move the setting of the supply name from arizona_micsupp_common_init() to arizona_micsupp_probe() and madera_micsupp_probe(). Signed-off-by: Richard Fitzgerald --- drivers/regulator/arizona-micsupp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index 21c36972a8e9..596ecd8041cd 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -262,7 +262,6 @@ static int arizona_micsupp_common_init(struct platform_device *pdev, INIT_WORK(&micsupp->check_cp_work, arizona_micsupp_check_cp); micsupp->init_data.consumer_supplies = &micsupp->supply; - micsupp->supply.supply = "MICVDD"; micsupp->supply.dev_name = dev_name(micsupp->dev); micsupp->desc = desc; @@ -319,6 +318,8 @@ static int arizona_micsupp_probe(struct platform_device *pdev) micsupp->dapm = &arizona->dapm; micsupp->dev = arizona->dev; + micsupp->supply.supply = "MICVDD"; + /* * Since the chip usually supplies itself we provide some * default init_data for it. This will be overridden with @@ -354,6 +355,8 @@ static int madera_micsupp_probe(struct platform_device *pdev) micsupp->dev = madera->dev; micsupp->init_data = arizona_micsupp_ext_default; + micsupp->supply.supply = "MICVDD"; + return arizona_micsupp_common_init(pdev, micsupp, &madera_micsupp, &madera->pdata.micvdd); } From patchwork Wed Nov 9 16:53:27 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 622949 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 7E09AC433FE for ; Wed, 9 Nov 2022 16:56:21 +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 9A31E1621; Wed, 9 Nov 2022 17:55:29 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 9A31E1621 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1668012979; bh=K2KlKDIBuHsWNz6q0lKTqmG6JpQ8YzaIBSto60HStVA=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=fG4su5PEYGQzDd99xAsdKodIQICQvijCjksOuOcpbQ7YiOfka7yRotMEZPxpQx6Wc EvTGYzNLaRJpZK4dqpcTwzG5237XElWfUd6MVboK/L1/gG9z6ouoxsROAt7d7yB7UU QRtWcrXIbhfTmrzy3B/+9XMrn1sewqzYkGludAuc= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 6A925F805A8; Wed, 9 Nov 2022 17:54:00 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id CC4CEF80580; Wed, 9 Nov 2022 17:53:53 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0b-001ae601.pphosted.com [67.231.152.168]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 20AD4F80548 for ; Wed, 9 Nov 2022 17:53:40 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 20AD4F80548 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="hwQoy/ji" Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 2A9EDNjX027347; Wed, 9 Nov 2022 10:53:39 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=7U2hym3PhMYqBCChlIWoRSTMF7ZHOcUZRXT6D8RO+p0=; b=hwQoy/jis+57XFlEISXXFo0MN+KcCsu44zAvmmg2WV4/+xvPK5zXV1guZqKgFS8z3n7x lqZTf2qxHwfu+9cAPiY3ZZFIBUa6TixjS9X4cPP12sjPmTytt4oV5bZAY74CHUy+wN1H BynWWTor+KSNn43X3iTabtKif4eGbwu0cEcAtZMMY3MeD+IW2+efRXsC2eLkswIojyeE U6t7Vymd7n7iC2QAS4SOcJqy4LhHD94KPgj+DR5WHwIbNvsc2INKBnzOaIqNM5qgl+Zy 9yDWOoe1R/NAD0Id+mSR69fu8IGe3hT7dFpOcKwG6o0gg7wbsAQZaMJ93AEPIz3jJAsz JQ== Received: from ediex02.ad.cirrus.com ([84.19.233.68]) by mx0b-001ae601.pphosted.com (PPS) with ESMTPS id 3knm8pde7v-7 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Nov 2022 10:53:39 -0600 Received: from ediex01.ad.cirrus.com (198.61.84.80) by ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.15; Wed, 9 Nov 2022 10:53:36 -0600 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by ediex01.ad.cirrus.com (198.61.84.80) with Microsoft SMTP Server id 15.2.1118.15 via Frontend Transport; Wed, 9 Nov 2022 10:53:36 -0600 Received: from debianA11184.ad.cirrus.com (AUSNPC0LSNW1.ad.cirrus.com [198.61.65.92]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id 130E7468; Wed, 9 Nov 2022 16:53:36 +0000 (UTC) From: Richard Fitzgerald To: , , , , , , Subject: [PATCH 08/12] regulator: arizona-micsupp: Support Cirrus Logic CS48L31/32/33 Date: Wed, 9 Nov 2022 16:53:27 +0000 Message-ID: <20221109165331.29332-9-rf@opensource.cirrus.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221109165331.29332-1-rf@opensource.cirrus.com> References: <20221109165331.29332-1-rf@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: fYDF8kqs5RQSNj7ODaY7winktvXz4zTy X-Proofpoint-GUID: fYDF8kqs5RQSNj7ODaY7winktvXz4zTy X-Proofpoint-Spam-Reason: safe Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, patches@opensource.cirrus.com, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Richard Fitzgerald 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" This adds a new driver identity "cs48l32-micsupp" and probe function so that this driver can be used to control the micsupp regulator on Cirrus Logic CS48L31/32/33 audio codecs. Signed-off-by: Richard Fitzgerald --- drivers/regulator/Kconfig | 8 ++-- drivers/regulator/arizona-micsupp.c | 58 +++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 070e4403c6c2..1d6813b24f85 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -139,12 +139,12 @@ config REGULATOR_ARIZONA_LDO1 config REGULATOR_ARIZONA_MICSUPP tristate "Cirrus Madera and Wolfson Arizona class devices MICSUPP" - depends on MFD_ARIZONA || MFD_MADERA + depends on MFD_ARIZONA || MFD_MADERA || MFD_CS48L32 depends on SND_SOC help - Support for the MICSUPP regulators found on Cirrus Logic Madera codecs - and Wolfson Microelectronic Arizona codecs - devices. + Support for the MICSUPP regulators found on Cirrus Logic Madera, + Cirrus Logic CS48L31/32/33, and on Wolfson Microelectronic + Arizona codecs. config REGULATOR_ARM_SCMI tristate "SCMI based regulator driver" diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index 596ecd8041cd..bf154067ed34 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -24,6 +24,9 @@ #include #include +#include +#include + #include #include #include @@ -225,6 +228,28 @@ static const struct regulator_desc madera_micsupp = { .owner = THIS_MODULE, }; +static const struct regulator_desc cs48l32_micsupp = { + .name = "VOUT_MIC", + .supply_name = "VDD_CP", + .type = REGULATOR_VOLTAGE, + .n_voltages = 40, + .ops = &arizona_micsupp_ops, + + .vsel_reg = CS48L32_LDO2_CTRL1, + .vsel_mask = CS48L32_LDO2_VSEL_MASK, + .enable_reg = CS48L32_CHARGE_PUMP1, + .enable_mask = CS48L32_CP2_EN_MASK, + .bypass_reg = CS48L32_CHARGE_PUMP1, + .bypass_mask = CS48L32_CP2_BYPASS_MASK, + + .linear_ranges = arizona_micsupp_ext_ranges, + .n_linear_ranges = ARRAY_SIZE(arizona_micsupp_ext_ranges), + + .enable_time = 3000, + + .owner = THIS_MODULE, +}; + static int arizona_micsupp_of_get_pdata(struct arizona_micsupp_pdata *pdata, struct regulator_config *config, const struct regulator_desc *desc) @@ -361,6 +386,29 @@ static int madera_micsupp_probe(struct platform_device *pdev) &madera->pdata.micvdd); } +static int cs48l32_micsupp_probe(struct platform_device *pdev) +{ + struct cs48l32_mfd *mfd = dev_get_drvdata(pdev->dev.parent); + struct arizona_micsupp *micsupp; + struct arizona_micsupp_pdata *pdata; + + micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL); + if (!micsupp) + return -ENOMEM; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + micsupp->regmap = mfd->regmap; + micsupp->dapm = &mfd->dapm; + micsupp->dev = mfd->dev; + micsupp->init_data = arizona_micsupp_ext_default; + micsupp->supply.supply = "VOUT_MIC"; + + return arizona_micsupp_common_init(pdev, micsupp, &cs48l32_micsupp, pdata); +} + static struct platform_driver arizona_micsupp_driver = { .probe = arizona_micsupp_probe, .driver = { @@ -375,9 +423,17 @@ static struct platform_driver madera_micsupp_driver = { }, }; +static struct platform_driver cs48l32_micsupp_driver = { + .probe = cs48l32_micsupp_probe, + .driver = { + .name = "cs48l32-micsupp", + }, +}; + static struct platform_driver * const arizona_micsupp_drivers[] = { &arizona_micsupp_driver, &madera_micsupp_driver, + &cs48l32_micsupp_driver, }; static int __init arizona_micsupp_init(void) @@ -396,7 +452,9 @@ module_exit(arizona_micsupp_exit); /* Module information */ MODULE_AUTHOR("Mark Brown "); +MODULE_AUTHOR("Richard Fitzgerald "); MODULE_DESCRIPTION("Arizona microphone supply driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:arizona-micsupp"); +MODULE_ALIAS("platform:cs48l32-micsupp"); MODULE_ALIAS("platform:madera-micsupp"); From patchwork Wed Nov 9 16:53:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 622948 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 CB6CBC433FE for ; Wed, 9 Nov 2022 16:56: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 05DDB1675; Wed, 9 Nov 2022 17:55:58 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 05DDB1675 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1668013008; bh=2nnGAceG+WDZxGAx3gL84yKXylMTOCULhZ0VOtOx/6U=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=lI1KGeZ7hXFP5kZ82BVvsXWo85vwZz0oSdBL8vLX7xM9iwH1R7Gw1ISqsf3uZXF8l GtuJRCF7I9YjY4FJExOFvQ22a09QU35i/wg9Vjz2xW+8wvo4BKzAqR/O3jHKBjc/2C 85fPsKyEvedW4G/f9tZlT1told5sGfbvoR5Mw6Vs= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 977A2F805B5; Wed, 9 Nov 2022 17:54:02 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 1C170F805AB; Wed, 9 Nov 2022 17:53:59 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0b-001ae601.pphosted.com [67.231.152.168]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id BEB20F8023B for ; Wed, 9 Nov 2022 17:53:42 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz BEB20F8023B Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="aj0ow1uY" Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 2A9EDNjZ027347; Wed, 9 Nov 2022 10:53:40 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=L7wlWbL5uEHVlUgGNogFf74St6uXnoRWkEOZtPEuo60=; b=aj0ow1uYt9Lpd/pLYXEvpFrna+L0my93R33bJf1qPa12TQXgD2WmIEjhwB7zXONN120E vGgp5PwlcemZca4Qu2aEhfX4ytYhKo/roLc3GxQRuK8jU+w5mCjWX2BipEVLrNEcJIXm WBr0Acn39CWeWMDeLvFQ1ZDfkhVGXQSjyi/v++0c1YnvR0kM2MK9QME+eyXzmZOQ4ePJ P9kP/x1V3lFSGyrw8YQ9I3hb5D28+Z48txkFCjmSm0WHBN6I/0UIT1UHsQcQknVoENk3 DyTcyrUGTJFkcDp1paxsbl64hay3byId5wIQj+sI9pbbjf6QZEqWiCvMCoumoFIqqM2i 5w== Received: from ediex02.ad.cirrus.com ([84.19.233.68]) by mx0b-001ae601.pphosted.com (PPS) with ESMTPS id 3knm8pde7v-8 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Nov 2022 10:53:40 -0600 Received: from ediex02.ad.cirrus.com (198.61.84.81) by ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.15; Wed, 9 Nov 2022 10:53:37 -0600 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by anon-ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server id 15.2.1118.15 via Frontend Transport; Wed, 9 Nov 2022 10:53:37 -0600 Received: from debianA11184.ad.cirrus.com (AUSNPC0LSNW1.ad.cirrus.com [198.61.65.92]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id A0103478; Wed, 9 Nov 2022 16:53:36 +0000 (UTC) From: Richard Fitzgerald To: , , , , , , Subject: [PATCH 09/12] irqchip: cirrus: Add driver for Cirrus Logic CS48L31/32/33 codecs Date: Wed, 9 Nov 2022 16:53:28 +0000 Message-ID: <20221109165331.29332-10-rf@opensource.cirrus.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221109165331.29332-1-rf@opensource.cirrus.com> References: <20221109165331.29332-1-rf@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: P9X1g0Q0gFoCRfeVf1Z6ZXct2yQIe8Zd X-Proofpoint-GUID: P9X1g0Q0gFoCRfeVf1Z6ZXct2yQIe8Zd X-Proofpoint-Spam-Reason: safe Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, patches@opensource.cirrus.com, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Richard Fitzgerald 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" The Cirrus Logic CS48L31/32/33 audio codecs contain a programmable interrupt controller with a variety of interrupt sources, including GPIOs that can be used as interrupt inputs. This driver provides the handling for the interrupt controller. As the codec is accessed via regmap, the generic regmap_irq functionality is used to do most of the work. Signed-off-by: Richard Fitzgerald --- MAINTAINERS | 2 + drivers/irqchip/Kconfig | 3 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-cirrus-cs48l32.c | 281 +++++++++++++++++++++ drivers/irqchip/irq-cirrus-cs48l32.h | 74 ++++++ include/linux/irqchip/irq-cirrus-cs48l32.h | 101 ++++++++ 6 files changed, 462 insertions(+) create mode 100644 drivers/irqchip/irq-cirrus-cs48l32.c create mode 100644 drivers/irqchip/irq-cirrus-cs48l32.h create mode 100644 include/linux/irqchip/irq-cirrus-cs48l32.h diff --git a/MAINTAINERS b/MAINTAINERS index cd1773d39dd8..f52e9a6e290c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5015,12 +5015,14 @@ F: Documentation/devicetree/bindings/pinctrl/cirrus,cs48l32.yaml F: Documentation/devicetree/bindings/pinctrl/cirrus,madera.yaml F: Documentation/devicetree/bindings/sound/cirrus,madera.yaml F: drivers/gpio/gpio-madera* +F: drivers/irqchip/irq-cirrus-cs48l32* F: drivers/irqchip/irq-madera* F: drivers/mfd/cs47l* F: drivers/mfd/cs48l* F: drivers/mfd/madera* F: drivers/pinctrl/cirrus/* F: include/dt-bindings/sound/madera* +F: include/linux/irqchip/irq-cirrus-cs48l32* F: include/linux/irqchip/irq-madera* F: include/linux/mfd/cs48l32/* F: include/linux/mfd/madera/* diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 7ef9f5e696d3..d4521158849c 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -136,6 +136,9 @@ config BRCMSTB_L2_IRQ select GENERIC_IRQ_CHIP select IRQ_DOMAIN +config CIRRUS_CS48L32_IRQ + tristate + config DAVINCI_AINTC bool select GENERIC_IRQ_CHIP diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 87b49a10962c..049796365232 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -121,3 +121,4 @@ obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o +obj-$(CONFIG_CIRRUS_CS48L32_IRQ) += irq-cirrus-cs48l32.o diff --git a/drivers/irqchip/irq-cirrus-cs48l32.c b/drivers/irqchip/irq-cirrus-cs48l32.c new file mode 100644 index 000000000000..3ca9f34a6289 --- /dev/null +++ b/drivers/irqchip/irq-cirrus-cs48l32.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Interrupt support for Cirrus Logic CS48L32 audio codec +// +// Copyright (C) 2020, 2022 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "irq-cirrus-cs48l32.h" + +#define CS48L32_IRQ(_irq, _reg) \ + [CS48L32_IRQ_ ## _irq] = { \ + .reg_offset = (_reg) - CS48L32_IRQ1_EINT_1, \ + .mask = CS48L32_ ## _irq ## _EINT1_MASK \ + } + +static const struct regmap_irq cs48l32_irqs[] = { + CS48L32_IRQ(DSP1_IRQ0, CS48L32_IRQ1_EINT_9), + CS48L32_IRQ(DSP1_IRQ1, CS48L32_IRQ1_EINT_9), + CS48L32_IRQ(DSP1_IRQ2, CS48L32_IRQ1_EINT_9), + CS48L32_IRQ(DSP1_IRQ3, CS48L32_IRQ1_EINT_9), + + CS48L32_IRQ(US1_ACT_DET_RISE, CS48L32_IRQ1_EINT_5), + CS48L32_IRQ(US1_ACT_DET_FALL, CS48L32_IRQ1_EINT_5), + CS48L32_IRQ(US2_ACT_DET_RISE, CS48L32_IRQ1_EINT_5), + CS48L32_IRQ(US2_ACT_DET_FALL, CS48L32_IRQ1_EINT_5), + + CS48L32_IRQ(INPUTS_SIG_DET_RISE, CS48L32_IRQ1_EINT_5), + CS48L32_IRQ(INPUTS_SIG_DET_FALL, CS48L32_IRQ1_EINT_5), + + CS48L32_IRQ(GPIO1_RISE, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO1_FALL, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO2_RISE, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO2_FALL, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO3_RISE, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO3_FALL, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO4_RISE, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO4_FALL, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO5_RISE, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO5_FALL, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO6_RISE, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO6_FALL, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO7_RISE, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO7_FALL, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO8_RISE, CS48L32_IRQ1_EINT_11), + CS48L32_IRQ(GPIO8_FALL, CS48L32_IRQ1_EINT_11), + + CS48L32_IRQ(DRC1_SIG_DET_RISE, CS48L32_IRQ1_EINT_5), + CS48L32_IRQ(DRC1_SIG_DET_FALL, CS48L32_IRQ1_EINT_5), + CS48L32_IRQ(DRC2_SIG_DET_RISE, CS48L32_IRQ1_EINT_5), + CS48L32_IRQ(DRC2_SIG_DET_FALL, CS48L32_IRQ1_EINT_5), + + CS48L32_IRQ(FLL1_LOCK_RISE, CS48L32_IRQ1_EINT_6), + CS48L32_IRQ(FLL1_LOCK_FALL, CS48L32_IRQ1_EINT_6), + CS48L32_IRQ(FLL1_REF_LOST, CS48L32_IRQ1_EINT_6), + + CS48L32_IRQ(SYSCLK_FAIL, CS48L32_IRQ1_EINT_1), + CS48L32_IRQ(CTRLIF_ERR, CS48L32_IRQ1_EINT_1), + CS48L32_IRQ(SYSCLK_ERR, CS48L32_IRQ1_EINT_1), + CS48L32_IRQ(DSPCLK_ERR, CS48L32_IRQ1_EINT_1), + + CS48L32_IRQ(DSP1_NMI_ERR, CS48L32_IRQ1_EINT_7), + CS48L32_IRQ(DSP1_WDT_EXPIRE, CS48L32_IRQ1_EINT_7), + CS48L32_IRQ(DSP1_MPU_ERR, CS48L32_IRQ1_EINT_7), + + CS48L32_IRQ(BOOT_DONE, CS48L32_IRQ1_EINT_2), +}; + +static const struct regmap_irq_chip cs48l32_irqchip = { + .name = "CS48L32 IRQ", + .status_base = CS48L32_IRQ1_EINT_1, + .mask_base = CS48L32_IRQ1_MASK_1, + .ack_base = CS48L32_IRQ1_EINT_1, + .runtime_pm = true, + .num_regs = 11, + .irqs = cs48l32_irqs, + .num_irqs = ARRAY_SIZE(cs48l32_irqs), +}; + +static int __maybe_unused cs48l32_suspend(struct device *dev) +{ + struct cs48l32_mfd *mfd = dev_get_drvdata(dev->parent); + + dev_dbg(mfd->irq_dev, "Suspend, disabling IRQ\n"); + + /* + * A runtime resume would be needed to access the chip interrupt + * controller but runtime pm doesn't function during suspend. + * Temporarily disable interrupts until we reach suspend_noirq state. + */ + disable_irq(mfd->irq); + + return 0; +} + +static int __maybe_unused cs48l32_suspend_noirq(struct device *dev) +{ + struct cs48l32_mfd *mfd = dev_get_drvdata(dev->parent); + + dev_dbg(mfd->irq_dev, "No IRQ suspend, reenabling IRQ\n"); + + /* Re-enable interrupts to service wakeup interrupts from the chip */ + enable_irq(mfd->irq); + + return 0; +} + +static int __maybe_unused cs48l32_resume_noirq(struct device *dev) +{ + struct cs48l32_mfd *mfd = dev_get_drvdata(dev->parent); + + dev_dbg(mfd->irq_dev, "No IRQ resume, disabling IRQ\n"); + + /* + * We can't handle interrupts until runtime pm is available again. + * Disable them temporarily. + */ + disable_irq(mfd->irq); + + return 0; +} + +static int __maybe_unused cs48l32_resume(struct device *dev) +{ + struct cs48l32_mfd *mfd = dev_get_drvdata(dev->parent); + + dev_dbg(mfd->irq_dev, "Resume, enabling IRQ\n"); + + /* Interrupts can now be handled */ + enable_irq(mfd->irq); + + return 0; +} + +static const struct dev_pm_ops cs48l32_irq_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cs48l32_suspend, cs48l32_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cs48l32_suspend_noirq, cs48l32_resume_noirq) +}; + +static irqreturn_t cs48l32_sysclk_fail(int irq, void *data) +{ + struct cs48l32_mfd *mfd = data; + + dev_warn(mfd->dev, "SYSCLK fail\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t cs48l32_sysclk_error(int irq, void *data) +{ + struct cs48l32_mfd *mfd = data; + + dev_warn(mfd->dev, "SYSCLK error\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t cs48l32_ctrlif_error(int irq, void *data) +{ + struct cs48l32_mfd *mfd = data; + + dev_warn(mfd->dev, "CTRLIF error\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t cs48l32_boot_done(int irq, void *data) +{ + struct cs48l32_mfd *mfd = data; + + dev_dbg(mfd->dev, "BOOT_DONE\n"); + + return IRQ_HANDLED; +} + +static int cs48l32_irq_probe(struct platform_device *pdev) +{ + struct cs48l32_mfd *mfd = dev_get_drvdata(pdev->dev.parent); + struct irq_data *irq_data; + unsigned int irq_flags; + int ret; + + irq_data = irq_get_irq_data(mfd->irq); + if (!irq_data) + return dev_err_probe(&pdev->dev, -EINVAL, "Invalid IRQ: %d\n", mfd->irq); + + irq_flags = irqd_get_trigger_type(irq_data); + + /* Codec defaults to trigger low, use this if no flags given */ + if (irq_flags == IRQ_TYPE_NONE) + irq_flags = IRQF_TRIGGER_LOW; + + if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) + return dev_err_probe(&pdev->dev, -EINVAL, "Host interrupt not level-triggered\n"); + + /* + * The silicon always starts at active-low, check if we need to + * switch to active-high. + */ + if (irq_flags & IRQF_TRIGGER_HIGH) + ret = regmap_clear_bits(mfd->regmap, CS48L32_IRQ1_CTRL_AOD, + CS48L32_IRQ_POL_MASK); + else + ret = regmap_set_bits(mfd->regmap, CS48L32_IRQ1_CTRL_AOD, + CS48L32_IRQ_POL_MASK); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to set IRQ polarity\n"); + + /* + * NOTE: regmap registers this against the OF node of the parent of + * the regmap - that is, against the mfd driver + */ + ret = regmap_add_irq_chip(mfd->regmap, mfd->irq, IRQF_ONESHOT, 0, + &cs48l32_irqchip, &mfd->irq_data); + if (ret) + return dev_err_probe(&pdev->dev, ret, "add_irq_chip failed\n"); + + /* Save dev in parent MFD struct so it is accessible to siblings */ + mfd->irq_dev = &pdev->dev; + + /* + * Log global chip error conditions that aren't specific to + * any particular sibling driver. + */ + cs48l32_request_irq(mfd, CS48L32_IRQ_SYSCLK_FAIL, "SYSCLK fail", + cs48l32_sysclk_fail, mfd); + cs48l32_request_irq(mfd, CS48L32_IRQ_SYSCLK_ERR, "SYSCLK error", + cs48l32_sysclk_error, mfd); + cs48l32_request_irq(mfd, CS48L32_IRQ_CTRLIF_ERR, "CTRLIF error", + cs48l32_ctrlif_error, mfd); + cs48l32_request_irq(mfd, CS48L32_IRQ_BOOT_DONE, "BOOT_DONE", + cs48l32_boot_done, mfd); + + return 0; +} + +static int cs48l32_irq_remove(struct platform_device *pdev) +{ + struct cs48l32_mfd *mfd = dev_get_drvdata(pdev->dev.parent); + + /* + * The IRQ is disabled by the parent MFD driver before + * it starts cleaning up all child drivers + */ + cs48l32_free_irq(mfd, CS48L32_IRQ_BOOT_DONE, mfd); + cs48l32_free_irq(mfd, CS48L32_IRQ_CTRLIF_ERR, mfd); + cs48l32_free_irq(mfd, CS48L32_IRQ_SYSCLK_ERR, mfd); + cs48l32_free_irq(mfd, CS48L32_IRQ_SYSCLK_FAIL, mfd); + + mfd->irq_dev = NULL; + regmap_del_irq_chip(mfd->irq, mfd->irq_data); + + return 0; +} + +static struct platform_driver cs48l32_irq_driver = { + .probe = &cs48l32_irq_probe, + .remove = &cs48l32_irq_remove, + .driver = { + .name = "cs48l32-irq", + .pm = &cs48l32_irq_pm_ops, + } +}; + +module_platform_driver(cs48l32_irq_driver); + +MODULE_DESCRIPTION("CS48L32 IRQ driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL"); diff --git a/drivers/irqchip/irq-cirrus-cs48l32.h b/drivers/irqchip/irq-cirrus-cs48l32.h new file mode 100644 index 000000000000..6dae6ddf724d --- /dev/null +++ b/drivers/irqchip/irq-cirrus-cs48l32.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Interrupt support for Cirrus Logic CS48L32 audio codec + * + * Copyright (C) 2020, 2022 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef IRQ_CIRRUS_CS48L32_H +#define IRQ_CIRRUS_CS48L32_H + +/* (0x18010) IRQ1_EINT_1 */ +#define CS48L32_DSPCLK_ERR_EINT1_MASK 0x00001000 +#define CS48L32_SYSCLK_ERR_EINT1_MASK 0x00000400 +#define CS48L32_CTRLIF_ERR_EINT1_MASK 0x00000200 +#define CS48L32_SYSCLK_FAIL_EINT1_MASK 0x00000100 + +/* (0x18014) IRQ1_EINT_2 */ +#define CS48L32_BOOT_DONE_EINT1_MASK 0x00000008 + +/* (0x18020) IRQ1_EINT_5 */ +#define CS48L32_US2_ACT_DET_FALL_EINT1_MASK 0x02000000 +#define CS48L32_US2_ACT_DET_RISE_EINT1_MASK 0x01000000 +#define CS48L32_US1_ACT_DET_FALL_EINT1_MASK 0x00800000 +#define CS48L32_US1_ACT_DET_RISE_EINT1_MASK 0x00400000 +#define CS48L32_INPUTS_SIG_DET_FALL_EINT1_MASK 0x00200000 +#define CS48L32_INPUTS_SIG_DET_RISE_EINT1_MASK 0x00100000 +#define CS48L32_DRC2_SIG_DET_FALL_EINT1_MASK 0x00080000 +#define CS48L32_DRC2_SIG_DET_RISE_EINT1_MASK 0x00040000 +#define CS48L32_DRC1_SIG_DET_FALL_EINT1_MASK 0x00020000 +#define CS48L32_DRC1_SIG_DET_RISE_EINT1_MASK 0x00010000 + +/* (0x18024) IRQ1_EINT_6 */ +#define CS48L32_FLL1_REF_LOST_EINT1_MASK 0x00000100 +#define CS48L32_FLL1_LOCK_FALL_EINT1_MASK 0x00000002 +#define CS48L32_FLL1_LOCK_RISE_EINT1_MASK 0x00000001 + +/* (0x18028) IRQ1_EINT_7 */ +#define CS48L32_DSP1_MPU_ERR_EINT1_MASK 0x00200000 +#define CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK 0x00100000 +#define CS48L32_DSP1_IHB_ERR_EINT1_MASK 0x00080000 +#define CS48L32_DSP1_AHB_SYS_ERR_EINT1_MASK 0x00040000 +#define CS48L32_DSP1_AHB_PACK_ERR_EINT1_MASK 0x00020000 +#define CS48L32_DSP1_NMI_ERR_EINT1_MASK 0x00010000 + +/* (0x18030) IRQ1_EINT_9 */ +#define CS48L32_MCU_HWERR_IRQ_OUT_EINT1_MASK 0x80000000 +#define CS48L32_DSP1_IRQ3_EINT1_MASK 0x00000008 +#define CS48L32_DSP1_IRQ2_EINT1_MASK 0x00000004 +#define CS48L32_DSP1_IRQ1_EINT1_MASK 0x00000002 +#define CS48L32_DSP1_IRQ0_EINT1_MASK 0x00000001 + +/* (0x18034) IRQ1_EINT_10 */ +#define CS48L32_CLOCK_DETECT_EINT1_MASK 0x01000000 + +/* (0x18038) IRQ1_EINT_11 */ +#define CS48L32_GPIO8_FALL_EINT1_MASK 0x80000000 +#define CS48L32_GPIO8_RISE_EINT1_MASK 0x40000000 +#define CS48L32_GPIO7_FALL_EINT1_MASK 0x20000000 +#define CS48L32_GPIO7_RISE_EINT1_MASK 0x10000000 +#define CS48L32_GPIO6_FALL_EINT1_MASK 0x08000000 +#define CS48L32_GPIO6_RISE_EINT1_MASK 0x04000000 +#define CS48L32_GPIO5_FALL_EINT1_MASK 0x02000000 +#define CS48L32_GPIO5_RISE_EINT1_MASK 0x01000000 +#define CS48L32_GPIO4_FALL_EINT1_MASK 0x00800000 +#define CS48L32_GPIO4_RISE_EINT1_MASK 0x00400000 +#define CS48L32_GPIO3_FALL_EINT1_MASK 0x00200000 +#define CS48L32_GPIO3_RISE_EINT1_MASK 0x00100000 +#define CS48L32_GPIO2_FALL_EINT1_MASK 0x00080000 +#define CS48L32_GPIO2_RISE_EINT1_MASK 0x00040000 +#define CS48L32_GPIO1_FALL_EINT1_MASK 0x00020000 +#define CS48L32_GPIO1_RISE_EINT1_MASK 0x00010000 + +#endif diff --git a/include/linux/irqchip/irq-cirrus-cs48l32.h b/include/linux/irqchip/irq-cirrus-cs48l32.h new file mode 100644 index 000000000000..c94d31cef96f --- /dev/null +++ b/include/linux/irqchip/irq-cirrus-cs48l32.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Interrupt support for Cirrus Logic CS48L32 audio codec + * + * Copyright (C) 2017-2020, 2022 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef IRQCHIP_CIRRUS_CS48L32_H +#define IRQCHIP_CIRRUS_CS48L32_H + +#include +#include + +/* Main interrupts, organized by priority order - highest first */ +#define CS48L32_IRQ_DSP1_IRQ0 0 +#define CS48L32_IRQ_DSP1_IRQ1 1 +#define CS48L32_IRQ_DSP1_IRQ2 2 +#define CS48L32_IRQ_DSP1_IRQ3 3 +#define CS48L32_IRQ_US1_ACT_DET_RISE 4 +#define CS48L32_IRQ_US1_ACT_DET_FALL 5 +#define CS48L32_IRQ_US2_ACT_DET_RISE 6 +#define CS48L32_IRQ_US2_ACT_DET_FALL 7 +#define CS48L32_IRQ_INPUTS_SIG_DET_RISE 8 +#define CS48L32_IRQ_INPUTS_SIG_DET_FALL 9 +#define CS48L32_IRQ_GPIO1_RISE 10 +#define CS48L32_IRQ_GPIO1_FALL 11 +#define CS48L32_IRQ_GPIO2_RISE 12 +#define CS48L32_IRQ_GPIO2_FALL 13 +#define CS48L32_IRQ_GPIO3_RISE 14 +#define CS48L32_IRQ_GPIO3_FALL 15 +#define CS48L32_IRQ_GPIO4_RISE 16 +#define CS48L32_IRQ_GPIO4_FALL 17 +#define CS48L32_IRQ_GPIO5_RISE 18 +#define CS48L32_IRQ_GPIO5_FALL 19 +#define CS48L32_IRQ_GPIO6_RISE 20 +#define CS48L32_IRQ_GPIO6_FALL 21 +#define CS48L32_IRQ_GPIO7_RISE 22 +#define CS48L32_IRQ_GPIO7_FALL 23 +#define CS48L32_IRQ_GPIO8_RISE 24 +#define CS48L32_IRQ_GPIO8_FALL 25 +#define CS48L32_IRQ_DRC1_SIG_DET_RISE 26 +#define CS48L32_IRQ_DRC1_SIG_DET_FALL 27 +#define CS48L32_IRQ_DRC2_SIG_DET_RISE 28 +#define CS48L32_IRQ_DRC2_SIG_DET_FALL 29 +#define CS48L32_IRQ_FLL1_LOCK_RISE 30 +#define CS48L32_IRQ_FLL1_LOCK_FALL 31 +#define CS48L32_IRQ_FLL1_REF_LOST 32 +#define CS48L32_IRQ_SYSCLK_FAIL 33 +#define CS48L32_IRQ_CTRLIF_ERR 34 +#define CS48L32_IRQ_SYSCLK_ERR 35 +#define CS48L32_IRQ_DSPCLK_ERR 36 +#define CS48L32_IRQ_DSP1_NMI_ERR 37 +#define CS48L32_IRQ_DSP1_WDT_EXPIRE 38 +#define CS48L32_IRQ_DSP1_MPU_ERR 39 +#define CS48L32_IRQ_BOOT_DONE 40 + +struct cs48l32_mfd; + +/* + * These wrapper functions are for use by other child drivers of the + * same parent MFD. + */ +static inline int cs48l32_get_irq_mapping(struct cs48l32_mfd *cs48l32, int irq) +{ + if (!cs48l32->irq_dev) + return -ENODEV; + + return regmap_irq_get_virq(cs48l32->irq_data, irq); +} + +static inline int cs48l32_request_irq(struct cs48l32_mfd *cs48l32, int irq, + const char *name, + irq_handler_t handler, void *data) +{ + irq = cs48l32_get_irq_mapping(cs48l32, irq); + if (irq < 0) + return irq; + + return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, name, data); +} + +static inline void cs48l32_free_irq(struct cs48l32_mfd *cs48l32, int irq, void *data) +{ + irq = cs48l32_get_irq_mapping(cs48l32, irq); + if (irq < 0) + return; + + free_irq(irq, data); +} + +static inline int cs48l32_set_irq_wake(struct cs48l32_mfd *cs48l32, int irq, int on) +{ + irq = cs48l32_get_irq_mapping(cs48l32, irq); + if (irq < 0) + return irq; + + return irq_set_irq_wake(irq, on); +} + +#endif From patchwork Wed Nov 9 16:53:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 622947 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 3CDBBC433FE for ; Wed, 9 Nov 2022 16:57:21 +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 6F932168B; Wed, 9 Nov 2022 17:56:29 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 6F932168B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1668013039; bh=2T8L8sYRWzGqrDY2cUOkUx0oiy3tgh6JwaPPHYQDzLU=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=HYO/D6lU1pd+s9+hwaagNxs8sNBz2jDR6T9FneXPDlXXCRq6vOm5dpBtIsNDJmL0N rPCourwk7V/TF98AUgtZ2VLtqekdpPMBPkic725FBmqJKAfi/zGgn243oumil141/f 8Zl9OrYEG26qNOCoWpmQQ185b6md6pDP2iOlrrr0= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id B1D9AF805C3; Wed, 9 Nov 2022 17:54:04 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 3767DF805B5; Wed, 9 Nov 2022 17:54:02 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0b-001ae601.pphosted.com [67.231.152.168]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 65B9BF8055C for ; Wed, 9 Nov 2022 17:53:42 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 65B9BF8055C Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="qTstbbsq" Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 2A9EDNja027347; Wed, 9 Nov 2022 10:53:41 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=1/iqdbl79p07gdmIWrlfhsD3njth3q+lAyMGYPPce6s=; b=qTstbbsq0uaNGrb9U9nPZAfnISIIkYaZ0BRhOBGsG1dhW5/ZKqm6ay9vt7NkDLQCt8Ct /lQsropm3vuktG8MdsjJcfjZ8WRYfWA/GL5vSUmuhCQV6PBLwUGcR9/8HcN+MrQiihjE p/c6qS8E/ydQ2yjW+uZJL4HPGe/8qhQJk2j6BxTUyIb+CCcUsE3VTXrSLWbN1TZ2el/T aPqfheLmFfbA5sWoGJFykNIzgqUoPE+uUxP6H0ZRAf0JH7z3gluaLXtpNJ9yct2GVsF0 aV962D0xDcrT53mqhKzA3wdwW23D/yZ6uZXkuHI0Ng/SUFllLLJ5Vyr4WOJS8+mcedai SQ== Received: from ediex02.ad.cirrus.com ([84.19.233.68]) by mx0b-001ae601.pphosted.com (PPS) with ESMTPS id 3knm8pde7v-9 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Nov 2022 10:53:41 -0600 Received: from ediex02.ad.cirrus.com (198.61.84.81) by ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.15; Wed, 9 Nov 2022 10:53:37 -0600 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by anon-ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server id 15.2.1118.15 via Frontend Transport; Wed, 9 Nov 2022 10:53:37 -0600 Received: from debianA11184.ad.cirrus.com (AUSNPC0LSNW1.ad.cirrus.com [198.61.65.92]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id 2FFE0468; Wed, 9 Nov 2022 16:53:37 +0000 (UTC) From: Richard Fitzgerald To: , , , , , , Subject: [PATCH 10/12] ASoC: wm_adsp: Allow client to hook into pre_run callback Date: Wed, 9 Nov 2022 16:53:29 +0000 Message-ID: <20221109165331.29332-11-rf@opensource.cirrus.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221109165331.29332-1-rf@opensource.cirrus.com> References: <20221109165331.29332-1-rf@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: d-QuLTs7FgaCQWnVepVFNDjx8Dyo62fV X-Proofpoint-GUID: d-QuLTs7FgaCQWnVepVFNDjx8Dyo62fV X-Proofpoint-Spam-Reason: safe Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, patches@opensource.cirrus.com, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Richard Fitzgerald 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" Some HALO-based codecs need some additional custom setup in the pre_run stage of cs_dsp. Implement the callback in wm_adsp to call an optional codec driver callback. Signed-off-by: Richard Fitzgerald --- sound/soc/codecs/wm_adsp.c | 11 +++++++++++ sound/soc/codecs/wm_adsp.h | 1 + 2 files changed, 12 insertions(+) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 8a2e9771bb50..34a94b011518 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1035,6 +1035,16 @@ int wm_adsp_early_event(struct snd_soc_dapm_widget *w, } EXPORT_SYMBOL_GPL(wm_adsp_early_event); +static int wm_adsp_pre_run(struct cs_dsp *cs_dsp) +{ + struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); + + if (!dsp->pre_run) + return 0; + + return (*dsp->pre_run)(dsp); +} + static int wm_adsp_event_post_run(struct cs_dsp *cs_dsp) { struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); @@ -2043,6 +2053,7 @@ static const struct cs_dsp_client_ops wm_adsp1_client_ops = { static const struct cs_dsp_client_ops wm_adsp2_client_ops = { .control_add = wm_adsp_control_add, .control_remove = wm_adsp_control_remove, + .pre_run = wm_adsp_pre_run, .post_run = wm_adsp_event_post_run, .post_stop = wm_adsp_event_post_stop, .watchdog_expired = wm_adsp_fatal_error, diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 375009a65828..dc2f7a096e26 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -36,6 +36,7 @@ struct wm_adsp { int fw; struct work_struct boot_work; + int (*pre_run)(struct wm_adsp *dsp); bool preloaded; bool fatal_error; From patchwork Wed Nov 9 16:53:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 623806 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 D41B9C433FE for ; Wed, 9 Nov 2022 16:57:12 +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 1342D1681; Wed, 9 Nov 2022 17:56:21 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 1342D1681 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1668013031; bh=BggOddZQgDmbchAspX+/2rXyvXjp9JbgAaeJVSGEi+8=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=UrirnNvdcEbj/PFUUnfOVgaZTu6+87A0uQ2HXmbVTw2l1yOKQg3ih82qvOERKJz7f Eu5tFjQfRdgUSVd7whqqoAua4tnj/1urr+MAQGUPP9Uz3d0sbh3nvmZnpz7wIvBelk wnYtxU58V5J3FCqiIHK60huNIMpIl57yvgc1Gw1I= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 0B911F805BF; Wed, 9 Nov 2022 17:54:04 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id B3397F805AB; Wed, 9 Nov 2022 17:53:59 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0b-001ae601.pphosted.com [67.231.152.168]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 1C04DF80121 for ; Wed, 9 Nov 2022 17:53:43 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 1C04DF80121 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="JNzbdB0b" Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 2A9EDNjb027347; Wed, 9 Nov 2022 10:53:42 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=420u/HEI3tHbzNq5I478ikRYO0zF6aKkT5Y5X4qFOYU=; b=JNzbdB0bA239WwHJ1g82qd89rfnQUcUE03rVXDxU+AK0TtLOD1IygZU5mHjZU2+B4oPU dubZeatH0NeoqUNFoZkmh3iMuDdJoQY+BdJGavFTUElDdLH/y9fCX/J4lGP69N/PyJTP YOWLAk9lELtqkoRrku1fPVvD64gZVryC0U4p9J9O0IWQ6N7Z8C9uiGE+5aHVG7xBIPKh n9G+WLSUpQXMGiW5Mt4KCKdFVbimT8pBY2rgJK5/0tP8WUbhLOrgRWJg50YnzVv9sevw zyedOPsp8BGnCehpxtUQ082SNTwfjTnp6c7OCjH3kynS8XST3gAwm4ktAruZ7ovNiUZW NA== Received: from ediex02.ad.cirrus.com ([84.19.233.68]) by mx0b-001ae601.pphosted.com (PPS) with ESMTPS id 3knm8pde7v-10 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Nov 2022 10:53:42 -0600 Received: from ediex01.ad.cirrus.com (198.61.84.80) by ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.15; Wed, 9 Nov 2022 10:53:38 -0600 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by ediex01.ad.cirrus.com (198.61.84.80) with Microsoft SMTP Server id 15.2.1118.15 via Frontend Transport; Wed, 9 Nov 2022 10:53:38 -0600 Received: from debianA11184.ad.cirrus.com (AUSNPC0LSNW1.ad.cirrus.com [198.61.65.92]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id AFD9B478; Wed, 9 Nov 2022 16:53:37 +0000 (UTC) From: Richard Fitzgerald To: , , , , , , Subject: [PATCH 11/12] dt-bindings: sound: Add Cirrus Logic CS48L31/32/33 codecs Date: Wed, 9 Nov 2022 16:53:30 +0000 Message-ID: <20221109165331.29332-12-rf@opensource.cirrus.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221109165331.29332-1-rf@opensource.cirrus.com> References: <20221109165331.29332-1-rf@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: FGTbUh0PSuxbtYxLMg3cAsBS8B1BY4j- X-Proofpoint-GUID: FGTbUh0PSuxbtYxLMg3cAsBS8B1BY4j- X-Proofpoint-Spam-Reason: safe Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, patches@opensource.cirrus.com, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Richard Fitzgerald 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" Codecs in this family have multiple digital and analog audio I/O that support a variety of external hardware connections and configurations. Signed-off-by: Richard Fitzgerald --- .../bindings/sound/cirrus,cs48l32.yaml | 96 +++++++++++++++++++ include/dt-bindings/sound/cs48l32.h | 25 +++++ 2 files changed, 121 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/cirrus,cs48l32.yaml create mode 100644 include/dt-bindings/sound/cs48l32.h diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs48l32.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs48l32.yaml new file mode 100644 index 000000000000..70fb294c6dc1 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs48l32.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs48l32.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS48L31/32/33 audio CODECs + +maintainers: + - patches@opensource.cirrus.com + +description: | + This describes audio configuration bindings for these codecs. + + See also the core bindings for the parent MFD driver: + + Documentation/devicetree/bindings/mfd/cirrus,cs48l32.yaml + + and defines for values used in these bindings: + + include/dt-bindings/sound/cs48l32.h + + The properties are all contained in the parent MFD node. + +properties: + '#sound-dai-cells': + const: 1 + + cirrus,in-type: + description: + A list of input type settings for each input. A maximum of 8 cells, + with four cells per input in the order INnL_1, INnR_1 INnL_2 INnR_2. + (where _1 and _2 are the alternative mux selections for that INn). + If the array is shorter than the number of inputs the unspecified + inputs default to CS48L32_IN_TYPE_DIFF. + $ref: "/schemas/types.yaml#/definitions/uint32-matrix" + minItems: 1 + maxItems: 8 + items: + items: + - description: + The first cell is INnL_1 input type. One of the CS48L32_IN_TYPE_xxx. + For non-muxed inputs this sets the type of INnL. + minimum: 0 + maximum: 1 + - description: + The second cell is INnR_1 input type. One of the CS48L32_IN_TYPE_xxx. + For non-muxed inputs this sets the type of INnR. + minimum: 0 + maximum: 1 + - description: + The third cell is INnL_2 input type. One of the CS48L32_IN_TYPE_xxx. + For non-muxed inputs this cell must be 0. + minimum: 0 + maximum: 1 + - description: + The fourth cell is INnR_2 input type. One of the CS48L32_IN_TYPE_xxx. + For non-muxed inputs this cell must be 0. + minimum: 0 + maximum: 1 + + cirrus,max-channels-clocked: + description: + Maximum number of channels that clocks will be generated for. When using + multiple data lines, every sample slot can transfer multiple channels + (one per data line). This pdata sets the maximum number of slots. + One cell for each ASP, use a value of zero for ASPs that should be + handled normally. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 4 + items: + default: 0 + + cirrus,pdm-sup: + description: + Indicates how the MICBIAS pins have been externally connected to DMICs + on each input. One cell per input (IN1, IN2, ...). One of the + CS48L32_MICBIAS_xxx values. + See the INn_PDM_SUP field in the datasheet for a description. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 4 + +examples: + - | + cs48l32@0 { + compatible = "cirrus,cs48l32"; + + cirrus,in-type = < + CS48L32_IN_TYPE_DIFF CS48L32_IN_TYPE_DIFF /* IN1[LR]_1 differential */ + CS48L32_IN_TYPE_SE CS48L32_IN_TYPE_SE /* IN1[LR]_2 single-ended */ + CS48L32_IN_TYPE_DIFF CS48L32_IN_TYPE_DIFF /* IN2[LR]_1 differential */ + >; + cirrus,max-channels-clocked = <2 0 0>; + }; diff --git a/include/dt-bindings/sound/cs48l32.h b/include/dt-bindings/sound/cs48l32.h new file mode 100644 index 000000000000..0b774da0a6c8 --- /dev/null +++ b/include/dt-bindings/sound/cs48l32.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Device Tree defines for CS48L32 codec. + * + * Copyright (C) 2016-2018, 2022 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef DT_BINDINGS_SOUND_CS48L32_H +#define DT_BINDINGS_SOUND_CS48L32_H + +#define CS48L32_IN_TYPE_DIFF 0 +#define CS48L32_IN_TYPE_SE 1 + +#define CS48L32_PDM_SUP_VOUT_MIC 0 +#define CS48L32_PDM_SUP_MICBIAS1 1 +#define CS48L32_PDM_SUP_MICBIAS2 2 +#define CS48L32_PDM_SUP_MICBIAS3 3 + +#define CS48L32_PDM_FMT_MODE_A_LSB_FIRST 0x0000 +#define CS48L32_PDM_FMT_MODE_B_LSB_FIRST 0x4000 +#define CS48L32_PDM_FMT_MODE_A_MSB_FIRST 0x8000 +#define CS48L32_PDM_FMT_MODE_B_MSB_FIRST 0xc000 + +#endif From patchwork Wed Nov 9 16:53:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 622946 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 6753FC4332F for ; Wed, 9 Nov 2022 16:58:04 +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 6F9F21696; Wed, 9 Nov 2022 17:57:12 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 6F9F21696 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1668013082; bh=0qrJNegmQWIqo7g+dvtTuJLECKakvc2RzpIbDMvzR60=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=stp/OspBybOpXFOwgtMioxi5pxQET5ckJaRbbcxGA0JNPfry0+WBfKJ1GGFN6JD88 c0uQ6ariWvLfBo8avH/04xXLz04l286LtnC6bXvNl2CIJlAYj2NphiQtfZl8vTECsE J7y1bvUB++depODFGX00cAczaEjDJdqa0/95ucRE= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 2F7FAF805D3; Wed, 9 Nov 2022 17:54:10 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 7AFB6F805D4; Wed, 9 Nov 2022 17:54:09 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0b-001ae601.pphosted.com [67.231.152.168]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id BC696F80568 for ; Wed, 9 Nov 2022 17:53:45 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz BC696F80568 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="RCtGJsuZ" Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 2A9EDShC027448; Wed, 9 Nov 2022 10:53:44 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=WM6jpW6DgTPjmvd0oslLtgv34/8CnmjVbElriUhAJ7E=; b=RCtGJsuZ6il9LrTz8eIp/sQMI+iCv+RGIzfZlTAFcmqlnahhgPhoi4tLEsd1azxCfryq a+6EwJalLMDEEGD6/iJjgtxptIPvW82dA/lh4uaRJhfawvuxehrKEG7xcRhkZ/C/97mo 4vXqbwdSg6xUupiy1hyEsggpgS6cVj28rdDYmQOFVkT1viRhZ+KATlm/7hSEUCNtaPKq ZuVrMRFDUdLDRFPMeuh4fB2wqexrkY6JqR1VlGHSLNnHCSJTuwv6XpYpbhu6VATx+5A5 MRX0+SexegibP6xKtk8SznWAluePlnxyjLmbRkKOH7SFIKmHEjHqrKhYZY0iq/p0OEqd +g== Received: from ediex01.ad.cirrus.com ([84.19.233.68]) by mx0b-001ae601.pphosted.com (PPS) with ESMTPS id 3knm8pde8f-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Nov 2022 10:53:43 -0600 Received: from ediex02.ad.cirrus.com (198.61.84.81) by ediex01.ad.cirrus.com (198.61.84.80) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.15; Wed, 9 Nov 2022 10:53:38 -0600 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by anon-ediex02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server id 15.2.1118.15 via Frontend Transport; Wed, 9 Nov 2022 10:53:38 -0600 Received: from debianA11184.ad.cirrus.com (AUSNPC0LSNW1.ad.cirrus.com [198.61.65.92]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id 41AC4468; Wed, 9 Nov 2022 16:53:38 +0000 (UTC) From: Richard Fitzgerald To: , , , , , , Subject: [PATCH 12/12] ASoC: cs48l32: Add codec driver for Cirrus Logic CS48L31/32/33 Date: Wed, 9 Nov 2022 16:53:31 +0000 Message-ID: <20221109165331.29332-13-rf@opensource.cirrus.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221109165331.29332-1-rf@opensource.cirrus.com> References: <20221109165331.29332-1-rf@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-ORIG-GUID: I1EANI6z0MOXU1V1RIAxLhcETBc4_Rbt X-Proofpoint-GUID: I1EANI6z0MOXU1V1RIAxLhcETBc4_Rbt X-Proofpoint-Spam-Reason: safe Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, patches@opensource.cirrus.com, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, Richard Fitzgerald 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: Stuart Henderson The CS48L32 is a high-performance low-power audio DSP for smartphones and other portable audio devices. The CS48L32 combines a programmable Halo Core DSP with a variety of power-efficient fixed-function audio processors. Signed-off-by: Piotr Stankiewicz Signed-off-by: Stuart Henderson Signed-off-by: Qi Zhou Signed-off-by: Charles Keepax Signed-off-by: Richard Fitzgerald --- MAINTAINERS | 4 + include/sound/cs48l32.h | 89 + sound/soc/codecs/Kconfig | 9 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs48l32-core.c | 2782 +++++++++++++++++++++++++++++++ sound/soc/codecs/cs48l32.c | 1211 ++++++++++++++ sound/soc/codecs/cs48l32.h | 386 +++++ 7 files changed, 4483 insertions(+) create mode 100644 include/sound/cs48l32.h create mode 100644 sound/soc/codecs/cs48l32-core.c create mode 100644 sound/soc/codecs/cs48l32.c create mode 100644 sound/soc/codecs/cs48l32.h diff --git a/MAINTAINERS b/MAINTAINERS index f52e9a6e290c..9d5ddb50f388 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5013,6 +5013,7 @@ F: Documentation/devicetree/bindings/mfd/cirrus,cs48l32.yaml F: Documentation/devicetree/bindings/mfd/cirrus,madera.yaml F: Documentation/devicetree/bindings/pinctrl/cirrus,cs48l32.yaml F: Documentation/devicetree/bindings/pinctrl/cirrus,madera.yaml +F: Documentation/devicetree/bindings/sound/cirrus,cs48l32.yaml F: Documentation/devicetree/bindings/sound/cirrus,madera.yaml F: drivers/gpio/gpio-madera* F: drivers/irqchip/irq-cirrus-cs48l32* @@ -5021,13 +5022,16 @@ F: drivers/mfd/cs47l* F: drivers/mfd/cs48l* F: drivers/mfd/madera* F: drivers/pinctrl/cirrus/* +F: include/dt-bindings/sound/cs48l32.h F: include/dt-bindings/sound/madera* F: include/linux/irqchip/irq-cirrus-cs48l32* F: include/linux/irqchip/irq-madera* F: include/linux/mfd/cs48l32/* F: include/linux/mfd/madera/* +F: include/sound/cs48l32.h F: include/sound/madera* F: sound/soc/codecs/cs47l* +F: sound/soc/codecs/cs48l32* F: sound/soc/codecs/madera* CISCO FCOE HBA DRIVER diff --git a/include/sound/cs48l32.h b/include/sound/cs48l32.h new file mode 100644 index 000000000000..3078bb520dae --- /dev/null +++ b/include/sound/cs48l32.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Cirrus Logic CS48L32 codec + * + * Copyright (C) 2016-2018, 2022 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef SOUND_CS48L32_H +#define SOUND_CS48L32_H + +#include +#include +#include + +/* pll_id for snd_soc_component_set_pll() */ +#define CS48L32_FLL1_REFCLK 1 + +/* source for snd_soc_component_set_pll() */ +#define CS48L32_FLL_SRC_NONE -1 +#define CS48L32_FLL_SRC_MCLK1 0 +#define CS48L32_FLL_SRC_PDMCLK 5 +#define CS48L32_FLL_SRC_ASP1_BCLK 8 +#define CS48L32_FLL_SRC_ASP2_BCLK 9 +#define CS48L32_FLL_SRC_ASP1_FSYNC 12 +#define CS48L32_FLL_SRC_ASP2_FSYNC 13 + +/* clk_id for snd_soc_component_set_sysclk() and snd_soc_dai_set_sysclk() */ +#define CS48L32_CLK_SYSCLK_1 1 +#define CS48L32_CLK_SYSCLK_2 2 +#define CS48L32_CLK_SYSCLK_3 3 +#define CS48L32_CLK_SYSCLK_4 4 +#define CS48L32_CLK_DSPCLK 7 +#define CS48L32_CLK_OPCLK 9 +#define CS48L32_CLK_PDM_FLLCLK 13 + +/* source for snd_soc_component_set_sysclk() */ +#define CS48L32_CLK_SRC_MCLK1 0x0 +#define CS48L32_CLK_SRC_FLL1 0x4 +#define CS48L32_CLK_SRC_ASP1_BCLK 0x8 +#define CS48L32_CLK_SRC_ASP2_BCLK 0x9 + +/* Notifier events */ +#define CS48L32_NOTIFY_ULTRASONIC 0x1 + +/** + * struct cs48l32_us_notify_data - Ultrasonic event notification data + * @us_no: Index of ultrasonic block that reported the event. + * + * Data passed in an ultrasonic notification event callback. + */ +struct cs48l32_us_notify_data { + unsigned int us_no; +}; + +/** + * cs48l32_register_notifier() - Register notification event callback + * @component: ASoC component of the cs48l32 driver. + * @nb: Client struct notifier_block. + * + * Return: 0 on success else standard negative error code. + */ +static inline int cs48l32_register_notifier(struct snd_soc_component *component, + struct notifier_block *nb) +{ + struct cs48l32_mfd *mfd = dev_get_drvdata(component->dev->parent); + + return blocking_notifier_chain_register(&mfd->notifier, nb); +} + +/** + * cs48l32_unregister_notifier() - Unregister notification event callback + * @component: ASoC component of the cs48l32 driver. + * @nb: Client struct notifier_block. + * + * Unregister an event notifier callback that was registered by + * cs48l32_register_notifier(). + * + * Return: 0 on success else standard negative error code. + */ +static inline int cs48l32_unregister_notifier(struct snd_soc_component *component, + struct notifier_block *nb) +{ + struct cs48l32_mfd *mfd = dev_get_drvdata(component->dev->parent); + + return blocking_notifier_chain_unregister(&mfd->notifier, nb); +} + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7a13e750751a..aadbdf33b373 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -87,6 +87,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS47L85 imply SND_SOC_CS47L90 imply SND_SOC_CS47L92 + imply SND_SOC_CS48L32 imply SND_SOC_CS53L30 imply SND_SOC_CX20442 imply SND_SOC_CX2072X @@ -351,6 +352,7 @@ config SND_SOC_WM_ADSP select CS_DSP select SND_SOC_COMPRESS default y if SND_SOC_MADERA=y + default y if SND_SOC_CS48L32=y default y if SND_SOC_CS47L24=y default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y @@ -358,6 +360,7 @@ config SND_SOC_WM_ADSP default y if SND_SOC_CS35L41_SPI=y default y if SND_SOC_CS35L41_I2C=y default m if SND_SOC_MADERA=m + default m if SND_SOC_CS48L32=m default m if SND_SOC_CS47L24=m default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m @@ -816,6 +819,12 @@ config SND_SOC_CS47L92 tristate depends on MFD_CS47L92 +config SND_SOC_CS48L32 + tristate "Cirrus Logic CS48L32 CODEC" + depends on MFD_CS48L32 + help + Enable support for the Cirrus Logic CS48L32 codec. + # Cirrus Logic Quad-Channel ADC config SND_SOC_CS53L30 tristate "Cirrus Logic CS53L30 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 9170ee1447dd..2c59463d1085 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -89,6 +89,7 @@ snd-soc-cs47l35-objs := cs47l35.o snd-soc-cs47l85-objs := cs47l85.o snd-soc-cs47l90-objs := cs47l90.o snd-soc-cs47l92-objs := cs47l92.o +snd-soc-cs48l32-objs := cs48l32.o cs48l32-core.o snd-soc-cs53l30-objs := cs53l30.o snd-soc-cx20442-objs := cx20442.o snd-soc-cx2072x-objs := cx2072x.o @@ -449,6 +450,7 @@ obj-$(CONFIG_SND_SOC_CS47L35) += snd-soc-cs47l35.o obj-$(CONFIG_SND_SOC_CS47L85) += snd-soc-cs47l85.o obj-$(CONFIG_SND_SOC_CS47L90) += snd-soc-cs47l90.o obj-$(CONFIG_SND_SOC_CS47L92) += snd-soc-cs47l92.o +obj-$(CONFIG_SND_SOC_CS48L32) += snd-soc-cs48l32.o obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_CX2072X) += snd-soc-cx2072x.o diff --git a/sound/soc/codecs/cs48l32-core.c b/sound/soc/codecs/cs48l32-core.c new file mode 100644 index 000000000000..8daebbb0ea50 --- /dev/null +++ b/sound/soc/codecs/cs48l32-core.c @@ -0,0 +1,2782 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Cirrus Logic CS48L32 codec core support +// +// Copyright (C) 2016-2018, 2020, 2022 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs48l32.h" + +#define CS48L32_ASP_ENABLES1 0x00 +#define CS48L32_ASP_CONTROL1 0x04 +#define CS48L32_ASP_CONTROL2 0x08 +#define CS48L32_ASP_CONTROL3 0x0c +#define CS48L32_ASP_FRAME_CONTROL1 0x10 +#define CS48L32_ASP_FRAME_CONTROL2 0x14 +#define CS48L32_ASP_FRAME_CONTROL5 0x20 +#define CS48L32_ASP_FRAME_CONTROL6 0x24 +#define CS48L32_ASP_DATA_CONTROL1 0x30 +#define CS48L32_ASP_DATA_CONTROL5 0x40 + +#define CS48L32_SYSCLK_RATE_6MHZ 0 +#define CS48L32_SYSCLK_RATE_12MHZ 1 +#define CS48L32_SYSCLK_RATE_24MHZ 2 +#define CS48L32_SYSCLK_RATE_49MHZ 3 +#define CS48L32_SYSCLK_RATE_98MHZ 4 + +#define CS48L32_FLLHJ_INT_MAX_N 1023 +#define CS48L32_FLLHJ_INT_MIN_N 1 +#define CS48L32_FLLHJ_FRAC_MAX_N 255 +#define CS48L32_FLLHJ_FRAC_MIN_N 2 +#define CS48L32_FLLHJ_LP_INT_MODE_THRESH 100000 +#define CS48L32_FLLHJ_LOW_THRESH 192000 +#define CS48L32_FLLHJ_MID_THRESH 1152000 +#define CS48L32_FLLHJ_MAX_THRESH 13000000 +#define CS48L32_FLLHJ_LOW_GAINS 0x23f0 +#define CS48L32_FLLHJ_MID_GAINS 0x22f2 +#define CS48L32_FLLHJ_HIGH_GAINS 0x21f0 +#define CS48L32_FLL_MAX_FOUT 50000000 +#define CS48L32_FLL_MAX_REFDIV 8 + +#define CS48L32_FLL_CONTROL1_OFFS 0x00 +#define CS48L32_FLL_CONTROL2_OFFS 0x04 +#define CS48L32_FLL_CONTROL3_OFFS 0x08 +#define CS48L32_FLL_CONTROL4_OFFS 0x0c +#define CS48L32_FLL_CONTROL5_OFFS 0x10 +#define CS48L32_FLL_CONTROL6_OFFS 0x14 +#define CS48L32_FLL_DIGITAL_TEST2_OFFS 0x34 +#define CS48L32_FLL_GPIO_CLOCK_OFFS 0xa0 + +#define CS48L32_DSP_CLOCK_FREQ_OFFS 0x00000 + +#define CS48L32_ASP_FMT_DSP_MODE_A 0 +#define CS48L32_ASP_FMT_DSP_MODE_B 1 +#define CS48L32_ASP_FMT_I2S_MODE 2 +#define CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE 3 + +#define CS48L32_HALO_SAMPLE_RATE_RX1 0x00080 +#define CS48L32_HALO_SAMPLE_RATE_TX1 0x00280 +#define CS48L32_HALO_DSP_RATE_MASK 0x1f + +#define cs48l32_fll_err(_fll, fmt, ...) \ + dev_err(_fll->cs48l32->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) +#define cs48l32_fll_warn(_fll, fmt, ...) \ + dev_warn(_fll->cs48l32->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) +#define cs48l32_fll_dbg(_fll, fmt, ...) \ + dev_dbg(_fll->cs48l32->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) + +#define cs48l32_asp_err(_dai, fmt, ...) \ + dev_err(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__) +#define cs48l32_asp_warn(_dai, fmt, ...) \ + dev_warn(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__) +#define cs48l32_asp_dbg(_dai, fmt, ...) \ + dev_dbg(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__) + +const char * const cs48l32_mixer_texts[] = { + "None", + "Tone Generator 1", + "Tone Generator 2", + "Noise Generator", + "IN1L", + "IN1R", + "IN2L", + "IN2R", + "ASP1RX1", + "ASP1RX2", + "ASP1RX3", + "ASP1RX4", + "ASP1RX5", + "ASP1RX6", + "ASP1RX7", + "ASP1RX8", + "ASP2RX1", + "ASP2RX2", + "ASP2RX3", + "ASP2RX4", + "ISRC1INT1", + "ISRC1INT2", + "ISRC1INT3", + "ISRC1INT4", + "ISRC1DEC1", + "ISRC1DEC2", + "ISRC1DEC3", + "ISRC1DEC4", + "ISRC2INT1", + "ISRC2INT2", + "ISRC2DEC1", + "ISRC2DEC2", + "ISRC3INT1", + "ISRC3INT2", + "ISRC3DEC1", + "ISRC3DEC2", + "EQ1", + "EQ2", + "EQ3", + "EQ4", + "DRC1L", + "DRC1R", + "DRC2L", + "DRC2R", + "LHPF1", + "LHPF2", + "LHPF3", + "LHPF4", + "Ultrasonic 1", + "Ultrasonic 2", + "DSP1.1", + "DSP1.2", + "DSP1.3", + "DSP1.4", + "DSP1.5", + "DSP1.6", + "DSP1.7", + "DSP1.8", +}; + +unsigned int cs48l32_mixer_values[] = { + 0x000, /* Silence (mute) */ + 0x004, /* Tone generator 1 */ + 0x005, /* Tone generator 2 */ + 0x00C, /* Noise Generator */ + 0x010, /* IN1L signal path */ + 0x011, /* IN1R signal path */ + 0x012, /* IN2L signal path */ + 0x013, /* IN2R signal path */ + 0x020, /* ASP1 RX1 */ + 0x021, /* ASP1 RX2 */ + 0x022, /* ASP1 RX3 */ + 0x023, /* ASP1 RX4 */ + 0x024, /* ASP1 RX5 */ + 0x025, /* ASP1 RX6 */ + 0x026, /* ASP1 RX7 */ + 0x027, /* ASP1 RX8 */ + 0x030, /* ASP2 RX1 */ + 0x031, /* ASP2 RX2 */ + 0x032, /* ASP2 RX3 */ + 0x033, /* ASP2 RX4 */ + 0x098, /* ISRC1 INT1 */ + 0x099, /* ISRC1 INT2 */ + 0x09a, /* ISRC1 INT3 */ + 0x09b, /* ISRC1 INT4 */ + 0x09C, /* ISRC1 DEC1 */ + 0x09D, /* ISRC1 DEC2 */ + 0x09e, /* ISRC1 DEC3 */ + 0x09f, /* ISRC1 DEC4 */ + 0x0A0, /* ISRC2 INT1 */ + 0x0A1, /* ISRC2 INT2 */ + 0x0A4, /* ISRC2 DEC1 */ + 0x0A5, /* ISRC2 DEC2 */ + 0x0A8, /* ISRC3 INT1 */ + 0x0A9, /* ISRC3 INT2 */ + 0x0AC, /* ISRC3 DEC1 */ + 0x0AD, /* ISRC3 DEC2 */ + 0x0B8, /* EQ1 */ + 0x0B9, /* EQ2 */ + 0x0BA, /* EQ3 */ + 0x0BB, /* EQ4 */ + 0x0C0, /* DRC1 Left */ + 0x0C1, /* DRC1 Right */ + 0x0C2, /* DRC2 Left */ + 0x0C3, /* DRC2 Right */ + 0x0C8, /* LHPF1 */ + 0x0C9, /* LHPF2 */ + 0x0CA, /* LHPF3 */ + 0x0CB, /* LHPF4 */ + 0x0D8, /* Ultrasonic 1 */ + 0x0D9, /* Ultrasonic 2 */ + 0x100, /* DSP1 channel 1 */ + 0x101, /* DSP1 channel 2 */ + 0x102, /* DSP1 channel 3 */ + 0x103, /* DSP1 channel 4 */ + 0x104, /* DSP1 channel 5 */ + 0x105, /* DSP1 channel 6 */ + 0x106, /* DSP1 channel 7 */ + 0x107, /* DSP1 channel 8 */ +}; + +const DECLARE_TLV_DB_SCALE(cs48l32_ana_tlv, 0, 100, 0); +const DECLARE_TLV_DB_SCALE(cs48l32_eq_tlv, -1200, 100, 0); +const DECLARE_TLV_DB_SCALE(cs48l32_digital_tlv, -6400, 50, 0); +const DECLARE_TLV_DB_SCALE(cs48l32_noise_tlv, -10800, 600, 0); +const DECLARE_TLV_DB_SCALE(cs48l32_mixer_tlv, -3200, 100, 0); +const DECLARE_TLV_DB_SCALE(cs48l32_us_tlv, 0, 600, 0); + +static void cs48l32_spin_sysclk(struct cs48l32 *cs48l32) +{ + struct cs48l32_mfd *mfd = cs48l32->mfd; + unsigned int val; + int ret, i; + + /* Skip this if the chip is down */ + if (pm_runtime_suspended(mfd->dev)) + return; + + /* + * Just read a register a few times to ensure the internal + * oscillator sends out a some clocks. + */ + for (i = 0; i < 4; i++) { + ret = regmap_read(mfd->regmap, CS48L32_DEVID, &val); + if (ret) + dev_err(cs48l32->dev, "%s Failed to read register: %d (%d)\n", + __func__, ret, i); + } + + udelay(300); +} + +static const char * const cs48l32_rate_text[] = { + "Sample Rate 1", + "Sample Rate 2", + "Sample Rate 3", + "Sample Rate 4", +}; + +static const unsigned int cs48l32_rate_val[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +int cs48l32_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + int ret; + + /* Prevent any mixer mux changes while we do this */ + mutex_lock(&cs48l32->rate_lock); + + /* The write must be guarded by a number of SYSCLK cycles */ + cs48l32_spin_sysclk(cs48l32); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + cs48l32_spin_sysclk(cs48l32); + + mutex_unlock(&cs48l32->rate_lock); + + return ret; +} + +static const char * const cs48l32_sample_rate_text[] = { + "12kHz", + "24kHz", + "48kHz", + "96kHz", + "192kHz", + "384kHz", + "768kHz", + "11.025kHz", + "22.05kHz", + "44.1kHz", + "88.2kHz", + "176.4kHz", + "352.8kHz", + "705.6kHz", + "8kHz", + "16kHz", + "32kHz", +}; + +static const unsigned int cs48l32_sample_rate_val[] = { + 0x01, /* 12kHz */ + 0x02, /* 24kHz */ + 0x03, /* 48kHz */ + 0x04, /* 96kHz */ + 0x05, /* 192kHz */ + 0x06, /* 384kHz */ + 0x07, /* 768kHz */ + 0x09, /* 11.025kHz */ + 0x0a, /* 22.05kHz */ + 0x0b, /* 44.1kHz */ + 0x0c, /* 88.2kHz */ + 0x0d, /* 176.4kHz */ + 0x0e, /* 352.8kHz */ + 0x0f, /* 705.6kHz */ + 0x11, /* 8kHz */ + 0x12, /* 16kHz */ + 0x13, /* 32kHz */ +}; + +const struct soc_enum cs48l32_sample_rate[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE1, + CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_ENUM_SIZE, + cs48l32_sample_rate_text, + cs48l32_sample_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE2, + CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_ENUM_SIZE, + cs48l32_sample_rate_text, + cs48l32_sample_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE3, + CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_ENUM_SIZE, + cs48l32_sample_rate_text, + cs48l32_sample_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE4, + CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT, + CS48L32_SAMPLE_RATE_ENUM_SIZE, + cs48l32_sample_rate_text, + cs48l32_sample_rate_val), +}; + +static int cs48l32_inmux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *) kcontrol->private_value; + unsigned int mux, src_val, in_type; + int ret; + + mux = ucontrol->value.enumerated.item[0]; + if (mux > 1) + return -EINVAL; + + switch (e->reg) { + case CS48L32_IN1L_CONTROL1: + in_type = cs48l32->in_type[0][2 * mux]; + break; + case CS48L32_IN1R_CONTROL1: + in_type = cs48l32->in_type[0][1 + (2 * mux)]; + break; + case CS48L32_IN2L_CONTROL1: + in_type = cs48l32->in_type[1][2 * mux]; + break; + case CS48L32_IN2R_CONTROL1: + in_type = cs48l32->in_type[1][1 + (2 * mux)]; + break; + default: + return -EINVAL; + } + + src_val = mux << e->shift_l; + + if (in_type == CS48L32_IN_TYPE_SE) + src_val |= 1 << CS48L32_INx_SRC_SHIFT; + + dev_dbg(cs48l32->dev, "mux=%u reg=0x%x in_type=0x%x val=0x%x\n", + mux, e->reg, in_type, src_val); + + ret = snd_soc_component_update_bits(dapm->component, + e->reg, + CS48L32_INx_SRC_MASK, + src_val); + if (ret > 0) + snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); + + return ret; +} + +static const char * const cs48l32_inmux_texts[] = { + "Analog 1", "Analog 2", +}; + +static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxl_enum, + CS48L32_IN1L_CONTROL1, + CS48L32_INx_SRC_SHIFT + 1, + cs48l32_inmux_texts); + +static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxr_enum, + CS48L32_IN1R_CONTROL1, + CS48L32_INx_SRC_SHIFT + 1, + cs48l32_inmux_texts); + +static SOC_ENUM_SINGLE_DECL(cs48l32_in2muxl_enum, + CS48L32_IN2L_CONTROL1, + CS48L32_INx_SRC_SHIFT + 1, + cs48l32_inmux_texts); + +static SOC_ENUM_SINGLE_DECL(cs48l32_in2muxr_enum, + CS48L32_IN2R_CONTROL1, + CS48L32_INx_SRC_SHIFT + 1, + cs48l32_inmux_texts); + +const struct snd_kcontrol_new cs48l32_inmux[] = { + SOC_DAPM_ENUM_EXT("IN1L Mux", cs48l32_in1muxl_enum, + snd_soc_dapm_get_enum_double, cs48l32_inmux_put), + SOC_DAPM_ENUM_EXT("IN1R Mux", cs48l32_in1muxr_enum, + snd_soc_dapm_get_enum_double, cs48l32_inmux_put), + SOC_DAPM_ENUM_EXT("IN2L Mux", cs48l32_in2muxl_enum, + snd_soc_dapm_get_enum_double, cs48l32_inmux_put), + SOC_DAPM_ENUM_EXT("IN2R Mux", cs48l32_in2muxr_enum, + snd_soc_dapm_get_enum_double, cs48l32_inmux_put), +}; + +static const char * const cs48l32_dmode_texts[] = { + "Analog", "Digital", +}; + +static int cs48l32_dmode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + struct soc_enum *e = (struct soc_enum *) kcontrol->private_value; + unsigned int mode; + int ret, result; + + mode = ucontrol->value.enumerated.item[0]; + switch (mode) { + case 0: + ret = snd_soc_component_update_bits(component, + CS48L32_ADC1L_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + CS48L32_ADC1x_INT_ENA_FRC_MASK); + if (ret < 0) { + dev_err(component->dev, + "Failed to set ADC1L_INT_ENA_FRC: %d\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, + CS48L32_ADC1R_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + CS48L32_ADC1x_INT_ENA_FRC_MASK); + if (ret < 0) { + dev_err(component->dev, + "Failed to set ADC1R_INT_ENA_FRC: %d\n", ret); + return ret; + } + + result = snd_soc_component_update_bits(component, + e->reg, + BIT(CS48L32_IN1_MODE_SHIFT), + 0); + if (result < 0) { + dev_err(component->dev, "Failed to set input mode: %d\n", result); + return result; + } + + usleep_range(200, 300); + + ret = snd_soc_component_update_bits(component, + CS48L32_ADC1L_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + 0); + if (ret < 0) { + dev_err(component->dev, + "Failed to clear ADC1L_INT_ENA_FRC: %d\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, + CS48L32_ADC1R_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + 0); + if (ret < 0) { + dev_err(component->dev, + "Failed to clear ADC1R_INT_ENA_FRC: %d\n", ret); + return ret; + } + + if (result > 0) + snd_soc_dapm_mux_update_power(dapm, kcontrol, mode, e, NULL); + + return result; + case 1: + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + default: + return -EINVAL; + } +} + +static SOC_ENUM_SINGLE_DECL(cs48l32_in1dmode_enum, + CS48L32_INPUT1_CONTROL1, + CS48L32_IN1_MODE_SHIFT, + cs48l32_dmode_texts); + +const struct snd_kcontrol_new cs48l32_dmode_mux[] = { + SOC_DAPM_ENUM_EXT("IN1 Mode", cs48l32_in1dmode_enum, + snd_soc_dapm_get_enum_double, cs48l32_dmode_put), +}; + +static const char * const cs48l32_in_texts[] = { + "IN1L", "IN1R", "IN2L", "IN2R", +}; + +static const char * const cs48l32_us_freq_texts[] = { + "24.5-40.5kHz", "18-22kHz", "16-24kHz", "20-28kHz", +}; + +static const unsigned int cs48l32_us_freq_val[] = { + 0x2, 0x3, +}; + +const struct soc_enum cs48l32_us_freq[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL, + CS48L32_US1_FREQ_SHIFT, + CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT, + ARRAY_SIZE(cs48l32_us_freq_val), + &cs48l32_us_freq_texts[2], + cs48l32_us_freq_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL, + CS48L32_US1_FREQ_SHIFT, + CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT, + ARRAY_SIZE(cs48l32_us_freq_val), + &cs48l32_us_freq_texts[2], + cs48l32_us_freq_val), +}; + +static const unsigned int cs48l32_us_in_val[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static const struct soc_enum cs48l32_us_inmux_enum[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL, + CS48L32_US1_SRC_SHIFT, + CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT, + ARRAY_SIZE(cs48l32_us_in_val), + cs48l32_in_texts, + cs48l32_us_in_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL, + CS48L32_US1_SRC_SHIFT, + CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT, + ARRAY_SIZE(cs48l32_us_in_val), + cs48l32_in_texts, + cs48l32_us_in_val), +}; + +const struct snd_kcontrol_new cs48l32_us_inmux[] = { + SOC_DAPM_ENUM("Ultrasonic 1 Input", cs48l32_us_inmux_enum[0]), + SOC_DAPM_ENUM("Ultrasonic 2 Input", cs48l32_us_inmux_enum[1]), +}; + +static const char * const cs48l32_us_det_thr_texts[] = { + "-6dB", "-9dB", "-12dB", "-15dB", "-18dB", "-21dB", "-24dB", "-27dB", +}; + +const struct soc_enum cs48l32_us_det_thr[] = { + SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL, + CS48L32_US1_DET_THR_SHIFT, + ARRAY_SIZE(cs48l32_us_det_thr_texts), + cs48l32_us_det_thr_texts), + SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL, + CS48L32_US1_DET_THR_SHIFT, + ARRAY_SIZE(cs48l32_us_det_thr_texts), + cs48l32_us_det_thr_texts), +}; + +static const char * const cs48l32_us_det_num_texts[] = { + "1 Sample", + "2 Samples", + "4 Samples", + "8 Samples", + "16 Samples", + "32 Samples", + "64 Samples", + "128 Samples", + "256 Samples", + "512 Samples", + "1024 Samples", + "2048 Samples", + "4096 Samples", + "8192 Samples", + "16384 Samples", + "32768 Samples", +}; + +const struct soc_enum cs48l32_us_det_num[] = { + SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL, + CS48L32_US1_DET_NUM_SHIFT, + ARRAY_SIZE(cs48l32_us_det_num_texts), + cs48l32_us_det_num_texts), + SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL, + CS48L32_US1_DET_NUM_SHIFT, + ARRAY_SIZE(cs48l32_us_det_num_texts), + cs48l32_us_det_num_texts), +}; + +static const char * const cs48l32_us_det_hold_texts[] = { + "0 Samples", + "31 Samples", + "63 Samples", + "127 Samples", + "255 Samples", + "511 Samples", + "1023 Samples", + "2047 Samples", + "4095 Samples", + "8191 Samples", + "16383 Samples", + "32767 Samples", + "65535 Samples", + "131071 Samples", + "262143 Samples", + "524287 Samples", +}; + +const struct soc_enum cs48l32_us_det_hold[] = { + SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL, + CS48L32_US1_DET_HOLD_SHIFT, + ARRAY_SIZE(cs48l32_us_det_hold_texts), + cs48l32_us_det_hold_texts), + SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL, + CS48L32_US1_DET_HOLD_SHIFT, + ARRAY_SIZE(cs48l32_us_det_hold_texts), + cs48l32_us_det_hold_texts), +}; + +const struct soc_enum cs48l32_us_output_rate[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL, + CS48L32_US1_RATE_SHIFT, + CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL, + CS48L32_US1_RATE_SHIFT, + CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), +}; + +static const char * const cs48l32_us_det_lpf_cut_texts[] = { + "1722Hz", "833Hz", "408Hz", "203Hz", +}; + +const struct soc_enum cs48l32_us_det_lpf_cut[] = { + SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL, + CS48L32_US1_DET_LPF_CUT_SHIFT, + ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts), + cs48l32_us_det_lpf_cut_texts), + SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL, + CS48L32_US1_DET_LPF_CUT_SHIFT, + ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts), + cs48l32_us_det_lpf_cut_texts), +}; + +static const char * const cs48l32_us_det_dcy_texts[] = { + "0 ms", "0.79 ms", "1.58 ms", "3.16 ms", "6.33 ms", "12.67 ms", + "25.34 ms", "50.69 ms", +}; + +const struct soc_enum cs48l32_us_det_dcy[] = { + SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL, + CS48L32_US1_DET_DCY_SHIFT, + ARRAY_SIZE(cs48l32_us_det_dcy_texts), + cs48l32_us_det_dcy_texts), + SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL, + CS48L32_US1_DET_DCY_SHIFT, + ARRAY_SIZE(cs48l32_us_det_dcy_texts), + cs48l32_us_det_dcy_texts), +}; + +const struct snd_kcontrol_new cs48l32_us_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const char * const cs48l32_vol_ramp_text[] = { + "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", + "16ms/6dB", "32ms/6dB", +}; + +irqreturn_t cs48l32_us1_activity(int irq, void *data) +{ + struct cs48l32 *cs48l32 = data; + struct cs48l32_us_notify_data us_data; + + us_data.us_no = 1; + cs48l32_call_notifiers(cs48l32->mfd, CS48L32_NOTIFY_ULTRASONIC, &us_data); + + return IRQ_HANDLED; +} + +irqreturn_t cs48l32_us2_activity(int irq, void *data) +{ + struct cs48l32 *cs48l32 = data; + struct cs48l32_us_notify_data us_data; + + us_data.us_no = 2; + cs48l32_call_notifiers(cs48l32->mfd, CS48L32_NOTIFY_ULTRASONIC, &us_data); + + return IRQ_HANDLED; +} + +SOC_ENUM_SINGLE_DECL(cs48l32_in_vd_ramp, + CS48L32_INPUT_VOL_CONTROL, + CS48L32_IN_VD_RAMP_SHIFT, + cs48l32_vol_ramp_text); + +SOC_ENUM_SINGLE_DECL(cs48l32_in_vi_ramp, + CS48L32_INPUT_VOL_CONTROL, + CS48L32_IN_VI_RAMP_SHIFT, + cs48l32_vol_ramp_text); + +static const char * const cs48l32_in_hpf_cut_text[] = { + "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz" +}; + +SOC_ENUM_SINGLE_DECL(cs48l32_in_hpf_cut_enum, + CS48L32_INPUT_HPF_CONTROL, + CS48L32_IN_HPF_CUT_SHIFT, + cs48l32_in_hpf_cut_text); + +static const char * const cs48l32_in_dmic_osr_text[] = { + "384kHz", "768kHz", "1.536MHz", "2.048MHz", "2.4576MHz", "3.072MHz", + "6.144MHz", +}; + +const struct soc_enum cs48l32_in_dmic_osr[] = { + SOC_ENUM_SINGLE(CS48L32_INPUT1_CONTROL1, + CS48L32_IN1_OSR_SHIFT, + ARRAY_SIZE(cs48l32_in_dmic_osr_text), + cs48l32_in_dmic_osr_text), + SOC_ENUM_SINGLE(CS48L32_INPUT2_CONTROL1, + CS48L32_IN1_OSR_SHIFT, + ARRAY_SIZE(cs48l32_in_dmic_osr_text), + cs48l32_in_dmic_osr_text), +}; + +int cs48l32_in_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int reg, shift; + int ret = 0; + + snd_soc_dapm_mutex_lock(dapm); + + /* Cannot change rate on an active input */ + reg = snd_soc_component_read(component, CS48L32_INPUT_CONTROL); + shift = (e->reg - CS48L32_IN1L_CONTROL1) / 0x20; + shift ^= 0x1; /* Flip bottom bit for channel order */ + + if ((reg) & (1 << shift)) { + ret = -EBUSY; + goto exit; + } + + ret = snd_soc_put_enum_double(kcontrol, ucontrol); +exit: + snd_soc_dapm_mutex_unlock(dapm); + return ret; +} + +const struct soc_enum cs48l32_input_rate[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_IN1L_CONTROL1, + CS48L32_INx_RATE_SHIFT, + CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_IN1R_CONTROL1, + CS48L32_INx_RATE_SHIFT, + CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_IN2L_CONTROL1, + CS48L32_INx_RATE_SHIFT, + CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_IN2R_CONTROL1, + CS48L32_INx_RATE_SHIFT, + CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), +}; + +int cs48l32_low_power_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + unsigned int reg, mask; + int ret; + + snd_soc_dapm_mutex_lock(dapm); + + /* Cannot change low power mode on an active input */ + reg = snd_soc_component_read(component, CS48L32_INPUT_CONTROL); + mask = (mc->reg - CS48L32_IN1L_CONTROL1) / 0x20; + mask ^= 0x1; /* Flip bottom bit for channel order */ + + if ((reg) & (1 << mask)) { + ret = -EBUSY; + dev_err(component->dev, "Can't change lp mode on an active input\n"); + goto exit; + } + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + +exit: + snd_soc_dapm_mutex_unlock(dapm); + return ret; +} + +const struct soc_enum noise_gen_rate = + SOC_VALUE_ENUM_SINGLE(CS48L32_COMFORT_NOISE_GENERATOR, + CS48L32_NOISE_GEN_RATE_SHIFT, + CS48L32_NOISE_GEN_RATE_MASK >> CS48L32_NOISE_GEN_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val); + +static const char * const cs48l32_auxpdm_freq_texts[] = { + "3.072MHz", "2.048MHz", "1.536MHz", "768kHz", +}; + +SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_freq, + CS48L32_AUXPDM1_CONTROL1, + CS48L32_AUXPDM1_FREQ_SHIFT, + cs48l32_auxpdm_freq_texts); + +SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_freq, + CS48L32_AUXPDM2_CONTROL1, + CS48L32_AUXPDM1_FREQ_SHIFT, + cs48l32_auxpdm_freq_texts); + +static const char * const cs48l32_auxpdm_src_texts[] = { + "Analog", "IN1 Digital", "IN2 Digital", +}; + +static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_in, + CS48L32_AUXPDM_CTRL2, + CS48L32_AUXPDMDAT1_SRC_SHIFT, + cs48l32_auxpdm_src_texts); + +static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_in, + CS48L32_AUXPDM_CTRL2, + CS48L32_AUXPDMDAT2_SRC_SHIFT, + cs48l32_auxpdm_src_texts); + +const struct snd_kcontrol_new cs48l32_auxpdm_inmux[] = { + SOC_DAPM_ENUM("AUXPDM1 Input", cs48l32_auxpdm1_in), + SOC_DAPM_ENUM("AUXPDM2 Input", cs48l32_auxpdm2_in), +}; + +static const unsigned int cs48l32_auxpdm_analog_in_val[] = { + 0x0, 0x1, +}; + +static const struct soc_enum cs48l32_auxpdm_analog_inmux_enum[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM1_CONTROL1, + CS48L32_AUXPDM1_SRC_SHIFT, + CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT, + ARRAY_SIZE(cs48l32_auxpdm_analog_in_val), + cs48l32_in_texts, + cs48l32_auxpdm_analog_in_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM2_CONTROL1, + CS48L32_AUXPDM1_SRC_SHIFT, + CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT, + ARRAY_SIZE(cs48l32_auxpdm_analog_in_val), + cs48l32_in_texts, + cs48l32_auxpdm_analog_in_val), +}; + +const struct snd_kcontrol_new cs48l32_auxpdm_analog_inmux[] = { + SOC_DAPM_ENUM("AUXPDM1 Analog Input", cs48l32_auxpdm_analog_inmux_enum[0]), + SOC_DAPM_ENUM("AUXPDM2 Analog Input", cs48l32_auxpdm_analog_inmux_enum[1]), +}; + +const struct snd_kcontrol_new cs48l32_auxpdm_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +const struct soc_enum cs48l32_isrc_fsh[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1, + CS48L32_ISRC1_FSH_SHIFT, + CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1, + CS48L32_ISRC1_FSH_SHIFT, + CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1, + CS48L32_ISRC1_FSH_SHIFT, + CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), +}; + +const struct soc_enum cs48l32_isrc_fsl[] = { + SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1, + CS48L32_ISRC1_FSL_SHIFT, + CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1, + CS48L32_ISRC1_FSL_SHIFT, + CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1, + CS48L32_ISRC1_FSL_SHIFT, + CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val), +}; + +const struct soc_enum cs48l32_fx_rate = + SOC_VALUE_ENUM_SINGLE(CS48L32_FX_SAMPLE_RATE, + CS48L32_FX_RATE_SHIFT, + CS48L32_FX_RATE_MASK >> CS48L32_FX_RATE_SHIFT, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, + cs48l32_rate_val); + +static const char * const cs48l32_lhpf_mode_text[] = { + "Low-pass", "High-pass" +}; + +const struct soc_enum cs48l32_lhpf_mode[] = { + SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 0, + ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text), + SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 1, + ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text), + SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 2, + ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text), + SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 3, + ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text), +}; + +int cs48l32_lhpf_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + __be32 *data = (__be32 *)ucontrol->value.bytes.data; + s16 val = (s16)be32_to_cpu(*data); + + if (abs(val) >= 4096) { + dev_err(cs48l32->dev, "Rejecting unstable LHPF coefficients\n"); + return -EINVAL; + } + + return snd_soc_bytes_put(kcontrol, ucontrol); +} + +static const char * const cs48l32_eq_mode_text[] = { + "Low-pass", "High-pass", +}; + +const struct soc_enum cs48l32_eq_mode[] = { + SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 0, + ARRAY_SIZE(cs48l32_eq_mode_text), + cs48l32_eq_mode_text), + SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 1, + ARRAY_SIZE(cs48l32_eq_mode_text), + cs48l32_eq_mode_text), + SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 2, + ARRAY_SIZE(cs48l32_eq_mode_text), + cs48l32_eq_mode_text), + SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 3, + ARRAY_SIZE(cs48l32_eq_mode_text), + cs48l32_eq_mode_text), +}; + +int cs48l32_eq_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *) kcontrol->private_value; + unsigned int item; + + item = snd_soc_enum_val_to_item(e, cs48l32->eq_mode[e->shift_l]); + ucontrol->value.enumerated.item[0] = item; + + return 0; +} + +int cs48l32_eq_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *) kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val; + bool changed = false; + + if (item[0] >= e->items) + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]); + + snd_soc_dapm_mutex_lock(dapm); + if (cs48l32->eq_mode[e->shift_l] != val) { + cs48l32->eq_mode[e->shift_l] = val; + changed = true; + } + snd_soc_dapm_mutex_unlock(dapm); + + return changed; +} + +int cs48l32_eq_coeff_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct cs48l32_eq_control *ctl = (void *) kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = ctl->max; + + return 0; +} + +int cs48l32_eq_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_eq_control *params = (void *)kcontrol->private_value; + __be16 *coeffs; + unsigned int coeff_idx; + int block_idx; + + block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1); + block_idx /= 68; + + coeffs = &cs48l32->eq_coefficients[block_idx][0]; + + coeff_idx = (params->reg - params->block_base) / 2; + coeff_idx += ((params->shift == 0) ? 1 : 0); + + ucontrol->value.integer.value[0] = be16_to_cpu(coeffs[coeff_idx]); + + return 0; +} + +int cs48l32_eq_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_eq_control *params = (void *)kcontrol->private_value; + __be16 *coeffs; + unsigned int coeff_idx; + int block_idx; + + block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1); + block_idx /= 68; + + coeffs = &cs48l32->eq_coefficients[block_idx][0]; + + coeff_idx = (params->reg - params->block_base) / 2; + coeff_idx += ((params->shift == 0) ? 1 : 0); + + snd_soc_dapm_mutex_lock(dapm); + coeffs[coeff_idx] = cpu_to_be16(ucontrol->value.integer.value[0]); + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} + +const struct snd_kcontrol_new cs48l32_drc_activity_output_mux[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +const struct snd_kcontrol_new cs48l32_dsp_trigger_output_mux[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +int cs48l32_dsp_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *) kcontrol->private_value; + unsigned int cached_rate; + const unsigned int rate_num = e->mask; + int item; + + if (rate_num >= ARRAY_SIZE(cs48l32->dsp_dma_rates)) + return -EINVAL; + + cached_rate = cs48l32->dsp_dma_rates[rate_num]; + item = snd_soc_enum_val_to_item(e, cached_rate); + ucontrol->value.enumerated.item[0] = item; + + return 0; +} + +int cs48l32_dsp_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *) kcontrol->private_value; + const unsigned int rate_num = e->mask; + const unsigned int item = ucontrol->value.enumerated.item[0]; + unsigned int val; + bool changed = false; + + if (item >= e->items) + return -EINVAL; + + if (rate_num >= ARRAY_SIZE(cs48l32->dsp_dma_rates)) + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item); + + snd_soc_dapm_mutex_lock(dapm); + if (cs48l32->dsp_dma_rates[rate_num] != val) { + cs48l32->dsp_dma_rates[rate_num] = val; + changed = true; + } + snd_soc_dapm_mutex_unlock(dapm); + + return changed; +} + +const struct soc_enum cs48l32_dsp_rate_enum[] = { + /* RX rates */ + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 0, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 1, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 2, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 3, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 4, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 5, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 6, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 7, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + /* TX rates */ + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 8, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 9, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 10, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 11, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 12, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 13, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 14, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, + 15, + ARRAY_SIZE(cs48l32_rate_text), + cs48l32_rate_text, cs48l32_rate_val), +}; + +int cs48l32_dsp_pre_run(struct wm_adsp *dsp) +{ + struct cs48l32 *cs48l32 = container_of(dsp, struct cs48l32, dsp); + unsigned int reg; + const u8 *rate = cs48l32->dsp_dma_rates; + int i; + + reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_RX1; + for (i = 0; i < CS48L32_DSP_N_RX_CHANNELS; ++i) { + regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate); + reg += 8; + rate++; + } + + reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_TX1; + for (i = 0; i < CS48L32_DSP_N_TX_CHANNELS; ++i) { + regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate); + reg += 8; + rate++; + } + + usleep_range(300, 600); + + return 0; +} + +int cs48l32_dsp_memory_enable(struct cs48l32 *cs48l32, + const struct cs48l32_dsp_power_regs *regs) +{ + struct regmap *regmap = cs48l32->mfd->regmap; + int i, j, ret; + + /* disable power-off */ + for (i = 0; i < regs->n_ext; ++i) { + for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) { + ret = regmap_write(regmap, j, 0x3); + if (ret) + goto err; + } + } + + /* power-up the banks in sequence */ + for (i = 0; i < regs->n_pwd; ++i) { + ret = regmap_write(regmap, regs->pwd[i], 0x1); + if (ret) + goto err; + + udelay(1); /* allow bank to power-up */ + + ret = regmap_write(regmap, regs->pwd[i], 0x3); + if (ret) + goto err; + + udelay(1); /* allow bank to power-up */ + } + + return 0; + +err: + dev_err(cs48l32->dev, "Failed to write SRAM enables (%d)\n", ret); + cs48l32_dsp_memory_disable(cs48l32, regs); + + return ret; +} + +void cs48l32_dsp_memory_disable(struct cs48l32 *cs48l32, + const struct cs48l32_dsp_power_regs *regs) +{ + struct regmap *regmap = cs48l32->mfd->regmap; + int i, j, ret; + + for (i = 0; i < regs->n_pwd; ++i) { + ret = regmap_write(regmap, regs->pwd[i], 0); + if (ret) + dev_warn(cs48l32->dev, "Failed to write SRAM enables (%d)\n", ret); + } + + for (i = 0; i < regs->n_ext; ++i) { + for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) { + ret = regmap_write(regmap, j, 0); + if (ret) + dev_warn(cs48l32->dev, "Failed to write SRAM enables (%d)\n", ret); + } + } +} + +static int cs48l32_dsp_freq_update(struct snd_soc_dapm_widget *w, unsigned int freq_reg, + unsigned int freqsel_reg) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_mfd *mfd = cs48l32->mfd; + struct wm_adsp *dsp = &cs48l32->dsp; + int ret; + unsigned int freq, freq_sel, freq_sts; + + if (!freq_reg) + return -EINVAL; + + ret = regmap_read(mfd->regmap, freq_reg, &freq); + if (ret) { + dev_err(component->dev, "Failed to read 0x%x: %d\n", freq_reg, ret); + return ret; + } + + if (freqsel_reg) { + freq_sts = (freq & CS48L32_SYSCLK_FREQ_STS_MASK) >> CS48L32_SYSCLK_FREQ_STS_SHIFT; + + ret = regmap_read(mfd->regmap, freqsel_reg, &freq_sel); + if (ret) { + dev_err(component->dev, "Failed to read 0x%x: %d\n", freqsel_reg, ret); + return ret; + } + freq_sel = (freq_sel & CS48L32_SYSCLK_FREQ_MASK) >> CS48L32_SYSCLK_FREQ_SHIFT; + + if (freq_sts != freq_sel) { + dev_err(component->dev, "SYSCLK FREQ (0x%x) != FREQ STS (0x%x)\n", + freq_sel, freq_sts); + return -ETIMEDOUT; + } + } + + freq &= CS48L32_DSP_CLK_FREQ_MASK; + freq >>= CS48L32_DSP_CLK_FREQ_SHIFT; + + ret = regmap_write(dsp->cs_dsp.regmap, + dsp->cs_dsp.base + CS48L32_DSP_CLOCK_FREQ_OFFS, freq); + if (ret) { + dev_err(component->dev, "Failed to set HALO clock freq: %d\n", ret); + return ret; + } + + return 0; +} + +int cs48l32_dsp_freq_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + return cs48l32_dsp_freq_update(w, CS48L32_SYSTEM_CLOCK2, CS48L32_SYSTEM_CLOCK1); + default: + return 0; + } +} + +irqreturn_t cs48l32_dsp1_irq(int irq, void *data) +{ + struct cs48l32 *cs48l32 = data; + int ret; + + ret = wm_adsp_compr_handle_irq(&cs48l32->dsp); + if (ret == -ENODEV) { + dev_err(cs48l32->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static const unsigned int cs48l32_opclk_ref_48k_rates[] = { + 6144000, + 12288000, + 24576000, + 49152000, +}; + +static const unsigned int cs48l32_opclk_ref_44k1_rates[] = { + 5644800, + 11289600, + 22579200, + 45158400, +}; + +static int cs48l32_set_opclk(struct snd_soc_component *component, unsigned int clk, + unsigned int freq) +{ + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + unsigned int reg; + const unsigned int *rates; + int ref, div, refclk; + + BUILD_BUG_ON(ARRAY_SIZE(cs48l32_opclk_ref_48k_rates) != + ARRAY_SIZE(cs48l32_opclk_ref_44k1_rates)); + + switch (clk) { + case CS48L32_CLK_OPCLK: + reg = CS48L32_OUTPUT_SYS_CLK; + refclk = cs48l32->sysclk; + break; + default: + return -EINVAL; + } + + if (refclk % 4000) + rates = cs48l32_opclk_ref_44k1_rates; + else + rates = cs48l32_opclk_ref_48k_rates; + + for (ref = 0; ref < ARRAY_SIZE(cs48l32_opclk_ref_48k_rates); ++ref) { + if (rates[ref] > refclk) + continue; + + div = 2; + while ((rates[ref] / div >= freq) && (div <= 30)) { + if (rates[ref] / div == freq) { + dev_dbg(component->dev, "Configured %dHz OPCLK\n", freq); + snd_soc_component_update_bits(component, reg, + CS48L32_OPCLK_DIV_MASK | + CS48L32_OPCLK_SEL_MASK, + (div << CS48L32_OPCLK_DIV_SHIFT) | + ref); + return 0; + } + div += 2; + } + } + + dev_err(component->dev, "Unable to generate %dHz OPCLK\n", freq); + return -EINVAL; +} + +static int cs48l32_get_dspclk_setting(struct cs48l32 *cs48l32, unsigned int freq, + int src, unsigned int *val) +{ + freq /= 15625; /* convert to 1/64ths of 1MHz */ + *val |= freq << CS48L32_DSP_CLK_FREQ_SHIFT; + + return 0; +} + +static int cs48l32_get_sysclk_setting(unsigned int freq) +{ + switch (freq) { + case 0: + case 5644800: + case 6144000: + return CS48L32_SYSCLK_RATE_6MHZ; + case 11289600: + case 12288000: + return CS48L32_SYSCLK_RATE_12MHZ << CS48L32_SYSCLK_FREQ_SHIFT; + case 22579200: + case 24576000: + return CS48L32_SYSCLK_RATE_24MHZ << CS48L32_SYSCLK_FREQ_SHIFT; + case 45158400: + case 49152000: + return CS48L32_SYSCLK_RATE_49MHZ << CS48L32_SYSCLK_FREQ_SHIFT; + case 90316800: + case 98304000: + return CS48L32_SYSCLK_RATE_98MHZ << CS48L32_SYSCLK_FREQ_SHIFT; + default: + return -EINVAL; + } +} + +static int cs48l32_set_pdm_fllclk(struct snd_soc_component *component, int source) +{ + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_mfd *mfd = cs48l32->mfd; + unsigned int val; + + switch (source) { + case CS48L32_PDMCLK_SRC_IN1_PDMCLK: + case CS48L32_PDMCLK_SRC_IN2_PDMCLK: + case CS48L32_PDMCLK_SRC_IN3_PDMCLK: + case CS48L32_PDMCLK_SRC_IN4_PDMCLK: + case CS48L32_PDMCLK_SRC_AUXPDM1_CLK: + case CS48L32_PDMCLK_SRC_AUXPDM2_CLK: + val = source << CS48L32_PDM_FLLCLK_SRC_SHIFT; + break; + default: + dev_err(cs48l32->dev, "Invalid PDM FLLCLK src %d\n", source); + return -EINVAL; + } + + return regmap_update_bits(mfd->regmap, CS48L32_INPUT_CONTROL2, + CS48L32_PDM_FLLCLK_SRC_MASK, val); +} + +int cs48l32_set_sysclk(struct snd_soc_component *component, int clk_id, int source, + unsigned int freq, int dir) +{ + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_mfd *mfd = cs48l32->mfd; + char *name; + unsigned int reg; + unsigned int mask = CS48L32_SYSCLK_SRC_MASK; + unsigned int val = source << CS48L32_SYSCLK_SRC_SHIFT; + int clk_freq_sel, *clk; + + switch (clk_id) { + case CS48L32_CLK_SYSCLK_1: + name = "SYSCLK"; + reg = CS48L32_SYSTEM_CLOCK1; + clk = &cs48l32->sysclk; + clk_freq_sel = cs48l32_get_sysclk_setting(freq); + mask |= CS48L32_SYSCLK_FREQ_MASK | CS48L32_SYSCLK_FRAC_MASK; + break; + case CS48L32_CLK_DSPCLK: + name = "DSPCLK"; + reg = CS48L32_DSP_CLOCK1; + clk = &cs48l32->dspclk; + clk_freq_sel = cs48l32_get_dspclk_setting(cs48l32, freq, source, &val); + mask |= CS48L32_DSP_CLK_FREQ_MASK; + break; + case CS48L32_CLK_OPCLK: + return cs48l32_set_opclk(component, clk_id, freq); + case CS48L32_CLK_PDM_FLLCLK: + return cs48l32_set_pdm_fllclk(component, source); + default: + return -EINVAL; + } + + if (clk_freq_sel < 0) { + dev_err(cs48l32->dev, "Failed to get %s setting for %dHZ\n", name, freq); + return clk_freq_sel; + } + + *clk = freq; + + if (freq == 0) { + dev_dbg(cs48l32->dev, "%s cleared\n", name); + return 0; + } + + val |= clk_freq_sel; + + if (freq % 6144000) + val |= CS48L32_SYSCLK_FRAC_MASK; + + dev_dbg(cs48l32->dev, "%s set to %uHz", name, freq); + + return regmap_update_bits(mfd->regmap, reg, mask, val); +} + +static int cs48l32_is_enabled_fll(struct cs48l32_fll *fll, int base) +{ + struct cs48l32_mfd *mfd = fll->cs48l32->mfd; + unsigned int reg; + int ret; + + ret = regmap_read(mfd->regmap, base + CS48L32_FLL_CONTROL1_OFFS, ®); + if (ret != 0) { + cs48l32_fll_err(fll, "Failed to read current state: %d\n", ret); + return ret; + } + + return reg & CS48L32_FLL_EN_MASK; +} + +static int cs48l32_wait_for_fll(struct cs48l32_fll *fll, bool requested) +{ + struct cs48l32_mfd *mfd = fll->cs48l32->mfd; + unsigned int val = 0; + int i; + + cs48l32_fll_dbg(fll, "Waiting for FLL...\n"); + + for (i = 0; i < 30; i++) { + regmap_read(mfd->regmap, fll->sts_addr, &val); + if (!!(val & fll->sts_mask) == requested) + return 0; + + switch (i) { + case 0 ... 5: + usleep_range(75, 125); + break; + case 11 ... 20: + usleep_range(750, 1250); + break; + default: + msleep(20); + break; + } + } + + cs48l32_fll_warn(fll, "Timed out waiting for %s\n", requested ? "lock" : "unlock"); + + return -ETIMEDOUT; +} + +static int cs48l32_fllhj_disable(struct cs48l32_fll *fll) +{ + struct cs48l32_mfd *mfd = fll->cs48l32->mfd; + bool change; + + cs48l32_fll_dbg(fll, "Disabling FLL\n"); + + /* + * Disable lockdet, but don't set ctrl_upd update bit. This allows the + * lock status bit to clear as normal, but should the FLL be enabled + * again due to a control clock being required, the lock won't re-assert + * as the FLL config registers are automatically applied when the FLL + * enables. + */ + regmap_set_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_HOLD_MASK); + regmap_clear_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL2_OFFS, + CS48L32_FLL_LOCKDET_MASK); + regmap_set_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL5_OFFS, + CS48L32_FLL_FRC_INTEG_UPD_MASK); + regmap_update_bits_check(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_EN_MASK, + 0, + &change); + + cs48l32_wait_for_fll(fll, false); + + /* + * ctrl_up gates the writes to all the fll's registers, setting it to 0 + * here ensures that after a runtime suspend/resume cycle when one + * enables the fll then ctrl_up is the last bit that is configured + * by the fll enable code rather than the cache sync operation which + * would have updated it much earlier before writing out all fll + * registers + */ + regmap_clear_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_CTRL_UPD_MASK); + + if (change) + pm_runtime_put_autosuspend(mfd->dev); + + return 0; +} + +static int cs48l32_fllhj_apply(struct cs48l32_fll *fll, int fin) +{ + struct cs48l32_mfd *mfd = fll->cs48l32->mfd; + int refdiv, fref, fout, lockdet_thr, fbdiv, fllgcd; + bool frac = false; + unsigned int fll_n, min_n, max_n, ratio, theta, lambda, hp; + unsigned int gains, num; + + cs48l32_fll_dbg(fll, "fin=%d, fout=%d\n", fin, fll->fout); + + for (refdiv = 0; refdiv < 4; refdiv++) { + if ((fin / (1 << refdiv)) <= CS48L32_FLLHJ_MAX_THRESH) + break; + } + + fref = fin / (1 << refdiv); + fout = fll->fout; + frac = fout % fref; + + if (frac && fll->integer_only) { + cs48l32_fll_err(fll, "%u:%u not an integer ratio\n", fin, fout); + return -EINVAL; + } + + if (fll->max_fref && (fref > fll->max_fref)) { + cs48l32_fll_err(fll, "fref=%u too high (max %u)\n", + fref, fll->max_fref); + return -EINVAL; + } + + /* + * Use simple heuristic approach to find a configuration that + * should work for most input clocks. + */ + if (fref < CS48L32_FLLHJ_LOW_THRESH) { + lockdet_thr = 2; + gains = CS48L32_FLLHJ_LOW_GAINS; + + if (frac) + fbdiv = 256; + else + fbdiv = 4; + } else if (fref < CS48L32_FLLHJ_MID_THRESH) { + lockdet_thr = 8; + gains = CS48L32_FLLHJ_MID_GAINS; + fbdiv = (frac) ? 16 : 2; + } else { + lockdet_thr = 8; + gains = CS48L32_FLLHJ_HIGH_GAINS; + fbdiv = 1; + } + /* Use high performance mode for fractional configurations. */ + if (frac) { + hp = 0x3; + min_n = CS48L32_FLLHJ_FRAC_MIN_N; + max_n = CS48L32_FLLHJ_FRAC_MAX_N; + } else { + if (fll->has_lp && (fref < CS48L32_FLLHJ_LP_INT_MODE_THRESH)) + hp = 0x0; + else + hp = 0x1; + + min_n = CS48L32_FLLHJ_INT_MIN_N; + max_n = CS48L32_FLLHJ_INT_MAX_N; + } + + ratio = fout / fref; + + cs48l32_fll_dbg(fll, "refdiv=%d, fref=%d, frac:%d\n", refdiv, fref, frac); + + while (ratio / fbdiv < min_n) { + fbdiv /= 2; + if (fbdiv < min_n) { + cs48l32_fll_err(fll, "FBDIV (%u) < minimum N (%u)\n", fbdiv, min_n); + return -EINVAL; + } + } + while (frac && (ratio / fbdiv > max_n)) { + fbdiv *= 2; + if (fbdiv >= 1024) { + cs48l32_fll_err(fll, "FBDIV (%u) >= 1024\n", fbdiv); + return -EINVAL; + } + } + + cs48l32_fll_dbg(fll, "lockdet=%d, hp=0x%x, fbdiv:%d\n", lockdet_thr, hp, fbdiv); + + /* Calculate N.K values */ + fllgcd = gcd(fout, fbdiv * fref); + num = fout / fllgcd; + lambda = (fref * fbdiv) / fllgcd; + fll_n = num / lambda; + theta = num % lambda; + + cs48l32_fll_dbg(fll, "fll_n=%d, gcd=%d, theta=%d, lambda=%d\n", + fll_n, fllgcd, theta, lambda); + + /* Some sanity checks before any registers are written. */ + if (fll_n < min_n || fll_n > max_n) { + cs48l32_fll_err(fll, "N not in valid %s mode range %d-%d: %d\n", + frac ? "fractional" : "integer", min_n, max_n, fll_n); + return -EINVAL; + } + if (fbdiv < 1 || (frac && fbdiv >= 1024) || (!frac && fbdiv >= 256)) { + cs48l32_fll_err(fll, "Invalid fbdiv for %s mode (%u)\n", + frac ? "fractional" : "integer", fbdiv); + return -EINVAL; + } + + /* clear the ctrl_upd bit to guarantee we write to it later. */ + regmap_update_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL2_OFFS, + CS48L32_FLL_LOCKDET_THR_MASK | + CS48L32_FLL_PHASEDET_MASK | + CS48L32_FLL_REFCLK_DIV_MASK | + CS48L32_FLL_N_MASK | + CS48L32_FLL_CTRL_UPD_MASK, + (lockdet_thr << CS48L32_FLL_LOCKDET_THR_SHIFT) | + (1 << CS48L32_FLL_PHASEDET_SHIFT) | + (refdiv << CS48L32_FLL_REFCLK_DIV_SHIFT) | + (fll_n << CS48L32_FLL_N_SHIFT)); + + regmap_update_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL3_OFFS, + CS48L32_FLL_LAMBDA_MASK | + CS48L32_FLL_THETA_MASK, + (lambda << CS48L32_FLL_LAMBDA_SHIFT) | + (theta << CS48L32_FLL_THETA_SHIFT)); + + regmap_update_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL4_OFFS, + (0xffff << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) | + CS48L32_FLL_HP_MASK | + CS48L32_FLL_FB_DIV_MASK, + (gains << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) | + (hp << CS48L32_FLL_HP_SHIFT) | + (fbdiv << CS48L32_FLL_FB_DIV_SHIFT)); + + return 0; +} + +static int cs48l32_fllhj_enable(struct cs48l32_fll *fll) +{ + struct cs48l32_mfd *mfd = fll->cs48l32->mfd; + int already_enabled = cs48l32_is_enabled_fll(fll, fll->base); + int ret; + + if (already_enabled < 0) + return already_enabled; + + if (!already_enabled) + pm_runtime_get_sync(mfd->dev); + + cs48l32_fll_dbg(fll, "Enabling FLL, initially %s\n", + already_enabled ? "enabled" : "disabled"); + + /* FLLn_HOLD must be set before configuring any registers */ + regmap_set_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_HOLD_MASK); + + /* Apply refclk */ + ret = cs48l32_fllhj_apply(fll, fll->ref_freq); + if (ret) { + cs48l32_fll_err(fll, "Failed to set FLL: %d\n", ret); + goto out; + } + regmap_update_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL2_OFFS, + CS48L32_FLL_REFCLK_SRC_MASK, + fll->ref_src << CS48L32_FLL_REFCLK_SRC_SHIFT); + + regmap_set_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_EN_MASK); + +out: + regmap_set_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL2_OFFS, + CS48L32_FLL_LOCKDET_MASK); + + regmap_set_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_CTRL_UPD_MASK); + + /* Release the hold so that flln locks to external frequency */ + regmap_clear_bits(mfd->regmap, + fll->base + CS48L32_FLL_CONTROL1_OFFS, + CS48L32_FLL_HOLD_MASK); + + if (!already_enabled) + cs48l32_wait_for_fll(fll, true); + + return 0; +} + +static int cs48l32_fllhj_validate(struct cs48l32_fll *fll, + unsigned int ref_in, + unsigned int fout) +{ + if (fout && !ref_in) { + cs48l32_fll_err(fll, "fllout set without valid input clk\n"); + return -EINVAL; + } + + if (fll->fout && fout != fll->fout) { + cs48l32_fll_err(fll, "Can't change output on active FLL\n"); + return -EINVAL; + } + + if (ref_in / CS48L32_FLL_MAX_REFDIV > CS48L32_FLLHJ_MAX_THRESH) { + cs48l32_fll_err(fll, "Can't scale %dMHz to <=13MHz\n", ref_in); + return -EINVAL; + } + + if (fout > CS48L32_FLL_MAX_FOUT) { + cs48l32_fll_err(fll, "Fout=%dMHz exceeeds maximum %dMHz\n", + fout, CS48L32_FLL_MAX_FOUT); + return -EINVAL; + } + + return 0; +} + +int cs48l32_fllhj_set_refclk(struct cs48l32_fll *fll, int source, + unsigned int fin, unsigned int fout) +{ + int ret = 0; + + if (fll->ref_src == source && fll->ref_freq == fin && fll->fout == fout) + return 0; + + if (fin && fout && cs48l32_fllhj_validate(fll, fin, fout)) + return -EINVAL; + + fll->ref_src = source; + fll->ref_freq = fin; + fll->fout = fout; + + if (fout) + ret = cs48l32_fllhj_enable(fll); + else + cs48l32_fllhj_disable(fll); + + return ret; +} + +int cs48l32_init_fll(struct cs48l32_fll *fll) +{ + fll->ref_src = CS48L32_FLL_SRC_NONE; + + return 0; +} + +static int cs48l32_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_mfd *mfd = cs48l32->mfd; + unsigned int val = 0U; + unsigned int base = dai->driver->base; + unsigned int mask = CS48L32_ASP_FMT_MASK | CS48L32_ASP_BCLK_INV_MASK | + CS48L32_ASP_BCLK_MSTR_MASK | + CS48L32_ASP_FSYNC_INV_MASK | + CS48L32_ASP_FSYNC_MSTR_MASK; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + val |= (CS48L32_ASP_FMT_DSP_MODE_A << CS48L32_ASP_FMT_SHIFT); + break; + case SND_SOC_DAIFMT_DSP_B: + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) { + cs48l32_asp_err(dai, "DSP_B cannot be clock consumer\n"); + return -EINVAL; + } + val |= (CS48L32_ASP_FMT_DSP_MODE_B << CS48L32_ASP_FMT_SHIFT); + break; + case SND_SOC_DAIFMT_I2S: + val |= (CS48L32_ASP_FMT_I2S_MODE << CS48L32_ASP_FMT_SHIFT); + break; + case SND_SOC_DAIFMT_LEFT_J: + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) { + cs48l32_asp_err(dai, "LEFT_J cannot be clock consumer\n"); + return -EINVAL; + } + val |= (CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE << CS48L32_ASP_FMT_SHIFT); + break; + default: + cs48l32_asp_err(dai, "Unsupported DAI format %d\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BC_FC: + break; + case SND_SOC_DAIFMT_BC_FP: + val |= CS48L32_ASP_FSYNC_MSTR_MASK; + break; + case SND_SOC_DAIFMT_BP_FC: + val |= CS48L32_ASP_BCLK_MSTR_MASK; + break; + case SND_SOC_DAIFMT_BP_FP: + val |= CS48L32_ASP_BCLK_MSTR_MASK; + val |= CS48L32_ASP_FSYNC_MSTR_MASK; + break; + default: + cs48l32_asp_err(dai, "Unsupported clock direction %d\n", + fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + val |= CS48L32_ASP_BCLK_INV_MASK; + val |= CS48L32_ASP_FSYNC_INV_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + val |= CS48L32_ASP_BCLK_INV_MASK; + break; + case SND_SOC_DAIFMT_NB_IF: + val |= CS48L32_ASP_FSYNC_INV_MASK; + break; + default: + return -EINVAL; + } + + regmap_update_bits(mfd->regmap, base + CS48L32_ASP_CONTROL2, mask, val); + + return 0; +} + +static const struct { + u32 freq; + u32 id; +} cs48l32_sclk_rates[] = { + { 128000, 12 }, + { 176400, 13 }, + { 192000, 14 }, + { 256000, 15 }, + { 352800, 16 }, + { 384000, 17 }, + { 512000, 18 }, + { 705600, 19 }, + { 768000, 21 }, + { 1024000, 23 }, + { 1411200, 25 }, + { 1536000, 27 }, + { 2048000, 29 }, + { 2822400, 31 }, + { 3072000, 33 }, + { 4096000, 36 }, + { 5644800, 38 }, + { 6144000, 40 }, + { 8192000, 47 }, + { 11289600, 49 }, + { 12288000, 51 }, + { 22579200, 57 }, + { 24576000, 59 }, +}; + +#define CS48L32_48K_RATE_MASK 0x0e00fe +#define CS48L32_44K1_RATE_MASK 0x00fe00 +#define CS48L32_RATE_MASK (CS48L32_48K_RATE_MASK | CS48L32_44K1_RATE_MASK) + +static const unsigned int cs48l32_sr_vals[] = { + 0, + 12000, /* CS48L32_48K_RATE_MASK */ + 24000, /* CS48L32_48K_RATE_MASK */ + 48000, /* CS48L32_48K_RATE_MASK */ + 96000, /* CS48L32_48K_RATE_MASK */ + 192000, /* CS48L32_48K_RATE_MASK */ + 384000, /* CS48L32_48K_RATE_MASK */ + 768000, /* CS48L32_48K_RATE_MASK */ + 0, + 11025, /* CS48L32_44K1_RATE_MASK */ + 22050, /* CS48L32_44K1_RATE_MASK */ + 44100, /* CS48L32_44K1_RATE_MASK */ + 88200, /* CS48L32_44K1_RATE_MASK */ + 176400, /* CS48L32_44K1_RATE_MASK */ + 352800, /* CS48L32_44K1_RATE_MASK */ + 705600, /* CS48L32_44K1_RATE_MASK */ + 0, + 8000, /* CS48L32_48K_RATE_MASK */ + 16000, /* CS48L32_48K_RATE_MASK */ + 32000, /* CS48L32_48K_RATE_MASK */ +}; + +static const struct snd_pcm_hw_constraint_list cs48l32_constraint = { + .count = ARRAY_SIZE(cs48l32_sr_vals), + .list = cs48l32_sr_vals, +}; + +static int cs48l32_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_dai_priv *dai_priv = &cs48l32->dai[dai->id - 1]; + unsigned int base_rate; + + if (!substream->runtime) + return 0; + + switch (dai_priv->clk) { + case CS48L32_CLK_SYSCLK_1: + case CS48L32_CLK_SYSCLK_2: + case CS48L32_CLK_SYSCLK_3: + case CS48L32_CLK_SYSCLK_4: + base_rate = cs48l32->sysclk; + break; + default: + return 0; + } + + if (base_rate == 0) + dai_priv->constraint.mask = CS48L32_RATE_MASK; + else if (base_rate % 4000) + dai_priv->constraint.mask = CS48L32_44K1_RATE_MASK; + else + dai_priv->constraint.mask = CS48L32_48K_RATE_MASK; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &dai_priv->constraint); +} + +static int cs48l32_hw_params_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_dai_priv *dai_priv = &cs48l32->dai[dai->id - 1]; + unsigned int sr_val, sr_reg, rate; + + rate = params_rate(params); + for (sr_val = 0; sr_val < ARRAY_SIZE(cs48l32_sr_vals); sr_val++) + if (cs48l32_sr_vals[sr_val] == rate) + break; + + if (sr_val == ARRAY_SIZE(cs48l32_sr_vals)) { + cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate); + return -EINVAL; + } + + switch (dai_priv->clk) { + case CS48L32_CLK_SYSCLK_1: + sr_reg = CS48L32_SAMPLE_RATE1; + break; + case CS48L32_CLK_SYSCLK_2: + sr_reg = CS48L32_SAMPLE_RATE2; + break; + case CS48L32_CLK_SYSCLK_3: + sr_reg = CS48L32_SAMPLE_RATE3; + break; + case CS48L32_CLK_SYSCLK_4: + sr_reg = CS48L32_SAMPLE_RATE3; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, sr_reg, CS48L32_SAMPLE_RATE_1_MASK, sr_val); + + return 0; +} + +static bool cs48l32_asp_cfg_changed(struct snd_soc_component *component, + unsigned int base, unsigned int sclk, + unsigned int slotws, unsigned int dataw) +{ + unsigned int val; + + val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL1); + if (sclk != (val & CS48L32_ASP_BCLK_FREQ_MASK)) + return true; + + val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2); + if (slotws != (val & (CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK))) + return true; + + val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL1); + if (dataw != (val & (CS48L32_ASP_TX_WL_MASK))) + return true; + + val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL5); + if (dataw != (val & (CS48L32_ASP_RX_WL_MASK))) + return true; + + return false; +} + +static int cs48l32_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 cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_mfd *mfd = cs48l32->mfd; + int base = dai->driver->base; + int dai_id = dai->id - 1; + unsigned int rate = params_rate(params); + unsigned int dataw = snd_pcm_format_width(params_format(params)); + unsigned int chan_limit = cs48l32->max_channels_clocked[dai_id]; + unsigned int asp_state = 0; + int sclk, sclk_target; + unsigned int slotw, n_slots, n_slots_multiple, val; + int i, ret; + + cs48l32_asp_dbg(dai, "hwparams in: ch:%u dataw:%u rate:%u\n", + params_channels(params), dataw, rate); + /* + * The following calculations hold only under the assumption that + * symmetric_[rates|channels|samplebits] are set to 1 + */ + if (cs48l32->tdm_slots[dai_id]) { + n_slots = cs48l32->tdm_slots[dai_id]; + slotw = cs48l32->tdm_width[dai_id]; + } else { + n_slots = params_channels(params); + slotw = dataw; + } + + if (chan_limit) + n_slots = min(n_slots, chan_limit); + + val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2); + val = (val & CS48L32_ASP_FMT_MASK) >> CS48L32_ASP_FMT_SHIFT; + if (val == CS48L32_ASP_FMT_I2S_MODE) + n_slots_multiple = 2; + else + n_slots_multiple = 1; + + sclk_target = snd_soc_tdm_params_to_bclk(params, slotw, n_slots, n_slots_multiple); + + for (i = 0; i < ARRAY_SIZE(cs48l32_sclk_rates); i++) { + if ((cs48l32_sclk_rates[i].freq >= sclk_target) && + (cs48l32_sclk_rates[i].freq % rate == 0)) { + sclk = cs48l32_sclk_rates[i].id; + break; + } + } + if (i == ARRAY_SIZE(cs48l32_sclk_rates)) { + cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate); + return -EINVAL; + } + + cs48l32_asp_dbg(dai, "hwparams out: n_slots:%u dataw:%u slotw:%u bclk:%u bclkid:%u\n", + n_slots, dataw, slotw, sclk_target, sclk); + + slotw = (slotw << CS48L32_ASP_TX_WIDTH_SHIFT) | + (slotw << CS48L32_ASP_RX_WIDTH_SHIFT); + + if (!cs48l32_asp_cfg_changed(component, base, sclk, slotw, dataw)) + return cs48l32_hw_params_rate(substream, params, dai); + + /* ASP must be disabled while changing configuration */ + asp_state = snd_soc_component_read(component, base + CS48L32_ASP_ENABLES1); + regmap_clear_bits(mfd->regmap, base + CS48L32_ASP_ENABLES1, 0xff00ff); + + ret = cs48l32_hw_params_rate(substream, params, dai); + if (ret != 0) + goto restore_asp; + + regmap_update_bits_async(mfd->regmap, + base + CS48L32_ASP_CONTROL1, + CS48L32_ASP_BCLK_FREQ_MASK, + sclk); + regmap_update_bits_async(mfd->regmap, + base + CS48L32_ASP_CONTROL2, + CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK, + slotw); + regmap_update_bits_async(mfd->regmap, + base + CS48L32_ASP_DATA_CONTROL1, + CS48L32_ASP_TX_WL_MASK, + dataw); + regmap_update_bits(mfd->regmap, + base + CS48L32_ASP_DATA_CONTROL5, + CS48L32_ASP_RX_WL_MASK, + dataw); + +restore_asp: + /* Restore ASP TX/RX enable state */ + regmap_update_bits(mfd->regmap, + base + CS48L32_ASP_ENABLES1, + 0xff00ff, + asp_state); + return ret; +} + +static const char * const cs48l32_dai_clk_str(int clk_id) +{ + switch (clk_id) { + case CS48L32_CLK_SYSCLK_1: + case CS48L32_CLK_SYSCLK_2: + case CS48L32_CLK_SYSCLK_3: + case CS48L32_CLK_SYSCLK_4: + return "SYSCLK"; + default: + return "Unknown clock"; + } +} + +static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_dai_priv *dai_priv = &cs48l32->dai[dai->id - 1]; + unsigned int base = dai->driver->base; + unsigned int current_asp_rate, target_asp_rate; + bool change_rate_domain = false; + int ret; + + if (clk_id == dai_priv->clk) + return 0; + + if (snd_soc_dai_active(dai)) { + cs48l32_asp_err(dai, "Can't change clock on active DAI\n"); + return -EBUSY; + } + + switch (clk_id) { + case CS48L32_CLK_SYSCLK_1: + target_asp_rate = 0U << CS48L32_ASP_RATE_SHIFT; + break; + case CS48L32_CLK_SYSCLK_2: + target_asp_rate = 1U << CS48L32_ASP_RATE_SHIFT; + break; + case CS48L32_CLK_SYSCLK_3: + target_asp_rate = 2U << CS48L32_ASP_RATE_SHIFT; + break; + case CS48L32_CLK_SYSCLK_4: + target_asp_rate = 3U << CS48L32_ASP_RATE_SHIFT; + break; + default: + return -EINVAL; + } + + dai_priv->clk = clk_id; + cs48l32_asp_dbg(dai, "Setting to %s\n", cs48l32_dai_clk_str(clk_id)); + + if (base) { + ret = regmap_read(cs48l32->mfd->regmap, + base + CS48L32_ASP_CONTROL1, + ¤t_asp_rate); + if (ret != 0) { + cs48l32_asp_err(dai, "Failed to check rate: %d\n", ret); + return ret; + } + + if ((current_asp_rate & CS48L32_ASP_RATE_MASK) != + (target_asp_rate & CS48L32_ASP_RATE_MASK)) { + change_rate_domain = true; + + mutex_lock(&cs48l32->rate_lock); + /* Guard the rate change with SYSCLK cycles */ + cs48l32_spin_sysclk(cs48l32); + } + + snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1, + CS48L32_ASP_RATE_MASK, target_asp_rate); + + if (change_rate_domain) { + cs48l32_spin_sysclk(cs48l32); + mutex_unlock(&cs48l32->rate_lock); + } + } + + return 0; +} + +static void cs48l32_set_channels_to_mask(struct snd_soc_dai *dai, + unsigned int base, + int channels, unsigned int mask) +{ + struct snd_soc_component *component = dai->component; + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_mfd *mfd = cs48l32->mfd; + int slot, i, j = 0, shift; + unsigned int frame_ctls[2] = {0, 0}; + + for (i = 0; i < channels; ++i) { + slot = ffs(mask) - 1; + if (slot < 0) + return; + + if (i - (j * 4) >= 4) { + ++j; + if (j >= 2) + break; + } + + shift = (8 * (i - j * 4)); + + frame_ctls[j] |= slot << shift; + + mask &= ~(1 << slot); /* ? mask ^= 1 << slot ? */ + } + + regmap_write(mfd->regmap, base, frame_ctls[0]); + regmap_write(mfd->regmap, base + 0x4, frame_ctls[1]); + + if (mask) + cs48l32_asp_warn(dai, "Too many channels in TDM mask\n"); +} + +static int cs48l32_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + int base = dai->driver->base; + int rx_max_chan = dai->driver->playback.channels_max; + int tx_max_chan = dai->driver->capture.channels_max; + + /* Only support TDM for the physical ASPs */ + if (dai->id > CS48L32_MAX_ASP) + return -EINVAL; + + if (slots == 0) { + tx_mask = (1 << tx_max_chan) - 1; + rx_mask = (1 << rx_max_chan) - 1; + } + + cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL1, + tx_max_chan, tx_mask); + cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL5, + rx_max_chan, rx_mask); + + cs48l32->tdm_width[dai->id - 1] = slot_width; + cs48l32->tdm_slots[dai->id - 1] = slots; + + return 0; +} + +const struct snd_soc_dai_ops cs48l32_dai_ops = { + .startup = &cs48l32_startup, + .set_fmt = &cs48l32_set_fmt, + .set_tdm_slot = &cs48l32_set_tdm_slot, + .hw_params = &cs48l32_hw_params, + .set_sysclk = &cs48l32_dai_set_sysclk, +}; + +int cs48l32_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + + cs48l32_spin_sysclk(cs48l32); + + return 0; +} + +int cs48l32_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + unsigned int reg; + + if (w->shift % 2) + reg = CS48L32_IN1L_CONTROL2 + ((w->shift / 2) * 0x40); + else + reg = CS48L32_IN1R_CONTROL2 + ((w->shift / 2) * 0x40); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (w->shift) { + case CS48L32_IN1L_EN_SHIFT: + snd_soc_component_update_bits(component, + CS48L32_ADC1L_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + CS48L32_ADC1x_INT_ENA_FRC_MASK); + break; + case CS48L32_IN1R_EN_SHIFT: + snd_soc_component_update_bits(component, + CS48L32_ADC1R_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + CS48L32_ADC1x_INT_ENA_FRC_MASK); + break; + default: + break; + } + cs48l32->in_up_pending++; + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(200, 300); + + switch (w->shift) { + case CS48L32_IN1L_EN_SHIFT: + snd_soc_component_update_bits(component, + CS48L32_ADC1L_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + 0); + break; + case CS48L32_IN1R_EN_SHIFT: + snd_soc_component_update_bits(component, + CS48L32_ADC1R_ANA_CONTROL1, + CS48L32_ADC1x_INT_ENA_FRC_MASK, + 0); + break; + + default: + break; + } + cs48l32->in_up_pending--; + snd_soc_component_update_bits(component, reg, CS48L32_INx_MUTE_MASK, 0); + + /* Uncached write-only register, no need for update_bits */ + if (!cs48l32->in_up_pending) + snd_soc_component_write(component, cs48l32->in_vu_reg, CS48L32_IN_VU_MASK); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, reg, + CS48L32_INx_MUTE_MASK, CS48L32_INx_MUTE_MASK); + snd_soc_component_write(component, cs48l32->in_vu_reg, CS48L32_IN_VU_MASK); + break; + default: + break; + } + + return 0; +} + +int cs48l32_in_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + /* + * Uncached write-only register, no need for update_bits. + * Will fail if codec is off but that will be handled by cs48l32_in_ev + */ + snd_soc_component_write(component, cs48l32->in_vu_reg, CS48L32_IN_VU); + + return ret; +} + +static bool cs48l32_eq_filter_unstable(bool mode, __be16 in_a, __be16 in_b) +{ + s16 a = be16_to_cpu(in_a); + s16 b = be16_to_cpu(in_b); + + if (!mode) + return abs(a) >= 4096; + + if (abs(b) >= 4096) + return true; + + return (abs((a << 16) / (4096 - b)) >= 4096 << 4); +} + +int cs48l32_eq_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_mfd *mfd = cs48l32->mfd; + unsigned int mode = cs48l32->eq_mode[w->shift]; + unsigned int reg = CS48L32_EQ1_BAND1_COEFF1 + (68 * w->shift); + __be16 *data = &cs48l32->eq_coefficients[w->shift][0]; + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (cs48l32_eq_filter_unstable(!!mode, data[1], data[0]) || + cs48l32_eq_filter_unstable(true, data[7], data[6]) || + cs48l32_eq_filter_unstable(true, data[13], data[12]) || + cs48l32_eq_filter_unstable(true, data[19], data[18]) || + cs48l32_eq_filter_unstable(false, data[25], data[24])) { + dev_err(cs48l32->dev, "Rejecting unstable EQ coefficients.\n"); + ret = -EINVAL; + } else { + ret = regmap_raw_write(mfd->regmap, reg, data, + CS48L32_EQ_BLOCK_SZ); + if (ret < 0) { + dev_err(cs48l32->dev, "Error writing EQ coefficients: %d\n", ret); + goto out; + } + + ret = snd_soc_component_update_bits(component, + CS48L32_EQ_CONTROL2, + w->mask, + mode << w->shift); + if (ret < 0) + dev_err(cs48l32->dev, "Error writing EQ mode: %d\n", ret); + } + break; + default: + break; + } + +out: + return ret; +} + +static int cs48l32_get_variable_u32_array(struct cs48l32 *cs48l32, + const char *propname, + u32 *dest, + int n_max, + int multiple) +{ + struct cs48l32_mfd *mfd = cs48l32->mfd; + int n, ret; + + n = device_property_read_u32_array(mfd->dev, propname, NULL, 0); + if (n == -EINVAL) { + return 0; /* missing, ignore */ + } else if (n < 0) { + dev_warn(cs48l32->dev, "%s malformed (%d)\n", propname, n); + return -EINVAL; + } else if ((n % multiple) != 0) { + dev_warn(cs48l32->dev, "%s not a multiple of %d entries\n", propname, multiple); + return -EINVAL; + } + + if (n > n_max) + n = n_max; + + ret = device_property_read_u32_array(mfd->dev, propname, dest, n); + + if (ret < 0) + return ret; + else + return n; +} + +static void cs48l32_prop_get_in_type(struct cs48l32 *cs48l32) +{ + u32 tmp[CS48L32_MAX_INPUT * CS48L32_MAX_MUXED_IN_CHANNELS]; + int n, i, in_idx, ch_idx; + + BUILD_BUG_ON(ARRAY_SIZE(cs48l32->in_type) != CS48L32_MAX_INPUT); + BUILD_BUG_ON(ARRAY_SIZE(cs48l32->in_type[0]) != + CS48L32_MAX_MUXED_IN_CHANNELS); + + n = cs48l32_get_variable_u32_array(cs48l32, + "cirrus,in-type", + tmp, + ARRAY_SIZE(tmp), + CS48L32_MAX_MUXED_IN_CHANNELS); + if (n < 0) + return; + + in_idx = 0; + ch_idx = 0; + for (i = 0; i < n; ++i) { + cs48l32->in_type[in_idx][ch_idx] = tmp[i]; + + if (++ch_idx == CS48L32_MAX_MUXED_IN_CHANNELS) { + ch_idx = 0; + ++in_idx; + } + } +} + +static void cs48l32_prop_get(struct cs48l32 *cs48l32) +{ + int ret; + + ret = cs48l32_get_variable_u32_array(cs48l32, + "cirrus,max-channels-clocked", + cs48l32->max_channels_clocked, + ARRAY_SIZE(cs48l32->max_channels_clocked), + 1); + if (ret < 0) + return; + + cs48l32_prop_get_in_type(cs48l32); + + cs48l32_get_variable_u32_array(cs48l32, + "cirrus,pdm-sup", + cs48l32->pdm_sup, + ARRAY_SIZE(cs48l32->pdm_sup), + 1); +} + +int cs48l32_init_inputs(struct snd_soc_component *component) +{ + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + struct cs48l32_mfd *mfd = cs48l32->mfd; + unsigned int ana_mode_l, ana_mode_r, dig_mode; + int i; + + /* + * Initialize input modes from the A settings. For muxed inputs the + * B settings will be applied if the mux is changed + */ + for (i = 0; i < cs48l32->max_analogue_inputs; i++) { + dev_dbg(cs48l32->dev, "IN%d type %u:%u:%u:%u\n", i + 1, + cs48l32->in_type[i][0], + cs48l32->in_type[i][1], + cs48l32->in_type[i][2], + cs48l32->in_type[i][3]); + + switch (cs48l32->in_type[i][0]) { + case CS48L32_IN_TYPE_DIFF: + ana_mode_l = 0; + break; + case CS48L32_IN_TYPE_SE: + ana_mode_l = 1 << CS48L32_INx_SRC_SHIFT; + break; + default: + dev_warn(cs48l32->dev, "IN%dL_1 Illegal in_type %u ignored\n", + i + 1, cs48l32->in_type[i][0]); + continue; + } + + switch (cs48l32->in_type[i][1]) { + case CS48L32_IN_TYPE_DIFF: + ana_mode_r = 0; + break; + case CS48L32_IN_TYPE_SE: + ana_mode_r = 1 << CS48L32_INx_SRC_SHIFT; + break; + default: + dev_warn(cs48l32->dev, "IN%dR_1 Illegal in_type %u ignored\n", + i + 1, cs48l32->in_type[i][1]); + continue; + } + + dev_dbg(cs48l32->dev, "IN%d_1 Analogue mode=0x%x,0x%x\n", + i + 1, ana_mode_l, ana_mode_r); + + regmap_update_bits(mfd->regmap, + CS48L32_IN1L_CONTROL1 + (i * 0x40), + CS48L32_INx_SRC_MASK, + ana_mode_l); + + regmap_update_bits(mfd->regmap, + CS48L32_IN1R_CONTROL1 + (i * 0x40), + CS48L32_INx_SRC_MASK, + ana_mode_r); + } + + for (i = 0; i < cs48l32->max_pdm_sup; i++) { + dig_mode = cs48l32->pdm_sup[i] << CS48L32_IN1_PDM_SUP_SHIFT; + + dev_dbg(cs48l32->dev, "IN%d PDM_SUP=0x%x\n", i + 1, dig_mode); + + regmap_update_bits(mfd->regmap, + CS48L32_INPUT1_CONTROL1 + (i * 0x40), + CS48L32_IN1_PDM_SUP_MASK, dig_mode); + } + + return 0; +} + +int cs48l32_init_dai(struct cs48l32 *cs48l32, int id) +{ + struct cs48l32_dai_priv *dai_priv = &cs48l32->dai[id]; + + dai_priv->clk = CS48L32_CLK_SYSCLK_1; + dai_priv->constraint = cs48l32_constraint; + + return 0; +} + +int cs48l32_init_eq(struct cs48l32 *cs48l32) +{ + struct cs48l32_mfd *mfd = cs48l32->mfd; + unsigned int reg = CS48L32_EQ1_BAND1_COEFF1, mode; + __be16 *data; + int i, ret; + + ret = regmap_read(mfd->regmap, CS48L32_EQ_CONTROL2, &mode); + if (ret < 0) { + dev_err(cs48l32->dev, "Error reading EQ mode: %d\n", ret); + goto out; + } + + for (i = 0; i < 4; ++i) { + cs48l32->eq_mode[i] = (mode >> i) & 0x1; + + data = &cs48l32->eq_coefficients[i][0]; + ret = regmap_raw_read(mfd->regmap, reg + (i * 68), data, + CS48L32_EQ_BLOCK_SZ); + if (ret < 0) { + dev_err(cs48l32->dev, "Error reading EQ coefficients: %d\n", ret); + goto out; + } + } + +out: + return ret; +} + +int cs48l32_core_init(struct cs48l32 *cs48l32) +{ + BUILD_BUG_ON(ARRAY_SIZE(cs48l32_mixer_texts) != CS48L32_NUM_MIXER_INPUTS); + BUILD_BUG_ON(ARRAY_SIZE(cs48l32_mixer_values) != CS48L32_NUM_MIXER_INPUTS); + BUILD_BUG_ON(cs48l32_sample_rate_text[CS48L32_SAMPLE_RATE_ENUM_SIZE - 1] == NULL); + BUILD_BUG_ON(cs48l32_sample_rate_val[CS48L32_SAMPLE_RATE_ENUM_SIZE - 1] == 0); + BUILD_BUG_ON(ARRAY_SIZE(cs48l32_in_texts) != CS48L32_MAX_INPUT); + + cs48l32_prop_get(cs48l32); + + mutex_init(&cs48l32->rate_lock); + + return 0; +} + +int cs48l32_core_destroy(struct cs48l32 *cs48l32) +{ + mutex_destroy(&cs48l32->rate_lock); + + return 0; +} diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c new file mode 100644 index 000000000000..64763772b881 --- /dev/null +++ b/sound/soc/codecs/cs48l32.c @@ -0,0 +1,1211 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// ALSA SoC Audio driver for CS48L32 codec. +// +// Copyright (C) 2018-2020, 2022 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs48l32.h" +#include "wm_adsp.h" + +static const struct cs_dsp_region cs48l32_dsp1_regions[] = { + { .type = WMFW_HALO_PM_PACKED, .base = 0x3800000 }, + { .type = WMFW_HALO_XM_PACKED, .base = 0x2000000 }, + { .type = WMFW_ADSP2_XM, .base = 0x2800000 }, + { .type = WMFW_HALO_YM_PACKED, .base = 0x2C00000 }, + { .type = WMFW_ADSP2_YM, .base = 0x3400000 }, +}; + +static const struct cs48l32_dsp_power_reg_block cs48l32_dsp1_sram_ext_regs[] = { + { CS48L32_DSP1_XM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24 }, + { CS48L32_DSP1_YM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8 }, + { CS48L32_DSP1_PM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7 }, +}; + +static const unsigned int cs48l32_dsp1_sram_pwd_regs[] = { + CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0, + CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0, + CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0, +}; + +static const struct cs48l32_dsp_power_regs cs48l32_dsp_sram_regs = { + .ext = cs48l32_dsp1_sram_ext_regs, + .n_ext = ARRAY_SIZE(cs48l32_dsp1_sram_ext_regs), + .pwd = cs48l32_dsp1_sram_pwd_regs, + .n_pwd = ARRAY_SIZE(cs48l32_dsp1_sram_pwd_regs), +}; + +static const struct snd_kcontrol_new cs48l32_snd_controls[] = { +SOC_ENUM("IN1 OSR", cs48l32_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", cs48l32_in_dmic_osr[1]), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", CS48L32_IN1L_CONTROL2, + CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", CS48L32_IN1R_CONTROL2, + CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv), + +SOC_ENUM("IN HPF Cutoff Frequency", cs48l32_in_hpf_cut_enum), + +SOC_SINGLE_EXT("IN1L LP Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_LP_MODE_SHIFT, + 1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put), +SOC_SINGLE_EXT("IN1R LP Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_LP_MODE_SHIFT, + 1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put), + +SOC_SINGLE("IN1L HPF Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2L HPF Switch", CS48L32_IN2L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2R HPF Switch", CS48L32_IN2R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0), + +SOC_SINGLE_EXT_TLV("IN1L Digital Volume", CS48L32_IN1L_CONTROL2, + CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw, + cs48l32_in_put_volsw, cs48l32_digital_tlv), +SOC_SINGLE_EXT_TLV("IN1R Digital Volume", CS48L32_IN1R_CONTROL2, + CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw, + cs48l32_in_put_volsw, cs48l32_digital_tlv), +SOC_SINGLE_EXT_TLV("IN2L Digital Volume", CS48L32_IN2L_CONTROL2, + CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw, + cs48l32_in_put_volsw, cs48l32_digital_tlv), +SOC_SINGLE_EXT_TLV("IN2R Digital Volume", CS48L32_IN2R_CONTROL2, + CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw, + cs48l32_in_put_volsw, cs48l32_digital_tlv), + +SOC_ENUM("Input Ramp Up", cs48l32_in_vi_ramp), +SOC_ENUM("Input Ramp Down", cs48l32_in_vd_ramp), + +CS48L32_RATE_ENUM("Ultrasonic 1 Rate", cs48l32_us_output_rate[0]), +CS48L32_RATE_ENUM("Ultrasonic 2 Rate", cs48l32_us_output_rate[1]), + +SOC_ENUM("Ultrasonic 1 Freq", cs48l32_us_freq[0]), +SOC_ENUM("Ultrasonic 2 Freq", cs48l32_us_freq[1]), + +SOC_SINGLE_TLV("Ultrasonic 1 Volume", CS48L32_US1_CONTROL, CS48L32_US1_GAIN_SHIFT, + 3, 0, cs48l32_us_tlv), +SOC_SINGLE_TLV("Ultrasonic 2 Volume", CS48L32_US2_CONTROL, CS48L32_US1_GAIN_SHIFT, + 3, 0, cs48l32_us_tlv), + +SOC_ENUM("Ultrasonic 1 Activity Detect Threshold", cs48l32_us_det_thr[0]), +SOC_ENUM("Ultrasonic 2 Activity Detect Threshold", cs48l32_us_det_thr[1]), + +SOC_ENUM("Ultrasonic 1 Activity Detect Pulse Length", cs48l32_us_det_num[0]), +SOC_ENUM("Ultrasonic 2 Activity Detect Pulse Length", cs48l32_us_det_num[1]), + +SOC_ENUM("Ultrasonic 1 Activity Detect Hold", cs48l32_us_det_hold[0]), +SOC_ENUM("Ultrasonic 2 Activity Detect Hold", cs48l32_us_det_hold[1]), + +SOC_ENUM("Ultrasonic 1 Activity Detect Decay", cs48l32_us_det_dcy[0]), +SOC_ENUM("Ultrasonic 2 Activity Detect Decay", cs48l32_us_det_dcy[1]), + +SOC_SINGLE("Ultrasonic 1 Activity Detect LPF Switch", + CS48L32_US1_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0), +SOC_SINGLE("Ultrasonic 2 Activity Detect LPF Switch", + CS48L32_US2_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0), + +SOC_ENUM("Ultrasonic 1 Activity Detect LPF Cut-off", cs48l32_us_det_lpf_cut[0]), +SOC_ENUM("Ultrasonic 2 Activity Detect LPF Cut-off", cs48l32_us_det_lpf_cut[1]), + +CS48L32_MIXER_CONTROLS("EQ1", CS48L32_EQ1_INPUT1), +CS48L32_MIXER_CONTROLS("EQ2", CS48L32_EQ2_INPUT1), +CS48L32_MIXER_CONTROLS("EQ3", CS48L32_EQ3_INPUT1), +CS48L32_MIXER_CONTROLS("EQ4", CS48L32_EQ4_INPUT1), + +SOC_ENUM_EXT("EQ1 Mode", cs48l32_eq_mode[0], cs48l32_eq_mode_get, cs48l32_eq_mode_put), + +CS48L32_EQ_COEFF_CONTROLS(EQ1), + +SOC_SINGLE_TLV("EQ1 B1 Volume", CS48L32_EQ1_GAIN1, 0, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", CS48L32_EQ1_GAIN1, 8, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", CS48L32_EQ1_GAIN1, 16, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", CS48L32_EQ1_GAIN1, 24, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", CS48L32_EQ1_GAIN2, 0, 24, 0, cs48l32_eq_tlv), + +SOC_ENUM_EXT("EQ2 Mode", cs48l32_eq_mode[1], cs48l32_eq_mode_get, cs48l32_eq_mode_put), +CS48L32_EQ_COEFF_CONTROLS(EQ2), +SOC_SINGLE_TLV("EQ2 B1 Volume", CS48L32_EQ2_GAIN1, 0, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", CS48L32_EQ2_GAIN1, 8, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", CS48L32_EQ2_GAIN1, 16, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", CS48L32_EQ2_GAIN1, 24, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", CS48L32_EQ2_GAIN2, 0, 24, 0, cs48l32_eq_tlv), + +SOC_ENUM_EXT("EQ3 Mode", cs48l32_eq_mode[2], cs48l32_eq_mode_get, cs48l32_eq_mode_put), +CS48L32_EQ_COEFF_CONTROLS(EQ3), +SOC_SINGLE_TLV("EQ3 B1 Volume", CS48L32_EQ3_GAIN1, 0, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", CS48L32_EQ3_GAIN1, 8, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", CS48L32_EQ3_GAIN1, 16, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", CS48L32_EQ3_GAIN1, 24, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", CS48L32_EQ3_GAIN2, 0, 24, 0, cs48l32_eq_tlv), + +SOC_ENUM_EXT("EQ4 Mode", cs48l32_eq_mode[3], cs48l32_eq_mode_get, cs48l32_eq_mode_put), +CS48L32_EQ_COEFF_CONTROLS(EQ4), +SOC_SINGLE_TLV("EQ4 B1 Volume", CS48L32_EQ4_GAIN1, 0, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", CS48L32_EQ4_GAIN1, 8, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", CS48L32_EQ4_GAIN1, 16, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", CS48L32_EQ4_GAIN1, 24, 24, 0, cs48l32_eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", CS48L32_EQ4_GAIN2, 0, 24, 0, cs48l32_eq_tlv), + +CS48L32_MIXER_CONTROLS("DRC1L", CS48L32_DRC1L_INPUT1), +CS48L32_MIXER_CONTROLS("DRC1R", CS48L32_DRC1R_INPUT1), +CS48L32_MIXER_CONTROLS("DRC2L", CS48L32_DRC2L_INPUT1), +CS48L32_MIXER_CONTROLS("DRC2R", CS48L32_DRC2R_INPUT1), + +SND_SOC_BYTES_MASK("DRC1 Coefficients", CS48L32_DRC1_CONTROL1, 4, + BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)), +SND_SOC_BYTES_MASK("DRC2 Coefficients", CS48L32_DRC2_CONTROL1, 4, + BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)), + +CS48L32_MIXER_CONTROLS("LHPF1", CS48L32_LHPF1_INPUT1), +CS48L32_MIXER_CONTROLS("LHPF2", CS48L32_LHPF2_INPUT1), +CS48L32_MIXER_CONTROLS("LHPF3", CS48L32_LHPF3_INPUT1), +CS48L32_MIXER_CONTROLS("LHPF4", CS48L32_LHPF4_INPUT1), + +CS48L32_LHPF_CONTROL("LHPF1 Coefficients", CS48L32_LHPF1_COEFF), +CS48L32_LHPF_CONTROL("LHPF2 Coefficients", CS48L32_LHPF2_COEFF), +CS48L32_LHPF_CONTROL("LHPF3 Coefficients", CS48L32_LHPF3_COEFF), +CS48L32_LHPF_CONTROL("LHPF4 Coefficients", CS48L32_LHPF4_COEFF), + +SOC_ENUM("LHPF1 Mode", cs48l32_lhpf_mode[0]), +SOC_ENUM("LHPF2 Mode", cs48l32_lhpf_mode[1]), +SOC_ENUM("LHPF3 Mode", cs48l32_lhpf_mode[2]), +SOC_ENUM("LHPF4 Mode", cs48l32_lhpf_mode[3]), + +CS48L32_RATE_CONTROL("Sample Rate 1", 1), +CS48L32_RATE_CONTROL("Sample Rate 2", 2), +CS48L32_RATE_CONTROL("Sample Rate 3", 3), +CS48L32_RATE_CONTROL("Sample Rate 4", 4), + +CS48L32_RATE_ENUM("FX Rate", cs48l32_fx_rate), + +CS48L32_RATE_ENUM("ISRC1 FSL", cs48l32_isrc_fsl[0]), +CS48L32_RATE_ENUM("ISRC2 FSL", cs48l32_isrc_fsl[1]), +CS48L32_RATE_ENUM("ISRC3 FSL", cs48l32_isrc_fsl[2]), +CS48L32_RATE_ENUM("ISRC1 FSH", cs48l32_isrc_fsh[0]), +CS48L32_RATE_ENUM("ISRC2 FSH", cs48l32_isrc_fsh[1]), +CS48L32_RATE_ENUM("ISRC3 FSH", cs48l32_isrc_fsh[2]), + +SOC_ENUM("AUXPDM1 Rate", cs48l32_auxpdm1_freq), +SOC_ENUM("AUXPDM2 Rate", cs48l32_auxpdm2_freq), + +SOC_ENUM_EXT("IN1L Rate", cs48l32_input_rate[0], snd_soc_get_enum_double, cs48l32_in_rate_put), +SOC_ENUM_EXT("IN1R Rate", cs48l32_input_rate[1], snd_soc_get_enum_double, cs48l32_in_rate_put), +SOC_ENUM_EXT("IN2L Rate", cs48l32_input_rate[2], snd_soc_get_enum_double, cs48l32_in_rate_put), +SOC_ENUM_EXT("IN2R Rate", cs48l32_input_rate[3], snd_soc_get_enum_double, cs48l32_in_rate_put), + +CS48L32_RATE_ENUM("Noise Generator Rate", noise_gen_rate), + +SOC_SINGLE_TLV("Noise Generator Volume", CS48L32_COMFORT_NOISE_GENERATOR, + CS48L32_NOISE_GEN_GAIN_SHIFT, 0x12, 0, cs48l32_noise_tlv), + +CS48L32_MIXER_CONTROLS("ASP1TX1", CS48L32_ASP1TX1_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX2", CS48L32_ASP1TX2_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX3", CS48L32_ASP1TX3_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX4", CS48L32_ASP1TX4_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX5", CS48L32_ASP1TX5_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX6", CS48L32_ASP1TX6_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX7", CS48L32_ASP1TX7_INPUT1), +CS48L32_MIXER_CONTROLS("ASP1TX8", CS48L32_ASP1TX8_INPUT1), + +CS48L32_MIXER_CONTROLS("ASP2TX1", CS48L32_ASP2TX1_INPUT1), +CS48L32_MIXER_CONTROLS("ASP2TX2", CS48L32_ASP2TX2_INPUT1), +CS48L32_MIXER_CONTROLS("ASP2TX3", CS48L32_ASP2TX3_INPUT1), +CS48L32_MIXER_CONTROLS("ASP2TX4", CS48L32_ASP2TX4_INPUT1), + +WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), + +CS48L32_MIXER_CONTROLS("DSP1RX1", CS48L32_DSP1RX1_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX2", CS48L32_DSP1RX2_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX3", CS48L32_DSP1RX3_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX4", CS48L32_DSP1RX4_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX5", CS48L32_DSP1RX5_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX6", CS48L32_DSP1RX6_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX7", CS48L32_DSP1RX7_INPUT1), +CS48L32_MIXER_CONTROLS("DSP1RX8", CS48L32_DSP1RX8_INPUT1), + +WM_ADSP_FW_CONTROL("DSP1", 0), + +CS48L32_DSP_RATE_CONTROL("DSP1RX1", 0), +CS48L32_DSP_RATE_CONTROL("DSP1RX2", 1), +CS48L32_DSP_RATE_CONTROL("DSP1RX3", 2), +CS48L32_DSP_RATE_CONTROL("DSP1RX4", 3), +CS48L32_DSP_RATE_CONTROL("DSP1RX5", 4), +CS48L32_DSP_RATE_CONTROL("DSP1RX6", 5), +CS48L32_DSP_RATE_CONTROL("DSP1RX7", 6), +CS48L32_DSP_RATE_CONTROL("DSP1RX8", 7), +CS48L32_DSP_RATE_CONTROL("DSP1TX1", 8), +CS48L32_DSP_RATE_CONTROL("DSP1TX2", 9), +CS48L32_DSP_RATE_CONTROL("DSP1TX3", 10), +CS48L32_DSP_RATE_CONTROL("DSP1TX4", 11), +CS48L32_DSP_RATE_CONTROL("DSP1TX5", 12), +CS48L32_DSP_RATE_CONTROL("DSP1TX6", 13), +CS48L32_DSP_RATE_CONTROL("DSP1TX7", 14), +CS48L32_DSP_RATE_CONTROL("DSP1TX8", 15), +}; + +CS48L32_MIXER_ENUMS(EQ1, CS48L32_EQ1_INPUT1); +CS48L32_MIXER_ENUMS(EQ2, CS48L32_EQ2_INPUT1); +CS48L32_MIXER_ENUMS(EQ3, CS48L32_EQ3_INPUT1); +CS48L32_MIXER_ENUMS(EQ4, CS48L32_EQ4_INPUT1); + +CS48L32_MIXER_ENUMS(DRC1L, CS48L32_DRC1L_INPUT1); +CS48L32_MIXER_ENUMS(DRC1R, CS48L32_DRC1R_INPUT1); +CS48L32_MIXER_ENUMS(DRC2L, CS48L32_DRC2L_INPUT1); +CS48L32_MIXER_ENUMS(DRC2R, CS48L32_DRC2R_INPUT1); + +CS48L32_MIXER_ENUMS(LHPF1, CS48L32_LHPF1_INPUT1); +CS48L32_MIXER_ENUMS(LHPF2, CS48L32_LHPF2_INPUT1); +CS48L32_MIXER_ENUMS(LHPF3, CS48L32_LHPF3_INPUT1); +CS48L32_MIXER_ENUMS(LHPF4, CS48L32_LHPF4_INPUT1); + +CS48L32_MIXER_ENUMS(ASP1TX1, CS48L32_ASP1TX1_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX2, CS48L32_ASP1TX2_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX3, CS48L32_ASP1TX3_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX4, CS48L32_ASP1TX4_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX5, CS48L32_ASP1TX5_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX6, CS48L32_ASP1TX6_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX7, CS48L32_ASP1TX7_INPUT1); +CS48L32_MIXER_ENUMS(ASP1TX8, CS48L32_ASP1TX8_INPUT1); + +CS48L32_MIXER_ENUMS(ASP2TX1, CS48L32_ASP2TX1_INPUT1); +CS48L32_MIXER_ENUMS(ASP2TX2, CS48L32_ASP2TX2_INPUT1); +CS48L32_MIXER_ENUMS(ASP2TX3, CS48L32_ASP2TX3_INPUT1); +CS48L32_MIXER_ENUMS(ASP2TX4, CS48L32_ASP2TX4_INPUT1); + +CS48L32_MUX_ENUMS(ISRC1INT1, CS48L32_ISRC1INT1_INPUT1); +CS48L32_MUX_ENUMS(ISRC1INT2, CS48L32_ISRC1INT2_INPUT1); +CS48L32_MUX_ENUMS(ISRC1INT3, CS48L32_ISRC1INT3_INPUT1); +CS48L32_MUX_ENUMS(ISRC1INT4, CS48L32_ISRC1INT4_INPUT1); + +CS48L32_MUX_ENUMS(ISRC1DEC1, CS48L32_ISRC1DEC1_INPUT1); +CS48L32_MUX_ENUMS(ISRC1DEC2, CS48L32_ISRC1DEC2_INPUT1); +CS48L32_MUX_ENUMS(ISRC1DEC3, CS48L32_ISRC1DEC3_INPUT1); +CS48L32_MUX_ENUMS(ISRC1DEC4, CS48L32_ISRC1DEC4_INPUT1); + +CS48L32_MUX_ENUMS(ISRC2INT1, CS48L32_ISRC2INT1_INPUT1); +CS48L32_MUX_ENUMS(ISRC2INT2, CS48L32_ISRC2INT2_INPUT1); + +CS48L32_MUX_ENUMS(ISRC2DEC1, CS48L32_ISRC2DEC1_INPUT1); +CS48L32_MUX_ENUMS(ISRC2DEC2, CS48L32_ISRC2DEC2_INPUT1); + +CS48L32_MUX_ENUMS(ISRC3INT1, CS48L32_ISRC3INT1_INPUT1); +CS48L32_MUX_ENUMS(ISRC3INT2, CS48L32_ISRC3INT2_INPUT1); + +CS48L32_MUX_ENUMS(ISRC3DEC1, CS48L32_ISRC3DEC1_INPUT1); +CS48L32_MUX_ENUMS(ISRC3DEC2, CS48L32_ISRC3DEC2_INPUT1); + +CS48L32_MIXER_ENUMS(DSP1RX1, CS48L32_DSP1RX1_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX2, CS48L32_DSP1RX2_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX3, CS48L32_DSP1RX3_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX4, CS48L32_DSP1RX4_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX5, CS48L32_DSP1RX5_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX6, CS48L32_DSP1RX6_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX7, CS48L32_DSP1RX7_INPUT1); +CS48L32_MIXER_ENUMS(DSP1RX8, CS48L32_DSP1RX8_INPUT1); + +static int cs48l32_dsp_mem_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + return cs48l32_dsp_memory_enable(cs48l32, &cs48l32_dsp_sram_regs); + case SND_SOC_DAPM_PRE_PMD: + cs48l32_dsp_memory_disable(cs48l32, &cs48l32_dsp_sram_regs); + return 0; + default: + return 0; + } +} + +static const struct snd_soc_dapm_widget cs48l32_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", CS48L32_SYSTEM_CLOCK1, CS48L32_SYSCLK_EN_SHIFT, 0, + cs48l32_sysclk_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_SUPPLY("OPCLK", CS48L32_OUTPUT_SYS_CLK, CS48L32_OPCLK_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_CP", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("VOUT_MIC", 0, SND_SOC_DAPM_REGULATOR_BYPASS), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", CS48L32_MICBIAS_CTRL1, CS48L32_MICB1_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("MICBIAS1A", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1A_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1B", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1B_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1C", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1C_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("DSP1MEM", SND_SOC_NOPM, 0, 0, cs48l32_dsp_mem_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +CS48L32_DSP_FREQ_WIDGET_EV("DSP1", 0, cs48l32_dsp_freq_ev), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), + +SND_SOC_DAPM_INPUT("IN1LN_1"), +SND_SOC_DAPM_INPUT("IN1LN_2"), +SND_SOC_DAPM_INPUT("IN1LP_1"), +SND_SOC_DAPM_INPUT("IN1LP_2"), +SND_SOC_DAPM_INPUT("IN1RN_1"), +SND_SOC_DAPM_INPUT("IN1RN_2"), +SND_SOC_DAPM_INPUT("IN1RP_1"), +SND_SOC_DAPM_INPUT("IN1RP_2"), +SND_SOC_DAPM_INPUT("IN1_PDMCLK"), +SND_SOC_DAPM_INPUT("IN1_PDMDATA"), + +SND_SOC_DAPM_INPUT("IN2_PDMCLK"), +SND_SOC_DAPM_INPUT("IN2_PDMDATA"), + +SND_SOC_DAPM_MUX("Ultrasonic 1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[0]), +SND_SOC_DAPM_MUX("Ultrasonic 2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[1]), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + +SND_SOC_DAPM_OUTPUT("DSP Trigger Out"), + +SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[0]), +SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[1]), + +SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]), +SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]), + +SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS48L32_ASP1_ENABLES1, 0, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS48L32_ASP1_ENABLES1, 1, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS48L32_ASP1_ENABLES1, 2, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS48L32_ASP1_ENABLES1, 3, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX5", NULL, 4, CS48L32_ASP1_ENABLES1, 4, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX6", NULL, 5, CS48L32_ASP1_ENABLES1, 5, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX7", NULL, 6, CS48L32_ASP1_ENABLES1, 6, 0), +SND_SOC_DAPM_AIF_OUT("ASP1TX8", NULL, 7, CS48L32_ASP1_ENABLES1, 7, 0), + +SND_SOC_DAPM_AIF_OUT("ASP2TX1", NULL, 0, CS48L32_ASP2_ENABLES1, 0, 0), +SND_SOC_DAPM_AIF_OUT("ASP2TX2", NULL, 1, CS48L32_ASP2_ENABLES1, 1, 0), +SND_SOC_DAPM_AIF_OUT("ASP2TX3", NULL, 2, CS48L32_ASP2_ENABLES1, 2, 0), +SND_SOC_DAPM_AIF_OUT("ASP2TX4", NULL, 3, CS48L32_ASP2_ENABLES1, 3, 0), + +SND_SOC_DAPM_SWITCH("AUXPDM1 Output", CS48L32_AUXPDM_CONTROL1, 0, 0, &cs48l32_auxpdm_switch[0]), +SND_SOC_DAPM_SWITCH("AUXPDM2 Output", CS48L32_AUXPDM_CONTROL1, 1, 0, &cs48l32_auxpdm_switch[1]), + +SND_SOC_DAPM_MUX("AUXPDM1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[0]), +SND_SOC_DAPM_MUX("AUXPDM2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[1]), + +SND_SOC_DAPM_MUX("AUXPDM1 Analog Input", SND_SOC_NOPM, 0, 0, + &cs48l32_auxpdm_analog_inmux[0]), +SND_SOC_DAPM_MUX("AUXPDM2 Analog Input", SND_SOC_NOPM, 0, 0, + &cs48l32_auxpdm_analog_inmux[1]), + +SND_SOC_DAPM_SWITCH("Ultrasonic 1 Activity Detect", CS48L32_US_CONTROL, + CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[0]), +SND_SOC_DAPM_SWITCH("Ultrasonic 2 Activity Detect", CS48L32_US_CONTROL, + CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[1]), + +/* + * mux_in widgets : arranged in the order of sources + * specified in CS48L32_MIXER_INPUT_ROUTES + */ +SND_SOC_DAPM_PGA("Tone Generator 1", CS48L32_TONE_GENERATOR1, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", CS48L32_TONE_GENERATOR1, 1, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Noise Generator", CS48L32_COMFORT_NOISE_GENERATOR, + CS48L32_NOISE_GEN_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA_E("IN1L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1L_EN_SHIFT, + 0, NULL, 0, cs48l32_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1R_EN_SHIFT, + 0, NULL, 0, cs48l32_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2L_EN_SHIFT, + 0, NULL, 0, cs48l32_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2R_EN_SHIFT, + 0, NULL, 0, cs48l32_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS48L32_ASP1_ENABLES1, 16, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS48L32_ASP1_ENABLES1, 17, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX3", NULL, 2, CS48L32_ASP1_ENABLES1, 18, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX4", NULL, 3, CS48L32_ASP1_ENABLES1, 19, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX5", NULL, 4, CS48L32_ASP1_ENABLES1, 20, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX6", NULL, 5, CS48L32_ASP1_ENABLES1, 21, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX7", NULL, 6, CS48L32_ASP1_ENABLES1, 22, 0), +SND_SOC_DAPM_AIF_IN("ASP1RX8", NULL, 7, CS48L32_ASP1_ENABLES1, 23, 0), + +SND_SOC_DAPM_AIF_IN("ASP2RX1", NULL, 0, CS48L32_ASP2_ENABLES1, 16, 0), +SND_SOC_DAPM_AIF_IN("ASP2RX2", NULL, 1, CS48L32_ASP2_ENABLES1, 17, 0), +SND_SOC_DAPM_AIF_IN("ASP2RX3", NULL, 2, CS48L32_ASP2_ENABLES1, 18, 0), +SND_SOC_DAPM_AIF_IN("ASP2RX4", NULL, 3, CS48L32_ASP2_ENABLES1, 19, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC3_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC4_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1INT1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT3_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT4_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3DEC1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3INT1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA_E("EQ1", CS48L32_EQ_CONTROL1, 0, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("EQ2", CS48L32_EQ_CONTROL1, 1, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("EQ3", CS48L32_EQ_CONTROL1, 2, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("EQ4", CS48L32_EQ_CONTROL1, 3, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_PGA("DRC1L", CS48L32_DRC1_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", CS48L32_DRC1_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", CS48L32_DRC2_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", CS48L32_DRC2_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", CS48L32_LHPF_CONTROL1, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", CS48L32_LHPF_CONTROL1, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", CS48L32_LHPF_CONTROL1, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", CS48L32_LHPF_CONTROL1, 3, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Ultrasonic 1", CS48L32_US_CONTROL, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("Ultrasonic 2", CS48L32_US_CONTROL, 1, 0, NULL, 0), + +WM_ADSP2("DSP1", 0, wm_adsp_early_event), + +/* end of ordered widget list */ + +CS48L32_MIXER_WIDGETS(EQ1, "EQ1"), +CS48L32_MIXER_WIDGETS(EQ2, "EQ2"), +CS48L32_MIXER_WIDGETS(EQ3, "EQ3"), +CS48L32_MIXER_WIDGETS(EQ4, "EQ4"), + +CS48L32_MIXER_WIDGETS(DRC1L, "DRC1L"), +CS48L32_MIXER_WIDGETS(DRC1R, "DRC1R"), +CS48L32_MIXER_WIDGETS(DRC2L, "DRC2L"), +CS48L32_MIXER_WIDGETS(DRC2R, "DRC2R"), + +SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0, + &cs48l32_drc_activity_output_mux[0]), +SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0, + &cs48l32_drc_activity_output_mux[1]), + +CS48L32_MIXER_WIDGETS(LHPF1, "LHPF1"), +CS48L32_MIXER_WIDGETS(LHPF2, "LHPF2"), +CS48L32_MIXER_WIDGETS(LHPF3, "LHPF3"), +CS48L32_MIXER_WIDGETS(LHPF4, "LHPF4"), + +CS48L32_MIXER_WIDGETS(ASP1TX1, "ASP1TX1"), +CS48L32_MIXER_WIDGETS(ASP1TX2, "ASP1TX2"), +CS48L32_MIXER_WIDGETS(ASP1TX3, "ASP1TX3"), +CS48L32_MIXER_WIDGETS(ASP1TX4, "ASP1TX4"), +CS48L32_MIXER_WIDGETS(ASP1TX5, "ASP1TX5"), +CS48L32_MIXER_WIDGETS(ASP1TX6, "ASP1TX6"), +CS48L32_MIXER_WIDGETS(ASP1TX7, "ASP1TX7"), +CS48L32_MIXER_WIDGETS(ASP1TX8, "ASP1TX8"), + +CS48L32_MIXER_WIDGETS(ASP2TX1, "ASP2TX1"), +CS48L32_MIXER_WIDGETS(ASP2TX2, "ASP2TX2"), +CS48L32_MIXER_WIDGETS(ASP2TX3, "ASP2TX3"), +CS48L32_MIXER_WIDGETS(ASP2TX4, "ASP2TX4"), + +CS48L32_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +CS48L32_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +CS48L32_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +CS48L32_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +CS48L32_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +CS48L32_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +CS48L32_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +CS48L32_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +CS48L32_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +CS48L32_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +CS48L32_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +CS48L32_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + +CS48L32_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"), +CS48L32_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"), + +CS48L32_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"), +CS48L32_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"), + +CS48L32_MIXER_WIDGETS(DSP1RX1, "DSP1RX1"), +CS48L32_MIXER_WIDGETS(DSP1RX2, "DSP1RX2"), +CS48L32_MIXER_WIDGETS(DSP1RX3, "DSP1RX3"), +CS48L32_MIXER_WIDGETS(DSP1RX4, "DSP1RX4"), +CS48L32_MIXER_WIDGETS(DSP1RX5, "DSP1RX5"), +CS48L32_MIXER_WIDGETS(DSP1RX6, "DSP1RX6"), +CS48L32_MIXER_WIDGETS(DSP1RX7, "DSP1RX7"), +CS48L32_MIXER_WIDGETS(DSP1RX8, "DSP1RX8"), + +SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0, + &cs48l32_dsp_trigger_output_mux[0]), + +SND_SOC_DAPM_OUTPUT("AUXPDM1_CLK"), +SND_SOC_DAPM_OUTPUT("AUXPDM1_DOUT"), +SND_SOC_DAPM_OUTPUT("AUXPDM2_CLK"), +SND_SOC_DAPM_OUTPUT("AUXPDM2_DOUT"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), + +SND_SOC_DAPM_OUTPUT("Ultrasonic Dummy Output"), +}; + +#define CS48L32_MIXER_INPUT_ROUTES(name) \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "ASP1RX1", "ASP1RX1" }, \ + { name, "ASP1RX2", "ASP1RX2" }, \ + { name, "ASP1RX3", "ASP1RX3" }, \ + { name, "ASP1RX4", "ASP1RX4" }, \ + { name, "ASP1RX5", "ASP1RX5" }, \ + { name, "ASP1RX6", "ASP1RX6" }, \ + { name, "ASP1RX7", "ASP1RX7" }, \ + { name, "ASP1RX8", "ASP1RX8" }, \ + { name, "ASP2RX1", "ASP2RX1" }, \ + { name, "ASP2RX2", "ASP2RX2" }, \ + { name, "ASP2RX3", "ASP2RX3" }, \ + { name, "ASP2RX4", "ASP2RX4" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "ISRC3DEC1", "ISRC3DEC1" }, \ + { name, "ISRC3DEC2", "ISRC3DEC2" }, \ + { name, "ISRC3INT1", "ISRC3INT1" }, \ + { name, "ISRC3INT2", "ISRC3INT2" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "Ultrasonic 1", "Ultrasonic 1" }, \ + { name, "Ultrasonic 2", "Ultrasonic 2" }, \ + { name, "DSP1.1", "DSP1" }, \ + { name, "DSP1.2", "DSP1" }, \ + { name, "DSP1.3", "DSP1" }, \ + { name, "DSP1.4", "DSP1" }, \ + { name, "DSP1.5", "DSP1" }, \ + { name, "DSP1.6", "DSP1" }, \ + { name, "DSP1.7", "DSP1" }, \ + { name, "DSP1.8", "DSP1" } + +static const struct snd_soc_dapm_route cs48l32_dapm_routes[] = { + { "OPCLK", NULL, "SYSCLK" }, + + { "IN1LN_1", NULL, "SYSCLK" }, + { "IN1LN_2", NULL, "SYSCLK" }, + { "IN1LP_1", NULL, "SYSCLK" }, + { "IN1LP_2", NULL, "SYSCLK" }, + { "IN1RN_1", NULL, "SYSCLK" }, + { "IN1RN_2", NULL, "SYSCLK" }, + { "IN1RP_1", NULL, "SYSCLK" }, + { "IN1RP_2", NULL, "SYSCLK" }, + + { "IN1_PDMCLK", NULL, "SYSCLK" }, + { "IN1_PDMDATA", NULL, "SYSCLK" }, + { "IN2_PDMCLK", NULL, "SYSCLK" }, + { "IN2_PDMDATA", NULL, "SYSCLK" }, + + { "DSP1 Preloader", NULL, "DSP1MEM" }, + { "DSP1", NULL, "DSP1FREQ" }, + + { "Audio Trace DSP", NULL, "DSP1" }, + { "Voice Ctrl DSP", NULL, "DSP1" }, + + { "MICBIAS1", NULL, "VOUT_MIC" }, + + { "MICBIAS1A", NULL, "MICBIAS1" }, + { "MICBIAS1B", NULL, "MICBIAS1" }, + { "MICBIAS1C", NULL, "MICBIAS1" }, + + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + { "Noise Generator", NULL, "SYSCLK" }, + + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + { "Noise Generator", NULL, "NOISE" }, + + { "ASP1 Capture", NULL, "ASP1TX1" }, + { "ASP1 Capture", NULL, "ASP1TX2" }, + { "ASP1 Capture", NULL, "ASP1TX3" }, + { "ASP1 Capture", NULL, "ASP1TX4" }, + { "ASP1 Capture", NULL, "ASP1TX5" }, + { "ASP1 Capture", NULL, "ASP1TX6" }, + { "ASP1 Capture", NULL, "ASP1TX7" }, + { "ASP1 Capture", NULL, "ASP1TX8" }, + + { "ASP1RX1", NULL, "ASP1 Playback" }, + { "ASP1RX2", NULL, "ASP1 Playback" }, + { "ASP1RX3", NULL, "ASP1 Playback" }, + { "ASP1RX4", NULL, "ASP1 Playback" }, + { "ASP1RX5", NULL, "ASP1 Playback" }, + { "ASP1RX6", NULL, "ASP1 Playback" }, + { "ASP1RX7", NULL, "ASP1 Playback" }, + { "ASP1RX8", NULL, "ASP1 Playback" }, + + { "ASP2 Capture", NULL, "ASP2TX1" }, + { "ASP2 Capture", NULL, "ASP2TX2" }, + { "ASP2 Capture", NULL, "ASP2TX3" }, + { "ASP2 Capture", NULL, "ASP2TX4" }, + + { "ASP2RX1", NULL, "ASP2 Playback" }, + { "ASP2RX2", NULL, "ASP2 Playback" }, + { "ASP2RX3", NULL, "ASP2 Playback" }, + { "ASP2RX4", NULL, "ASP2 Playback" }, + + { "ASP1 Playback", NULL, "SYSCLK" }, + { "ASP2 Playback", NULL, "SYSCLK" }, + + { "ASP1 Capture", NULL, "SYSCLK" }, + { "ASP2 Capture", NULL, "SYSCLK" }, + + { "IN1L Mux", "Analog 1", "IN1LN_1" }, + { "IN1L Mux", "Analog 2", "IN1LN_2" }, + { "IN1L Mux", "Analog 1", "IN1LP_1" }, + { "IN1L Mux", "Analog 2", "IN1LP_2" }, + { "IN1R Mux", "Analog 1", "IN1RN_1" }, + { "IN1R Mux", "Analog 2", "IN1RN_2" }, + { "IN1R Mux", "Analog 1", "IN1RP_1" }, + { "IN1R Mux", "Analog 2", "IN1RP_2" }, + + { "IN1L PGA", NULL, "IN1L Mode" }, + { "IN1R PGA", NULL, "IN1R Mode" }, + + { "IN1L Mode", "Analog", "IN1L Mux" }, + { "IN1R Mode", "Analog", "IN1R Mux" }, + + { "IN1L Mode", "Digital", "IN1_PDMCLK" }, + { "IN1L Mode", "Digital", "IN1_PDMDATA" }, + { "IN1R Mode", "Digital", "IN1_PDMCLK" }, + { "IN1R Mode", "Digital", "IN1_PDMDATA" }, + + { "IN1L PGA", NULL, "VOUT_MIC" }, + { "IN1R PGA", NULL, "VOUT_MIC" }, + + { "IN2L PGA", NULL, "VOUT_MIC" }, + { "IN2R PGA", NULL, "VOUT_MIC" }, + + { "IN2L PGA", NULL, "IN2_PDMCLK" }, + { "IN2R PGA", NULL, "IN2_PDMCLK" }, + { "IN2L PGA", NULL, "IN2_PDMDATA" }, + { "IN2R PGA", NULL, "IN2_PDMDATA" }, + + { "Ultrasonic 1", NULL, "Ultrasonic 1 Input" }, + { "Ultrasonic 2", NULL, "Ultrasonic 2 Input" }, + + { "Ultrasonic 1 Input", "IN1L", "IN1L PGA" }, + { "Ultrasonic 1 Input", "IN1R", "IN1R PGA" }, + { "Ultrasonic 1 Input", "IN2L", "IN2L PGA" }, + { "Ultrasonic 1 Input", "IN2R", "IN2R PGA" }, + + { "Ultrasonic 2 Input", "IN1L", "IN1L PGA" }, + { "Ultrasonic 2 Input", "IN1R", "IN1R PGA" }, + { "Ultrasonic 2 Input", "IN2L", "IN2L PGA" }, + { "Ultrasonic 2 Input", "IN2R", "IN2R PGA" }, + + { "Ultrasonic 1 Activity Detect", "Switch", "Ultrasonic 1 Input" }, + { "Ultrasonic 2 Activity Detect", "Switch", "Ultrasonic 2 Input" }, + + { "Ultrasonic Dummy Output", NULL, "Ultrasonic 1 Activity Detect" }, + { "Ultrasonic Dummy Output", NULL, "Ultrasonic 2 Activity Detect" }, + + CS48L32_MIXER_ROUTES("ASP1TX1", "ASP1TX1"), + CS48L32_MIXER_ROUTES("ASP1TX2", "ASP1TX2"), + CS48L32_MIXER_ROUTES("ASP1TX3", "ASP1TX3"), + CS48L32_MIXER_ROUTES("ASP1TX4", "ASP1TX4"), + CS48L32_MIXER_ROUTES("ASP1TX5", "ASP1TX5"), + CS48L32_MIXER_ROUTES("ASP1TX6", "ASP1TX6"), + CS48L32_MIXER_ROUTES("ASP1TX7", "ASP1TX7"), + CS48L32_MIXER_ROUTES("ASP1TX8", "ASP1TX8"), + + CS48L32_MIXER_ROUTES("ASP2TX1", "ASP2TX1"), + CS48L32_MIXER_ROUTES("ASP2TX2", "ASP2TX2"), + CS48L32_MIXER_ROUTES("ASP2TX3", "ASP2TX3"), + CS48L32_MIXER_ROUTES("ASP2TX4", "ASP2TX4"), + + CS48L32_MIXER_ROUTES("EQ1", "EQ1"), + CS48L32_MIXER_ROUTES("EQ2", "EQ2"), + CS48L32_MIXER_ROUTES("EQ3", "EQ3"), + CS48L32_MIXER_ROUTES("EQ4", "EQ4"), + + CS48L32_MIXER_ROUTES("DRC1L", "DRC1L"), + CS48L32_MIXER_ROUTES("DRC1R", "DRC1R"), + CS48L32_MIXER_ROUTES("DRC2L", "DRC2L"), + CS48L32_MIXER_ROUTES("DRC2R", "DRC2R"), + + CS48L32_MIXER_ROUTES("LHPF1", "LHPF1"), + CS48L32_MIXER_ROUTES("LHPF2", "LHPF2"), + CS48L32_MIXER_ROUTES("LHPF3", "LHPF3"), + CS48L32_MIXER_ROUTES("LHPF4", "LHPF4"), + + CS48L32_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + CS48L32_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + CS48L32_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + CS48L32_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + CS48L32_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + CS48L32_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + CS48L32_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + CS48L32_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + CS48L32_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + CS48L32_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + + CS48L32_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + CS48L32_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + CS48L32_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"), + CS48L32_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"), + + CS48L32_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"), + CS48L32_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"), + + CS48L32_DSP_ROUTES_1_8_SYSCLK("DSP1"), + + { "DSP Trigger Out", NULL, "DSP1 Trigger Output" }, + + { "DSP1 Trigger Output", "Switch", "DSP1" }, + + { "AUXPDM1 Analog Input", "IN1L", "IN1L PGA" }, + { "AUXPDM1 Analog Input", "IN1R", "IN1R PGA" }, + + { "AUXPDM2 Analog Input", "IN1L", "IN1L PGA" }, + { "AUXPDM2 Analog Input", "IN1R", "IN1R PGA" }, + + { "AUXPDM1 Input", "Analog", "AUXPDM1 Analog Input" }, + { "AUXPDM1 Input", "IN1 Digital", "IN1L PGA" }, + { "AUXPDM1 Input", "IN1 Digital", "IN1R PGA" }, + { "AUXPDM1 Input", "IN2 Digital", "IN2L PGA" }, + { "AUXPDM1 Input", "IN2 Digital", "IN2R PGA" }, + + { "AUXPDM2 Input", "Analog", "AUXPDM2 Analog Input" }, + { "AUXPDM2 Input", "IN1 Digital", "IN1L PGA" }, + { "AUXPDM2 Input", "IN1 Digital", "IN1R PGA" }, + { "AUXPDM2 Input", "IN2 Digital", "IN2L PGA" }, + { "AUXPDM2 Input", "IN2 Digital", "IN2R PGA" }, + + { "AUXPDM1 Output", "Switch", "AUXPDM1 Input" }, + { "AUXPDM1_CLK", NULL, "AUXPDM1 Output" }, + { "AUXPDM1_DOUT", NULL, "AUXPDM1 Output" }, + + { "AUXPDM2 Output", "Switch", "AUXPDM2 Input" }, + { "AUXPDM2_CLK", NULL, "AUXPDM2 Output" }, + { "AUXPDM2_DOUT", NULL, "AUXPDM2 Output" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" }, + { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" }, + { "DRC1 Activity Output", "Switch", "DRC1L" }, + { "DRC1 Activity Output", "Switch", "DRC1R" }, + { "DRC2 Activity Output", "Switch", "DRC2L" }, + { "DRC2 Activity Output", "Switch", "DRC2R" }, +}; + +static struct snd_soc_dai_driver cs48l32_dai[] = { + { + .name = "cs48l32-asp1", + .id = 1, + .base = CS48L32_ASP1_ENABLES1, + .playback = { + .stream_name = "ASP1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + .capture = { + .stream_name = "ASP1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + .ops = &cs48l32_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs48l32-asp2", + .id = 2, + .base = CS48L32_ASP2_ENABLES1, + .playback = { + .stream_name = "ASP2 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + .capture = { + .stream_name = "ASP2 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + .ops = &cs48l32_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs48l32-cpu-trace", + .id = 3, + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 8, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + .compress_new = &snd_soc_new_compress, + }, + { + .name = "cs48l32-dsp-trace", + .id = 4, + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 8, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + }, + { + .name = "cs48l32-cpu-voicectrl", + .id = 5, + .capture = { + .stream_name = "Voice Ctrl CPU", + .channels_min = 1, + .channels_max = 8, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + .compress_new = &snd_soc_new_compress, + }, + { + .name = "cs48l32-dsp-voicectrl", + .id = 6, + .capture = { + .stream_name = "Voice Ctrl DSP", + .channels_min = 1, + .channels_max = 8, + .rates = CS48L32_RATES, + .formats = CS48L32_FORMATS, + }, + }, +}; + +static int cs48l32_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-trace") && + strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-voicectrl")) { + dev_err(cs48l32->dev, "No suitable compressed stream for DAI '%s'\n", + asoc_rtd_to_codec(rtd, 0)->name); + return -EINVAL; + } + + return wm_adsp_compr_open(&cs48l32->dsp, stream); +} + +static int cs48l32_component_probe(struct snd_soc_component *component) +{ + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + int ret; + + snd_soc_component_init_regmap(component, cs48l32->mfd->regmap); + cs48l32->mfd->dapm = snd_soc_component_get_dapm(component); + + ret = cs48l32_init_inputs(component); + if (ret) + return ret; + + ret = cs48l32_init_eq(cs48l32); + if (ret) + return ret; + + wm_adsp2_component_probe(&cs48l32->dsp, component); + + return 0; +} + +static void cs48l32_component_remove(struct snd_soc_component *component) +{ + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + + wm_adsp2_component_remove(&cs48l32->dsp, component); + cs48l32->mfd->dapm = NULL; +} + +static int cs48l32_set_fll(struct snd_soc_component *component, int fll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct cs48l32 *cs48l32 = snd_soc_component_get_drvdata(component); + + switch (fll_id) { + case CS48L32_FLL1_REFCLK: + break; + default: + return -EINVAL; + } + + return cs48l32_fllhj_set_refclk(&cs48l32->fll, source, fref, fout); +} + +static const struct snd_compress_ops cs48l32_compress_ops = { + .open = &cs48l32_compr_open, + .free = &wm_adsp_compr_free, + .set_params = &wm_adsp_compr_set_params, + .get_caps = &wm_adsp_compr_get_caps, + .trigger = &wm_adsp_compr_trigger, + .pointer = &wm_adsp_compr_pointer, + .copy = &wm_adsp_compr_copy, +}; + +static const struct snd_soc_component_driver soc_component_dev_cs48l32 = { + .probe = &cs48l32_component_probe, + .remove = &cs48l32_component_remove, + .set_sysclk = &cs48l32_set_sysclk, + .set_pll = &cs48l32_set_fll, + .name = "cs48l32-codec", + .compress_ops = &cs48l32_compress_ops, + .controls = cs48l32_snd_controls, + .num_controls = ARRAY_SIZE(cs48l32_snd_controls), + .dapm_widgets = cs48l32_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs48l32_dapm_widgets), + .dapm_routes = cs48l32_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs48l32_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct { + int id; + bool wake_source; + const char * const name; + irq_handler_t handler; +} cs48l32_interrupts[] = { + { CS48L32_IRQ_US1_ACT_DET_RISE, false, "US1 activity", cs48l32_us1_activity }, + { CS48L32_IRQ_US2_ACT_DET_RISE, false, "US2 activity", cs48l32_us2_activity }, + { CS48L32_IRQ_DSP1_IRQ0, true, "DSP1 Buffer IRQ", cs48l32_dsp1_irq }, + { CS48L32_IRQ_DSP1_MPU_ERR, false, "DSP1 MPU", wm_halo_bus_error }, + { CS48L32_IRQ_DSP1_WDT_EXPIRE, false, "DSP1 WDT", wm_halo_wdt_expire }, +}; + +static void cs48l32_free_interrupts(struct cs48l32 *cs48l32, int num) +{ + for (--num; num >= 0; --num) { + cs48l32_free_irq(cs48l32->mfd, cs48l32_interrupts[num].id, cs48l32); + if (cs48l32_interrupts[num].wake_source) + cs48l32_set_irq_wake(cs48l32->mfd, cs48l32_interrupts[num].id, 0); + } +} + +static int cs48l32_request_interrupts(struct cs48l32 *cs48l32) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(cs48l32_interrupts); ++i) { + ret = cs48l32_request_irq(cs48l32->mfd, + cs48l32_interrupts[i].id, + cs48l32_interrupts[i].name, + cs48l32_interrupts[i].handler, + cs48l32); + if (ret != 0) + goto err; + + if (cs48l32_interrupts[i].wake_source) { + ret = cs48l32_set_irq_wake(cs48l32->mfd, cs48l32_interrupts[i].id, 1); + if (ret) + dev_warn(cs48l32->dev, "Failed to set %s wake: %d\n", + cs48l32_interrupts[i].name, ret); + } + } + + return 0; +err: + dev_err_probe(cs48l32->dev, ret, "Failed to get %s IRQ\n", cs48l32_interrupts[i].name); + cs48l32_free_interrupts(cs48l32, i); + + return ret; +} + +static int cs48l32_probe(struct platform_device *pdev) +{ + struct cs48l32_mfd *mfd = dev_get_drvdata(pdev->dev.parent); + struct cs48l32 *cs48l32; + struct wm_adsp *dsp; + int i, ret; + + BUILD_BUG_ON(offsetof(struct cs48l32, dsp) != 0); + BUILD_BUG_ON(ARRAY_SIZE(cs48l32_dai) > CS48L32_MAX_DAI); + + /* quick exit if irqchip driver hasn't completed probe */ + if (!mfd->irq_dev) { + dev_dbg(&pdev->dev, "irqchip driver not ready\n"); + return -EPROBE_DEFER; + } + + cs48l32 = devm_kzalloc(&pdev->dev, sizeof(struct cs48l32), GFP_KERNEL); + if (!cs48l32) + return -ENOMEM; + + platform_set_drvdata(pdev, cs48l32); + pdev->dev.of_node = of_node_get(mfd->dev->of_node); + + cs48l32->mfd = mfd; + cs48l32->dev = &pdev->dev; + cs48l32->max_analogue_inputs = 1; + cs48l32->max_pdm_sup = 2; + cs48l32->in_vu_reg = CS48L32_INPUT_CONTROL3; + + ret = cs48l32_core_init(cs48l32); + if (ret) + return ret; + + ret = cs48l32_request_interrupts(cs48l32); + if (ret) + goto err; + + dsp = &cs48l32->dsp; + + switch (mfd->part) { + case 0x31: + dsp->part = "cs48l31"; + break; + case 0x32: + dsp->part = "cs48l32"; + break; + case 0x33: + dsp->part = "cs48l33"; + break; + default: + ret = -EINVAL; + goto err; + } + + dsp->cs_dsp.num = 1; + dsp->cs_dsp.type = WMFW_HALO; + dsp->cs_dsp.rev = 0; + dsp->cs_dsp.dev = mfd->dev; + dsp->cs_dsp.regmap = mfd->regmap; + + dsp->cs_dsp.base = CS48L32_DSP1_CLOCK_FREQ; + dsp->cs_dsp.base_sysinfo = CS48L32_DSP1_SYS_INFO_ID; + + dsp->cs_dsp.mem = cs48l32_dsp1_regions; + dsp->cs_dsp.num_mems = ARRAY_SIZE(cs48l32_dsp1_regions); + dsp->pre_run = cs48l32_dsp_pre_run; + + ret = wm_halo_init(dsp); + if (ret != 0) + goto err; + + cs48l32->fll.cs48l32 = cs48l32; + cs48l32->fll.id = 1; + cs48l32->fll.base = CS48L32_FLL1_CONTROL1; + cs48l32->fll.sts_addr = CS48L32_IRQ1_STS_6; + cs48l32->fll.sts_mask = CS48L32_FLL1_LOCK_STS1_MASK; + cs48l32->fll.has_lp = 1; + cs48l32_init_fll(&cs48l32->fll); + + for (i = 0; i < ARRAY_SIZE(cs48l32_dai); i++) + cs48l32_init_dai(cs48l32, i); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_cs48l32, + cs48l32_dai, + ARRAY_SIZE(cs48l32_dai)); + if (ret < 0) { + dev_err_probe(&pdev->dev, ret, "Failed to register component\n"); + goto err_dsp; + } + + return ret; +err_dsp: + wm_adsp2_remove(&cs48l32->dsp); +err: + cs48l32_core_destroy(cs48l32); + + return ret; +} + +static int cs48l32_remove(struct platform_device *pdev) +{ + struct cs48l32 *cs48l32 = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + cs48l32_free_interrupts(cs48l32, ARRAY_SIZE(cs48l32_interrupts)); + wm_adsp2_remove(&cs48l32->dsp); + + cs48l32_core_destroy(cs48l32); + + return 0; +} + +static struct platform_driver cs48l32_codec_driver = { + .driver = { + .name = "cs48l32-codec", + }, + .probe = &cs48l32_probe, + .remove = &cs48l32_remove, +}; + +module_platform_driver(cs48l32_codec_driver); + +MODULE_SOFTDEP("pre: cs48l32 irq-cirrus-cs48l32 arizona-micsupp"); +MODULE_DESCRIPTION("ASoC CS48L32 driver"); +MODULE_AUTHOR("Stuart Henderson "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cs48l32-codec"); diff --git a/sound/soc/codecs/cs48l32.h b/sound/soc/codecs/cs48l32.h new file mode 100644 index 000000000000..7cbd85077902 --- /dev/null +++ b/sound/soc/codecs/cs48l32.h @@ -0,0 +1,386 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Cirrus Logic CS48L32 codec core support + * + * Copyright (C) 2016-2018, 2022 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef ASOC_CS48L32_H +#define ASOC_CS48L32_H + +#include +#include +#include + +#include "wm_adsp.h" + +#define CS48L32_PDMCLK_SRC_IN1_PDMCLK 0x0 +#define CS48L32_PDMCLK_SRC_IN2_PDMCLK 0x1 +#define CS48L32_PDMCLK_SRC_IN3_PDMCLK 0x2 +#define CS48L32_PDMCLK_SRC_IN4_PDMCLK 0x3 +#define CS48L32_PDMCLK_SRC_AUXPDM1_CLK 0x8 +#define CS48L32_PDMCLK_SRC_AUXPDM2_CLK 0x9 + +#define CS48L32_MAX_DAI 12 +#define CS48L32_MAX_INPUT 4 +#define CS48L32_MAX_MUXED_IN_CHANNELS 4 +#define CS48L32_MAX_ASP 2 + +#define CS48L32_NUM_MIXER_INPUTS 58 + +#define CS48L32_EQ_BLOCK_SZ 60 +#define CS48L32_N_EQ_BLOCKS 4 + +#define CS48L32_DSP_N_RX_CHANNELS 8 +#define CS48L32_DSP_N_TX_CHANNELS 8 + +#define CS48L32_SAMPLE_RATE_ENUM_SIZE 17 + +#define CS48L32_MIXER_CONTROLS(name, base) \ + SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base, \ + CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs48l32_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 4, \ + CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs48l32_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 8, \ + CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs48l32_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 12, \ + CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs48l32_mixer_tlv) + +#define CS48L32_MUX_ENUM_DECL(name, reg) \ + SOC_VALUE_ENUM_SINGLE_DECL( \ + name, reg, 0, CS48L32_MIXER_SRC_MASK, \ + cs48l32_mixer_texts, cs48l32_mixer_values) + +#define CS48L32_MUX_CTL_DECL(name) \ + const struct snd_kcontrol_new name##_mux = SOC_DAPM_ENUM("Route", name##_enum) + +#define CS48L32_MUX_ENUMS(name, base_reg) \ + static CS48L32_MUX_ENUM_DECL(name##_enum, base_reg); \ + static CS48L32_MUX_CTL_DECL(name) + +#define CS48L32_MIXER_ENUMS(name, base_reg) \ + CS48L32_MUX_ENUMS(name##_in1, base_reg); \ + CS48L32_MUX_ENUMS(name##_in2, base_reg + 4); \ + CS48L32_MUX_ENUMS(name##_in3, base_reg + 8); \ + CS48L32_MUX_ENUMS(name##_in4, base_reg + 12) + +#define CS48L32_MUX(name, ctrl) SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl) + +#define CS48L32_MUX_WIDGETS(name, name_str) CS48L32_MUX(name_str " Input 1", &name##_mux) + +#define CS48L32_MIXER_WIDGETS(name, name_str) \ + CS48L32_MUX(name_str " Input 1", &name##_in1_mux), \ + CS48L32_MUX(name_str " Input 2", &name##_in2_mux), \ + CS48L32_MUX(name_str " Input 3", &name##_in3_mux), \ + CS48L32_MUX(name_str " Input 4", &name##_in4_mux), \ + SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0) + +#define CS48L32_MUX_ROUTES(widget, name) \ + { widget, NULL, name " Input 1" }, \ + CS48L32_MIXER_INPUT_ROUTES(name " Input 1") + +#define CS48L32_MIXER_ROUTES(widget, name) \ + { widget, NULL, name " Mixer" }, \ + { name " Mixer", NULL, name " Input 1" }, \ + { name " Mixer", NULL, name " Input 2" }, \ + { name " Mixer", NULL, name " Input 3" }, \ + { name " Mixer", NULL, name " Input 4" }, \ + CS48L32_MIXER_INPUT_ROUTES(name " Input 1"), \ + CS48L32_MIXER_INPUT_ROUTES(name " Input 2"), \ + CS48L32_MIXER_INPUT_ROUTES(name " Input 3"), \ + CS48L32_MIXER_INPUT_ROUTES(name " Input 4") + +#define CS48L32_DSP_ROUTES_1_8_SYSCLK(name) \ + { name, NULL, name " Preloader" }, \ + { name, NULL, "SYSCLK" }, \ + { name " Preload", NULL, name " Preloader" }, \ + CS48L32_MIXER_ROUTES(name, name "RX1"), \ + CS48L32_MIXER_ROUTES(name, name "RX2"), \ + CS48L32_MIXER_ROUTES(name, name "RX3"), \ + CS48L32_MIXER_ROUTES(name, name "RX4"), \ + CS48L32_MIXER_ROUTES(name, name "RX5"), \ + CS48L32_MIXER_ROUTES(name, name "RX6"), \ + CS48L32_MIXER_ROUTES(name, name "RX7"), \ + CS48L32_MIXER_ROUTES(name, name "RX8") \ + +#define CS48L32_DSP_ROUTES_1_8(name) \ + { name, NULL, "DSPCLK" }, \ + CS48L32_DSP_ROUTES_1_8_SYSCLK(name) \ + +#define CS48L32_RATE_CONTROL(name, domain) SOC_ENUM(name, cs48l32_sample_rate[(domain) - 1]) + +#define CS48L32_RATE_ENUM(name, enum) \ + SOC_ENUM_EXT(name, enum, snd_soc_get_enum_double, cs48l32_rate_put) + +#define CS48L32_DSP_RATE_CONTROL(name, num) \ + SOC_ENUM_EXT(name " Rate", cs48l32_dsp_rate_enum[num], \ + cs48l32_dsp_rate_get, cs48l32_dsp_rate_put) + +#define CS48L32_EQ_COEFF_CONTROL(xname, xreg, xbase, xshift) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = cs48l32_eq_coeff_info, .get = cs48l32_eq_coeff_get, \ + .put = cs48l32_eq_coeff_put, .private_value = \ + (unsigned long)&(struct cs48l32_eq_control) { .reg = xreg,\ + .shift = xshift, .block_base = xbase, .max = 65535 } } + +#define CS48L32_EQ_REG_NAME_PASTER(eq, band, type) \ + CS48L32_ ## eq ## _ ## band ## _ ## type + +#define CS48L32_EQ_BAND_COEFF_CONTROLS(name, band) \ + CS48L32_EQ_COEFF_CONTROL(#name " " #band " A", \ + CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF1), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 0), \ + CS48L32_EQ_COEFF_CONTROL(#name " " #band " B", \ + CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF1), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 16), \ + CS48L32_EQ_COEFF_CONTROL(#name " " #band " C", \ + CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF2), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 0), \ + CS48L32_EQ_COEFF_CONTROL(#name " " #band " PG", \ + CS48L32_EQ_REG_NAME_PASTER(name, band, PG), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 0) + +#define CS48L32_EQ_COEFF_CONTROLS(name) \ + CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND1), \ + CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND2), \ + CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND3), \ + CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND4), \ + CS48L32_EQ_COEFF_CONTROL(#name " BAND5 A", \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND5, COEFF1), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 0), \ + CS48L32_EQ_COEFF_CONTROL(#name " BAND5 B", \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND5, COEFF1), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 16), \ + CS48L32_EQ_COEFF_CONTROL(#name " BAND5 PG", \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND5, PG), \ + CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \ + 0) + +#define CS48L32_LHPF_CONTROL(xname, xbase) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \ + .put = cs48l32_lhpf_coeff_put, .private_value = \ + ((unsigned long)&(struct soc_bytes) { .base = xbase, \ + .num_regs = 1 }) } + +/* these have a subseq number so they run after SYSCLK and DSPCLK widgets */ +#define CS48L32_DSP_FREQ_WIDGET_EV(name, num, event) \ + SND_SOC_DAPM_SUPPLY_S(name "FREQ", 100, SND_SOC_NOPM, num, 0, \ + event, SND_SOC_DAPM_POST_PMU) + +#define CS48L32_RATES SNDRV_PCM_RATE_KNOT + +#define CS48L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct cs48l32_dai_priv { + int clk; + struct snd_pcm_hw_constraint_list constraint; +}; + +struct cs48l32_dsp_power_reg_block { + unsigned int start; + unsigned int end; +}; + +struct cs48l32_dsp_power_regs { + const unsigned int *pwd; + unsigned int n_pwd; + const struct cs48l32_dsp_power_reg_block *ext; + unsigned int n_ext; +}; + +struct cs48l32; + +struct cs48l32_fll_cfg { + int n; + unsigned int theta; + unsigned int lambda; + int refdiv; + int fratio; + int gain; + int alt_gain; +}; + +struct cs48l32_fll { + struct cs48l32 *cs48l32; + int id; + unsigned int base; + + unsigned int sts_addr; + unsigned int sts_mask; + + unsigned int fout; + + int ref_src; + unsigned int ref_freq; + struct cs48l32_fll_cfg ref_cfg; + + unsigned int max_fref; + + unsigned int integer_only:1; + unsigned int has_lp:1; +}; + +struct cs48l32 { + struct wm_adsp dsp; + struct cs48l32_mfd *mfd; + struct device *dev; + int sysclk; + int dspclk; + struct cs48l32_dai_priv dai[CS48L32_MAX_DAI]; + struct cs48l32_fll fll; + + int max_analogue_inputs; + int max_pdm_sup; + int num_dmic_clksrc; + u32 max_channels_clocked[CS48L32_MAX_ASP]; + u32 pdm_sup[CS48L32_MAX_INPUT]; + u32 in_type[CS48L32_MAX_INPUT][CS48L32_MAX_MUXED_IN_CHANNELS]; + + unsigned int in_up_pending; + unsigned int in_vu_reg; + + struct mutex rate_lock; + + u8 dsp_dma_rates[CS48L32_DSP_N_RX_CHANNELS + CS48L32_DSP_N_TX_CHANNELS]; + + int tdm_width[CS48L32_MAX_ASP]; + int tdm_slots[CS48L32_MAX_ASP]; + + unsigned int eq_mode[CS48L32_N_EQ_BLOCKS]; + __be16 eq_coefficients[CS48L32_N_EQ_BLOCKS][CS48L32_EQ_BLOCK_SZ / 2]; + + const struct cs48l32_dsp_power_regs *dsp_power_regs; +}; + +struct cs48l32_enum { + struct soc_enum mixer_enum; + int val; +}; + +struct cs48l32_eq_control { + unsigned int reg; + unsigned int shift; + unsigned int block_base; + unsigned int max; +}; + +extern const char * const cs48l32_mixer_texts[CS48L32_NUM_MIXER_INPUTS]; +extern unsigned int cs48l32_mixer_values[CS48L32_NUM_MIXER_INPUTS]; + +extern const unsigned int cs48l32_ana_tlv[]; +extern const unsigned int cs48l32_eq_tlv[]; +extern const unsigned int cs48l32_digital_tlv[]; +extern const unsigned int cs48l32_noise_tlv[]; +extern const unsigned int cs48l32_mixer_tlv[]; +extern const unsigned int cs48l32_us_tlv[]; + +int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); + +extern const struct soc_enum cs48l32_sample_rate[]; + +extern const struct snd_kcontrol_new cs48l32_inmux[]; +extern const struct snd_kcontrol_new cs48l32_dmode_mux[]; + +extern const struct soc_enum cs48l32_us_freq[]; +extern const struct snd_kcontrol_new cs48l32_us_inmux[]; +extern const struct soc_enum cs48l32_us_output_rate[]; +extern const struct soc_enum cs48l32_us_det_lpf_cut[]; +extern const struct soc_enum cs48l32_us_det_dcy[]; +extern const struct snd_kcontrol_new cs48l32_us_switch[]; +extern const struct soc_enum cs48l32_us_det_thr[]; +extern const struct soc_enum cs48l32_us_det_num[]; +extern const struct soc_enum cs48l32_us_det_hold[]; + +extern const struct soc_enum cs48l32_in_vi_ramp; +extern const struct soc_enum cs48l32_in_vd_ramp; +extern const struct soc_enum cs48l32_in_hpf_cut_enum; +extern const struct soc_enum cs48l32_in_dmic_osr[]; + +irqreturn_t cs48l32_us1_activity(int irq, void *data); +irqreturn_t cs48l32_us2_activity(int irq, void *data); + +int cs48l32_in_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +extern const struct soc_enum cs48l32_input_rate[]; +int cs48l32_low_power_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +extern const struct soc_enum noise_gen_rate; + +extern const struct soc_enum cs48l32_auxpdm1_freq; +extern const struct soc_enum cs48l32_auxpdm2_freq; +extern const struct snd_kcontrol_new cs48l32_auxpdm_inmux[]; +extern const struct snd_kcontrol_new cs48l32_auxpdm_analog_inmux[]; +extern const struct snd_kcontrol_new cs48l32_auxpdm_switch[]; + +extern const struct soc_enum cs48l32_isrc_fsl[]; +extern const struct soc_enum cs48l32_isrc_fsh[]; + +extern const struct soc_enum cs48l32_fx_rate; + +extern const struct soc_enum cs48l32_lhpf_mode[]; + +int cs48l32_lhpf_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +extern const struct soc_enum cs48l32_eq_mode[]; +int cs48l32_eq_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int cs48l32_eq_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int cs48l32_eq_coeff_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); +int cs48l32_eq_coeff_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int cs48l32_eq_coeff_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); + +extern const struct snd_kcontrol_new cs48l32_drc_activity_output_mux[]; +extern const struct snd_kcontrol_new cs48l32_dsp_trigger_output_mux[]; +int cs48l32_dsp_rate_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int cs48l32_dsp_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +extern const struct soc_enum cs48l32_dsp_rate_enum[]; + +int cs48l32_dsp_pre_run(struct wm_adsp *dsp); +int cs48l32_dsp_memory_enable(struct cs48l32 *priv, + const struct cs48l32_dsp_power_regs *regs); +void cs48l32_dsp_memory_disable(struct cs48l32 *priv, + const struct cs48l32_dsp_power_regs *regs); +int cs48l32_dsp_freq_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +irqreturn_t cs48l32_dsp1_irq(int irq, void *data); + +int cs48l32_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir); + +extern const struct snd_soc_dai_ops cs48l32_dai_ops; + +int cs48l32_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event); +int cs48l32_in_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event); +int cs48l32_in_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int cs48l32_eq_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event); + +int cs48l32_fllhj_set_refclk(struct cs48l32_fll *fll, int source, + unsigned int fin, unsigned int fout); +int cs48l32_init_fll(struct cs48l32_fll *fll); +int cs48l32_init_inputs(struct snd_soc_component *component); +int cs48l32_init_dai(struct cs48l32 *priv, int dai); +int cs48l32_init_eq(struct cs48l32 *priv); +int cs48l32_core_init(struct cs48l32 *priv); +int cs48l32_core_destroy(struct cs48l32 *priv); + +#endif