diff options
94 files changed, 4613 insertions, 403 deletions
diff --git a/Documentation/devicetree/bindings/sound/ak4375.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4375.yaml index 587598e122c6..bc07fcba535b 100644 --- a/Documentation/devicetree/bindings/sound/ak4375.yaml +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4375.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/ak4375.yaml# +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4375.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: AK4375 DAC and headphones amplifier diff --git a/Documentation/devicetree/bindings/sound/ak4613.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4613.yaml index 75e13414d6eb..b49a6cff9f1f 100644 --- a/Documentation/devicetree/bindings/sound/ak4613.yaml +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4613.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/ak4613.yaml# +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4613.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: AK4613 I2C transmitter diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml new file mode 100644 index 000000000000..d412531ef9a2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4619.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AK4619 I2C transmitter + +maintainers: + - Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + - Khanh Le <khanh.le.xr@renesas.com> + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: asahi-kasei,ak4619 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: mclk + + "#sound-dai-cells": + const: 0 + + port: + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + codec@10 { + compatible = "asahi-kasei,ak4619"; + reg = <0x10>; + + clocks = <&rcar_sound>; + clock-names = "mclk"; + + #sound-dai-cells = <0>; + port { + ak4619_endpoint: endpoint { + remote-endpoint = <&rsnd_endpoint>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/ak4642.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4642.yaml index 437fe5d7cae1..fc03f0373a1a 100644 --- a/Documentation/devicetree/bindings/sound/ak4642.yaml +++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4642.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/ak4642.yaml# +$id: http://devicetree.org/schemas/sound/asahi-kasei,ak4642.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: AK4642 I2C transmitter diff --git a/Documentation/devicetree/bindings/sound/audio-graph-card2.yaml b/Documentation/devicetree/bindings/sound/audio-graph-card2.yaml index d3ce4de449d5..f943f90d8b15 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-card2.yaml +++ b/Documentation/devicetree/bindings/sound/audio-graph-card2.yaml @@ -23,6 +23,11 @@ properties: Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. $ref: /schemas/types.yaml#/definitions/non-unique-string-array + aux-devs: + description: | + List of phandles pointing to auxiliary devices, such + as amplifiers, to be added to the sound card. + $ref: /schemas/types.yaml#/definitions/phandle-array multi: type: object description: Multi-CPU/Codec node diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml new file mode 100644 index 000000000000..9582eb8eb418 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs530x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic cs530x family of audio ADCs + +maintainers: + - Paul Handrigan <paulha@opensource.cirrus.com> + - patches@opensource.cirrus.com + +description: + The CS530X devices are a family of high performance audio ADCs. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - cirrus,cs5302 + - cirrus,cs5304 + - cirrus,cs5308 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 1 + + reset-gpios: + maxItems: 1 + + vdd-a-supply: + description: Analog power supply + + vdd-io-supply: + description: Digital IO power supply + + cirrus,in-hiz-pin12: + description: + Sets input channels one and two to high impedance. + type: boolean + + cirrus,in-hiz-pin34: + description: + Sets input channels three and four to high impedance. + type: boolean + + cirrus,in-hiz-pin56: + description: + Sets input channels five and six to high impedance. + type: boolean + + cirrus,in-hiz-pin78: + description: + Sets input channels seven and eight to high impedance. + type: boolean + +required: + - compatible + - reg + - "#sound-dai-cells" + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + cs5304: adc@48 { + compatible = "cirrus,cs5304"; + reg = <0x48>; + #sound-dai-cells = <1>; + reset-gpios = <&gpio 110 GPIO_ACTIVE_LOW>; + vdd-a-supply = <&vreg>; + vdd-io-supply = <&vreg>; + cirrus,in-hiz-pin34; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/everest,es7134.txt b/Documentation/devicetree/bindings/sound/everest,es7134.txt deleted file mode 100644 index 091666069bde..000000000000 --- a/Documentation/devicetree/bindings/sound/everest,es7134.txt +++ /dev/null @@ -1,15 +0,0 @@ -ES7134 i2s DA converter - -Required properties: -- compatible : "everest,es7134" or - "everest,es7144" or - "everest,es7154" -- VDD-supply : regulator phandle for the VDD supply -- PVDD-supply: regulator phandle for the PVDD supply for the es7154 - -Example: - -i2s_codec: external-codec { - compatible = "everest,es7134"; - VDD-supply = <&vcc_5v>; -}; diff --git a/Documentation/devicetree/bindings/sound/everest,es71x4.yaml b/Documentation/devicetree/bindings/sound/everest,es71x4.yaml new file mode 100644 index 000000000000..fd1b32812228 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/everest,es71x4.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/everest,es71x4.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Everest ES7134/7144/7154 2 channels I2S analog to digital converter + +maintainers: + - Neil Armstrong <neil.armstrong@linaro.org> + +properties: + compatible: + enum: + - everest,es7134 + - everest,es7144 + - everest,es7154 + + VDD-supply: true + PVDD-supply: true + + '#sound-dai-cells': + const: 0 + +required: + - compatible + - VDD-supply + +allOf: + - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - everest,es7134 + - everest,es7144 + then: + properties: + PVDD-supply: false + + - if: + properties: + compatible: + contains: + enum: + - everest,es7154 + then: + required: + - PVDD-supply + +unevaluatedProperties: false + +examples: + - | + codec { + compatible = "everest,es7134"; + #sound-dai-cells = <0>; + VDD-supply = <&vdd_supply>; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/everest,es7241.txt b/Documentation/devicetree/bindings/sound/everest,es7241.txt deleted file mode 100644 index 28f82cf4959f..000000000000 --- a/Documentation/devicetree/bindings/sound/everest,es7241.txt +++ /dev/null @@ -1,28 +0,0 @@ -ES7241 i2s AD converter - -Required properties: -- compatible : "everest,es7241" -- VDDP-supply: regulator phandle for the VDDA supply -- VDDA-supply: regulator phandle for the VDDP supply -- VDDD-supply: regulator phandle for the VDDD supply - -Optional properties: -- reset-gpios: gpio connected to the reset pin -- m0-gpios : gpio connected to the m0 pin -- m1-gpios : gpio connected to the m1 pin -- everest,sdout-pull-down: - Format used by the serial interface is controlled by pulling - the sdout. If the sdout is pulled down, leftj format is used. - If this property is not provided, sdout is assumed to pulled - up and i2s format is used - -Example: - -linein: audio-codec@2 { - #sound-dai-cells = <0>; - compatible = "everest,es7241"; - VDDA-supply = <&vcc_3v3>; - VDDP-supply = <&vcc_3v3>; - VDDD-supply = <&vcc_3v3>; - reset-gpios = <&gpio GPIOH_42>; -}; diff --git a/Documentation/devicetree/bindings/sound/everest,es7241.yaml b/Documentation/devicetree/bindings/sound/everest,es7241.yaml new file mode 100644 index 000000000000..f179af758730 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/everest,es7241.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/everest,es7241.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Everest ES7241 2 channels I2S analog to digital converter + +maintainers: + - Neil Armstrong <neil.armstrong@linaro.org> + +properties: + compatible: + enum: + - everest,es7241 + + reset-gpios: + maxItems: 1 + description: GPIO connected to the reset pin + + m0-gpios: + maxItems: 1 + description: GPIO connected to the m0 pin + + m1-gpios: + maxItems: 1 + description: GPIO connected to the m0 pin + + everest,sdout-pull-down: + type: boolean + description: + Format used by the serial interface is controlled by pulling + the sdout. If the sdout is pulled down, leftj format is used. + If this property is not provided, sdout is assumed to pulled + up and i2s format is used + + VDDP-supply: true + VDDA-supply: true + VDDD-supply: true + + '#sound-dai-cells': + const: 0 + +required: + - compatible + - VDDP-supply + - VDDA-supply + - VDDD-supply + +allOf: + - $ref: dai-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + codec { + compatible = "everest,es7241"; + #sound-dai-cells = <0>; + reset-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; + VDDP-supply = <&vddp_supply>; + VDDA-supply = <&vdda_supply>; + VDDD-supply = <&vddd_supply>; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.yaml b/Documentation/devicetree/bindings/sound/fsl,sgtl5000.yaml index 1353c051488f..c6ab1ca16763 100644 --- a/Documentation/devicetree/bindings/sound/sgtl5000.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,sgtl5000.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/sgtl5000.yaml# +$id: http://devicetree.org/schemas/sound/fsl,sgtl5000.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Freescale SGTL5000 Stereo Codec diff --git a/Documentation/devicetree/bindings/sound/zl38060.yaml b/Documentation/devicetree/bindings/sound/mscc,zl38060.yaml index 8bd201e573aa..994313fd12b2 100644 --- a/Documentation/devicetree/bindings/sound/zl38060.yaml +++ b/Documentation/devicetree/bindings/sound/mscc,zl38060.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/zl38060.yaml# +$id: http://devicetree.org/schemas/sound/mscc,zl38060.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: ZL38060 Connected Home Audio Processor from Microsemi. diff --git a/Documentation/devicetree/bindings/sound/pcm512x.txt b/Documentation/devicetree/bindings/sound/pcm512x.txt index 77006a4aec4a..47878a6df608 100644 --- a/Documentation/devicetree/bindings/sound/pcm512x.txt +++ b/Documentation/devicetree/bindings/sound/pcm512x.txt @@ -6,7 +6,7 @@ on the board). The TAS575x devices only support I2C. Required properties: - compatible : One of "ti,pcm5121", "ti,pcm5122", "ti,pcm5141", - "ti,pcm5142", "ti,tas5754" or "ti,tas5756" + "ti,pcm5142", "ti,pcm5242", "ti,tas5754" or "ti,tas5756" - reg : the I2C address of the device for I2C, the chip select number for SPI. diff --git a/Documentation/devicetree/bindings/sound/rt1019.yaml b/Documentation/devicetree/bindings/sound/realtek,rt1019.yaml index 3d5a91a942f4..adf5e38f4dbc 100644 --- a/Documentation/devicetree/bindings/sound/rt1019.yaml +++ b/Documentation/devicetree/bindings/sound/realtek,rt1019.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/rt1019.yaml# +$id: http://devicetree.org/schemas/sound/realtek,rt1019.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: RT1019 Mono Class-D Audio Amplifier diff --git a/Documentation/devicetree/bindings/sound/tas2562.yaml b/Documentation/devicetree/bindings/sound/ti,tas2562.yaml index d28c102c0ce7..8bc3b0c7531e 100644 --- a/Documentation/devicetree/bindings/sound/tas2562.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas2562.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019 Texas Instruments Incorporated %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/tas2562.yaml# +$id: http://devicetree.org/schemas/sound/ti,tas2562.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments TAS2562 Smart PA diff --git a/Documentation/devicetree/bindings/sound/tas2770.yaml b/Documentation/devicetree/bindings/sound/ti,tas2770.yaml index be2536e8c440..362c2e6154f0 100644 --- a/Documentation/devicetree/bindings/sound/tas2770.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas2770.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019-20 Texas Instruments Incorporated %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/tas2770.yaml# +$id: http://devicetree.org/schemas/sound/ti,tas2770.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments TAS2770 Smart PA diff --git a/Documentation/devicetree/bindings/sound/tas27xx.yaml b/Documentation/devicetree/bindings/sound/ti,tas27xx.yaml index f2d878f6f495..530bc3937847 100644 --- a/Documentation/devicetree/bindings/sound/tas27xx.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas27xx.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2020-2022 Texas Instruments Incorporated %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/tas27xx.yaml# +$id: http://devicetree.org/schemas/sound/ti,tas27xx.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments TAS2764/TAS2780 Smart PA diff --git a/Documentation/devicetree/bindings/sound/tas5805m.yaml b/Documentation/devicetree/bindings/sound/ti,tas5805m.yaml index 12c41974274e..c2c2835a9e1d 100644 --- a/Documentation/devicetree/bindings/sound/tas5805m.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tas5805m.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/tas5805m.yaml# +$id: http://devicetree.org/schemas/sound/ti,tas5805m.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: TAS5805M audio amplifier diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml index 8ac741f4cd56..66b76656229f 100644 --- a/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tlv320adc3xxx.yaml @@ -82,6 +82,26 @@ properties: Note that there is currently no support for reading the GPIO pins as inputs. + ti,micbias1-gpo: + type: boolean + description: | + When set, the MICBIAS1 pin may be controlled via the GPIO framework, + as pin number 3 on the device. + + In this mode, when the pin is activated, it will be set to the voltage + specified by the ti,micbias1-vg property. When deactivated, the pin will + float. + + ti,micbias2-gpo: + type: boolean + description: | + When set, the MICBIAS2 pin may be controlled via the GPIO framework, + as pin number 4 on the device. + + In this mode, when the pin is activated, it will be set to the voltage + specified by the ti,micbias2-vg property. When deactivated, the pin will + float. + ti,micbias1-vg: $ref: /schemas/types.yaml#/definitions/uint32 enum: @@ -104,6 +124,10 @@ properties: description: | Mic bias voltage output on MICBIAS2 pin +dependencies: + ti,micbias1-gpo: ['ti,micbias1-vg'] + ti,micbias2-gpo: ['ti,micbias2-vg'] + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml index f3274bcc4c05..876fa97bfbcd 100644 --- a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml +++ b/Documentation/devicetree/bindings/sound/ti,tlv320adcx140.yaml @@ -2,7 +2,7 @@ # Copyright (C) 2019 Texas Instruments Incorporated %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/tlv320adcx140.yaml# +$id: http://devicetree.org/schemas/sound/ti,tlv320adcx140.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments TLV320ADCX140 Quad Channel Analog-to-Digital Converter diff --git a/Documentation/devicetree/bindings/sound/wm8750.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8750.yaml index 24246ac7bbdf..96859e38315b 100644 --- a/Documentation/devicetree/bindings/sound/wm8750.yaml +++ b/Documentation/devicetree/bindings/sound/wlf,wm8750.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/wm8750.yaml# +$id: http://devicetree.org/schemas/sound/wlf,wm8750.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: WM8750 and WM8987 audio CODECs diff --git a/MAINTAINERS b/MAINTAINERS index aacccb376c28..c35c03901252 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18210,6 +18210,7 @@ QCOM AUDIO (ASoC) DRIVERS M: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> M: Banajit Goswami <bgoswami@quicinc.com> L: alsa-devel@alsa-project.org (moderated for non-subscribers) +L: linux-arm-msm@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/soc/qcom/qcom,apr* F: Documentation/devicetree/bindings/sound/qcom,* diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index c11aaf8079fb..f6baa9a01868 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -36,6 +36,7 @@ snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, struct dma_chan *chan); int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream); +int snd_dmaengine_pcm_sync_stop(struct snd_pcm_substream *substream); int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, dma_filter_fn filter_fn, void *filter_data); diff --git a/include/sound/rt1318.h b/include/sound/rt1318.h new file mode 100644 index 000000000000..fe6bff06036c --- /dev/null +++ b/include/sound/rt1318.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * linux/sound/rt1318.h -- Platform data for RT1318 + * + * Copyright 2024 Realtek Semiconductor Corp. + */ + +#ifndef __LINUX_SND_RT1318_H +#define __LINUX_SND_RT1318_H + +struct rt1318_platform_data { + unsigned int init_r0_l; + unsigned int init_r0_r; +}; + +#endif diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index bd249710d716..bbb72ad4c951 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -361,7 +361,7 @@ struct snd_soc_dai_ops { * see * snd_soc_dai_get_fmt() */ - u64 *auto_selectable_formats; + const u64 *auto_selectable_formats; int num_auto_selectable_formats; /* probe ordering - for components with runtime dependencies */ diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h index 7fba7ea26a4b..3cda9da14f6d 100644 --- a/include/sound/tas2781-dsp.h +++ b/include/sound/tas2781-dsp.h @@ -117,10 +117,17 @@ struct tasdevice_fw { struct device *dev; }; -enum tasdevice_dsp_fw_state { - TASDEVICE_DSP_FW_NONE = 0, +enum tasdevice_fw_state { + /* Driver in startup mode, not load any firmware. */ TASDEVICE_DSP_FW_PENDING, + /* DSP firmware in the system, but parsing error. */ TASDEVICE_DSP_FW_FAIL, + /* + * Only RCA (Reconfigurable Architecture) firmware load + * successfully. + */ + TASDEVICE_RCA_FW_OK, + /* Both RCA and DSP firmware load successfully. */ TASDEVICE_DSP_FW_ALL_OK, }; diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 12aa1cef11a1..ed07fa5693d2 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -349,6 +349,16 @@ int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan); +int snd_dmaengine_pcm_sync_stop(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + + dmaengine_synchronize(prtd->dma_chan); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_sync_stop); + /** * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream * @substream: PCM substream diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 0bc8617e922a..97258b4cf89b 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -369,12 +369,12 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct } writel(period_bytes, adata->acp_base + water_val); writel(buf_size, adata->acp_base + buf_reg); + if (rsrc->soc_mclk) + acp_set_i2s_clk(adata, dai->driver->id); val = readl(adata->acp_base + reg_val); val = val | BIT(0); writel(val, adata->acp_base + reg_val); writel(1, adata->acp_base + ier_val); - if (rsrc->soc_mclk) - acp_set_i2s_clk(adata, dai->driver->id); return 0; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: @@ -584,29 +584,7 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d return 0; } -static int acp_i2s_probe(struct snd_soc_dai *dai) -{ - struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); - struct acp_resource *rsrc = adata->rsrc; - unsigned int val; - - if (!adata->acp_base) { - dev_err(dev, "I2S base is NULL\n"); - return -EINVAL; - } - - val = readl(adata->acp_base + rsrc->i2s_pin_cfg_offset); - if (val != rsrc->i2s_mode) { - dev_err(dev, "I2S Mode not supported val %x\n", val); - return -EINVAL; - } - - return 0; -} - const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { - .probe = acp_i2s_probe, .startup = acp_i2s_startup, .hw_params = acp_i2s_hwparams, .prepare = acp_i2s_prepare, diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index 565623afd42e..b0304b813cad 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -100,6 +100,7 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id ret = -EINVAL; goto release_regions; } + chip->flag = flag; dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0); if (IS_ERR(dmic_dev)) { dev_err(dev, "failed to create DMIC device\n"); @@ -139,7 +140,6 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id } } - chip->flag = flag; memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.name = chip->name; @@ -199,10 +199,12 @@ static int __maybe_unused snd_acp_resume(struct device *dev) ret = acp_init(chip); if (ret) dev_err(dev, "ACP init failed\n"); - child = chip->chip_pdev->dev; - adata = dev_get_drvdata(&child); - if (adata) - acp_enable_interrupts(adata); + if (chip->chip_pdev) { + child = chip->chip_pdev->dev; + adata = dev_get_drvdata(&child); + if (adata) + acp_enable_interrupts(adata); + } return ret; } diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c index aaac8aa744cb..4f409cd09c11 100644 --- a/sound/soc/amd/acp/acp-platform.c +++ b/sound/soc/amd/acp/acp-platform.c @@ -197,6 +197,20 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs else runtime->hw = acp_pcm_hardware_capture; + ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, DMA_SIZE); + if (ret) { + dev_err(component->dev, "set hw constraint HW_PARAM_PERIOD_BYTES failed\n"); + kfree(stream); + return ret; + } + + ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, DMA_SIZE); + if (ret) { + dev_err(component->dev, "set hw constraint HW_PARAM_BUFFER_BYTES failed\n"); + kfree(stream); + return ret; + } + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) { dev_err(component->dev, "set integer constraint failed\n"); diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c index 158f819f8da4..e19981c7d65a 100644 --- a/sound/soc/amd/acp/acp-rembrandt.c +++ b/sound/soc/amd/acp/acp-rembrandt.c @@ -39,8 +39,6 @@ static struct acp_resource rsrc = { .irqp_used = 1, .soc_mclk = true, .irq_reg_offset = 0x1a00, - .i2s_pin_cfg_offset = 0x1440, - .i2s_mode = 0x0a, .scratch_reg_offset = 0x12800, .sram_pte_offset = 0x03802800, }; @@ -231,12 +229,13 @@ static int rembrandt_audio_probe(struct platform_device *pdev) adata->rsrc = &rsrc; adata->platform = REMBRANDT; adata->flag = chip->flag; + adata->is_i2s_config = chip->is_i2s_config; adata->machines = snd_soc_acpi_amd_rmb_acp_machines; acp_machine_select(adata); dev_set_drvdata(dev, adata); - if (chip->flag != FLAG_AMD_LEGACY_ONLY_DMIC) { + if (chip->is_i2s_config && rsrc.soc_mclk) { ret = acp6x_master_clock_generate(dev); if (ret) return ret; @@ -269,7 +268,7 @@ static int __maybe_unused rmb_pcm_resume(struct device *dev) snd_pcm_uframes_t buf_in_frames; u64 buf_size; - if (adata->flag != FLAG_AMD_LEGACY_ONLY_DMIC) + if (adata->is_i2s_config && adata->rsrc->soc_mclk) acp6x_master_clock_generate(dev); spin_lock(&adata->acp_lock); diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index b0e181c9a733..db835ed7c208 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -32,8 +32,6 @@ static struct acp_resource rsrc = { .no_of_ctrls = 1, .irqp_used = 0, .irq_reg_offset = 0x1800, - .i2s_pin_cfg_offset = 0x1400, - .i2s_mode = 0x04, .scratch_reg_offset = 0x12800, .sram_pte_offset = 0x02052800, }; diff --git a/sound/soc/amd/acp/acp63.c b/sound/soc/amd/acp/acp63.c index 4d342441a650..f340920b3289 100644 --- a/sound/soc/amd/acp/acp63.c +++ b/sound/soc/amd/acp/acp63.c @@ -55,8 +55,6 @@ static struct acp_resource rsrc = { .irqp_used = 1, .soc_mclk = true, .irq_reg_offset = 0x1a00, - .i2s_pin_cfg_offset = 0x1440, - .i2s_mode = 0x0a, .scratch_reg_offset = 0x12800, .sram_pte_offset = 0x03802800, }; @@ -241,11 +239,12 @@ static int acp63_audio_probe(struct platform_device *pdev) adata->rsrc = &rsrc; adata->platform = ACP63; adata->flag = chip->flag; + adata->is_i2s_config = chip->is_i2s_config; adata->machines = snd_soc_acpi_amd_acp63_acp_machines; acp_machine_select(adata); dev_set_drvdata(dev, adata); - if (chip->flag != FLAG_AMD_LEGACY_ONLY_DMIC) { + if (chip->is_i2s_config && rsrc.soc_mclk) { ret = acp63_i2s_master_clock_generate(adata); if (ret) return ret; @@ -278,7 +277,7 @@ static int __maybe_unused acp63_pcm_resume(struct device *dev) snd_pcm_uframes_t buf_in_frames; u64 buf_size; - if (adata->flag != FLAG_AMD_LEGACY_ONLY_DMIC) + if (adata->is_i2s_config && adata->rsrc->soc_mclk) acp63_i2s_master_clock_generate(adata); spin_lock(&adata->acp_lock); diff --git a/sound/soc/amd/acp/acp70.c b/sound/soc/amd/acp/acp70.c index 0d7cdd4017e5..a2cbdcca4313 100644 --- a/sound/soc/amd/acp/acp70.c +++ b/sound/soc/amd/acp/acp70.c @@ -31,8 +31,6 @@ static struct acp_resource rsrc = { .irqp_used = 1, .soc_mclk = true, .irq_reg_offset = 0x1a00, - .i2s_pin_cfg_offset = 0x1440, - .i2s_mode = 0x0a, .scratch_reg_offset = 0x12800, .sram_pte_offset = 0x03802800, }; diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index d75b4eb34de8..87a4813783f9 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -162,8 +162,6 @@ struct acp_resource { int irqp_used; bool soc_mclk; u32 irq_reg_offset; - u32 i2s_pin_cfg_offset; - int i2s_mode; u64 scratch_reg_offset; u64 sram_pte_offset; }; @@ -175,6 +173,7 @@ struct acp_dev_data { unsigned int i2s_irq; bool tdm_mode; + bool is_i2s_config; /* SOC specific dais */ struct snd_soc_dai_driver *dai_driver; int num_dai; diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c index 6aed1ee443b4..ba314b279919 100644 --- a/sound/soc/atmel/atmel-classd.c +++ b/sound/soc/atmel/atmel-classd.c @@ -473,19 +473,22 @@ static int atmel_classd_asoc_card_init(struct device *dev, if (!dai_link) return -ENOMEM; - comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); + comp = devm_kzalloc(dev, 2 * sizeof(*comp), GFP_KERNEL); if (!comp) return -ENOMEM; - dai_link->cpus = comp; + dai_link->cpus = &comp[0]; dai_link->codecs = &snd_soc_dummy_dlc; + dai_link->platforms = &comp[1]; dai_link->num_cpus = 1; dai_link->num_codecs = 1; + dai_link->num_platforms = 1; dai_link->name = "CLASSD"; dai_link->stream_name = "CLASSD PCM"; dai_link->cpus->dai_name = dev_name(dev); + dai_link->platforms->name = dev_name(dev); card->dai_link = dai_link; card->num_links = 1; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7b99556f24d3..2019da190dcb 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -45,6 +45,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_AK4535 imply SND_SOC_AK4554 imply SND_SOC_AK4613 + imply SND_SOC_AK4619 imply SND_SOC_AK4641 imply SND_SOC_AK4642 imply SND_SOC_AK4671 @@ -99,6 +100,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS47L90 imply SND_SOC_CS47L92 imply SND_SOC_CS53L30 + imply SND_SOC_CS530X_I2C imply SND_SOC_CX20442 imply SND_SOC_CX2072X imply SND_SOC_DA7210 @@ -221,6 +223,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT722_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_RT1316_SDW + imply SND_SOC_RT1318 imply SND_SOC_RT1318_SDW imply SND_SOC_RT1320_SDW imply SND_SOC_RT9120 @@ -598,6 +601,10 @@ config SND_SOC_AK4613 tristate "AKM AK4613 CODEC" depends on I2C +config SND_SOC_AK4619 + tristate "AKM AK4619 CODEC" + depends on I2C + config SND_SOC_AK4641 tristate depends on I2C @@ -999,6 +1006,19 @@ config SND_SOC_CS53L30 tristate "Cirrus Logic CS53L30 CODEC" depends on I2C +config SND_SOC_CS530X + tristate + +config SND_SOC_CS530X_I2C + tristate "Cirrus Logic CS530x ADCs (I2C)" + depends on I2C + select REGMAP + select REGMAP_I2C + select SND_SOC_CS530X + help + Enable support for Cirrus Logic CS530X ADCs + with I2C control. + config SND_SOC_CX20442 tristate depends on TTY @@ -1576,6 +1596,10 @@ config SND_SOC_RT1316_SDW depends on SOUNDWIRE select REGMAP_SOUNDWIRE +config SND_SOC_RT1318 + tristate + depends on I2C + config SND_SOC_RT1318_SDW tristate "Realtek RT1318 Codec - SDW" depends on SOUNDWIRE diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ca69f35cc0f7..5868007e1045 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -39,6 +39,7 @@ snd-soc-ak4458-y := ak4458.o snd-soc-ak4535-y := ak4535.o snd-soc-ak4554-y := ak4554.o snd-soc-ak4613-y := ak4613.o +snd-soc-ak4619-y := ak4619.o snd-soc-ak4641-y := ak4641.o snd-soc-ak4642-y := ak4642.o snd-soc-ak4671-y := ak4671.o @@ -107,6 +108,8 @@ snd-soc-cs47l85-y := cs47l85.o snd-soc-cs47l90-y := cs47l90.o snd-soc-cs47l92-y := cs47l92.o snd-soc-cs53l30-y := cs53l30.o +snd-soc-cs530x-y := cs530x.o +snd-soc-cs530x-i2c-y := cs530x-i2c.o snd-soc-cx20442-y := cx20442.o snd-soc-cx2072x-y := cx2072x.o snd-soc-da7210-y := da7210.o @@ -222,6 +225,7 @@ snd-soc-rt1305-y := rt1305.o snd-soc-rt1308-y := rt1308.o snd-soc-rt1308-sdw-y := rt1308-sdw.o snd-soc-rt1316-sdw-y := rt1316-sdw.o +snd-soc-rt1318-y := rt1318.o snd-soc-rt1318-sdw-y := rt1318-sdw.o snd-soc-rt1320-sdw-y := rt1320-sdw.o snd-soc-rt274-y := rt274.o @@ -439,6 +443,7 @@ obj-$(CONFIG_SND_SOC_AK4458) += snd-soc-ak4458.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o +obj-$(CONFIG_SND_SOC_AK4619) += snd-soc-ak4619.o obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o @@ -508,6 +513,8 @@ 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_CS53L30) += snd-soc-cs53l30.o +obj-$(CONFIG_SND_SOC_CS530X) += snd-soc-cs530x.o +obj-$(CONFIG_SND_SOC_CS530X_I2C) += snd-soc-cs530x-i2c.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_CX2072X) += snd-soc-cx2072x.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o @@ -618,6 +625,7 @@ obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o obj-$(CONFIG_SND_SOC_RT1316_SDW) += snd-soc-rt1316-sdw.o +obj-$(CONFIG_SND_SOC_RT1318) += snd-soc-rt1318.o obj-$(CONFIG_SND_SOC_RT1318_SDW) += snd-soc-rt1318-sdw.o obj-$(CONFIG_SND_SOC_RT1320_SDW) += snd-soc-rt1320-sdw.o obj-$(CONFIG_SND_SOC_RT274) += snd-soc-rt274.o diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 73fb35560e51..551738abd1a5 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -753,7 +753,7 @@ static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd, * SND_SOC_DAIFMT_CBC_CFC * SND_SOC_DAIFMT_CBP_CFP */ -static u64 ak4613_dai_formats = +static const u64 ak4613_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J; diff --git a/sound/soc/codecs/ak4619.c b/sound/soc/codecs/ak4619.c new file mode 100644 index 000000000000..cbe27abe1f50 --- /dev/null +++ b/sound/soc/codecs/ak4619.c @@ -0,0 +1,912 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ak4619.c -- Asahi Kasei ALSA SoC Audio driver + * + * Copyright (C) 2023 Renesas Electronics Corporation + * Khanh Le <khanh.le.xr@renesas.com> + * + * Based on ak4613.c by Kuninori Morimoto + * Based on da7213.c by Adam Thomson + * Based on ak4641.c by Harald Welte + */ + +#include <linux/clk.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/of_device.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> + +/* + * Registers + */ + +#define PWR_MGMT 0x00 /* Power Management */ +#define AU_IFF1 0x01 /* Audio I/F Format */ +#define AU_IFF2 0x02 /* Audio I/F Format (Extended) */ +#define SYS_CLK 0x03 /* System Clock Setting */ +#define MIC_AMP1 0x04 /* MIC AMP Gain 1 */ +#define MIC_AMP2 0x05 /* MIC AMP Gain 2 */ +#define LADC1 0x06 /* ADC1 Lch Digital Volume */ +#define RADC1 0x07 /* ADC1 Rch Digital Volume */ +#define LADC2 0x08 /* ADC2 Lch Digital Volume */ +#define RADC2 0x09 /* ADC2 Rch Digital Volume */ +#define ADC_DF 0x0a /* ADC Digital Filter Setting */ +#define ADC_AI 0x0b /* ADC Analog Input Setting */ +#define ADC_MHPF 0x0D /* ADC Mute & HPF Control */ +#define LDAC1 0x0E /* DAC1 Lch Digital Volume */ +#define RDAC1 0x0F /* DAC1 Rch Digital Volume */ +#define LDAC2 0x10 /* DAC2 Lch Digital Volume */ +#define RDAC2 0x11 /* DAC2 Rch Digital Volume */ +#define DAC_IS 0x12 /* DAC Input Select Setting */ +#define DAC_DEMP 0x13 /* DAC De-Emphasis Setting */ +#define DAC_MF 0x14 /* DAC Mute & Filter Setting */ + +/* + * Bit fields + */ + +/* Power Management */ +#define PMAD2 BIT(5) +#define PMAD1 BIT(4) +#define PMDA2 BIT(2) +#define PMDA1 BIT(1) +#define RSTN BIT(0) + +/* Audio_I/F Format */ +#define DCF_STEREO_I2S (0x0 << 4) +#define DCF_STEREO_MSB (0x5 << 4) +#define DCF_PCM_SF (0x6 << 4) +#define DCF_PCM_LF (0x7 << 4) +#define DSL_32 (0x3 << 2) +#define DCF_MASK (0x7 << 4) +#define DSL_MASK (0x3 << 2) +#define BCKP BIT(1) + +/* Audio_I/F Format (Extended) */ +#define DIDL_24 (0x0 << 2) +#define DIDL_20 (0x1 << 2) +#define DIDL_16 (0x2 << 2) +#define DIDL_32 (0x3 << 2) +#define DODL_24 (0x0 << 0) +#define DODL_20 (0x1 << 0) +#define DODL_16 (0x2 << 0) +#define DIDL_MASK (0x3 << 2) +#define DODL_MASK (0x3 << 0) +#define SLOT BIT(4) + +/* System Clock Setting */ +#define FS_MASK 0x7 + +/* MIC AMP Gain */ +#define MGNL_SHIFT 4 +#define MGNR_SHIFT 0 +#define MGN_MAX 0xB + +/* ADC Digital Volume */ +#define VOLAD_SHIFT 0 +#define VOLAD_MAX 0xFF + +/* ADC Digital Filter Setting */ +#define AD1SL_SHIFT 0 +#define AD2SL_SHIFT 4 + +/* Analog Input Select */ +#define AD1LSEL_SHIFT 6 +#define AD1RSEL_SHIFT 4 +#define AD2LSEL_SHIFT 2 +#define AD2RSEL_SHIFT 0 + +/* ADC Mute & HPF Control */ +#define ATSPAD_SHIFT 7 +#define AD1MUTE_SHIFT 5 +#define AD2MUTE_SHIFT 6 +#define AD1MUTE_MAX 1 +#define AD2MUTE_MAX 1 +#define AD1MUTE_EN BIT(5) +#define AD2MUTE_EN BIT(6) +#define AD1HPFN_SHIFT 1 +#define AD1HPFN_MAX 1 +#define AD2HPFN_SHIFT 2 +#define AD2HPFN_MAX 1 + +/* DAC Digital Volume */ +#define VOLDA_SHIFT 0 +#define VOLDA_MAX 0xFF + +/* DAC Input Select Setting */ +#define DAC1SEL_SHIFT 0 +#define DAC2SEL_SHIFT 2 + +/* DAC De-Emphasis Setting */ +#define DEM1_32000 (0x3 << 0) +#define DEM1_44100 (0x0 << 0) +#define DEM1_48000 (0x2 << 0) +#define DEM1_OFF (0x1 << 0) +#define DEM2_32000 (0x3 << 2) +#define DEM2_44100 (0x0 << 2) +#define DEM2_48000 (0x2 << 2) +#define DEM2_OFF (0x1 << 2) +#define DEM1_MASK (0x3 << 0) +#define DEM2_MASK (0x3 << 2) +#define DEM1_SHIFT 0 +#define DEM2_SHIFT 2 + +/* DAC Mute & Filter Setting */ +#define DA1MUTE_SHIFT 4 +#define DA1MUTE_MAX 1 +#define DA2MUTE_SHIFT 5 +#define DA2MUTE_MAX 1 +#define DA1MUTE_EN BIT(4) +#define DA2MUTE_EN BIT(5) +#define ATSPDA_SHIFT 7 +#define DA1SL_SHIFT 0 +#define DA2SL_SHIFT 2 + +/* Codec private data */ +struct ak4619_priv { + struct regmap *regmap; + struct snd_pcm_hw_constraint_list constraint; + int deemph_en; + unsigned int playback_rate; + unsigned int sysclk; +}; + +/* + * DAC Volume + * + * max : 0x00 : +12.0 dB + * ( 0.5 dB step ) + * min : 0xFE : -115.0 dB + * mute: 0xFF + */ +static const DECLARE_TLV_DB_SCALE(dac_tlv, -11550, 50, 1); + +/* + * MIC Volume + * + * max : 0x0B : +27.0 dB + * ( 3 dB step ) + * min: 0x00 : -6.0 dB + */ +static const DECLARE_TLV_DB_SCALE(mic_tlv, -600, 300, 0); + +/* + * ADC Volume + * + * max : 0x00 : +24.0 dB + * ( 0.5 dB step ) + * min : 0xFE : -103.0 dB + * mute: 0xFF + */ +static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1); + +/* ADC & DAC Volume Level Transition Time select */ +static const char * const ak4619_vol_trans_txt[] = { + "4/fs", "16/fs" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_adc_vol_trans, ADC_MHPF, ATSPAD_SHIFT, ak4619_vol_trans_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_dac_vol_trans, DAC_MF, ATSPDA_SHIFT, ak4619_vol_trans_txt); + +/* ADC Digital Filter select */ +static const char * const ak4619_adc_digi_fil_txt[] = { + "Sharp Roll-Off Filter", + "Slow Roll-Off Filter", + "Short Delay Sharp Roll-Off Filter", + "Short Delay Slow Roll-Off Filter", + "Voice Filter" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_adc_1_digi_fil, ADC_DF, AD1SL_SHIFT, ak4619_adc_digi_fil_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_adc_2_digi_fil, ADC_DF, AD2SL_SHIFT, ak4619_adc_digi_fil_txt); + +/* DAC De-Emphasis Filter select */ +static const char * const ak4619_dac_de_emp_txt[] = { + "44.1kHz", "OFF", "48kHz", "32kHz" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_de_emp, DAC_DEMP, DEM1_SHIFT, ak4619_dac_de_emp_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_de_emp, DAC_DEMP, DEM2_SHIFT, ak4619_dac_de_emp_txt); + +/* DAC Digital Filter select */ +static const char * const ak4619_dac_digi_fil_txt[] = { + "Sharp Roll-Off Filter", + "Slow Roll-Off Filter", + "Short Delay Sharp Roll-Off Filter", + "Short Delay Slow Roll-Off Filter" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_digi_fil, DAC_MF, DA1SL_SHIFT, ak4619_dac_digi_fil_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_digi_fil, DAC_MF, DA2SL_SHIFT, ak4619_dac_digi_fil_txt); + +/* + * Control functions + */ + +static void ak4619_set_deemph(struct snd_soc_component *component) +{ + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + u8 dem = 0; + + if (!ak4619->deemph_en) + return; + + switch (ak4619->playback_rate) { + case 32000: + dem |= DEM1_32000 | DEM2_32000; + break; + case 44100: + dem |= DEM1_44100 | DEM2_44100; + break; + case 48000: + dem |= DEM1_48000 | DEM2_48000; + break; + default: + dem |= DEM1_OFF | DEM2_OFF; + break; + } + snd_soc_component_update_bits(component, DAC_DEMP, DEM1_MASK | DEM2_MASK, dem); +} + +static int ak4619_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + int deemph_en = ucontrol->value.integer.value[0]; + int ret = 0; + + switch (deemph_en) { + case 0: + case 1: + break; + default: + return -EINVAL; + } + + if (ak4619->deemph_en != deemph_en) + ret = 1; /* The value changed */ + + ak4619->deemph_en = deemph_en; + ak4619_set_deemph(component); + + return ret; +} + +static int ak4619_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = ak4619->deemph_en; + + return 0; +}; + +/* + * KControls + */ +static const struct snd_kcontrol_new ak4619_snd_controls[] = { + + /* Volume controls */ + SOC_DOUBLE_R_TLV("DAC 1 Volume", LDAC1, RDAC1, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv), + SOC_DOUBLE_R_TLV("DAC 2 Volume", LDAC2, RDAC2, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv), + SOC_DOUBLE_R_TLV("ADC 1 Volume", LADC1, RADC1, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv), + SOC_DOUBLE_R_TLV("ADC 2 Volume", LADC2, RADC2, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv), + + SOC_DOUBLE_TLV("Mic 1 Volume", MIC_AMP1, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv), + SOC_DOUBLE_TLV("Mic 2 Volume", MIC_AMP2, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv), + + /* Volume Level Transition Time controls */ + SOC_ENUM("ADC Volume Level Transition Time", ak4619_adc_vol_trans), + SOC_ENUM("DAC Volume Level Transition Time", ak4619_dac_vol_trans), + + /* Mute controls */ + SOC_SINGLE("DAC 1 Switch", DAC_MF, DA1MUTE_SHIFT, DA1MUTE_MAX, 1), + SOC_SINGLE("DAC 2 Switch", DAC_MF, DA2MUTE_SHIFT, DA2MUTE_MAX, 1), + + SOC_SINGLE("ADC 1 Switch", ADC_MHPF, AD1MUTE_SHIFT, AD1MUTE_MAX, 1), + SOC_SINGLE("ADC 2 Switch", ADC_MHPF, AD2MUTE_SHIFT, AD2MUTE_MAX, 1), + + /* Filter controls */ + SOC_ENUM("ADC 1 Digital Filter", ak4619_adc_1_digi_fil), + SOC_ENUM("ADC 2 Digital Filter", ak4619_adc_2_digi_fil), + + SOC_SINGLE("ADC 1 HPF", ADC_MHPF, AD1HPFN_SHIFT, AD1HPFN_MAX, 1), + SOC_SINGLE("ADC 2 HPF", ADC_MHPF, AD2HPFN_SHIFT, AD2HPFN_MAX, 1), + + SOC_ENUM("DAC 1 De-Emphasis Filter", ak4619_dac_1_de_emp), + SOC_ENUM("DAC 2 De-Emphasis Filter", ak4619_dac_2_de_emp), + + SOC_ENUM("DAC 1 Digital Filter", ak4619_dac_1_digi_fil), + SOC_ENUM("DAC 2 Digital Filter", ak4619_dac_2_digi_fil), + + SOC_SINGLE_BOOL_EXT("Playback De-Emphasis Switch", 0, ak4619_get_deemph, ak4619_put_deemph), +}; + +/* + * DAPM + */ + +/* Analog input mode */ +static const char * const ak4619_analog_in_txt[] = { + "Differential", "Single-Ended1", "Single-Ended2", "Pseudo Differential" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_left_in, ADC_AI, AD1LSEL_SHIFT, ak4619_analog_in_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_right_in, ADC_AI, AD1RSEL_SHIFT, ak4619_analog_in_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_left_in, ADC_AI, AD2LSEL_SHIFT, ak4619_analog_in_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_right_in, ADC_AI, AD2RSEL_SHIFT, ak4619_analog_in_txt); + +static const struct snd_kcontrol_new ak4619_ad_1_left_in_mux = + SOC_DAPM_ENUM("Analog Input 1 Left MUX", ak4619_ad_1_left_in); +static const struct snd_kcontrol_new ak4619_ad_1_right_in_mux = + SOC_DAPM_ENUM("Analog Input 1 Right MUX", ak4619_ad_1_right_in); +static const struct snd_kcontrol_new ak4619_ad_2_left_in_mux = + SOC_DAPM_ENUM("Analog Input 2 Left MUX", ak4619_ad_2_left_in); +static const struct snd_kcontrol_new ak4619_ad_2_right_in_mux = + SOC_DAPM_ENUM("Analog Input 2 Right MUX", ak4619_ad_2_right_in); + +/* DAC source mux */ +static const char * const ak4619_dac_in_txt[] = { + "SDIN1", "SDIN2", "SDOUT1", "SDOUT2" +}; + +static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_in, DAC_IS, DAC1SEL_SHIFT, ak4619_dac_in_txt); +static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_in, DAC_IS, DAC2SEL_SHIFT, ak4619_dac_in_txt); + +static const struct snd_kcontrol_new ak4619_dac_1_in_mux = + SOC_DAPM_ENUM("DAC 1 Source MUX", ak4619_dac_1_in); +static const struct snd_kcontrol_new ak4619_dac_2_in_mux = + SOC_DAPM_ENUM("DAC 2 Source MUX", ak4619_dac_2_in); + +static const struct snd_soc_dapm_widget ak4619_dapm_widgets[] = { + + /* DACs */ + SND_SOC_DAPM_DAC("DAC1", NULL, PWR_MGMT, 1, 0), + SND_SOC_DAPM_DAC("DAC2", NULL, PWR_MGMT, 2, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1", NULL, PWR_MGMT, 4, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, PWR_MGMT, 5, 0), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("AOUT1L"), + SND_SOC_DAPM_OUTPUT("AOUT2L"), + + SND_SOC_DAPM_OUTPUT("AOUT1R"), + SND_SOC_DAPM_OUTPUT("AOUT2R"), + + /* Inputs */ + SND_SOC_DAPM_INPUT("AIN1L"), + SND_SOC_DAPM_INPUT("AIN2L"), + SND_SOC_DAPM_INPUT("AIN4L"), + SND_SOC_DAPM_INPUT("AIN5L"), + + SND_SOC_DAPM_INPUT("AIN1R"), + SND_SOC_DAPM_INPUT("AIN2R"), + SND_SOC_DAPM_INPUT("AIN4R"), + SND_SOC_DAPM_INPUT("AIN5R"), + + SND_SOC_DAPM_INPUT("MIC1L"), + SND_SOC_DAPM_INPUT("MIC1R"), + SND_SOC_DAPM_INPUT("MIC2L"), + SND_SOC_DAPM_INPUT("MIC2R"), + + /* DAI */ + SND_SOC_DAPM_AIF_IN("SDIN1", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDIN2", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SDOUT1", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SDOUT2", "Capture", 0, SND_SOC_NOPM, 0, 0), + + /* MUXs for Mic PGA source selection */ + SND_SOC_DAPM_MUX("Analog Input 1 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_left_in_mux), + SND_SOC_DAPM_MUX("Analog Input 1 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_right_in_mux), + SND_SOC_DAPM_MUX("Analog Input 2 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_left_in_mux), + SND_SOC_DAPM_MUX("Analog Input 2 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_right_in_mux), + + /* MUXs for DAC source selection */ + SND_SOC_DAPM_MUX("DAC 1 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_1_in_mux), + SND_SOC_DAPM_MUX("DAC 2 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_2_in_mux), +}; + +static const struct snd_soc_dapm_route ak4619_intercon[] = { + /* Dest Connecting Widget Source */ + + /* Output path */ + {"AOUT1L", NULL, "DAC1"}, + {"AOUT2L", NULL, "DAC2"}, + + {"AOUT1R", NULL, "DAC1"}, + {"AOUT2R", NULL, "DAC2"}, + + {"DAC1", NULL, "DAC 1 Source MUX"}, + {"DAC2", NULL, "DAC 2 Source MUX"}, + + {"DAC 1 Source MUX", "SDIN1", "SDIN1"}, + {"DAC 1 Source MUX", "SDIN2", "SDIN2"}, + {"DAC 1 Source MUX", "SDOUT1", "SDOUT1"}, + {"DAC 1 Source MUX", "SDOUT2", "SDOUT2"}, + + {"DAC 2 Source MUX", "SDIN1", "SDIN1"}, + {"DAC 2 Source MUX", "SDIN2", "SDIN2"}, + {"DAC 2 Source MUX", "SDOUT1", "SDOUT1"}, + {"DAC 2 Source MUX", "SDOUT2", "SDOUT2"}, + + /* Input path */ + {"SDOUT1", NULL, "ADC1"}, + {"SDOUT2", NULL, "ADC2"}, + + {"ADC1", NULL, "Analog Input 1 Left MUX"}, + {"ADC1", NULL, "Analog Input 1 Right MUX"}, + + {"ADC2", NULL, "Analog Input 2 Left MUX"}, + {"ADC2", NULL, "Analog Input 2 Right MUX"}, + + {"Analog Input 1 Left MUX", "Differential", "MIC1L"}, + {"Analog Input 1 Left MUX", "Single-Ended1", "MIC1L"}, + {"Analog Input 1 Left MUX", "Single-Ended2", "MIC1L"}, + {"Analog Input 1 Left MUX", "Pseudo Differential", "MIC1L"}, + + {"Analog Input 1 Right MUX", "Differential", "MIC1R"}, + {"Analog Input 1 Right MUX", "Single-Ended1", "MIC1R"}, + {"Analog Input 1 Right MUX", "Single-Ended2", "MIC1R"}, + {"Analog Input 1 Right MUX", "Pseudo Differential", "MIC1R"}, + + {"Analog Input 2 Left MUX", "Differential", "MIC2L"}, + {"Analog Input 2 Left MUX", "Single-Ended1", "MIC2L"}, + {"Analog Input 2 Left MUX", "Single-Ended2", "MIC2L"}, + {"Analog Input 2 Left MUX", "Pseudo Differential", "MIC2L"}, + + {"Analog Input 2 Right MUX", "Differential", "MIC2R"}, + {"Analog Input 2 Right MUX", "Single-Ended1", "MIC2R"}, + {"Analog Input 2 Right MUX", "Single-Ended2", "MIC2R"}, + {"Analog Input 2 Right MUX", "Pseudo Differential", "MIC2R"}, + + {"MIC1L", NULL, "AIN1L"}, + {"MIC1L", NULL, "AIN2L"}, + + {"MIC1R", NULL, "AIN1R"}, + {"MIC1R", NULL, "AIN2R"}, + + {"MIC2L", NULL, "AIN4L"}, + {"MIC2L", NULL, "AIN5L"}, + + {"MIC2R", NULL, "AIN4R"}, + {"MIC2R", NULL, "AIN5R"}, +}; + +static const struct reg_default ak4619_reg_defaults[] = { + { PWR_MGMT, 0x00 }, + { AU_IFF1, 0x0C }, + { AU_IFF2, 0x0C }, + { SYS_CLK, 0x00 }, + { MIC_AMP1, 0x22 }, + { MIC_AMP2, 0x22 }, + { LADC1, 0x30 }, + { RADC1, 0x30 }, + { LADC2, 0x30 }, + { RADC2, 0x30 }, + { ADC_DF, 0x00 }, + { ADC_AI, 0x00 }, + { ADC_MHPF, 0x00 }, + { LDAC1, 0x18 }, + { RDAC1, 0x18 }, + { LDAC2, 0x18 }, + { RDAC2, 0x18 }, + { DAC_IS, 0x04 }, + { DAC_DEMP, 0x05 }, + { DAC_MF, 0x0A }, +}; + +static int ak4619_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + u8 pwr_ctrl = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + pwr_ctrl |= RSTN; + fallthrough; + case SND_SOC_BIAS_PREPARE: + pwr_ctrl |= PMAD1 | PMAD2 | PMDA1 | PMDA2; + fallthrough; + case SND_SOC_BIAS_STANDBY: + case SND_SOC_BIAS_OFF: + default: + break; + } + + snd_soc_component_write(component, PWR_MGMT, pwr_ctrl); + + return 0; +} + +static int ak4619_dai_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 ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + unsigned int width; + unsigned int rate; + unsigned int fs; + bool is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u8 dai_ctrl = 0; + u8 clk_mode = 0; + + width = params_width(params); + switch (width) { + case 16: + dai_ctrl |= is_play ? DIDL_16 : DODL_16; + break; + case 20: + dai_ctrl |= is_play ? DIDL_20 : DODL_20; + break; + case 24: + dai_ctrl |= is_play ? DIDL_24 : DODL_24; + break; + case 32: + if (is_play) + dai_ctrl |= DIDL_32; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + rate = params_rate(params); + if (rate) + fs = ak4619->sysclk / rate; + else + return -EINVAL; + + switch (rate) { + case 8000: + case 11025: + case 12000: + case 16000: + case 22050: + case 24000: + case 32000: + case 44100: + case 48000: + switch (fs) { + case 256: + clk_mode |= (0x0 << 0); + break; + case 384: + clk_mode |= (0x2 << 0); + break; + case 512: + clk_mode |= (0x3 << 0); + break; + default: + return -EINVAL; + } + break; + case 64000: + case 88200: + case 96000: + if (fs == 256) + clk_mode |= (0x1 << 0); + else + return -EINVAL; + break; + case 176400: + case 192000: + if (fs == 128) + clk_mode |= (0x4 << 0); + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, SYS_CLK, FS_MASK, clk_mode); + snd_soc_component_update_bits(component, AU_IFF2, + is_play ? DIDL_MASK : DODL_MASK, dai_ctrl); + + if (is_play) { + ak4619->playback_rate = rate; + ak4619_set_deemph(component); + } + + return 0; +} + +static int ak4619_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u8 dai_fmt1 = 0; + u8 dai_fmt2 = 0; + + /* Set clock normal/inverted */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + dai_fmt1 |= BCKP; + break; + case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_IF: + default: + return -EINVAL; + } + + /* Only Stereo modes are supported */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dai_fmt1 |= DCF_STEREO_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + dai_fmt1 |= DCF_STEREO_MSB; + break; + case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */ + dai_fmt1 |= DCF_PCM_SF; + dai_fmt2 |= SLOT; + break; + case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */ + dai_fmt1 |= DCF_PCM_LF; + dai_fmt2 |= SLOT; + break; + default: + return -EINVAL; + } + + /* Only slave mode is support */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + /* By default only 64 BICK per LRCLK is supported */ + dai_fmt1 |= DSL_32; + + snd_soc_component_update_bits(component, AU_IFF1, DCF_MASK | + DSL_MASK | BCKP, dai_fmt1); + snd_soc_component_update_bits(component, AU_IFF2, SLOT, dai_fmt2); + + return 0; +} + +static int ak4619_dai_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + + ak4619->sysclk = freq; + + return 0; +} + +static int ak4619_dai_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + snd_soc_component_update_bits(component, DAC_MF, DA1MUTE_EN, mute ? DA1MUTE_EN : 0); + snd_soc_component_update_bits(component, DAC_MF, DA2MUTE_EN, mute ? DA2MUTE_EN : 0); + + return 0; +} + +static void ak4619_hw_constraints(struct ak4619_priv *ak4619, + struct snd_pcm_runtime *runtime) +{ + struct snd_pcm_hw_constraint_list *constraint = &ak4619->constraint; + int ak4619_rate_mask = 0; + unsigned int fs; + int i; + static const unsigned int ak4619_sr[] = { + 8000, + 11025, + 12000, + 16000, + 22050, + 24000, + 32000, + 44100, + 48000, + 64000, + 88200, + 96000, + 176400, + 192000, + }; + + /* + * [8kHz - 48kHz] : 256fs, 384fs or 512fs + * [64kHz - 96kHz] : 256fs + * [176.4kHz, 192kHz] : 128fs + */ + + for (i = 0; i < ARRAY_SIZE(ak4619_sr); i++) { + fs = ak4619->sysclk / ak4619_sr[i]; + + switch (fs) { + case 512: + case 384: + case 256: + ak4619_rate_mask |= (1 << i); + break; + case 128: + switch (i) { + case (ARRAY_SIZE(ak4619_sr) - 1): + case (ARRAY_SIZE(ak4619_sr) - 2): + ak4619_rate_mask |= (1 << i); + break; + default: + break; + } + break; + default: + break; + } + }; + + constraint->list = ak4619_sr; + constraint->mask = ak4619_rate_mask; + constraint->count = ARRAY_SIZE(ak4619_sr); + + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, constraint); +}; + +#define PLAYBACK_MODE 0 +#define CAPTURE_MODE 1 + +static int ak4619_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component); + + ak4619_hw_constraints(ak4619, substream->runtime); + + return 0; +} + +static u64 ak4619_dai_formats[] = { + /* + * Select below from Sound Card, not here + * SND_SOC_DAIFMT_CBC_CFC + * SND_SOC_DAIFMT_CBP_CFP + */ + + /* First Priority */ + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J, + + /* Second Priority */ + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops ak4619_dai_ops = { + .startup = ak4619_dai_startup, + .set_sysclk = ak4619_dai_set_sysclk, + .set_fmt = ak4619_dai_set_fmt, + .hw_params = ak4619_dai_hw_params, + .mute_stream = ak4619_dai_mute, + .auto_selectable_formats = ak4619_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(ak4619_dai_formats), +}; + +static const struct snd_soc_component_driver soc_component_dev_ak4619 = { + .set_bias_level = ak4619_set_bias_level, + .controls = ak4619_snd_controls, + .num_controls = ARRAY_SIZE(ak4619_snd_controls), + .dapm_widgets = ak4619_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4619_dapm_widgets), + .dapm_routes = ak4619_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4619_intercon), + .idle_bias_on = 1, + .endianness = 1, +}; + +static const struct regmap_config ak4619_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x14, + .reg_defaults = ak4619_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4619_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +static const struct of_device_id ak4619_of_match[] = { + { .compatible = "asahi-kasei,ak4619", .data = &ak4619_regmap_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4619_of_match); + +static const struct i2c_device_id ak4619_i2c_id[] = { + { "ak4619", (kernel_ulong_t)&ak4619_regmap_cfg }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4619_i2c_id); + +#define AK4619_RATES SNDRV_PCM_RATE_8000_192000 + +#define AK4619_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +#define AK4619_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver ak4619_dai = { + .name = "ak4619-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AK4619_RATES, + .formats = AK4619_DAC_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AK4619_RATES, + .formats = AK4619_ADC_FORMATS, + }, + .ops = &ak4619_dai_ops, + .symmetric_rate = 1, +}; + +static int ak4619_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct ak4619_priv *ak4619; + int ret; + + ak4619 = devm_kzalloc(dev, sizeof(*ak4619), GFP_KERNEL); + if (!ak4619) + return -ENOMEM; + + i2c_set_clientdata(i2c, ak4619); + + ak4619->regmap = devm_regmap_init_i2c(i2c, &ak4619_regmap_cfg); + if (IS_ERR(ak4619->regmap)) { + ret = PTR_ERR(ak4619->regmap); + dev_err(dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_ak4619, + &ak4619_dai, 1); + if (ret < 0) { + dev_err(dev, "Failed to register ak4619 component: %d\n", + ret); + return ret; + } + + return 0; +} + +static struct i2c_driver ak4619_i2c_driver = { + .driver = { + .name = "ak4619-codec", + .of_match_table = ak4619_of_match, + }, + .probe = ak4619_i2c_probe, + .id_table = ak4619_i2c_id, +}; +module_i2c_driver(ak4619_i2c_driver); + +MODULE_DESCRIPTION("SoC AK4619 driver"); +MODULE_AUTHOR("Khanh Le <khanh.le.xr@renesas.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index e89027cd40d1..880228f89baf 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -215,6 +215,10 @@ static const struct reg_sequence cs35l56_asp1_defaults[] = { REG_SEQ0(CS35L56_ASP1_FRAME_CONTROL5, 0x00020100), REG_SEQ0(CS35L56_ASP1_DATA_CONTROL1, 0x00000018), REG_SEQ0(CS35L56_ASP1_DATA_CONTROL5, 0x00000018), + REG_SEQ0(CS35L56_ASP1TX1_INPUT, 0x00000000), + REG_SEQ0(CS35L56_ASP1TX2_INPUT, 0x00000000), + REG_SEQ0(CS35L56_ASP1TX3_INPUT, 0x00000000), + REG_SEQ0(CS35L56_ASP1TX4_INPUT, 0x00000000), }; /* @@ -393,7 +397,7 @@ int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) { int ret; - if (!irq) + if (irq < 1) return 0; ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 901b9dbcf585..d9ab003e166b 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -121,7 +121,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, priv->buttons[3] = 735; } - ret = cs42l43_find_index(priv, "cirrus,detect-us", 1000, &priv->detect_us, + ret = cs42l43_find_index(priv, "cirrus,detect-us", 50000, &priv->detect_us, cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us)); if (ret < 0) goto error; @@ -433,7 +433,7 @@ irqreturn_t cs42l43_button_press(int irq, void *data) // Wait for 2 full cycles of comb filter to ensure good reading queue_delayed_work(system_wq, &priv->button_press_work, - msecs_to_jiffies(10)); + msecs_to_jiffies(20)); return IRQ_HANDLED; } diff --git a/sound/soc/codecs/cs530x-i2c.c b/sound/soc/codecs/cs530x-i2c.c new file mode 100644 index 000000000000..56659bf735db --- /dev/null +++ b/sound/soc/codecs/cs530x-i2c.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS530x CODEC driver +// +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include "cs530x.h" + +static const struct of_device_id cs530x_of_match[] = { + { + .compatible = "cirrus,cs5302", + .data = (void *)CS5302, + }, { + .compatible = "cirrus,cs5304", + .data = (void *)CS5304, + }, { + .compatible = "cirrus,cs5308", + .data = (void *)CS5308, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cs530x_of_match); + +static const struct i2c_device_id cs530x_i2c_id[] = { + { "cs5302", CS5302 }, + { "cs5304", CS5304 }, + { "cs5308", CS5308 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs530x_i2c_id); + +static int cs530x_i2c_probe(struct i2c_client *client) +{ + struct cs530x_priv *cs530x; + + cs530x = devm_kzalloc(&client->dev, sizeof(*cs530x), GFP_KERNEL); + if (!cs530x) + return -ENOMEM; + + i2c_set_clientdata(client, cs530x); + + cs530x->regmap = devm_regmap_init_i2c(client, &cs530x_regmap); + if (IS_ERR(cs530x->regmap)) + return dev_err_probe(&client->dev, PTR_ERR(cs530x->regmap), + "Failed to allocate register map\n"); + + cs530x->devtype = (uintptr_t)i2c_get_match_data(client); + cs530x->dev = &client->dev; + + return cs530x_probe(cs530x); +} + +static struct i2c_driver cs530x_i2c_driver = { + .driver = { + .name = "cs530x", + .of_match_table = cs530x_of_match, + }, + .probe = cs530x_i2c_probe, + .id_table = cs530x_i2c_id, +}; +module_i2c_driver(cs530x_i2c_driver); + +MODULE_DESCRIPTION("I2C CS530X driver"); +MODULE_IMPORT_NS(SND_SOC_CS530X); +MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paulha@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c new file mode 100644 index 000000000000..036d0f45e3ba --- /dev/null +++ b/sound/soc/codecs/cs530x.c @@ -0,0 +1,966 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS530x CODEC driver +// +// Copyright (C) 2024 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <sound/core.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <sound/initval.h> +#include <linux/module.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <linux/pm.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "cs530x.h" + +#define CS530X_MAX_ADC_CH 8 +#define CS530X_MIN_ADC_CH 2 + +static const char *cs530x_supply_names[CS530X_NUM_SUPPLIES] = { + "vdd-a", + "vdd-io", +}; + +static const struct reg_default cs530x_reg_defaults[] = { + { CS530X_CLK_CFG_0, 0x30 }, + { CS530X_CLK_CFG_1, 0x0001 }, + { CS530X_CHIP_ENABLE, 0 }, + { CS530X_ASP_CFG, 0 }, + { CS530X_SIGNAL_PATH_CFG, 0 }, + { CS530X_IN_ENABLES, 0 }, + { CS530X_IN_RAMP_SUM, 0x0022 }, + { CS530X_IN_FILTER, 0 }, + { CS530X_IN_HIZ, 0 }, + { CS530X_IN_INV, 0 }, + { CS530X_IN_VOL_CTRL1_0, 0x8000 }, + { CS530X_IN_VOL_CTRL1_1, 0x8000 }, + { CS530X_IN_VOL_CTRL2_0, 0x8000 }, + { CS530X_IN_VOL_CTRL2_1, 0x8000 }, + { CS530X_IN_VOL_CTRL3_0, 0x8000 }, + { CS530X_IN_VOL_CTRL3_1, 0x8000 }, + { CS530X_IN_VOL_CTRL4_0, 0x8000 }, + { CS530X_IN_VOL_CTRL4_1, 0x8000 }, + { CS530X_PAD_FN, 0 }, + { CS530X_PAD_LVL, 0 }, +}; + +static bool cs530x_read_and_write_regs(unsigned int reg) +{ + switch (reg) { + case CS530X_CLK_CFG_0: + case CS530X_CLK_CFG_1: + case CS530X_CHIP_ENABLE: + case CS530X_ASP_CFG: + case CS530X_SIGNAL_PATH_CFG: + case CS530X_IN_ENABLES: + case CS530X_IN_RAMP_SUM: + case CS530X_IN_FILTER: + case CS530X_IN_HIZ: + case CS530X_IN_INV: + case CS530X_IN_VOL_CTRL1_0: + case CS530X_IN_VOL_CTRL1_1: + case CS530X_IN_VOL_CTRL2_0: + case CS530X_IN_VOL_CTRL2_1: + case CS530X_IN_VOL_CTRL3_0: + case CS530X_IN_VOL_CTRL3_1: + case CS530X_IN_VOL_CTRL4_0: + case CS530X_IN_VOL_CTRL4_1: + case CS530X_PAD_FN: + case CS530X_PAD_LVL: + return true; + default: + return false; + } +} + +static bool cs530x_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS530X_DEVID: + case CS530X_REVID: + return true; + default: + return cs530x_read_and_write_regs(reg); + } +} + +static bool cs530x_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS530X_SW_RESET: + case CS530X_IN_VOL_CTRL5: + return true; + default: + return cs530x_read_and_write_regs(reg); + } +} + +static int cs530x_put_volsw_vu(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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + int ret; + + snd_soc_dapm_mutex_lock(dapm); + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret) + goto volsw_err; + + /* Write IN_VU bit for the volume change to take effect */ + regmap_write(regmap, CS530X_IN_VOL_CTRL5, CS530X_IN_VU); + +volsw_err: + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1270, 50, 0); + +static const char * const cs530x_in_hpf_text[] = { + "Min Phase Slow Roll-off", + "Min Phase Fast Roll-off", + "Linear Phase Slow Roll-off", + "Linear Phase Fast Roll-off", +}; + +static SOC_ENUM_SINGLE_DECL(cs530x_in_hpf_enum, CS530X_IN_FILTER, + CS530X_IN_FILTER_SHIFT, + cs530x_in_hpf_text); + +static const char * const cs530x_in_4ch_sum_text[] = { + "None", + "Groups of 2", + "Groups of 4", +}; + +static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch4_enum, CS530X_IN_RAMP_SUM, + CS530X_IN_SUM_MODE_SHIFT, + cs530x_in_4ch_sum_text); + +static const struct snd_kcontrol_new cs530x_in_sum_4ch_controls[] = { +SOC_ENUM("IN Sum Select", cs530x_in_sum_ch4_enum), +}; + +static const char * const cs530x_in_8ch_sum_text[] = { + "None", + "Groups of 2", + "Groups of 4", + "Groups of 8", +}; + +static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch8_enum, CS530X_IN_RAMP_SUM, + CS530X_IN_SUM_MODE_SHIFT, + cs530x_in_8ch_sum_text); + +static const struct snd_kcontrol_new cs530x_in_sum_8ch_controls[] = { +SOC_ENUM("IN Sum Select", cs530x_in_sum_ch8_enum), +}; + + +static const char * const cs530x_vol_ramp_text[] = { + "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", + "15ms/6dB", "30ms/6dB", +}; + +static SOC_ENUM_SINGLE_DECL(cs530x_ramp_inc_enum, CS530X_IN_RAMP_SUM, + CS530X_RAMP_RATE_INC_SHIFT, + cs530x_vol_ramp_text); + +static SOC_ENUM_SINGLE_DECL(cs530x_ramp_dec_enum, CS530X_IN_RAMP_SUM, + CS530X_RAMP_RATE_DEC_SHIFT, + cs530x_vol_ramp_text); + +static const struct snd_kcontrol_new cs530x_in_1_to_2_controls[] = { +SOC_SINGLE_EXT_TLV("IN1 Volume", CS530X_IN_VOL_CTRL1_0, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN2 Volume", CS530X_IN_VOL_CTRL1_1, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), + +SOC_ENUM("IN HPF Select", cs530x_in_hpf_enum), +SOC_ENUM("Input Ramp Up", cs530x_ramp_inc_enum), +SOC_ENUM("Input Ramp Down", cs530x_ramp_dec_enum), + +SOC_SINGLE("ADC1 Invert Switch", CS530X_IN_INV, CS530X_IN1_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC2 Invert Switch", CS530X_IN_INV, CS530X_IN2_INV_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new cs530x_in_3_to_4_controls[] = { +SOC_SINGLE_EXT_TLV("IN3 Volume", CS530X_IN_VOL_CTRL2_0, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN4 Volume", CS530X_IN_VOL_CTRL2_1, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), + +SOC_SINGLE("ADC3 Invert Switch", CS530X_IN_INV, CS530X_IN3_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC4 Invert Switch", CS530X_IN_INV, CS530X_IN4_INV_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new cs530x_in_5_to_8_controls[] = { +SOC_SINGLE_EXT_TLV("IN5 Volume", CS530X_IN_VOL_CTRL3_0, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN6 Volume", CS530X_IN_VOL_CTRL3_1, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN7 Volume", CS530X_IN_VOL_CTRL4_0, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), +SOC_SINGLE_EXT_TLV("IN8 Volume", CS530X_IN_VOL_CTRL4_1, 0, 255, 1, + snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv), + +SOC_SINGLE("ADC5 Invert Switch", CS530X_IN_INV, CS530X_IN5_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC6 Invert Switch", CS530X_IN_INV, CS530X_IN6_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC7 Invert Switch", CS530X_IN_INV, CS530X_IN7_INV_SHIFT, 1, 0), +SOC_SINGLE("ADC8 Invert Switch", CS530X_IN_INV, CS530X_IN8_INV_SHIFT, 1, 0), +}; + +static int cs530x_adc_event(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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + cs530x->adc_pairs_count++; + break; + case SND_SOC_DAPM_POST_PMU: + regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 + + (w->shift * 2), CS530X_IN_MUTE); + regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 + + ((w->shift+1) * 2), CS530X_IN_MUTE); + + cs530x->adc_pairs_count--; + if (!cs530x->adc_pairs_count) { + usleep_range(1000, 1100); + return regmap_write(regmap, CS530X_IN_VOL_CTRL5, + CS530X_IN_VU); + } + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 + + (w->shift * 2), CS530X_IN_MUTE); + regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 + + ((w->shift+1) * 2), CS530X_IN_MUTE); + return regmap_write(regmap, CS530X_IN_VOL_CTRL5, + CS530X_IN_VU); + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_kcontrol_new adc12_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new adc34_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new adc56_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new adc78_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new in_hpf_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +/* General DAPM widgets for all devices */ +static const struct snd_soc_dapm_widget cs530x_gen_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("Global Enable", CS530X_CHIP_ENABLE, 0, 0, NULL, 0), +}; + +/* ADC's Channels 1 and 2 plus generic ADC DAPM events */ +static const struct snd_soc_dapm_widget cs530x_adc_ch12_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN1"), +SND_SOC_DAPM_INPUT("IN2"), +SND_SOC_DAPM_ADC_E("ADC1", NULL, CS530X_IN_ENABLES, 0, 0, + cs530x_adc_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_ADC("ADC2", NULL, CS530X_IN_ENABLES, 1, 0), +SND_SOC_DAPM_SWITCH("ADC12 Enable", SND_SOC_NOPM, 0, 0, &adc12_ctrl), +SND_SOC_DAPM_SWITCH("IN HPF", CS530X_IN_FILTER, CS530X_IN_HPF_EN_SHIFT, + 0, &in_hpf_ctrl), +}; + +/* ADC's Channels 3 and 4 */ +static const struct snd_soc_dapm_widget cs530x_adc_ch34_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN3"), +SND_SOC_DAPM_INPUT("IN4"), +SND_SOC_DAPM_ADC_E("ADC3", NULL, CS530X_IN_ENABLES, 2, 0, + cs530x_adc_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_ADC("ADC4", NULL, CS530X_IN_ENABLES, 3, 0), +SND_SOC_DAPM_SWITCH("ADC34 Enable", SND_SOC_NOPM, 0, 0, &adc34_ctrl), +}; + +/* ADC's Channels 5 to 8 */ +static const struct snd_soc_dapm_widget cs530x_adc_ch58_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN5"), +SND_SOC_DAPM_INPUT("IN6"), +SND_SOC_DAPM_INPUT("IN7"), +SND_SOC_DAPM_INPUT("IN8"), +SND_SOC_DAPM_ADC_E("ADC5", NULL, CS530X_IN_ENABLES, 4, 0, + cs530x_adc_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_ADC("ADC6", NULL, CS530X_IN_ENABLES, 5, 0), +SND_SOC_DAPM_SWITCH("ADC56 Enable", SND_SOC_NOPM, 0, 0, &adc56_ctrl), +SND_SOC_DAPM_ADC_E("ADC7", NULL, CS530X_IN_ENABLES, 6, 0, + cs530x_adc_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_ADC("ADC8", NULL, CS530X_IN_ENABLES, 7, 0), +SND_SOC_DAPM_SWITCH("ADC78 Enable", SND_SOC_NOPM, 0, 0, &adc78_ctrl), +}; + +static const struct snd_soc_dapm_route adc_ch1_2_routes[] = { + { "ADC1", NULL, "Global Enable" }, + { "ADC2", NULL, "Global Enable" }, + + { "ADC12 Enable", "Switch", "IN1" }, + { "ADC12 Enable", "Switch", "IN2" }, + { "ADC1", NULL, "ADC12 Enable" }, + { "ADC2", NULL, "ADC12 Enable" }, + { "IN HPF", "Switch", "ADC1" }, + { "IN HPF", "Switch", "ADC2" }, + + { "AIF Capture", NULL, "IN HPF" }, + { "AIF Capture", NULL, "ADC1" }, + { "AIF Capture", NULL, "ADC2" }, +}; + +static const struct snd_soc_dapm_route adc_ch3_4_routes[] = { + { "ADC3", NULL, "Global Enable" }, + { "ADC4", NULL, "Global Enable" }, + + { "ADC34 Enable", "Switch", "IN3" }, + { "ADC34 Enable", "Switch", "IN4" }, + { "ADC3", NULL, "ADC34 Enable" }, + { "ADC4", NULL, "ADC34 Enable" }, + { "IN HPF", "Switch", "ADC3" }, + { "IN HPF", "Switch", "ADC4" }, + + { "AIF Capture", NULL, "ADC3" }, + { "AIF Capture", NULL, "ADC4" }, +}; + +static const struct snd_soc_dapm_route adc_ch5_8_routes[] = { + { "ADC5", NULL, "Global Enable" }, + { "ADC6", NULL, "Global Enable" }, + { "ADC7", NULL, "Global Enable" }, + { "ADC8", NULL, "Global Enable" }, + + { "ADC56 Enable", "Switch", "IN5" }, + { "ADC56 Enable", "Switch", "IN6" }, + { "ADC5", NULL, "ADC56 Enable" }, + { "ADC6", NULL, "ADC56 Enable" }, + { "IN HPF", "Switch", "ADC5" }, + { "IN HPF", "Switch", "ADC6" }, + + { "AIF Capture", NULL, "ADC5" }, + { "AIF Capture", NULL, "ADC6" }, + + { "ADC78 Enable", "Switch", "IN7" }, + { "ADC78 Enable", "Switch", "IN8" }, + { "ADC7", NULL, "ADC78 Enable" }, + { "ADC8", NULL, "ADC78 Enable" }, + { "IN HPF", "Switch", "ADC7" }, + { "IN HPF", "Switch", "ADC8" }, + + { "AIF Capture", NULL, "ADC7" }, + { "AIF Capture", NULL, "ADC8" }, +}; + +static void cs530x_add_12_adc_widgets(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_add_component_controls(component, + cs530x_in_1_to_2_controls, + ARRAY_SIZE(cs530x_in_1_to_2_controls)); + + snd_soc_dapm_new_controls(dapm, cs530x_adc_ch12_dapm_widgets, + ARRAY_SIZE(cs530x_adc_ch12_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, adc_ch1_2_routes, + ARRAY_SIZE(adc_ch1_2_routes)); +} + +static void cs530x_add_34_adc_widgets(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_add_component_controls(component, + cs530x_in_3_to_4_controls, + ARRAY_SIZE(cs530x_in_3_to_4_controls)); + + snd_soc_dapm_new_controls(dapm, cs530x_adc_ch34_dapm_widgets, + ARRAY_SIZE(cs530x_adc_ch34_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, adc_ch3_4_routes, + ARRAY_SIZE(adc_ch3_4_routes)); +} + +static int cs530x_set_bclk(struct snd_soc_component *component) +{ + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + unsigned int bclk_val; + + switch (cs530x->bclk) { + case 2822400: + case 3072000: + bclk_val = CS530X_BCLK_2P822_3P072; + break; + case 5644800: + case 6144000: + bclk_val = CS530X_BCLK_5P6448_6P144; + break; + case 11289600: + case 12288000: + bclk_val = CS530X_BCLK_11P2896_12P288; + break; + case 22579200: + case 24576000: + bclk_val = CS530X_BCLK_24P5792_24P576; + break; + default: + dev_err(component->dev, "Invalid BCLK %d\n", cs530x->bclk); + return -EINVAL; + } + + dev_dbg(component->dev, "BCLK is %d\n", cs530x->bclk); + + return regmap_update_bits(regmap, CS530X_ASP_CFG, + CS530X_ASP_BCLK_FREQ_MASK, bclk_val); +} + +static int cs530x_set_pll_refclk(struct snd_soc_component *component, + const unsigned int freq) +{ + struct cs530x_priv *priv = snd_soc_component_get_drvdata(component); + struct regmap *regmap = priv->regmap; + unsigned int refclk; + + switch (freq) { + case 2822400: + case 3072000: + refclk = CS530X_REFCLK_2P822_3P072; + break; + case 5644800: + case 6144000: + refclk = CS530X_REFCLK_5P6448_6P144; + break; + case 11289600: + case 12288000: + refclk = CS530X_REFCLK_11P2896_12P288; + break; + case 22579200: + case 24576000: + refclk = CS530X_REFCLK_24P5792_24P576; + break; + default: + dev_err(component->dev, "Invalid PLL refclk %d\n", freq); + return -EINVAL; + } + + return regmap_update_bits(regmap, CS530X_CLK_CFG_0, + CS530X_PLL_REFCLK_FREQ_MASK, refclk); +} + +static int cs530x_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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + int ret = 0, fs = params_rate(params); + unsigned int fs_val; + + + switch (fs) { + case 32000: + fs_val = CS530X_FS_32K; + break; + case 44100: + case 48000: + fs_val = CS530X_FS_48K_44P1K; + break; + case 88200: + case 96000: + fs_val = CS530X_FS_96K_88P2K; + break; + case 176400: + case 192000: + fs_val = CS530X_FS_192K_176P4K; + break; + case 356800: + case 384000: + fs_val = CS530X_FS_384K_356P8K; + break; + case 705600: + case 768000: + fs_val = CS530X_FS_768K_705P6K; + break; + default: + dev_err(component->dev, "Invalid sample rate %d\n", fs); + return -EINVAL; + } + + cs530x->fs = fs; + regmap_update_bits(regmap, CS530X_CLK_CFG_1, + CS530X_SAMPLE_RATE_MASK, fs_val); + + if (cs530x->tdm_slots) { + dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n", + cs530x->tdm_slots, cs530x->tdm_width); + cs530x->bclk = snd_soc_calc_bclk(cs530x->fs, + cs530x->tdm_width, + params_channels(params), + cs530x->tdm_slots); + } else { + cs530x->bclk = snd_soc_params_to_bclk(params); + } + + if (!regmap_test_bits(regmap, CS530X_CLK_CFG_0, + CS530X_PLL_REFCLK_SRC_MASK)) { + ret = cs530x_set_pll_refclk(component, cs530x->bclk); + if (ret) + return ret; + } + + return cs530x_set_bclk(component); +} + +static int cs530x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct cs530x_priv *priv = snd_soc_component_get_drvdata(component); + struct regmap *regmap = priv->regmap; + unsigned int asp_fmt, asp_cfg = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + asp_cfg = CS530X_ASP_PRIMARY; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + asp_fmt = CS530X_ASP_FMT_DSP_A; + break; + case SND_SOC_DAIFMT_I2S: + asp_fmt = CS530X_ASP_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + asp_fmt = CS530X_ASP_FMT_LJ; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + asp_cfg |= CS530X_ASP_BCLK_INV; + break; + default: + return -EINVAL; + } + + regmap_update_bits(regmap, CS530X_ASP_CFG, + CS530X_ASP_PRIMARY | CS530X_ASP_BCLK_INV, + asp_cfg); + + return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG, + CS530X_ASP_FMT_MASK, asp_fmt); +} + +static bool cs530x_check_mclk_freq(struct snd_soc_component *component, + const unsigned int freq) +{ + switch (freq) { + case 24576000: + case 22579200: + case 12288000: + case 11289600: + return true; + default: + dev_err(component->dev, "Invalid MCLK %d\n", freq); + return false; + } +} + +static int cs530x_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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + unsigned int val; + + switch (tx_mask) { + case CS530X_0_1_TDM_SLOT_MASK: + case CS530X_0_3_TDM_SLOT_MASK: + case CS530X_0_7_TDM_SLOT_MASK: + val = CS530X_0_7_TDM_SLOT_VAL; + break; + case CS530X_2_3_TDM_SLOT_MASK: + val = CS530X_2_3_TDM_SLOT_VAL; + break; + case CS530X_4_5_TDM_SLOT_MASK: + case CS530X_4_7_TDM_SLOT_MASK: + val = CS530X_4_7_TDM_SLOT_VAL; + break; + case CS530X_6_7_TDM_SLOT_MASK: + val = CS530X_6_7_TDM_SLOT_VAL; + break; + case CS530X_8_9_TDM_SLOT_MASK: + case CS530X_8_11_TDM_SLOT_MASK: + case CS530X_8_15_TDM_SLOT_MASK: + val = CS530X_8_15_TDM_SLOT_VAL; + break; + case CS530X_10_11_TDM_SLOT_MASK: + val = CS530X_10_11_TDM_SLOT_VAL; + break; + case CS530X_12_13_TDM_SLOT_MASK: + case CS530X_12_15_TDM_SLOT_MASK: + val = CS530X_12_15_TDM_SLOT_VAL; + break; + case CS530X_14_15_TDM_SLOT_MASK: + val = CS530X_14_15_TDM_SLOT_VAL; + break; + default: + dev_err(component->dev, "Invalid TX slot(s) 0x%x\n", tx_mask); + return -EINVAL; + } + + return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG, + CS530X_ASP_TDM_SLOT_MASK, + val << CS530X_ASP_TDM_SLOT_SHIFT); +} + +static const struct snd_soc_dai_ops cs530x_dai_ops = { + .set_fmt = cs530x_set_fmt, + .hw_params = cs530x_hw_params, + .set_tdm_slot = cs530x_set_tdm_slot, +}; + +static const struct snd_soc_dai_driver cs530x_dai = { + .name = "cs530x-dai", + .capture = { + .stream_name = "AIF Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &cs530x_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, +}; + +static int cs530x_set_pll(struct snd_soc_component *component, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + unsigned int sysclk_src; + int ret; + + regmap_read(regmap, CS530X_CLK_CFG_0, &sysclk_src); + + /* Check if the source is the PLL */ + if ((sysclk_src & CS530X_SYSCLK_SRC_MASK) == 0) + return 0; + + switch (source) { + case CS530X_PLL_SRC_MCLK: + if (!cs530x_check_mclk_freq(component, freq_in)) + return -EINVAL; + + ret = cs530x_set_pll_refclk(component, freq_in); + if (ret) + return ret; + + break; + case CS530X_PLL_SRC_BCLK: + break; + default: + dev_err(component->dev, "Invalid PLL source %d\n", source); + return -EINVAL; + } + + return regmap_update_bits(regmap, CS530X_CLK_CFG_0, + CS530X_PLL_REFCLK_SRC_MASK, source); +} + +static int cs530x_component_probe(struct snd_soc_component *component) +{ + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int num_widgets; + + snd_soc_dapm_new_controls(dapm, cs530x_gen_dapm_widgets, + ARRAY_SIZE(cs530x_gen_dapm_widgets)); + + switch (cs530x->devtype) { + case CS5302: + cs530x_add_12_adc_widgets(component); + break; + case CS5304: + cs530x_add_12_adc_widgets(component); + cs530x_add_34_adc_widgets(component); + + num_widgets = ARRAY_SIZE(cs530x_in_sum_4ch_controls); + snd_soc_add_component_controls(component, + cs530x_in_sum_4ch_controls, + num_widgets); + break; + + case CS5308: + cs530x_add_12_adc_widgets(component); + cs530x_add_34_adc_widgets(component); + + num_widgets = ARRAY_SIZE(cs530x_in_5_to_8_controls); + snd_soc_add_component_controls(component, + cs530x_in_5_to_8_controls, + num_widgets); + + num_widgets = ARRAY_SIZE(cs530x_in_sum_8ch_controls); + snd_soc_add_component_controls(component, + cs530x_in_sum_8ch_controls, + num_widgets); + + num_widgets = ARRAY_SIZE(cs530x_adc_ch58_dapm_widgets); + snd_soc_dapm_new_controls(dapm, cs530x_adc_ch58_dapm_widgets, + num_widgets); + + snd_soc_dapm_add_routes(dapm, adc_ch5_8_routes, + ARRAY_SIZE(adc_ch5_8_routes)); + break; + default: + dev_err(component->dev, "Invalid device type %d\n", + cs530x->devtype); + return -EINVAL; + } + + return 0; +} + +static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cs530x->regmap; + + switch (source) { + case CS530X_SYSCLK_SRC_MCLK: + if (freq != 24560000 && freq != 22572000) { + dev_err(component->dev, "Invalid MCLK source rate %d\n", + freq); + return -EINVAL; + } + + cs530x->mclk_rate = freq; + break; + case CS530X_SYSCLK_SRC_PLL: + break; + default: + dev_err(component->dev, "Invalid clock id %d\n", clk_id); + return -EINVAL; + } + + return regmap_update_bits(regmap, CS530X_CLK_CFG_0, + CS530X_SYSCLK_SRC_MASK, + source << CS530X_SYSCLK_SRC_SHIFT); +} + +static const struct snd_soc_component_driver soc_component_dev_cs530x = { + .probe = cs530x_component_probe, + .set_sysclk = cs530x_set_sysclk, + .set_pll = cs530x_set_pll, + .endianness = 1, +}; + +const struct regmap_config cs530x_regmap = { + .reg_bits = 16, + .val_bits = 16, + + .max_register = CS530X_MAX_REGISTER, + .readable_reg = cs530x_readable_register, + .writeable_reg = cs530x_writeable_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = cs530x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs530x_reg_defaults), +}; +EXPORT_SYMBOL_NS_GPL(cs530x_regmap, SND_SOC_CS530X); + +static int cs530x_check_device_id(struct cs530x_priv *cs530x) +{ + struct device *dev = cs530x->dev; + unsigned int dev_id, rev; + int ret; + + ret = regmap_read(cs530x->regmap, CS530X_DEVID, &dev_id); + if (ret) + return dev_err_probe(dev, ret, "Can't read device ID\n"); + + ret = regmap_read(cs530x->regmap, CS530X_REVID, &rev); + if (ret) + return dev_err_probe(dev, ret, "Can't read REV ID\n"); + + dev_dbg(dev, "Device ID 0x%x Rev ID 0x%x\n", dev_id, rev); + + switch (dev_id) { + case CS530X_2CH_ADC_DEV_ID: + cs530x->num_adcs = 2; + break; + case CS530X_4CH_ADC_DEV_ID: + cs530x->num_adcs = 4; + break; + case CS530X_8CH_ADC_DEV_ID: + cs530x->num_adcs = 8; + break; + default: + return dev_err_probe(dev, -EINVAL, "Invalid device ID 0x%x\n", + dev_id); + } + + return 0; +} + +static int cs530x_parse_device_properties(struct cs530x_priv *cs530x) +{ + struct regmap *regmap = cs530x->regmap; + struct device *dev = cs530x->dev; + unsigned int val = 0; + + switch (cs530x->num_adcs) { + case 8: + if (device_property_read_bool(dev, "cirrus,in-hiz-pin78")) + val = CS530X_IN78_HIZ; + + if (device_property_read_bool(dev, "cirrus,in-hiz-pin56")) + val |= CS530X_IN56_HIZ; + + fallthrough; + case 4: + if (device_property_read_bool(dev, "cirrus,in-hiz-pin34")) + val |= CS530X_IN34_HIZ; + + fallthrough; + case 2: + if (device_property_read_bool(dev, "cirrus,in-hiz-pin12")) + val |= CS530X_IN12_HIZ; + + return regmap_set_bits(regmap, CS530X_IN_HIZ, val); + default: + return dev_err_probe(dev, -EINVAL, + "Invalid number of adcs %d\n", + cs530x->num_adcs); + } +} + +int cs530x_probe(struct cs530x_priv *cs530x) +{ + struct device *dev = cs530x->dev; + int ret, i; + + cs530x->dev_dai = devm_kmemdup(dev, &cs530x_dai, + sizeof(*(cs530x->dev_dai)), + GFP_KERNEL); + if (!cs530x->dev_dai) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(cs530x->supplies); i++) + cs530x->supplies[i].supply = cs530x_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs530x->supplies), + cs530x->supplies); + if (ret != 0) + return dev_err_probe(dev, ret, "Failed to request supplies"); + + ret = regulator_bulk_enable(ARRAY_SIZE(cs530x->supplies), + cs530x->supplies); + if (ret != 0) + return dev_err_probe(dev, ret, "Failed to enable supplies"); + + cs530x->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(cs530x->reset_gpio)) { + ret = dev_err_probe(dev, PTR_ERR(cs530x->reset_gpio), + "Reset gpio not available\n"); + goto err_regulator; + } + + if (cs530x->reset_gpio) { + usleep_range(2000, 2100); + gpiod_set_value_cansleep(cs530x->reset_gpio, 0); + } + + usleep_range(5000, 5100); + ret = cs530x_check_device_id(cs530x); + if (ret) + goto err_reset; + + if (!cs530x->reset_gpio) { + ret = regmap_write(cs530x->regmap, CS530X_SW_RESET, + CS530X_SW_RST_VAL); + if (ret) { + dev_err_probe(dev, ret, "Soft Reset Failed\n"); + goto err_reset; + } + } + + ret = cs530x_parse_device_properties(cs530x); + if (ret) + goto err_reset; + + cs530x->dev_dai->capture.channels_max = cs530x->num_adcs; + + ret = devm_snd_soc_register_component(dev, + &soc_component_dev_cs530x, cs530x->dev_dai, 1); + if (ret) { + dev_err_probe(dev, ret, "Can't register cs530x component\n"); + goto err_reset; + } + + return 0; + +err_reset: + gpiod_set_value_cansleep(cs530x->reset_gpio, 1); + +err_regulator: + regulator_bulk_disable(ARRAY_SIZE(cs530x->supplies), + cs530x->supplies); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs530x_probe, SND_SOC_CS530X); + +MODULE_DESCRIPTION("CS530X CODEC Driver"); +MODULE_AUTHOR("Paul Handrigan <paulha@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h new file mode 100644 index 000000000000..1c85310a5d03 --- /dev/null +++ b/sound/soc/codecs/cs530x.h @@ -0,0 +1,223 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CS530x CODEC driver internal data + * + * Copyright (C) 2023-2024 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef _CS530X_H +#define _CS530X_H + +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +/* Devices */ +#define CS530X_2CH_ADC_DEV_ID 0x5302 +#define CS530X_4CH_ADC_DEV_ID 0x5304 +#define CS530X_8CH_ADC_DEV_ID 0x5308 + +/* Registers */ + +#define CS530X_DEVID 0x0000000 +#define CS530X_REVID 0x0000004 +#define CS530X_SW_RESET 0x0000022 + +#define CS530X_CLK_CFG_0 0x0000040 +#define CS530X_CLK_CFG_1 0x0000042 +#define CS530X_CHIP_ENABLE 0x0000044 +#define CS530X_ASP_CFG 0x0000048 +#define CS530X_SIGNAL_PATH_CFG 0x0000050 +#define CS530X_IN_ENABLES 0x0000080 +#define CS530X_IN_RAMP_SUM 0x0000082 +#define CS530X_IN_FILTER 0x0000086 +#define CS530X_IN_HIZ 0x0000088 +#define CS530X_IN_INV 0x000008A +#define CS530X_IN_VOL_CTRL1_0 0x0000090 +#define CS530X_IN_VOL_CTRL1_1 0x0000092 +#define CS530X_IN_VOL_CTRL2_0 0x0000094 +#define CS530X_IN_VOL_CTRL2_1 0x0000096 +#define CS530X_IN_VOL_CTRL3_0 0x0000098 +#define CS530X_IN_VOL_CTRL3_1 0x000009A +#define CS530X_IN_VOL_CTRL4_0 0x000009C +#define CS530X_IN_VOL_CTRL4_1 0x000009E +#define CS530X_IN_VOL_CTRL5 0x00000A0 + +#define CS530X_PAD_FN 0x0003D24 +#define CS530X_PAD_LVL 0x0003D28 + +#define CS530X_MAX_REGISTER CS530X_PAD_LVL + +/* Register Fields */ + +/* REVID */ +#define CS530X_MTLREVID GENMASK(3, 0) +#define CS530X_AREVID GENMASK(7, 4) + +/* SW_RESET */ +#define CS530X_SW_RST_SHIFT 8 +#define CS530X_SW_RST_VAL (0x5A << CS530X_SW_RST_SHIFT) + +/* CLK_CFG_0 */ +#define CS530X_PLL_REFCLK_SRC_MASK BIT(0) +#define CS530X_PLL_REFCLK_FREQ_MASK GENMASK(5, 4) +#define CS530X_SYSCLK_SRC_MASK BIT(12) +#define CS530X_SYSCLK_SRC_SHIFT 12 +#define CS530X_REFCLK_2P822_3P072 0 +#define CS530X_REFCLK_5P6448_6P144 0x10 +#define CS530X_REFCLK_11P2896_12P288 0x20 +#define CS530X_REFCLK_24P5792_24P576 0x30 + +/* CLK_CFG_1 */ +#define CS530X_SAMPLE_RATE_MASK GENMASK(2, 0) +#define CS530X_FS_32K 0 +#define CS530X_FS_48K_44P1K 1 +#define CS530X_FS_96K_88P2K 2 +#define CS530X_FS_192K_176P4K 3 +#define CS530X_FS_384K_356P8K 4 +#define CS530X_FS_768K_705P6K 5 + +/* CHIP_ENABLE */ +#define CS530X_GLOBAL_EN BIT(0) + +/* ASP_CFG */ +#define CS530X_ASP_BCLK_FREQ_MASK GENMASK(1, 0) +#define CS530X_ASP_PRIMARY BIT(5) +#define CS530X_ASP_BCLK_INV BIT(6) +#define CS530X_BCLK_2P822_3P072 0 +#define CS530X_BCLK_5P6448_6P144 1 +#define CS530X_BCLK_11P2896_12P288 2 +#define CS530X_BCLK_24P5792_24P576 3 + +/* SIGNAL_PATH_CFG */ +#define CS530X_ASP_FMT_MASK GENMASK(2, 0) +#define CS530X_ASP_TDM_SLOT_MASK GENMASK(5, 3) +#define CS530X_ASP_TDM_SLOT_SHIFT 3 +#define CS530X_ASP_CH_REVERSE BIT(9) +#define CS530X_ASP_FMT_I2S 0 +#define CS530X_ASP_FMT_LJ 1 +#define CS530X_ASP_FMT_DSP_A 0x6 + +/* TDM Slots */ +#define CS530X_0_1_TDM_SLOT_MASK GENMASK(1, 0) +#define CS530X_0_3_TDM_SLOT_MASK GENMASK(3, 0) +#define CS530X_0_7_TDM_SLOT_MASK GENMASK(7, 0) +#define CS530X_0_7_TDM_SLOT_VAL 0 + +#define CS530X_2_3_TDM_SLOT_MASK GENMASK(3, 2) +#define CS530X_2_3_TDM_SLOT_VAL 1 + +#define CS530X_4_5_TDM_SLOT_MASK GENMASK(5, 4) +#define CS530X_4_7_TDM_SLOT_MASK GENMASK(7, 4) +#define CS530X_4_7_TDM_SLOT_VAL 2 + +#define CS530X_6_7_TDM_SLOT_MASK GENMASK(7, 6) +#define CS530X_6_7_TDM_SLOT_VAL 3 + +#define CS530X_8_9_TDM_SLOT_MASK GENMASK(9, 8) +#define CS530X_8_11_TDM_SLOT_MASK GENMASK(11, 8) +#define CS530X_8_15_TDM_SLOT_MASK GENMASK(15, 8) +#define CS530X_8_15_TDM_SLOT_VAL 4 + +#define CS530X_10_11_TDM_SLOT_MASK GENMASK(11, 10) +#define CS530X_10_11_TDM_SLOT_VAL 5 + +#define CS530X_12_13_TDM_SLOT_MASK GENMASK(13, 12) +#define CS530X_12_15_TDM_SLOT_MASK GENMASK(15, 12) +#define CS530X_12_15_TDM_SLOT_VAL 6 + +#define CS530X_14_15_TDM_SLOT_MASK GENMASK(15, 14) +#define CS530X_14_15_TDM_SLOT_VAL 7 + +/* IN_RAMP_SUM */ +#define CS530X_RAMP_RATE_INC_SHIFT 0 +#define CS530X_RAMP_RATE_DEC_SHIFT 4 +#define CS530X_IN_SUM_MODE_SHIFT 13 + +/* IN_FILTER */ +#define CS530X_IN_FILTER_SHIFT 8 +#define CS530X_IN_HPF_EN_SHIFT 12 + +/* IN_HIZ */ +#define CS530X_IN12_HIZ BIT(0) +#define CS530X_IN34_HIZ BIT(1) +#define CS530X_IN56_HIZ BIT(2) +#define CS530X_IN78_HIZ BIT(3) + +/* IN_INV */ +#define CS530X_IN1_INV_SHIFT 0 +#define CS530X_IN2_INV_SHIFT 1 +#define CS530X_IN3_INV_SHIFT 2 +#define CS530X_IN4_INV_SHIFT 3 +#define CS530X_IN5_INV_SHIFT 4 +#define CS530X_IN6_INV_SHIFT 5 +#define CS530X_IN7_INV_SHIFT 6 +#define CS530X_IN8_INV_SHIFT 7 + +/* IN_VOL_CTLy_z */ +#define CS530X_IN_MUTE BIT(15) + +/* IN_VOL_CTL5 */ +#define CS530X_IN_VU BIT(0) + +/* PAD_FN */ +#define CS530X_DOUT2_FN BIT(0) +#define CS530X_DOUT3_FN BIT(1) +#define CS530X_DOUT4_FN BIT(2) +#define CS530X_SPI_CS_FN BIT(3) +#define CS530X_CONFIG2_FN BIT(6) +#define CS530X_CONFIG3_FN BIT(7) +#define CS530X_CONFIG4_FN BIT(8) +#define CS530X_CONFIG5_FN BIT(9) + +/* PAD_LVL */ +#define CS530X_CONFIG2_LVL BIT(6) +#define CS530X_CONFIG3_LVL BIT(7) +#define CS530X_CONFIG4_LVL BIT(8) +#define CS530X_CONFIG5_LVL BIT(9) + +/* System Clock Source */ +#define CS530X_SYSCLK_SRC_MCLK 0 +#define CS530X_SYSCLK_SRC_PLL 1 + +/* PLL Reference Clock Source */ +#define CS530X_PLL_SRC_BCLK 0 +#define CS530X_PLL_SRC_MCLK 1 + +#define CS530X_NUM_SUPPLIES 2 + +enum cs530x_type { + CS5302, + CS5304, + CS5308, +}; + +/* codec private data */ +struct cs530x_priv { + struct regmap *regmap; + struct device *dev; + struct snd_soc_dai_driver *dev_dai; + + enum cs530x_type devtype; + int num_adcs; + int num_dacs; + + struct regulator_bulk_data supplies[CS530X_NUM_SUPPLIES]; + + unsigned int mclk_rate; + + int tdm_width; + int tdm_slots; + int bclk; + int fs; + int adc_pairs_count; + + struct gpio_desc *reset_gpio; +}; + +extern const struct regmap_config cs530x_regmap; +int cs530x_probe(struct cs530x_priv *cs530x); + +#endif diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index a2b328f3b39f..f3ef6fb55304 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1720,7 +1720,7 @@ static int da7213_set_component_pll(struct snd_soc_component *component, * SND_SOC_DAIFMT_CBC_CFC * SND_SOC_DAIFMT_CBP_CFP */ -static u64 da7213_dai_formats = +static const u64 da7213_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J | SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 03b539ba540f..6a4e42e5e35b 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -857,12 +857,16 @@ static void es8326_jack_detect_handler(struct work_struct *work) * set auto-check mode, then restart jack_detect_work after 400ms. * Don't report jack status. */ - regmap_write(es8326->regmap, ES8326_INT_SOURCE, - (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); + regmap_write(es8326->regmap, ES8326_INT_SOURCE, 0x00); regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x10, 0x00); es8326_enable_micbias(es8326->component); usleep_range(50000, 70000); regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x10, 0x10); + usleep_range(50000, 70000); + regmap_write(es8326->regmap, ES8326_INT_SOURCE, + (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x1f); regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x08); queue_delayed_work(system_wq, &es8326->jack_detect_work, diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c index e5fcde9ee308..6f57a3aeecc8 100644 --- a/sound/soc/codecs/framer-codec.c +++ b/sound/soc/codecs/framer-codec.c @@ -238,7 +238,7 @@ static int framer_dai_startup(struct snd_pcm_substream *substream, return 0; } -static u64 framer_dai_formats[] = { +static const u64 framer_dai_formats[] = { SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index d3abb7ce2153..74caae52e127 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -715,7 +715,7 @@ static int hdmi_codec_mute(struct snd_soc_dai *dai, int mute, int direction) * For example, * ${LINUX}/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c */ -static u64 hdmi_codec_formats = +static const u64 hdmi_codec_formats = SND_SOC_POSSIBLE_DAIFMT_NB_NF | SND_SOC_POSSIBLE_DAIFMT_NB_IF | SND_SOC_POSSIBLE_DAIFMT_IB_NF | diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c index 2cc7b9166e69..cb7a68c799f8 100644 --- a/sound/soc/codecs/idt821034.c +++ b/sound/soc/codecs/idt821034.c @@ -860,7 +860,7 @@ static int idt821034_dai_startup(struct snd_pcm_substream *substream, return 0; } -static u64 idt821034_dai_formats[] = { +static const u64 idt821034_dai_formats[] = { SND_SOC_POSSIBLE_DAIFMT_DSP_A | SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 3c0e0fdbfc5c..fac0617ab95b 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -562,7 +562,7 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, return 0; } -static u64 pcm3168a_dai_formats[] = { +static const u64 pcm3168a_dai_formats[] = { /* * Select below from Sound Card, not here * SND_SOC_DAIFMT_CBC_CFC diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 4be476a280e1..92bcf5179779 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -39,6 +39,7 @@ static const struct i2c_device_id pcm512x_i2c_id[] = { { "pcm5122", }, { "pcm5141", }, { "pcm5142", }, + { "pcm5242", }, { "tas5754", }, { "tas5756", }, { } @@ -51,6 +52,7 @@ static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5122", }, { .compatible = "ti,pcm5141", }, { .compatible = "ti,pcm5142", }, + { .compatible = "ti,pcm5242", }, { .compatible = "ti,tas5754", }, { .compatible = "ti,tas5756", }, { } diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c index 4d29e7196380..6629b862f47d 100644 --- a/sound/soc/codecs/pcm512x-spi.c +++ b/sound/soc/codecs/pcm512x-spi.c @@ -36,6 +36,7 @@ static const struct spi_device_id pcm512x_spi_id[] = { { "pcm5122", }, { "pcm5141", }, { "pcm5142", }, + { "pcm5242", }, { }, }; MODULE_DEVICE_TABLE(spi, pcm512x_spi_id); @@ -45,6 +46,7 @@ static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5122", }, { .compatible = "ti,pcm5141", }, { .compatible = "ti,pcm5142", }, + { .compatible = "ti,pcm5242", }, { } }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c index 86e126783a1d..8f7057e689fb 100644 --- a/sound/soc/codecs/pcm6240.c +++ b/sound/soc/codecs/pcm6240.c @@ -2087,10 +2087,8 @@ static int pcmdevice_i2c_probe(struct i2c_client *i2c) #endif pcm_dev = devm_kzalloc(&i2c->dev, sizeof(*pcm_dev), GFP_KERNEL); - if (!pcm_dev) { - ret = -ENOMEM; - goto out; - } + if (!pcm_dev) + return -ENOMEM; pcm_dev->chip_id = (id != NULL) ? id->driver_data : 0; diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c index 5dec69be0acb..76ee7e3f4d9b 100644 --- a/sound/soc/codecs/peb2466.c +++ b/sound/soc/codecs/peb2466.c @@ -814,7 +814,7 @@ static int peb2466_dai_startup(struct snd_pcm_substream *substream, &peb2466_sample_bits_constr); } -static u64 peb2466_dai_formats[] = { +static const u64 peb2466_dai_formats[] = { SND_SOC_POSSIBLE_DAIFMT_DSP_A | SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c new file mode 100644 index 000000000000..4e47db4fc7fb --- /dev/null +++ b/sound/soc/codecs/rt1318.c @@ -0,0 +1,1354 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1318.c -- RT1318 ALSA SoC audio amplifier driver +// Author: Jack Yu <jack.yu@realtek.com> +// +// Copyright(c) 2024 Realtek Semiconductor Corp. +// +// + +#include <linux/acpi.h> +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/rt1318.h> + +#include "rt1318.h" + +static struct reg_sequence init_list[] = { + { 0x0000C000, 0x01}, + { 0x0000F20D, 0x00}, + { 0x0000F212, 0x3E}, + { 0x0000C001, 0x02}, + { 0x0000C003, 0x22}, + { 0x0000C004, 0x44}, + { 0x0000C005, 0x44}, + { 0x0000C007, 0x64}, + { 0x0000C00E, 0xE7}, + { 0x0000F223, 0x7F}, + { 0x0000F224, 0xDB}, + { 0x0000F225, 0xEE}, + { 0x0000F226, 0x3F}, + { 0x0000F227, 0x0F}, + { 0x0000F21A, 0x78}, + { 0x0000F242, 0x3C}, + { 0x0000C120, 0x40}, + { 0x0000C125, 0x03}, + { 0x0000C321, 0x0A}, + { 0x0000C200, 0xD8}, + { 0x0000C201, 0x27}, + { 0x0000C202, 0x0F}, + { 0x0000C400, 0x0E}, + { 0x0000C401, 0x43}, + { 0x0000C402, 0xE0}, + { 0x0000C403, 0x00}, + { 0x0000C404, 0x4C}, + { 0x0000C406, 0x40}, + { 0x0000C407, 0x02}, + { 0x0000C408, 0x3F}, + { 0x0000C300, 0x01}, + { 0x0000C125, 0x03}, + { 0x0000DF00, 0x10}, + { 0x0000F20B, 0x2A}, + { 0x0000DF5F, 0x01}, + { 0x0000DF60, 0xA7}, + { 0x0000C203, 0x84}, + { 0x0000C206, 0x78}, + { 0x0000F10A, 0x09}, + { 0x0000F10B, 0x4C}, + { 0x0000F104, 0xF4}, + { 0x0000F105, 0x03}, + { 0x0000F109, 0xE0}, + { 0x0000F10B, 0x5C}, + { 0x0000F104, 0xF4}, + { 0x0000F105, 0x04}, + { 0x0000F109, 0x65}, + { 0x0000F10B, 0x5C}, + { 0x0000F104, 0xF4}, + { 0x0000F105, 0x02}, + { 0x0000F109, 0x30}, + { 0x0000F10B, 0x5C}, + { 0x0000E706, 0x0F}, + { 0x0000E707, 0x30}, + { 0x0000E806, 0x0F}, + { 0x0000E807, 0x30}, + { 0x0000CE04, 0x03}, + { 0x0000CE05, 0x5F}, + { 0x0000CE06, 0xA2}, + { 0x0000CE07, 0x6B}, + { 0x0000CF04, 0x03}, + { 0x0000CF05, 0x5F}, + { 0x0000CF06, 0xA2}, + { 0x0000CF07, 0x6B}, + { 0x0000CE60, 0xE3}, + { 0x0000C130, 0x51}, + { 0x0000E000, 0xA8}, + { 0x0000F102, 0x00}, + { 0x0000F103, 0x00}, + { 0x0000F104, 0xF5}, + { 0x0000F105, 0x23}, + { 0x0000F109, 0x04}, + { 0x0000F10A, 0x0B}, + { 0x0000F10B, 0x4C}, + { 0x0000F10B, 0x5C}, + { 0x41001888, 0x00}, + { 0x0000C121, 0x0B}, + { 0x0000F102, 0x00}, + { 0x0000F103, 0x00}, + { 0x0000F104, 0xF5}, + { 0x0000F105, 0x23}, + { 0x0000F109, 0x00}, + { 0x0000F10A, 0x0B}, + { 0x0000F10B, 0x4C}, + { 0x0000F10B, 0x5C}, + { 0x0000F800, 0x20}, + { 0x0000CA00, 0x80}, + { 0x0000CA10, 0x00}, + { 0x0000CA02, 0x78}, + { 0x0000CA12, 0x78}, + { 0x0000ED00, 0x90}, + { 0x0000E604, 0x00}, + { 0x0000DB00, 0x0C}, + { 0x0000DD00, 0x0C}, + { 0x0000DC19, 0x00}, + { 0x0000DC1A, 0x6A}, + { 0x0000DC1B, 0xAA}, + { 0x0000DC1C, 0xAB}, + { 0x0000DC1D, 0x00}, + { 0x0000DC1E, 0x16}, + { 0x0000DC1F, 0xDB}, + { 0x0000DC20, 0x6D}, + { 0x0000DE19, 0x00}, + { 0x0000DE1A, 0x6A}, + { 0x0000DE1B, 0xAA}, + { 0x0000DE1C, 0xAB}, + { 0x0000DE1D, 0x00}, + { 0x0000DE1E, 0x16}, + { 0x0000DE1F, 0xDB}, + { 0x0000DE20, 0x6D}, + { 0x0000DB32, 0x00}, + { 0x0000DD32, 0x00}, + { 0x0000DB33, 0x0A}, + { 0x0000DD33, 0x0A}, + { 0x0000DB34, 0x1A}, + { 0x0000DD34, 0x1A}, + { 0x0000DB15, 0xEF}, + { 0x0000DD15, 0xEF}, + { 0x0000DB17, 0xEF}, + { 0x0000DD17, 0xEF}, + { 0x0000DB94, 0x70}, + { 0x0000DD94, 0x70}, + { 0x0000DB19, 0x40}, + { 0x0000DD19, 0x40}, + { 0x0000DB12, 0xC0}, + { 0x0000DD12, 0xC0}, + { 0x0000DB00, 0x4C}, + { 0x0000DB04, 0x05}, + { 0x0000DB05, 0x03}, + { 0x0000DD04, 0x05}, + { 0x0000DD05, 0x03}, + { 0x0000DBBB, 0x09}, + { 0x0000DBBC, 0x30}, + { 0x0000DBBD, 0xF0}, + { 0x0000DBBE, 0xF1}, + { 0x0000DDBB, 0x09}, + { 0x0000DDBC, 0x30}, + { 0x0000DDBD, 0xF0}, + { 0x0000DDBE, 0xF1}, + { 0x0000DB01, 0x79}, + { 0x0000DD01, 0x79}, + { 0x0000DB08, 0x40}, + { 0x0000DD08, 0x40}, + { 0x0000DC52, 0xEF}, + { 0x0000DE52, 0xEF}, + { 0x0000DB00, 0xCC}, + { 0x0000CC2C, 0x00}, + { 0x0000CC2D, 0x2A}, + { 0x0000CC2E, 0x83}, + { 0x0000CC2F, 0xA8}, + { 0x0000CD2C, 0x00}, + { 0x0000CD2D, 0x2A}, + { 0x0000CD2E, 0x83}, + { 0x0000CD2F, 0xA8}, + { 0x0000CC24, 0x00}, + { 0x0000CC25, 0x51}, + { 0x0000CC26, 0xEB}, + { 0x0000CC27, 0x85}, + { 0x0000CD24, 0x00}, + { 0x0000CD25, 0x51}, + { 0x0000CD26, 0xEB}, + { 0x0000CD27, 0x85}, + { 0x0000CC20, 0x00}, + { 0x0000CC21, 0x00}, + { 0x0000CC22, 0x43}, + { 0x0000CD20, 0x00}, + { 0x0000CD21, 0x00}, + { 0x0000CD22, 0x43}, + { 0x0000CC16, 0x0F}, + { 0x0000CC17, 0x00}, + { 0x0000CD16, 0x0F}, + { 0x0000CD17, 0x00}, + { 0x0000CC29, 0x5D}, + { 0x0000CC2A, 0xC0}, + { 0x0000CD29, 0x5D}, + { 0x0000CD2A, 0xC0}, + { 0x0000CC31, 0x20}, + { 0x0000CC32, 0x00}, + { 0x0000CC33, 0x00}, + { 0x0000CC34, 0x00}, + { 0x0000CD31, 0x20}, + { 0x0000CD32, 0x00}, + { 0x0000CD33, 0x00}, + { 0x0000CD34, 0x00}, + { 0x0000CC36, 0x79}, + { 0x0000CC37, 0x99}, + { 0x0000CC38, 0x99}, + { 0x0000CC39, 0x99}, + { 0x0000CD36, 0x79}, + { 0x0000CD37, 0x99}, + { 0x0000CD38, 0x99}, + { 0x0000CD39, 0x99}, + { 0x0000CC09, 0x00}, + { 0x0000CC0A, 0x07}, + { 0x0000CC0B, 0x5F}, + { 0x0000CC0C, 0x6F}, + { 0x0000CD09, 0x00}, + { 0x0000CD0A, 0x07}, + { 0x0000CD0B, 0x5F}, + { 0x0000CD0C, 0x6F}, + { 0x0000CC0E, 0x00}, + { 0x0000CC0F, 0x03}, + { 0x0000CC10, 0xAF}, + { 0x0000CC11, 0xB7}, + { 0x0000CD0E, 0x00}, + { 0x0000CD0F, 0x03}, + { 0x0000CD10, 0xAF}, + { 0x0000CD11, 0xB7}, + { 0x0000CCD6, 0x00}, + { 0x0000CCD7, 0x03}, + { 0x0000CDD6, 0x00}, + { 0x0000CDD7, 0x03}, + { 0x0000CCD8, 0x00}, + { 0x0000CCD9, 0x03}, + { 0x0000CDD8, 0x00}, + { 0x0000CDD9, 0x03}, + { 0x0000CCDA, 0x00}, + { 0x0000CCDB, 0x03}, + { 0x0000CDDA, 0x00}, + { 0x0000CDDB, 0x03}, + { 0x0000C320, 0x20}, + { 0x0000C203, 0x9C}, +}; +#define rt1318_INIT_REG_LEN ARRAY_SIZE(init_list) + +static const struct reg_default rt1318_reg[] = { + { 0xc000, 0x00 }, + { 0xc001, 0x43 }, + { 0xc003, 0x22 }, + { 0xc004, 0x44 }, + { 0xc005, 0x44 }, + { 0xc006, 0x33 }, + { 0xc007, 0x64 }, + { 0xc008, 0x05 }, + { 0xc00a, 0xfc }, + { 0xc00b, 0x0f }, + { 0xc00c, 0x0e }, + { 0xc00d, 0xef }, + { 0xc00e, 0xe5 }, + { 0xc00f, 0xff }, + { 0xc120, 0xc0 }, + { 0xc121, 0x00 }, + { 0xc122, 0x00 }, + { 0xc123, 0x14 }, + { 0xc125, 0x00 }, + { 0xc130, 0x59 }, + { 0xc200, 0x00 }, + { 0xc201, 0x00 }, + { 0xc202, 0x00 }, + { 0xc203, 0x04 }, + { 0xc204, 0x00 }, + { 0xc205, 0x00 }, + { 0xc206, 0x68 }, + { 0xc207, 0x70 }, + { 0xc208, 0x00 }, + { 0xc20a, 0x00 }, + { 0xc20b, 0x01 }, + { 0xc20c, 0x7f }, + { 0xc20d, 0x01 }, + { 0xc20e, 0x7f }, + { 0xc300, 0x00 }, + { 0xc301, 0x00 }, + { 0xc303, 0x80 }, + { 0xc320, 0x00 }, + { 0xc321, 0x09 }, + { 0xc322, 0x02 }, + { 0xc400, 0x00 }, + { 0xc401, 0x00 }, + { 0xc402, 0x00 }, + { 0xc403, 0x00 }, + { 0xc404, 0x00 }, + { 0xc405, 0x00 }, + { 0xc406, 0x00 }, + { 0xc407, 0x00 }, + { 0xc408, 0x00 }, + { 0xc410, 0x04 }, + { 0xc430, 0x00 }, + { 0xc431, 0x00 }, + { 0xca00, 0x10 }, + { 0xca01, 0x00 }, + { 0xca02, 0x0b }, + { 0xca10, 0x10 }, + { 0xca11, 0x00 }, + { 0xca12, 0x0b }, + { 0xce04, 0x08 }, + { 0xce05, 0x00 }, + { 0xce06, 0x00 }, + { 0xce07, 0x00 }, + { 0xce60, 0x63 }, + { 0xcf04, 0x08 }, + { 0xcf05, 0x00 }, + { 0xcf06, 0x00 }, + { 0xcf07, 0x00 }, + { 0xdb00, 0x00 }, + { 0xdb08, 0x40 }, + { 0xdb12, 0x00 }, + { 0xdb35, 0x00 }, + { 0xdbb5, 0x00 }, + { 0xdbb6, 0x40 }, + { 0xdbb7, 0x00 }, + { 0xdbb8, 0x00 }, + { 0xdbc5, 0x00 }, + { 0xdbc6, 0x00 }, + { 0xdbc7, 0x00 }, + { 0xdbc8, 0x00 }, + { 0xdd08, 0x40 }, + { 0xdd12, 0x00 }, + { 0xdd35, 0x00 }, + { 0xddb5, 0x00 }, + { 0xddb6, 0x40 }, + { 0xddb7, 0x00 }, + { 0xddb8, 0x00 }, + { 0xddc5, 0x00 }, + { 0xddc6, 0x00 }, + { 0xddc7, 0x00 }, + { 0xddc8, 0x00 }, + { 0xdd93, 0x00 }, + { 0xdd94, 0x64 }, + { 0xdf00, 0x00 }, + { 0xdf5f, 0x00 }, + { 0xdf60, 0x00 }, + { 0xe000, 0x08 }, + { 0xe300, 0xa0 }, + { 0xe400, 0x22 }, + { 0xe706, 0x2f }, + { 0xe707, 0x2f }, + { 0xe806, 0x2f }, + { 0xe807, 0x2f }, + { 0xea00, 0x43 }, + { 0xed00, 0x80 }, + { 0xed01, 0x0f }, + { 0xed02, 0xff }, + { 0xed03, 0x00 }, + { 0xed04, 0x00 }, + { 0xed05, 0x0f }, + { 0xed06, 0xff }, + { 0xf010, 0x10 }, + { 0xf011, 0xec }, + { 0xf012, 0x68 }, + { 0xf013, 0x21 }, + { 0xf102, 0x00 }, + { 0xf103, 0x00 }, + { 0xf104, 0x00 }, + { 0xf105, 0x00 }, + { 0xf106, 0x00 }, + { 0xf107, 0x00 }, + { 0xf108, 0x00 }, + { 0xf109, 0x00 }, + { 0xf10a, 0x03 }, + { 0xf10b, 0x40 }, + { 0xf20b, 0x28 }, + { 0xf20d, 0x00 }, + { 0xf212, 0x00 }, + { 0xf21a, 0x00 }, + { 0xf223, 0x40 }, + { 0xf224, 0x00 }, + { 0xf225, 0x00 }, + { 0xf226, 0x00 }, + { 0xf227, 0x00 }, + { 0xf242, 0x0c }, + { 0xf800, 0x00 }, + { 0xf801, 0x12 }, + { 0xf802, 0xe0 }, + { 0xf803, 0x2f }, + { 0xf804, 0x00 }, + { 0xf805, 0x00 }, + { 0xf806, 0x07 }, + { 0xf807, 0xff }, +}; + +static bool rt1318_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0xc000: + case 0xc301: + case 0xc410: + case 0xc430 ... 0xc431: + case 0xdb06: + case 0xdb12: + case 0xdb1d ... 0xdb1f: + case 0xdb35: + case 0xdb37: + case 0xdb8a ... 0xdb92: + case 0xdbc5 ... 0xdbc8: + case 0xdc2b ... 0xdc49: + case 0xdd0b: + case 0xdd12: + case 0xdd1d ... 0xdd1f: + case 0xdd35: + case 0xdd8a ... 0xdd92: + case 0xddc5 ... 0xddc8: + case 0xde2b ... 0xde44: + case 0xdf4a ... 0xdf55: + case 0xe224 ... 0xe23b: + case 0xea01: + case 0xebc5: + case 0xebc8: + case 0xebcb ... 0xebcc: + case 0xed03 ... 0xed06: + case 0xf010 ... 0xf014: + return true; + + default: + return false; + } +} + +static bool rt1318_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0xc000 ... 0xc00f: + case 0xc120 ... 0xc130: + case 0xc200 ... 0xc20e: + case 0xc300 ... 0xc303: + case 0xc320 ... 0xc322: + case 0xc400 ... 0xc408: + case 0xc430 ... 0xc431: + case 0xca00 ... 0xca02: + case 0xca10 ... 0xca12: + case 0xcb00 ... 0xcb0b: + case 0xcc00 ... 0xcce5: + case 0xcd00 ... 0xcde5: + case 0xce00 ... 0xce6a: + case 0xcf00 ... 0xcf53: + case 0xd000 ... 0xd0cc: + case 0xd100 ... 0xd1b9: + case 0xdb00 ... 0xdc53: + case 0xdd00 ... 0xde53: + case 0xdf00 ... 0xdf6b: + case 0xe000: + case 0xe300: + case 0xe400: + case 0xe706 ... 0xe707: + case 0xe806 ... 0xe807: + case 0xea00: + case 0xeb00 ... 0xebcc: + case 0xec00 ... 0xecb9: + case 0xed00 ... 0xed06: + case 0xf010 ... 0xf014: + case 0xf102 ... 0xf10b: + case 0xf20b: + case 0xf20d ... 0xf242: + case 0xf800 ... 0xf807: + return true; + default: + return false; + } +} + +static int rt1318_dac_event(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 rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1, + RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_HIGH); + break; + + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1, + RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_LOW); + break; + + default: + break; + } + return 0; +} + +static int rt1318_dvol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + rt1318->rt1318_dvol = ucontrol->value.integer.value[0]; + + if (rt1318->rt1318_dvol <= RT1318_DVOL_STEP && rt1318->rt1318_dvol >= 0) { + regmap_write(rt1318->regmap, RT1318_DA_VOL_L_8, + rt1318->rt1318_dvol >> 8); + regmap_write(rt1318->regmap, RT1318_DA_VOL_L_1_7, + rt1318->rt1318_dvol & 0xff); + regmap_write(rt1318->regmap, RT1318_DA_VOL_R_8, + rt1318->rt1318_dvol >> 8); + regmap_write(rt1318->regmap, RT1318_DA_VOL_R_1_7, + rt1318->rt1318_dvol & 0xff); + return 1; + } + + return 0; +} + +static int rt1318_dvol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1318->rt1318_dvol; + + return 0; +} + +static const struct snd_kcontrol_new rt1318_snd_controls[] = { + SOC_SINGLE_EXT("Amp Playback Volume", SND_SOC_NOPM, 0, 383, 0, + rt1318_dvol_get, rt1318_dvol_put), +}; + +static const struct snd_soc_dapm_widget rt1318_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + /* DACs */ + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, + rt1318_dac_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("Amp"), +}; + +static const struct snd_soc_dapm_route rt1318_dapm_routes[] = { + {"DAC", NULL, "AIF1RX"}, + {"Amp", NULL, "DAC"}, +}; + +static int rt1318_get_clk_info(int sclk, int rate) +{ + int i, pd[] = {1, 2, 4, 8, 16, 24}; + + if (sclk <= 0 || rate <= 0) + return -EINVAL; + + rate = rate << 8; + for (i = 0; i < ARRAY_SIZE(pd); i++) + if (sclk == rate * pd[i]) + return i; + + return -EINVAL; +} + +static int rt1318_clk_ip_info(struct snd_soc_component *component, int lrclk) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + switch (lrclk) { + case RT1318_LRCLK_48000: + case RT1318_LRCLK_44100: + case RT1318_LRCLK_16000: + regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON, + RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK, + RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON4); + break; + case RT1318_LRCLK_96000: + regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON, + RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK, + RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON2); + break; + case RT1318_LRCLK_192000: + regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON, + RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK, + RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON1); + break; + default: + dev_err(component->dev, "Unsupported clock rate.\n"); + return -EINVAL; + } + + return 0; +} + +static int rt1318_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 rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + int data_len = 0, ch_len = 0; + int pre_div, ret; + + rt1318->lrck = params_rate(params); + pre_div = rt1318_get_clk_info(rt1318->sysclk, rt1318->lrck); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + ret = rt1318_clk_ip_info(component, rt1318->lrck); + if (ret < 0) { + dev_err(component->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + break; + case 20: + data_len = RT1318_I2S_DL_20; + ch_len = RT1318_I2S_DL_20; + break; + case 24: + data_len = RT1318_I2S_DL_24; + ch_len = RT1318_I2S_DL_24; + break; + case 32: + data_len = RT1318_I2S_DL_32; + ch_len = RT1318_I2S_DL_32; + break; + case 8: + data_len = RT1318_I2S_DL_8; + ch_len = RT1318_I2S_DL_8; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rt1318->regmap, RT1318_CLK2, + RT1318_DIV_AP_MASK | RT1318_DIV_DAMOD_MASK, + pre_div << RT1318_DIV_AP_SFT | + pre_div << RT1318_DIV_DAMOD_SFT); + regmap_update_bits(rt1318->regmap, RT1318_CLK3, + RT1318_AD_STO1_MASK | RT1318_AD_STO2_MASK, + pre_div << RT1318_AD_STO1_SFT | + pre_div << RT1318_AD_STO2_SFT); + regmap_update_bits(rt1318->regmap, RT1318_CLK4, + RT1318_AD_ANA_STO1_MASK | RT1318_AD_ANA_STO2_MASK, + pre_div << RT1318_AD_ANA_STO1_SFT | + pre_div << RT1318_AD_ANA_STO2_SFT); + regmap_update_bits(rt1318->regmap, RT1318_CLK5, + RT1318_DIV_FIFO_IN_MASK | RT1318_DIV_FIFO_OUT_MASK, + pre_div << RT1318_DIV_FIFO_IN_SFT | + pre_div << RT1318_DIV_FIFO_OUT_SFT); + regmap_update_bits(rt1318->regmap, RT1318_CLK6, + RT1318_DIV_NLMS_MASK | RT1318_DIV_AD_MONO_MASK | + RT1318_DIV_POST_G_MASK, pre_div << RT1318_DIV_NLMS_SFT | + pre_div << RT1318_DIV_AD_MONO_SFT | + pre_div << RT1318_DIV_POST_G_SFT); + + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2, + RT1318_I2S_DL_MASK, data_len << RT1318_I2S_DL_SFT); + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3, + RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK, + ch_len << RT1318_I2S_TX_CHL_SFT | + ch_len << RT1318_I2S_RX_CHL_SFT); + + return 0; +} + +static int rt1318_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, reg_val2 = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val2 |= RT1318_TDM_BCLK_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT1318_FMT_LEFT_J; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT1318_FMT_PCM_A_R; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT1318_FMT_PCM_B_R; + break; + + default: + return -EINVAL; + } + + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1, + RT1318_I2S_FMT_MASK, reg_val); + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1, + RT1318_TDM_BCLK_MASK, reg_val2); + + return 0; +} + +static int rt1318_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + int reg_val = 0; + + if (freq == rt1318->sysclk && clk_id == rt1318->sysclk_src) + return 0; + + switch (clk_id) { + case RT1318_SCLK_S_BCLK: + reg_val |= RT1318_SYSCLK_BCLK; + break; + case RT1318_SCLK_S_SDW: + reg_val |= RT1318_SYSCLK_SDW; + break; + case RT1318_SCLK_S_PLL2F: + reg_val |= RT1318_SYSCLK_PLL2F; + break; + case RT1318_SCLK_S_PLL2B: + reg_val |= RT1318_SYSCLK_PLL2B; + break; + case RT1318_SCLK_S_MCLK: + reg_val |= RT1318_SYSCLK_MCLK; + break; + case RT1318_SCLK_S_RC0: + reg_val |= RT1318_SYSCLK_RC1; + break; + case RT1318_SCLK_S_RC1: + reg_val |= RT1318_SYSCLK_RC2; + break; + case RT1318_SCLK_S_RC2: + reg_val |= RT1318_SYSCLK_RC3; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + rt1318->sysclk = freq; + rt1318->sysclk_src = clk_id; + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_SYSCLK_SEL_MASK, reg_val); + + return 0; +} + +static const struct pll_calc_map pll_preset_table[] = { + {512000, 4096000, 22, 190, 0, true, false}, + {1024000, 4096000, 22, 94, 0, true, false}, + {1024000, 16384000, 4, 190, 0, true, false}, + {1411200, 11289600, 6, 62, 0, true, false}, + {1536000, 12288000, 6, 62, 0, true, false}, + {2822400, 11289600, 6, 62, 0, true, false}, + {2822400, 45158400, 0, 62, 0, true, false}, + {2822400, 49152000, 0, 62, 0, true, false}, + {3072000, 12288000, 6, 62, 0, true, false}, + {3072000, 24576000, 2, 62, 0, true, false}, + {3072000, 49152000, 0, 62, 0, true, false}, + {6144000, 24576000, 2, 94, 4, false, false}, + {6144000, 49152000, 0, 30, 0, true, false}, + {6144000, 98304000, 0, 94, 4, false, true}, + {12288000, 49152000, 0, 62, 6, false, false}, +}; + +static int rt1318_pll_calc(const unsigned int freq_in, + const unsigned int freq_out, struct rt1318_pll_code *pll_code) +{ + int max_n = RT1318_PLL_N_MAX, max_m = RT1318_PLL_M_MAX; + int i, k, red, n_t, pll_out, in_t, out_t; + int n = 0, m = 0, m_t = 0; + int red_t = abs(freq_out - freq_in); + bool m_bypass = false, k_bypass = false; + + if (RT1318_PLL_INP_MAX < freq_in || RT1318_PLL_INP_MIN > freq_in) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) { + if (freq_in == pll_preset_table[i].pll_in && + freq_out == pll_preset_table[i].pll_out) { + k = pll_preset_table[i].k; + m = pll_preset_table[i].m; + n = pll_preset_table[i].n; + m_bypass = pll_preset_table[i].m_bp; + k_bypass = pll_preset_table[i].k_bp; + goto code_find; + } + } + + k = 100000000 / freq_out - 2; + if (k > RT1318_PLL_K_MAX) + k = RT1318_PLL_K_MAX; + if (k < 0) { + k = 0; + k_bypass = true; + } + for (n_t = 0; n_t <= max_n; n_t++) { + in_t = freq_in / (k_bypass ? 1 : (k + 2)); + pll_out = freq_out / (n_t + 2); + if (in_t < 0) + continue; + if (in_t == pll_out) { + m_bypass = true; + n = n_t; + goto code_find; + } + red = abs(in_t - pll_out); + if (red < red_t) { + m_bypass = true; + n = n_t; + m = m_t; + if (red == 0) + goto code_find; + red_t = red; + } + for (m_t = 0; m_t <= max_m; m_t++) { + out_t = in_t / (m_t + 2); + red = abs(out_t - pll_out); + if (red < red_t) { + m_bypass = false; + n = n_t; + m = m_t; + if (red == 0) + goto code_find; + red_t = red; + } + } + } + pr_debug("Only get approximation about PLL\n"); + +code_find: + + pll_code->m_bp = m_bypass; + pll_code->k_bp = k_bypass; + pll_code->m_code = m; + pll_code->n_code = n; + pll_code->k_code = k; + return 0; +} + +static int rt1318_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + struct rt1318_pll_code pll_code; + int ret; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + rt1318->pll_in = 0; + rt1318->pll_out = 0; + return 0; + } + + if (source == rt1318->pll_src && freq_in == rt1318->pll_in && + freq_out == rt1318->pll_out) + return 0; + + switch (source) { + case RT1318_PLL_S_BCLK0: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK0); + break; + case RT1318_PLL_S_BCLK1: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK1); + break; + case RT1318_PLL_S_RC: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_RC); + break; + case RT1318_PLL_S_MCLK: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_MCLK); + break; + case RT1318_PLL_S_SDW_IN_PLL: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_SDW1); + break; + case RT1318_PLL_S_SDW_0: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_SDW2); + break; + case RT1318_PLL_S_SDW_1: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_SDW3); + break; + case RT1318_PLL_S_SDW_2: + regmap_update_bits(rt1318->regmap, RT1318_CLK1, + RT1318_PLLIN_MASK, RT1318_PLLIN_SDW4); + break; + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rt1318_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + regmap_update_bits(rt1318->regmap, RT1318_PLL1_K, + RT1318_K_PLL1_MASK, pll_code.k_code); + regmap_update_bits(rt1318->regmap, RT1318_PLL1_M, + RT1318_M_PLL1_MASK, (pll_code.m_bp ? 0 : pll_code.m_code)); + regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_8, + RT1318_N_8_PLL1_MASK, pll_code.n_code >> 8); + regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_7_0, + RT1318_N_7_0_PLL1_MASK, pll_code.n_code); + + rt1318->pll_in = freq_in; + rt1318->pll_out = freq_out; + rt1318->pll_src = source; + + return 0; +} + +static int rt1318_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 rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + unsigned int cn = 0, cl = 0, rx_slotnum; + int ret = 0, first_bit; + + switch (slots) { + case 4: + cn |= RT1318_I2S_CH_TX_4CH; + cn |= RT1318_I2S_CH_RX_4CH; + break; + case 6: + cn |= RT1318_I2S_CH_TX_6CH; + cn |= RT1318_I2S_CH_RX_6CH; + break; + case 8: + cn |= RT1318_I2S_CH_TX_8CH; + cn |= RT1318_I2S_CH_RX_8CH; + break; + case 2: + break; + default: + return -EINVAL; + } + + switch (slot_width) { + case 20: + cl |= RT1318_I2S_TX_CHL_20; + cl |= RT1318_I2S_RX_CHL_20; + break; + case 24: + cl |= RT1318_I2S_TX_CHL_24; + cl |= RT1318_I2S_RX_CHL_24; + break; + case 32: + cl |= RT1318_I2S_TX_CHL_32; + cl |= RT1318_I2S_RX_CHL_32; + break; + case 8: + cl |= RT1318_I2S_TX_CHL_8; + cl |= RT1318_I2S_RX_CHL_8; + break; + case 16: + break; + default: + return -EINVAL; + } + + /* Rx slot configuration */ + rx_slotnum = hweight_long(rx_mask); + if (rx_slotnum != 1) { + ret = -EINVAL; + dev_err(component->dev, "too many rx slots or zero slot\n"); + goto _set_tdm_err_; + } + + first_bit = __ffs(rx_mask); + switch (first_bit) { + case 0: + case 2: + case 4: + case 6: + regmap_update_bits(rt1318->regmap, + RT1318_TDM_CTRL9, + RT1318_TDM_I2S_TX_L_DAC1_1_MASK | + RT1318_TDM_I2S_TX_R_DAC1_1_MASK, + (first_bit << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) | + ((first_bit + 1) << RT1318_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + case 1: + case 3: + case 5: + case 7: + regmap_update_bits(rt1318->regmap, + RT1318_TDM_CTRL9, + RT1318_TDM_I2S_TX_L_DAC1_1_MASK | + RT1318_TDM_I2S_TX_R_DAC1_1_MASK, + ((first_bit - 1) << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) | + (first_bit << RT1318_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2, + RT1318_I2S_CH_TX_MASK | RT1318_I2S_CH_RX_MASK, cn); + regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3, + RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK, cl); + +_set_tdm_err_: + return ret; +} + +static int rt1318_probe(struct snd_soc_component *component) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + rt1318->component = component; + + schedule_work(&rt1318->cali_work); + rt1318->rt1318_dvol = RT1318_DVOL_STEP; + + return 0; +} + +static void rt1318_remove(struct snd_soc_component *component) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + cancel_work_sync(&rt1318->cali_work); +} + +#ifdef CONFIG_PM +static int rt1318_suspend(struct snd_soc_component *component) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1318->regmap, true); + regcache_mark_dirty(rt1318->regmap); + return 0; +} + +static int rt1318_resume(struct snd_soc_component *component) +{ + struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1318->regmap, false); + regcache_sync(rt1318->regmap); + return 0; +} +#else +#define rt1318_suspend NULL +#define rt1318_resume NULL +#endif + +#define RT1318_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT1318_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt1318_aif_dai_ops = { + .hw_params = rt1318_hw_params, + .set_fmt = rt1318_set_dai_fmt, + .set_sysclk = rt1318_set_dai_sysclk, + .set_pll = rt1318_set_dai_pll, + .set_tdm_slot = rt1318_set_tdm_slot, +}; + +static struct snd_soc_dai_driver rt1318_dai[] = { + { + .name = "rt1318-aif", + .id = 0, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1318_STEREO_RATES, + .formats = RT1318_FORMATS, + }, + .ops = &rt1318_aif_dai_ops, + } +}; + +static const struct snd_soc_component_driver soc_component_dev_rt1318 = { + .probe = rt1318_probe, + .remove = rt1318_remove, + .suspend = rt1318_suspend, + .resume = rt1318_resume, + .controls = rt1318_snd_controls, + .num_controls = ARRAY_SIZE(rt1318_snd_controls), + .dapm_widgets = rt1318_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1318_dapm_widgets), + .dapm_routes = rt1318_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1318_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt1318_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt1318_readable_register, + .volatile_reg = rt1318_volatile_register, + .max_register = 0x41001888, + .reg_defaults = rt1318_reg, + .num_reg_defaults = ARRAY_SIZE(rt1318_reg), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct i2c_device_id rt1318_i2c_id[] = { + { "rt1318", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt1318_i2c_id); + +static const struct of_device_id rt1318_of_match[] = { + { .compatible = "realtek,rt1318", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt1318_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt1318_acpi_match[] = { + { "10EC1318", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt1318_acpi_match); +#endif + +static int rt1318_parse_dt(struct rt1318_priv *rt1318, struct device *dev) +{ + device_property_read_u32(dev, "realtek,r0_l", + &rt1318->pdata.init_r0_l); + device_property_read_u32(dev, "realtek,r0_r", + &rt1318->pdata.init_r0_r); + + return 0; +} + +static void rt1318_calibration_sequence(struct rt1318_priv *rt1318) +{ + regmap_write(rt1318->regmap, RT1318_CLK1, 0x22); + regmap_write(rt1318->regmap, RT1318_PLL1_N_7_0, 0x06); + regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xCC); + regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x40); + regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x40); + regmap_write(rt1318->regmap, RT1318_SINE_GEN0, 0x20); + regmap_write(rt1318->regmap, RT1318_SPK_VOL_TH, 0x00); + regmap_write(rt1318->regmap, RT1318_FEEDBACK_PATH, 0x0B); + regmap_write(rt1318->regmap, RT1318_TCON, 0x1C); + regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x58); + regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x78); + regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xC2); +} + +static void rt1318_r0_calculate(struct rt1318_priv *rt1318) +{ + unsigned int r0_l, r0_l_byte0, r0_l_byte1, r0_l_byte2, r0_l_byte3; + unsigned int r0_r, r0_r_byte0, r0_r_byte1, r0_r_byte2, r0_r_byte3; + unsigned int r0_l_integer, r0_l_factor, r0_r_integer, r0_r_factor; + unsigned int format = 16777216; /* 2^24 */ + + regmap_read(rt1318->regmap, RT1318_R0_L_24, &r0_l_byte0); + regmap_read(rt1318->regmap, RT1318_R0_L_23_16, &r0_l_byte1); + regmap_read(rt1318->regmap, RT1318_R0_L_15_8, &r0_l_byte2); + regmap_read(rt1318->regmap, RT1318_R0_L_7_0, &r0_l_byte3); + r0_l = r0_l_byte0 << 24 | r0_l_byte1 << 16 | r0_l_byte2 << 8 | r0_l_byte3; + r0_l_integer = format / r0_l; + r0_l_factor = (format * 10) / r0_l - r0_l_integer * 10; + + regmap_read(rt1318->regmap, RT1318_R0_R_24, &r0_r_byte0); + regmap_read(rt1318->regmap, RT1318_R0_R_23_16, &r0_r_byte1); + regmap_read(rt1318->regmap, RT1318_R0_R_15_8, &r0_r_byte2); + regmap_read(rt1318->regmap, RT1318_R0_R_7_0, &r0_r_byte3); + r0_r = r0_r_byte0 << 24 | r0_r_byte1 << 16 | r0_r_byte2 << 8 | r0_r_byte3; + r0_r_integer = format / r0_r; + r0_r_factor = (format * 10) / r0_r - r0_r_integer * 10; + + dev_dbg(rt1318->component->dev, "r0_l_ch:%d.%d ohm\n", r0_l_integer, r0_l_factor); + dev_dbg(rt1318->component->dev, "r0_r_ch:%d.%d ohm\n", r0_r_integer, r0_r_factor); +} + +static void rt1318_r0_restore(struct rt1318_priv *rt1318) +{ + regmap_write(rt1318->regmap, RT1318_PRE_R0_L_24, + (rt1318->pdata.init_r0_l >> 24) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_L_23_16, + (rt1318->pdata.init_r0_l >> 16) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_L_15_8, + (rt1318->pdata.init_r0_l >> 8) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_L_7_0, + (rt1318->pdata.init_r0_l >> 0) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_R_24, + (rt1318->pdata.init_r0_r >> 24) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_R_23_16, + (rt1318->pdata.init_r0_r >> 16) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_R_15_8, + (rt1318->pdata.init_r0_r >> 8) & 0xff); + regmap_write(rt1318->regmap, RT1318_PRE_R0_R_7_0, + (rt1318->pdata.init_r0_r >> 0) & 0xff); + regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x80); + regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x80); + regmap_write(rt1318->regmap, RT1318_R0_CMP_L_FLAG, 0xc0); + regmap_write(rt1318->regmap, RT1318_R0_CMP_R_FLAG, 0xc0); + regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xc0); + regmap_write(rt1318->regmap, RT1318_STP_R0_EN_R, 0xc0); + regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xcc); + regmap_write(rt1318->regmap, RT1318_TCON, 0x9c); +} + +static int rt1318_calibrate(struct rt1318_priv *rt1318) +{ + int chk_cnt = 30, count = 0; + int val, val2; + + regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x1); + usleep_range(0, 10000); + rt1318_calibration_sequence(rt1318); + + while (count < chk_cnt) { + msleep(100); + regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val); + regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2); + val = (val >> 1) & 0x1; + val2 = (val2 >> 1) & 0x1; + if (val & val2) { + dev_dbg(rt1318->component->dev, "Calibration done.\n"); + break; + } + count++; + if (count == chk_cnt) { + regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0); + return RT1318_R0_CALIB_NOT_DONE; + } + } + regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0); + regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val); + regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2); + if ((val & 0x1) & (val2 & 0x1)) + return RT1318_R0_IN_RANGE; + else + return RT1318_R0_OUT_OF_RANGE; +} + +static void rt1318_calibration_work(struct work_struct *work) +{ + struct rt1318_priv *rt1318 = + container_of(work, struct rt1318_priv, cali_work); + int ret; + + if (rt1318->pdata.init_r0_l && rt1318->pdata.init_r0_r) + rt1318_r0_restore(rt1318); + else { + ret = rt1318_calibrate(rt1318); + if (ret == RT1318_R0_IN_RANGE) + rt1318_r0_calculate(rt1318); + dev_dbg(rt1318->component->dev, "Calibrate R0 result:%d\n", ret); + } +} + +static int rt1318_i2c_probe(struct i2c_client *i2c) +{ + struct rt1318_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt1318_priv *rt1318; + int ret, val, val2, dev_id; + + rt1318 = devm_kzalloc(&i2c->dev, sizeof(struct rt1318_priv), + GFP_KERNEL); + if (!rt1318) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt1318); + + if (pdata) + rt1318->pdata = *pdata; + else + rt1318_parse_dt(rt1318, &i2c->dev); + + rt1318->regmap = devm_regmap_init_i2c(i2c, &rt1318_regmap); + if (IS_ERR(rt1318->regmap)) { + ret = PTR_ERR(rt1318->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt1318->regmap, RT1318_DEV_ID1, &val); + regmap_read(rt1318->regmap, RT1318_DEV_ID2, &val2); + dev_id = (val << 8) | val2; + if (dev_id != 0x6821) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt1318\n", + dev_id); + return -ENODEV; + } + + ret = regmap_register_patch(rt1318->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + INIT_WORK(&rt1318->cali_work, rt1318_calibration_work); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt1318, rt1318_dai, ARRAY_SIZE(rt1318_dai)); +} + +static struct i2c_driver rt1318_i2c_driver = { + .driver = { + .name = "rt1318", + .of_match_table = of_match_ptr(rt1318_of_match), + .acpi_match_table = ACPI_PTR(rt1318_acpi_match), + }, + .probe = rt1318_i2c_probe, + .id_table = rt1318_i2c_id, +}; +module_i2c_driver(rt1318_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT1318 driver"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt1318.h b/sound/soc/codecs/rt1318.h new file mode 100644 index 000000000000..cec40b484216 --- /dev/null +++ b/sound/soc/codecs/rt1318.h @@ -0,0 +1,342 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt1318.h -- Platform data for RT1318 + * + * Copyright 2024 Realtek Semiconductor Corp. + */ +#include <sound/rt1318.h> + +#ifndef __RT1318_H__ +#define __RT1318_H__ + +struct rt1318_priv { + struct snd_soc_component *component; + struct rt1318_platform_data pdata; + struct work_struct cali_work; + struct regmap *regmap; + + unsigned int r0_l_integer; + unsigned int r0_l_factor; + unsigned int r0_r_integer; + unsigned int r0_r_factor; + int rt1318_init; + int rt1318_dvol; + int sysclk_src; + int sysclk; + int lrck; + int bclk; + int master; + int pll_src; + int pll_in; + int pll_out; +}; + +#define RT1318_PLL_INP_MAX 40000000 +#define RT1318_PLL_INP_MIN 256000 +#define RT1318_PLL_N_MAX 0x1ff +#define RT1318_PLL_K_MAX 0x1f +#define RT1318_PLL_M_MAX 0x1f + +#define RT1318_LRCLK_192000 192000 +#define RT1318_LRCLK_96000 96000 +#define RT1318_LRCLK_48000 48000 +#define RT1318_LRCLK_44100 44100 +#define RT1318_LRCLK_16000 16000 +#define RT1318_DVOL_STEP 383 + +#define RT1318_CLK1 0xc001 +#define RT1318_CLK2 0xc003 +#define RT1318_CLK3 0xc004 +#define RT1318_CLK4 0xc005 +#define RT1318_CLK5 0xc006 +#define RT1318_CLK6 0xc007 +#define RT1318_CLK7 0xc008 +#define RT1318_PWR_STA1 0xc121 +#define RT1318_SPK_VOL_TH 0xc130 +#define RT1318_TCON 0xc203 +#define RT1318_SRC_TCON 0xc204 +#define RT1318_TCON_RELATE 0xc206 +#define RT1318_DA_VOL_L_8 0xc20b +#define RT1318_DA_VOL_L_1_7 0xc20c +#define RT1318_DA_VOL_R_8 0xc20d +#define RT1318_DA_VOL_R_1_7 0xc20e +#define RT1318_FEEDBACK_PATH 0xc321 +#define RT1318_STP_TEMP_L 0xdb00 +#define RT1318_STP_SEL_L 0xdb08 +#define RT1318_STP_R0_EN_L 0xdb12 +#define RT1318_R0_CMP_L_FLAG 0xdb35 +#define RT1318_PRE_R0_L_24 0xdbb5 +#define RT1318_PRE_R0_L_23_16 0xdbb6 +#define RT1318_PRE_R0_L_15_8 0xdbb7 +#define RT1318_PRE_R0_L_7_0 0xdbb8 +#define RT1318_R0_L_24 0xdbc5 +#define RT1318_R0_L_23_16 0xdbc6 +#define RT1318_R0_L_15_8 0xdbc7 +#define RT1318_R0_L_7_0 0xdbc8 +#define RT1318_STP_SEL_R 0xdd08 +#define RT1318_STP_R0_EN_R 0xdd12 +#define RT1318_R0_CMP_R_FLAG 0xdd35 +#define RT1318_PRE_R0_R_24 0xddb5 +#define RT1318_PRE_R0_R_23_16 0xddb6 +#define RT1318_PRE_R0_R_15_8 0xddb7 +#define RT1318_PRE_R0_R_7_0 0xddb8 +#define RT1318_R0_R_24 0xddc5 +#define RT1318_R0_R_23_16 0xddc6 +#define RT1318_R0_R_15_8 0xddc7 +#define RT1318_R0_R_7_0 0xddc8 +#define RT1318_DEV_ID1 0xf012 +#define RT1318_DEV_ID2 0xf013 +#define RT1318_PLL1_K 0xf20d +#define RT1318_PLL1_M 0xf20f +#define RT1318_PLL1_N_8 0xf211 +#define RT1318_PLL1_N_7_0 0xf212 +#define RT1318_SINE_GEN0 0xf800 +#define RT1318_TDM_CTRL1 0xf900 +#define RT1318_TDM_CTRL2 0xf901 +#define RT1318_TDM_CTRL3 0xf902 +#define RT1318_TDM_CTRL9 0xf908 + + +/* Clock-1 (0xC001) */ +#define RT1318_PLLIN_MASK (0x7 << 4) +#define RT1318_PLLIN_BCLK0 (0x0 << 4) +#define RT1318_PLLIN_BCLK1 (0x1 << 4) +#define RT1318_PLLIN_RC (0x2 << 4) +#define RT1318_PLLIN_MCLK (0x3 << 4) +#define RT1318_PLLIN_SDW1 (0x4 << 4) +#define RT1318_PLLIN_SDW2 (0x5 << 4) +#define RT1318_PLLIN_SDW3 (0x6 << 4) +#define RT1318_PLLIN_SDW4 (0x7 << 4) +#define RT1318_SYSCLK_SEL_MASK (0x7 << 0) +#define RT1318_SYSCLK_BCLK (0x0 << 0) +#define RT1318_SYSCLK_SDW (0x1 << 0) +#define RT1318_SYSCLK_PLL2F (0x2 << 0) +#define RT1318_SYSCLK_PLL2B (0x3 << 0) +#define RT1318_SYSCLK_MCLK (0x4 << 0) +#define RT1318_SYSCLK_RC1 (0x5 << 0) +#define RT1318_SYSCLK_RC2 (0x6 << 0) +#define RT1318_SYSCLK_RC3 (0x7 << 0) +/* Clock-2 (0xC003) */ +#define RT1318_DIV_AP_MASK (0x3 << 4) +#define RT1318_DIV_AP_SFT 4 +#define RT1318_DIV_AP_DIV1 (0x0 << 4) +#define RT1318_DIV_AP_DIV2 (0x1 << 4) +#define RT1318_DIV_AP_DIV4 (0x2 << 4) +#define RT1318_DIV_AP_DIV8 (0x3 << 4) +#define RT1318_DIV_DAMOD_MASK (0x3 << 0) +#define RT1318_DIV_DAMOD_SFT 0 +#define RT1318_DIV_DAMOD_DIV1 (0x0 << 0) +#define RT1318_DIV_DAMOD_DIV2 (0x1 << 0) +#define RT1318_DIV_DAMOD_DIV4 (0x2 << 0) +#define RT1318_DIV_DAMOD_DIV8 (0x3 << 0) +/* Clock-3 (0xC004) */ +#define RT1318_AD_STO1_MASK (0x7 << 4) +#define RT1318_AD_STO1_SFT 4 +#define RT1318_AD_STO1_DIV1 (0x0 << 4) +#define RT1318_AD_STO1_DIV2 (0x1 << 4) +#define RT1318_AD_STO1_DIV4 (0x2 << 4) +#define RT1318_AD_STO1_DIV8 (0x3 << 4) +#define RT1318_AD_STO1_DIV16 (0x4 << 4) +#define RT1318_AD_STO2_MASK (0x7 << 0) +#define RT1318_AD_STO2_SFT 0 +#define RT1318_AD_STO2_DIV1 (0x0 << 0) +#define RT1318_AD_STO2_DIV2 (0x1 << 0) +#define RT1318_AD_STO2_DIV4 (0x2 << 0) +#define RT1318_AD_STO2_DIV8 (0x3 << 0) +#define RT1318_AD_STO2_DIV16 (0x4 << 0) +#define RT1318_AD_STO2_SFT 0 +/* Clock-4 (0xC005) */ +#define RT1318_AD_ANA_STO1_MASK (0x7 << 4) +#define RT1318_AD_ANA_STO1_SFT 4 +#define RT1318_AD_ANA_STO1_DIV1 (0x0 << 4) +#define RT1318_AD_ANA_STO1_DIV2 (0x1 << 4) +#define RT1318_AD_ANA_STO1_DIV4 (0x2 << 4) +#define RT1318_AD_ANA_STO1_DIV8 (0x3 << 4) +#define RT1318_AD_ANA_STO1_DIV16 (0x4 << 4) +#define RT1318_AD_ANA_STO2_MASK (0x7 << 0) +#define RT1318_AD_ANA_STO2_DIV1 (0x0 << 0) +#define RT1318_AD_ANA_STO2_DIV2 (0x1 << 0) +#define RT1318_AD_ANA_STO2_DIV4 (0x2 << 0) +#define RT1318_AD_ANA_STO2_DIV8 (0x3 << 0) +#define RT1318_AD_ANA_STO2_DIV16 (0x4 << 0) +#define RT1318_AD_ANA_STO2_SFT 0 +/* Clock-5 (0xC006) */ +#define RT1318_DIV_FIFO_IN_MASK (0x3 << 4) +#define RT1318_DIV_FIFO_IN_SFT 4 +#define RT1318_DIV_FIFO_IN_DIV1 (0x0 << 4) +#define RT1318_DIV_FIFO_IN_DIV2 (0x1 << 4) +#define RT1318_DIV_FIFO_IN_DIV4 (0x2 << 4) +#define RT1318_DIV_FIFO_IN_DIV8 (0x3 << 4) +#define RT1318_DIV_FIFO_OUT_MASK (0x3 << 0) +#define RT1318_DIV_FIFO_OUT_DIV1 (0x0 << 0) +#define RT1318_DIV_FIFO_OUT_DIV2 (0x1 << 0) +#define RT1318_DIV_FIFO_OUT_DIV4 (0x2 << 0) +#define RT1318_DIV_FIFO_OUT_DIV8 (0x3 << 0) +#define RT1318_DIV_FIFO_OUT_SFT 0 +/* Clock-6 (0xC007) */ +#define RT1318_DIV_NLMS_MASK (0x3 << 6) +#define RT1318_DIV_NLMS_SFT 6 +#define RT1318_DIV_NLMS_DIV1 (0x0 << 6) +#define RT1318_DIV_NLMS_DIV2 (0x1 << 6) +#define RT1318_DIV_NLMS_DIV4 (0x2 << 6) +#define RT1318_DIV_NLMS_DIV8 (0x3 << 6) +#define RT1318_DIV_AD_MONO_MASK (0x7 << 3) +#define RT1318_DIV_AD_MONO_SFT 3 +#define RT1318_DIV_AD_MONO_DIV1 (0x0 << 3) +#define RT1318_DIV_AD_MONO_DIV2 (0x1 << 3) +#define RT1318_DIV_AD_MONO_DIV4 (0x2 << 3) +#define RT1318_DIV_AD_MONO_DIV8 (0x3 << 3) +#define RT1318_DIV_AD_MONO_DIV16 (0x4 << 3) +#define RT1318_DIV_POST_G_MASK (0x7 << 0) +#define RT1318_DIV_POST_G_SFT 0 +#define RT1318_DIV_POST_G_DIV1 (0x0 << 0) +#define RT1318_DIV_POST_G_DIV2 (0x1 << 0) +#define RT1318_DIV_POST_G_DIV4 (0x2 << 0) +#define RT1318_DIV_POST_G_DIV8 (0x3 << 0) +#define RT1318_DIV_POST_G_DIV16 (0x4 << 0) +/* Power Status 1 (0xC121) */ +#define RT1318_PDB_CTRL_MASK (0x1) +#define RT1318_PDB_CTRL_LOW (0x0) +#define RT1318_PDB_CTRL_HIGH (0x1) +#define RT1318_PDB_CTRL_SFT 0 +/* SRC Tcon(0xc204) */ +#define RT1318_SRCIN_IN_SEL_MASK (0x3 << 6) +#define RT1318_SRCIN_IN_48K (0x0 << 6) +#define RT1318_SRCIN_IN_44P1 (0x1 << 6) +#define RT1318_SRCIN_IN_32K (0x2 << 6) +#define RT1318_SRCIN_IN_16K (0x3 << 6) +#define RT1318_SRCIN_F12288_MASK (0x3 << 4) +#define RT1318_SRCIN_TCON1 (0x0 << 4) +#define RT1318_SRCIN_TCON2 (0x1 << 4) +#define RT1318_SRCIN_TCON4 (0x2 << 4) +#define RT1318_SRCIN_TCON8 (0x3 << 4) +#define RT1318_SRCIN_DACLK_MASK (0x3 << 2) +#define RT1318_DACLK_TCON1 (0x0 << 2) +#define RT1318_DACLK_TCON2 (0x1 << 2) +#define RT1318_DACLK_TCON4 (0x2 << 2) +#define RT1318_DACLK_TCON8 (0x3 << 2) +/* R0 Compare Flag (0xDB35) */ +#define RT1318_R0_RANGE_MASK (0x1) +#define RT1318_R0_OUTOFRANGE (0x0) +#define RT1318_R0_INRANGE (0x1) +/* PLL internal setting (0xF20D), K value */ +#define RT1318_K_PLL1_MASK (0x1f << 0) +/* PLL internal setting (0xF20F), M value */ +#define RT1318_M_PLL1_MASK (0x1f << 0) +/* PLL internal setting (0xF211), N_8 value */ +#define RT1318_N_8_PLL1_MASK (0x1 << 0) +/* PLL internal setting (0xF212), N_7_0 value */ +#define RT1318_N_7_0_PLL1_MASK (0xff << 0) +/* TDM CTRL 1 (0xf900) */ +#define RT1318_TDM_BCLK_MASK (0x1 << 7) +#define RT1318_TDM_BCLK_NORM (0x0 << 7) +#define RT1318_TDM_BCLK_INV (0x1 << 7) +#define RT1318_I2S_FMT_MASK (0x7 << 0) +#define RT1318_FMT_I2S (0x0 << 0) +#define RT1318_FMT_LEFT_J (0x1 << 0) +#define RT1318_FMT_PCM_A_R (0x2 << 0) +#define RT1318_FMT_PCM_B_R (0x3 << 0) +#define RT1318_FMT_PCM_A_F (0x6 << 0) +#define RT1318_FMT_PCM_B_F (0x7 << 0) +#define RT1318_I2S_FMT_SFT 0 +/* TDM CTRL 2 (0xf901) */ +#define RT1318_I2S_CH_TX_MASK (0x3 << 6) +#define RT1318_I2S_CH_TX_2CH (0x0 << 6) +#define RT1318_I2S_CH_TX_4CH (0x1 << 6) +#define RT1318_I2S_CH_TX_6CH (0x2 << 6) +#define RT1318_I2S_CH_TX_8CH (0x3 << 6) +#define RT1318_I2S_CH_RX_MASK (0x3 << 4) +#define RT1318_I2S_CH_RX_2CH (0x0 << 4) +#define RT1318_I2S_CH_RX_4CH (0x1 << 4) +#define RT1318_I2S_CH_RX_6CH (0x2 << 4) +#define RT1318_I2S_CH_RX_8CH (0x3 << 4) +#define RT1318_I2S_DL_MASK 0x7 +#define RT1318_I2S_DL_SFT 0 +#define RT1318_I2S_DL_16 0x0 +#define RT1318_I2S_DL_20 0x1 +#define RT1318_I2S_DL_24 0x2 +#define RT1318_I2S_DL_32 0x3 +#define RT1318_I2S_DL_8 0x4 +/* TDM CTRL 3 (0xf902) */ +#define RT1318_I2S_TX_CHL_MASK (0x7 << 4) +#define RT1318_I2S_TX_CHL_SFT 4 +#define RT1318_I2S_TX_CHL_16 (0x0 << 4) +#define RT1318_I2S_TX_CHL_20 (0x1 << 4) +#define RT1318_I2S_TX_CHL_24 (0x2 << 4) +#define RT1318_I2S_TX_CHL_32 (0x3 << 4) +#define RT1318_I2S_TX_CHL_8 (0x4 << 4) +#define RT1318_I2S_RX_CHL_MASK (0x7 << 0) +#define RT1318_I2S_RX_CHL_SFT 0 +#define RT1318_I2S_RX_CHL_16 (0x0 << 0) +#define RT1318_I2S_RX_CHL_20 (0x1 << 0) +#define RT1318_I2S_RX_CHL_24 (0x2 << 0) +#define RT1318_I2S_RX_CHL_32 (0x3 << 0) +#define RT1318_I2S_RX_CHL_8 (0x4 << 0) +/* TDM CTRL 9 (0xf908) */ +#define RT1318_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 4) +#define RT1318_TDM_I2S_TX_R_DAC1_1_MASK 0x7 +#define RT1318_TDM_I2S_TX_L_DAC1_1_SFT 4 +#define RT1318_TDM_I2S_TX_R_DAC1_1_SFT 0 + +#define RT1318_REG_DISP_LEN 23 + +/* System Clock Source */ +enum { + RT1318_SCLK_S_BCLK, + RT1318_SCLK_S_SDW, + RT1318_SCLK_S_PLL2F, + RT1318_SCLK_S_PLL2B, + RT1318_SCLK_S_MCLK, + RT1318_SCLK_S_RC0, + RT1318_SCLK_S_RC1, + RT1318_SCLK_S_RC2, +}; + +/* PLL Source */ +enum { + RT1318_PLL_S_BCLK0, + RT1318_PLL_S_BCLK1, + RT1318_PLL_S_RC, + RT1318_PLL_S_MCLK, + RT1318_PLL_S_SDW_IN_PLL, + RT1318_PLL_S_SDW_0, + RT1318_PLL_S_SDW_1, + RT1318_PLL_S_SDW_2, +}; + +/* TDM channel */ +enum { + RT1318_2CH, + RT1318_4CH, + RT1318_6CH, + RT1318_8CH, +}; + +/* R0 calibration result */ +enum { + RT1318_R0_OUT_OF_RANGE, + RT1318_R0_IN_RANGE, + RT1318_R0_CALIB_NOT_DONE, +}; + +/* PLL pre-defined M/N/K */ + +struct pll_calc_map { + unsigned int pll_in; + unsigned int pll_out; + int k; + int n; + int m; + bool m_bp; + bool k_bp; +}; + +struct rt1318_pll_code { + bool m_bp; /* Indicates bypass m code or not. */ + bool k_bp; /* Indicates bypass k code or not. */ + int m_code; + int n_code; + int k_code; +}; + +#endif /* __RT1318_H__ */ diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index b33da2215ade..87354bb1564e 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -68,6 +68,7 @@ static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int re case 0x200007f: case 0x2000082 ... 0x200008e: case 0x2000090 ... 0x2000094: + case 0x3110000: case 0x5300000 ... 0x5300002: case 0x5400002: case 0x5600000 ... 0x5600007: @@ -125,6 +126,7 @@ static bool rt722_sdca_mbq_volatile_register(struct device *dev, unsigned int re case 0x2000067: case 0x2000084: case 0x2000086: + case 0x3110000: return true; default: return false; @@ -350,7 +352,7 @@ static int rt722_sdca_interrupt_callback(struct sdw_slave *slave, if (status->sdca_cascade && !rt722->disable_irq) mod_delayed_work(system_power_efficient_wq, - &rt722->jack_detect_work, msecs_to_jiffies(30)); + &rt722->jack_detect_work, msecs_to_jiffies(280)); mutex_unlock(&rt722->disable_irq_lock); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 265a8ca25cbb..838d29fead96 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -2324,14 +2324,21 @@ void tasdevice_tuning_switch(void *context, int state) struct tasdevice_fw *tas_fmw = tas_priv->fmw; int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; - if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { - dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + /* + * Only RCA-based Playback can still work with no dsp program running + * inside the chip. + */ + switch (tas_priv->fw_state) { + case TASDEVICE_RCA_FW_OK: + case TASDEVICE_DSP_FW_ALL_OK: + break; + default: return; } if (state == 0) { - if (tas_priv->cur_prog < tas_fmw->nr_programs) { - /*dsp mode or tuning mode*/ + if (tas_fmw && tas_priv->cur_prog < tas_fmw->nr_programs) { + /* dsp mode or tuning mode */ profile_cfg_id = tas_priv->rcabin.profile_cfg_id; tasdevice_select_tuningprm_cfg(tas_priv, tas_priv->cur_prog, tas_priv->cur_conf, @@ -2340,9 +2347,10 @@ void tasdevice_tuning_switch(void *context, int state) tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, TASDEVICE_BIN_BLK_PRE_POWER_UP); - } else + } else { tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, TASDEVICE_BIN_BLK_PRE_SHUTDOWN); + } } EXPORT_SYMBOL_NS_GPL(tasdevice_tuning_switch, SND_SOC_TAS2781_FMWLIB); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 9350972dfefe..c64d458e524e 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -380,23 +380,37 @@ static void tasdevice_fw_ready(const struct firmware *fmw, mutex_lock(&tas_priv->codec_lock); ret = tasdevice_rca_parser(tas_priv, fmw); - if (ret) + if (ret) { + tasdevice_config_info_remove(tas_priv); goto out; + } tasdevice_create_control(tas_priv); tasdevice_dsp_remove(tas_priv); tasdevice_calbin_remove(tas_priv); - tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; + /* + * The baseline is the RCA-only case, and then the code attempts to + * load DSP firmware but in case of failures just keep going, i.e. + * failing to load DSP firmware is NOT an error. + */ + tas_priv->fw_state = TASDEVICE_RCA_FW_OK; scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin", tas_priv->dev_name); ret = tasdevice_dsp_parser(tas_priv); if (ret) { dev_err(tas_priv->dev, "dspfw load %s error\n", tas_priv->coef_binaryname); - tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; goto out; } - tasdevice_dsp_create_ctrls(tas_priv); + + /* + * If no dsp-related kcontrol created, the dsp resource will be freed. + */ + ret = tasdevice_dsp_create_ctrls(tas_priv); + if (ret) { + dev_err(tas_priv->dev, "dsp controls error\n"); + goto out; + } tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; @@ -417,9 +431,8 @@ static void tasdevice_fw_ready(const struct firmware *fmw, tasdevice_prmg_load(tas_priv, 0); tas_priv->cur_prog = 0; out: - if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { - /*If DSP FW fail, kcontrol won't be created */ - tasdevice_config_info_remove(tas_priv); + if (tas_priv->fw_state == TASDEVICE_RCA_FW_OK) { + /* If DSP FW fail, DSP kcontrol won't be created. */ tasdevice_dsp_remove(tas_priv); } mutex_unlock(&tas_priv->codec_lock); @@ -466,14 +479,14 @@ static int tasdevice_startup(struct snd_pcm_substream *substream, { struct snd_soc_component *codec = dai->component; struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); - int ret = 0; - if (tas_priv->fw_state != TASDEVICE_DSP_FW_ALL_OK) { - dev_err(tas_priv->dev, "DSP bin file not loaded\n"); - ret = -EINVAL; + switch (tas_priv->fw_state) { + case TASDEVICE_RCA_FW_OK: + case TASDEVICE_DSP_FW_ALL_OK: + return 0; + default: + return -EINVAL; } - - return ret; } static int tasdevice_hw_params(struct snd_pcm_substream *substream, diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index eb180df9a72a..7073b9d1cda8 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -39,9 +39,10 @@ */ #define ADC3XXX_MICBIAS_PINS 2 +#define ADC3XXX_GPIO_PINS 2 /* Number of GPIO pins exposed via the gpiolib interface */ -#define ADC3XXX_GPIOS_MAX 2 +#define ADC3XXX_GPIOS_MAX (ADC3XXX_MICBIAS_PINS + ADC3XXX_GPIO_PINS) #define ADC3XXX_RATES SNDRV_PCM_RATE_8000_96000 #define ADC3XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -320,7 +321,8 @@ struct adc3xxx { struct gpio_desc *rst_pin; unsigned int pll_mode; unsigned int sysclk; - unsigned int gpio_cfg[ADC3XXX_GPIOS_MAX]; /* value+1 (0 => not set) */ + unsigned int gpio_cfg[ADC3XXX_GPIO_PINS]; /* value+1 (0 => not set) */ + unsigned int micbias_gpo[ADC3XXX_MICBIAS_PINS]; /* 1 => pin is GPO */ unsigned int micbias_vg[ADC3XXX_MICBIAS_PINS]; int master; u8 page_no; @@ -328,7 +330,7 @@ struct adc3xxx { struct gpio_chip gpio_chip; }; -static const unsigned int adc3xxx_gpio_ctrl_reg[ADC3XXX_GPIOS_MAX] = { +static const unsigned int adc3xxx_gpio_ctrl_reg[ADC3XXX_GPIO_PINS] = { ADC3XXX_GPIO1_CTRL, ADC3XXX_GPIO2_CTRL }; @@ -959,14 +961,23 @@ static int adc3xxx_gpio_request(struct gpio_chip *chip, unsigned int offset) if (offset >= ADC3XXX_GPIOS_MAX) return -EINVAL; - /* GPIO1 is offset 0, GPIO2 is offset 1 */ - /* We check here that the GPIO pins are either not configured in the - * DT, or that they purposely are set as outputs. - * (Input mode not yet implemented). - */ - if (adc3xxx->gpio_cfg[offset] != 0 && - adc3xxx->gpio_cfg[offset] != ADC3XXX_GPIO_GPO + 1) - return -EINVAL; + if (offset >= 0 && offset < ADC3XXX_GPIO_PINS) { + /* GPIO1 is offset 0, GPIO2 is offset 1 */ + /* We check here that the GPIO pins are either not configured + * in the DT, or that they purposely are set as outputs. + * (Input mode not yet implemented). + */ + if (adc3xxx->gpio_cfg[offset] != 0 && + adc3xxx->gpio_cfg[offset] != ADC3XXX_GPIO_GPO + 1) + return -EINVAL; + } else if (offset >= ADC3XXX_GPIO_PINS && offset < ADC3XXX_GPIOS_MAX) { + /* MICBIAS1 is offset 2, MICBIAS2 is offset 3 */ + /* We check here if the MICBIAS pins are in fact configured + * as GPOs. + */ + if (!adc3xxx->micbias_gpo[offset - ADC3XXX_GPIO_PINS]) + return -EINVAL; + } return 0; } @@ -976,6 +987,21 @@ static int adc3xxx_gpio_direction_out(struct gpio_chip *chip, { struct adc3xxx *adc3xxx = gpiochip_get_data(chip); + /* For the MICBIAS pins, they are by definition outputs. */ + if (offset >= ADC3XXX_GPIO_PINS) { + unsigned int vg; + unsigned int micbias = offset - ADC3XXX_GPIO_PINS; + + if (value) + vg = adc3xxx->micbias_vg[micbias]; + else + vg = ADC3XXX_MICBIAS_OFF; + return regmap_update_bits(adc3xxx->regmap, + ADC3XXX_MICBIAS_CTRL, + ADC3XXX_MICBIAS_MASK << adc3xxx_micbias_shift[micbias], + vg << adc3xxx_micbias_shift[micbias]); + } + /* Set GPIO output function. */ return regmap_update_bits(adc3xxx->regmap, adc3xxx_gpio_ctrl_reg[offset], @@ -1004,9 +1030,17 @@ static int adc3xxx_gpio_get(struct gpio_chip *chip, unsigned int offset) unsigned int regval; int ret; - /* We only allow output pins, so just read the value set in the output - * pin register field. - */ + /* We only allow output pins, so just read the value prevously set. */ + if (offset >= ADC3XXX_GPIO_PINS) { + /* MICBIAS pins */ + unsigned int micbias = offset - ADC3XXX_GPIO_PINS; + + ret = regmap_read(adc3xxx->regmap, ADC3XXX_MICBIAS_CTRL, ®val); + if (ret) + return ret; + return ((regval >> adc3xxx_micbias_shift[micbias]) & ADC3XXX_MICBIAS_MASK) != + ADC3XXX_MICBIAS_OFF; + } ret = regmap_read(adc3xxx->regmap, adc3xxx_gpio_ctrl_reg[offset], ®val); if (ret) return ret; @@ -1048,7 +1082,7 @@ static void adc3xxx_init_gpio(struct adc3xxx *adc3xxx) * This allows us to set up things which are not software * controllable GPIOs, such as PDM microphone I/O, */ - for (gpio = 0; gpio < ADC3XXX_GPIOS_MAX; gpio++) { + for (gpio = 0; gpio < ADC3XXX_GPIO_PINS; gpio++) { unsigned int cfg = adc3xxx->gpio_cfg[gpio]; if (cfg) { @@ -1060,9 +1094,15 @@ static void adc3xxx_init_gpio(struct adc3xxx *adc3xxx) } } - /* Set up micbias voltage */ + /* Set up micbias voltage. */ + /* If pin is configured as GPO, set off initially. */ for (micbias = 0; micbias < ADC3XXX_MICBIAS_PINS; micbias++) { - unsigned int vg = adc3xxx->micbias_vg[micbias]; + unsigned int vg; + + if (adc3xxx->micbias_gpo[micbias]) + vg = ADC3XXX_MICBIAS_OFF; + else + vg = adc3xxx->micbias_vg[micbias]; regmap_update_bits(adc3xxx->regmap, ADC3XXX_MICBIAS_CTRL, @@ -1090,8 +1130,19 @@ static int adc3xxx_parse_dt_gpio(struct adc3xxx *adc3xxx, return 0; } -static int adc3xxx_parse_dt_micbias(struct adc3xxx *adc3xxx, - const char *propname, unsigned int *vg) +static int adc3xxx_parse_dt_micbias_gpo(struct adc3xxx *adc3xxx, + const char *propname, + unsigned int *cfg) +{ + struct device *dev = adc3xxx->dev; + struct device_node *np = dev->of_node; + + *cfg = of_property_read_bool(np, propname); + return 0; +} + +static int adc3xxx_parse_dt_micbias_vg(struct adc3xxx *adc3xxx, + const char *propname, unsigned int *vg) { struct device *dev = adc3xxx->dev; struct device_node *np = dev->of_node; @@ -1382,16 +1433,28 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c) dev_dbg(dev, "Enabled MCLK, freq %lu Hz\n", clk_get_rate(adc3xxx->mclk)); } + /* Configure mode for DMDIN/GPIO1 pin */ ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmdin-gpio1", &adc3xxx->gpio_cfg[0]); if (ret < 0) goto err_unprepare_mclk; + /* Configure mode for DMCLK/GPIO2 pin */ ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmclk-gpio2", &adc3xxx->gpio_cfg[1]); if (ret < 0) goto err_unprepare_mclk; - ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias1-vg", &adc3xxx->micbias_vg[0]); + /* Configure mode for MICBIAS1: as Mic Bias output or GPO */ + ret = adc3xxx_parse_dt_micbias_gpo(adc3xxx, "ti,micbias1-gpo", &adc3xxx->micbias_gpo[0]); + if (ret < 0) + goto err_unprepare_mclk; + /* Configure mode for MICBIAS2: as Mic Bias output or GPO */ + ret = adc3xxx_parse_dt_micbias_gpo(adc3xxx, "ti,micbias2-gpo", &adc3xxx->micbias_gpo[1]); + if (ret < 0) + goto err_unprepare_mclk; + /* Configure voltage for MICBIAS1 pin (ON voltage when used as GPO) */ + ret = adc3xxx_parse_dt_micbias_vg(adc3xxx, "ti,micbias1-vg", &adc3xxx->micbias_vg[0]); if (ret < 0) goto err_unprepare_mclk; - ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias2-vg", &adc3xxx->micbias_vg[1]); + /* Configure voltage for MICBIAS2 pin (ON voltage when used as GPO) */ + ret = adc3xxx_parse_dt_micbias_vg(adc3xxx, "ti,micbias2-vg", &adc3xxx->micbias_vg[1]); if (ret < 0) goto err_unprepare_mclk; diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 0e6218ed0e5e..d589a212b768 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -50,7 +50,7 @@ struct wcd_mbhc { struct wcd_mbhc_config *cfg; const struct wcd_mbhc_cb *mbhc_cb; const struct wcd_mbhc_intr *intr_ids; - struct wcd_mbhc_field *fields; + const struct wcd_mbhc_field *fields; /* Delayed work to report long button press */ struct delayed_work mbhc_btn_dwork; /* Work to handle plug report */ @@ -1505,7 +1505,7 @@ EXPORT_SYMBOL(wcd_dt_parse_mbhc_data); struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *intr_ids, - struct wcd_mbhc_field *fields, + const struct wcd_mbhc_field *fields, bool impedance_det_en) { struct device *dev = component->dev; diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h index df68e99c81a3..b977e8f87d7c 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.h +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -279,7 +279,7 @@ int wcd_mbhc_typec_report_unplug(struct wcd_mbhc *mbhc); struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, - struct wcd_mbhc_field *fields, + const struct wcd_mbhc_field *fields, bool impedance_det_en); int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, uint32_t *zr); @@ -300,7 +300,7 @@ static inline void wcd_mbhc_stop(struct wcd_mbhc *mbhc) static inline struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, const struct wcd_mbhc_cb *mbhc_cb, const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, - struct wcd_mbhc_field *fields, + const struct wcd_mbhc_field *fields, bool impedance_det_en) { return ERR_PTR(-ENOTSUPP); diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index fc0ab00a253f..1a20131e2a60 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -297,7 +297,6 @@ struct wcd9335_codec { struct clk *mclk; struct clk *native_clk; u32 mclk_rate; - u8 version; struct slim_device *slim; struct slim_device *slim_ifc_dev; @@ -345,10 +344,6 @@ struct wcd9335_codec { int dmic_0_1_clk_cnt; int dmic_2_3_clk_cnt; int dmic_4_5_clk_cnt; - int dmic_sample_rate; - int mad_dmic_sample_rate; - - int native_clk_users; }; struct wcd9335_irq { @@ -397,13 +392,13 @@ struct interp_sample_rate { int rate_val; }; -static struct interp_sample_rate int_mix_rate_val[] = { +static const struct interp_sample_rate int_mix_rate_val[] = { {48000, 0x4}, /* 48K */ {96000, 0x5}, /* 96K */ {192000, 0x6}, /* 192K */ }; -static struct interp_sample_rate int_prim_rate_val[] = { +static const struct interp_sample_rate int_prim_rate_val[] = { {8000, 0x0}, /* 8K */ {16000, 0x1}, /* 16K */ {24000, -EINVAL},/* 24K */ @@ -2847,56 +2842,15 @@ out: } static u8 wcd9335_get_dmic_clk_val(struct snd_soc_component *component, - u32 mclk_rate, u32 dmic_clk_rate) + u32 mclk_rate) { - u32 div_factor; u8 dmic_ctl_val; - dev_err(component->dev, - "%s: mclk_rate = %d, dmic_sample_rate = %d\n", - __func__, mclk_rate, dmic_clk_rate); - - /* Default value to return in case of error */ if (mclk_rate == WCD9335_MCLK_CLK_9P6MHZ) dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; else dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; - if (dmic_clk_rate == 0) { - dev_err(component->dev, - "%s: dmic_sample_rate cannot be 0\n", - __func__); - goto done; - } - - div_factor = mclk_rate / dmic_clk_rate; - switch (div_factor) { - case 2: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; - break; - case 3: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; - break; - case 4: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_4; - break; - case 6: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_6; - break; - case 8: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_8; - break; - case 16: - dmic_ctl_val = WCD9335_DMIC_CLK_DIV_16; - break; - default: - dev_err(component->dev, - "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n", - __func__, div_factor, mclk_rate, dmic_clk_rate); - break; - } - -done: return dmic_ctl_val; } @@ -2950,11 +2904,7 @@ static int wcd9335_codec_enable_dmic(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - dmic_rate_val = - wcd9335_get_dmic_clk_val(comp, - wcd->mclk_rate, - wcd->dmic_sample_rate); - + dmic_rate_val = wcd9335_get_dmic_clk_val(comp, wcd->mclk_rate); (*dmic_clk_cnt)++; if (*dmic_clk_cnt == 1) { snd_soc_component_update_bits(comp, dmic_clk_reg, @@ -2966,10 +2916,7 @@ static int wcd9335_codec_enable_dmic(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMD: - dmic_rate_val = - wcd9335_get_dmic_clk_val(comp, - wcd->mclk_rate, - wcd->mad_dmic_sample_rate); + dmic_rate_val = wcd9335_get_dmic_clk_val(comp, wcd->mclk_rate); (*dmic_clk_cnt)--; if (*dmic_clk_cnt == 0) { snd_soc_component_update_bits(comp, dmic_clk_reg, @@ -4026,7 +3973,7 @@ static irqreturn_t wcd9335_slimbus_irq(int irq, void *data) return ret; } -static struct wcd9335_irq wcd9335_irqs[] = { +static const struct wcd9335_irq wcd9335_irqs[] = { { .irq = WCD9335_IRQ_SLIMBUS, .handler = wcd9335_slimbus_irq, @@ -4963,7 +4910,7 @@ static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg) } } -static struct regmap_config wcd9335_regmap_config = { +static const struct regmap_config wcd9335_regmap_config = { .reg_bits = 16, .val_bits = 8, .cache_type = REGCACHE_MAPLE, @@ -4987,7 +4934,7 @@ static const struct regmap_range_cfg wcd9335_ifc_ranges[] = { }, }; -static struct regmap_config wcd9335_ifc_regmap_config = { +static const struct regmap_config wcd9335_ifc_regmap_config = { .reg_bits = 16, .val_bits = 8, .can_multi_write = true, @@ -5034,22 +4981,16 @@ static int wcd9335_parse_dt(struct wcd9335_codec *wcd) int ret; wcd->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); - if (wcd->reset_gpio < 0) { - dev_err(dev, "Reset GPIO missing from DT\n"); - return wcd->reset_gpio; - } + if (wcd->reset_gpio < 0) + return dev_err_probe(dev, wcd->reset_gpio, "Reset GPIO missing from DT\n"); wcd->mclk = devm_clk_get(dev, "mclk"); - if (IS_ERR(wcd->mclk)) { - dev_err(dev, "mclk not found\n"); - return PTR_ERR(wcd->mclk); - } + if (IS_ERR(wcd->mclk)) + return dev_err_probe(dev, PTR_ERR(wcd->mclk), "mclk not found\n"); wcd->native_clk = devm_clk_get(dev, "slimbus"); - if (IS_ERR(wcd->native_clk)) { - dev_err(dev, "slimbus clock not found\n"); - return PTR_ERR(wcd->native_clk); - } + if (IS_ERR(wcd->native_clk)) + return dev_err_probe(dev, PTR_ERR(wcd->native_clk), "slimbus clock not found\n"); wcd->supplies[0].supply = "vdd-buck"; wcd->supplies[1].supply = "vdd-buck-sido"; @@ -5058,10 +4999,8 @@ static int wcd9335_parse_dt(struct wcd9335_codec *wcd) wcd->supplies[4].supply = "vdd-io"; ret = regulator_bulk_get(dev, WCD9335_MAX_SUPPLY, wcd->supplies); - if (ret) { - dev_err(dev, "Failed to get supplies: err = %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to get supplies\n"); return 0; } @@ -5109,7 +5048,6 @@ static int wcd9335_bring_up(struct wcd9335_codec *wcd) if (byte0 == 0x1) { dev_info(wcd->dev, "WCD9335 CODEC version is v2.0\n"); - wcd->version = WCD9335_VERSION_2_0; regmap_write(rm, WCD9335_CODEC_RPM_RST_CTL, 0x01); regmap_write(rm, WCD9335_SIDO_SIDO_TEST_2, 0x00); regmap_write(rm, WCD9335_SIDO_SIDO_CCL_8, 0x6F); @@ -5161,10 +5099,8 @@ static int wcd9335_slim_probe(struct slim_device *slim) wcd->dev = dev; ret = wcd9335_parse_dt(wcd); - if (ret) { - dev_err(dev, "Error parsing DT: %d\n", ret); + if (ret) return ret; - } ret = wcd9335_power_on_reset(wcd); if (ret) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index b5a929659dc8..6c65b46e2dc9 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -475,17 +475,12 @@ enum { INTn_2_INP_SEL_PROXIMITY, }; -enum { - INTERP_MAIN_PATH, - INTERP_MIX_PATH, -}; - struct interp_sample_rate { int sample_rate; int rate_val; }; -static struct interp_sample_rate sr_val_tbl[] = { +static const struct interp_sample_rate sr_val_tbl[] = { {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, @@ -527,7 +522,7 @@ static const struct regmap_range_cfg wcd934x_ifc_ranges[] = { }, }; -static struct regmap_config wcd934x_ifc_regmap_config = { +static const struct regmap_config wcd934x_ifc_regmap_config = { .reg_bits = 16, .val_bits = 8, .max_register = 0xffff, @@ -571,10 +566,7 @@ struct wcd934x_codec { struct mutex micb_lock; u32 micb_ref[WCD934X_MAX_MICBIAS]; u32 pullup_ref[WCD934X_MAX_MICBIAS]; - u32 micb1_mv; u32 micb2_mv; - u32 micb3_mv; - u32 micb4_mv; }; #define to_wcd934x_codec(_hw) container_of(_hw, struct wcd934x_codec, hw) @@ -1217,7 +1209,7 @@ static const struct soc_enum cdc_if_tx13_mux_enum = SOC_ENUM_SINGLE(WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0, ARRAY_SIZE(cdc_if_tx13_mux_text), cdc_if_tx13_mux_text); -static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { +static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD934X_ANA_MBHC_MECH, 0x80), WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD934X_ANA_MBHC_MECH, 0x40), WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD934X_ANA_MBHC_MECH, 0x20), @@ -2208,7 +2200,8 @@ static int wcd934x_get_micbias_val(struct device *dev, const char *micbias, mv = WCD934X_DEF_MICBIAS_MV; } - *micb_mv = mv; + if (micb_mv) + *micb_mv = mv; return (mv - 1000) / 50; } @@ -2220,17 +2213,14 @@ static int wcd934x_init_dmic(struct snd_soc_component *comp) u32 def_dmic_rate, dmic_clk_drv; vout_ctl_1 = wcd934x_get_micbias_val(comp->dev, - "qcom,micbias1-microvolt", - &wcd->micb1_mv); + "qcom,micbias1-microvolt", NULL); vout_ctl_2 = wcd934x_get_micbias_val(comp->dev, "qcom,micbias2-microvolt", &wcd->micb2_mv); vout_ctl_3 = wcd934x_get_micbias_val(comp->dev, - "qcom,micbias3-microvolt", - &wcd->micb3_mv); + "qcom,micbias3-microvolt", NULL); vout_ctl_4 = wcd934x_get_micbias_val(comp->dev, - "qcom,micbias4-microvolt", - &wcd->micb4_mv); + "qcom,micbias4-microvolt", NULL); snd_soc_component_update_bits(comp, WCD934X_ANA_MICB1, WCD934X_MICB_VAL_MASK, vout_ctl_1); @@ -5866,17 +5856,13 @@ static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd) struct device_node *ifc_dev_np; ifc_dev_np = of_parse_phandle(dev->of_node, "slim-ifc-dev", 0); - if (!ifc_dev_np) { - dev_err(dev, "No Interface device found\n"); - return -EINVAL; - } + if (!ifc_dev_np) + return dev_err_probe(dev, -EINVAL, "No Interface device found\n"); wcd->sidev = of_slim_get_device(wcd->sdev->ctrl, ifc_dev_np); of_node_put(ifc_dev_np); - if (!wcd->sidev) { - dev_err(dev, "Unable to get SLIM Interface device\n"); - return -EINVAL; - } + if (!wcd->sidev) + return dev_err_probe(dev, -EINVAL, "Unable to get SLIM Interface device\n"); slim_get_logical_addr(wcd->sidev); wcd->if_regmap = regmap_init_slimbus(wcd->sidev, @@ -5922,10 +5908,8 @@ static int wcd934x_codec_probe(struct platform_device *pdev) mutex_init(&wcd->micb_lock); ret = wcd934x_codec_parse_data(wcd); - if (ret) { - dev_err(wcd->dev, "Failed to get SLIM IRQ\n"); + if (ret) return ret; - } /* set default rate 9P6MHz */ regmap_update_bits(wcd->regmap, WCD934X_CODEC_RPM_CLK_MCLK_CFG, diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c index a45025bf96c6..a24d6c620dba 100644 --- a/sound/soc/codecs/wcd937x-sdw.c +++ b/sound/soc/codecs/wcd937x-sdw.c @@ -19,7 +19,7 @@ #include <sound/soc.h> #include "wcd937x.h" -static struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { +static const struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD937X_HPH_L, WCD937X_HPH_PORT, BIT(0)), WCD_SDW_CH(WCD937X_HPH_R, WCD937X_HPH_PORT, BIT(1)), WCD_SDW_CH(WCD937X_CLSH, WCD937X_CLSH_PORT, BIT(0)), @@ -30,7 +30,7 @@ static struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD937X_DSD_R, WCD937X_DSD_PORT, BIT(1)), }; -static struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = { +static const struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = { WCD_SDW_CH(WCD937X_ADC1, WCD937X_ADC_1_PORT, BIT(0)), WCD_SDW_CH(WCD937X_ADC2, WCD937X_ADC_2_3_PORT, BIT(0)), WCD_SDW_CH(WCD937X_ADC3, WCD937X_ADC_2_3_PORT, BIT(0)), diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index ef649ed77fb2..4beb70bc4d8c 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -99,11 +99,9 @@ struct wcd937x_priv { s32 pullup_ref[WCD937X_MAX_MICBIAS]; u32 hph_mode; int ear_rx_path; - u32 chipid; u32 micb1_mv; u32 micb2_mv; u32 micb3_mv; - u32 micb4_mv; /* 9375 only */ int hphr_pdm_wd_int; int hphl_pdm_wd_int; int aux_pdm_wd_int; @@ -113,9 +111,6 @@ struct wcd937x_priv { struct gpio_desc *us_euro_gpio; struct gpio_desc *reset_gpio; - int dmic_0_1_clk_cnt; - int dmic_2_3_clk_cnt; - int dmic_4_5_clk_cnt; atomic_t rx_clk_cnt; atomic_t ana_clk_count; }; @@ -133,7 +128,7 @@ struct wcd937x_mbhc_zdet_param { u16 btn7; }; -static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { +static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD937X_ANA_MBHC_MECH, 0x80), WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD937X_ANA_MBHC_MECH, 0x40), WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD937X_ANA_MBHC_MECH, 0x20), @@ -227,7 +222,7 @@ static const u32 wcd937x_config_regs[] = { WCD937X_DIGITAL_INTR_LEVEL_0, }; -static struct regmap_irq_chip wcd937x_regmap_irq_chip = { +static const struct regmap_irq_chip wcd937x_regmap_irq_chip = { .name = "wcd937x", .irqs = wcd937x_irqs, .num_irqs = ARRAY_SIZE(wcd937x_irqs), @@ -1244,7 +1239,7 @@ static int wcd937x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, static int wcd937x_connect_port(struct wcd937x_sdw_priv *wcd, u8 port_idx, u8 ch_id, bool enable) { struct sdw_port_config *port_config = &wcd->port_config[port_idx - 1]; - struct wcd937x_sdw_ch_info *ch_info = &wcd->ch_info[ch_id]; + const struct wcd937x_sdw_ch_info *ch_info = &wcd->ch_info[ch_id]; u8 port_num = ch_info->port_num; u8 ch_mask = ch_info->ch_mask; @@ -2503,7 +2498,7 @@ static irqreturn_t wcd937x_wd_handle_irq(int irq, void *data) return IRQ_HANDLED; } -static struct irq_chip wcd_irq_chip = { +static const struct irq_chip wcd_irq_chip = { .name = "WCD937x", }; @@ -2543,6 +2538,7 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component) struct device *dev = component->dev; unsigned long time_left; int i, ret; + u32 chipid; time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, msecs_to_jiffies(5000)); @@ -2556,11 +2552,10 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component) if (ret < 0) return ret; - wcd937x->chipid = (snd_soc_component_read(component, - WCD937X_DIGITAL_EFUSE_REG_0) & 0x1e) >> 1; - if (wcd937x->chipid != CHIPID_WCD9370 && - wcd937x->chipid != CHIPID_WCD9375) { - dev_err(dev, "Got unknown chip id: 0x%x\n", wcd937x->chipid); + chipid = (snd_soc_component_read(component, + WCD937X_DIGITAL_EFUSE_REG_0) & 0x1e) >> 1; + if (chipid != CHIPID_WCD9370 && chipid != CHIPID_WCD9375) { + dev_err(dev, "Got unknown chip id: 0x%x\n", chipid); pm_runtime_put(dev); return -EINVAL; } @@ -2609,7 +2604,7 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component) disable_irq_nosync(wcd937x->hphl_pdm_wd_int); disable_irq_nosync(wcd937x->aux_pdm_wd_int); - if (wcd937x->chipid == CHIPID_WCD9375) { + if (chipid == CHIPID_WCD9375) { ret = snd_soc_dapm_new_controls(dapm, wcd9375_dapm_widgets, ARRAY_SIZE(wcd9375_dapm_widgets)); if (ret < 0) { diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h index e6e1ad5926d5..f267c66ca959 100644 --- a/sound/soc/codecs/wcd937x.h +++ b/sound/soc/codecs/wcd937x.h @@ -488,23 +488,6 @@ #define WCD937X_MAX_SWR_PORTS 5 #define WCD937X_MAX_SWR_CH_IDS 15 -/* Convert from vout ctl to micbias voltage in mV */ -#define WCD_VOUT_CTL_TO_MICB(v) (1000 + (v) * 50) -#define MAX_PORT 8 -#define MAX_CH_PER_PORT 8 -#define MAX_TX_PWR_CH 2 -#define SWR_NUM_PORTS 4 - -#define WCD937X_MAX_SLAVE_PORT_TYPES 10 - -struct codec_port_info { - u32 slave_port_type; - u32 master_port_type; - u32 ch_mask; - u32 num_ch; - u32 ch_rate; -}; - struct wcd937x_sdw_ch_info { int port_num; unsigned int ch_mask; @@ -522,10 +505,9 @@ struct wcd937x_sdw_priv { struct sdw_stream_config sconfig; struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WCD937X_MAX_SWR_PORTS]; - struct wcd937x_sdw_ch_info *ch_info; + const struct wcd937x_sdw_ch_info *ch_info; bool port_enable[WCD937X_MAX_SWR_CH_IDS]; int active_ports; - int num_ports; bool is_tx; struct wcd937x_priv *wcd937x; struct irq_domain *slave_irq; @@ -571,12 +553,6 @@ int wcd937x_sdw_hw_params(struct wcd937x_sdw_priv *wcd, #endif enum { - WCD_RX1, - WCD_RX2, - WCD_RX3 -}; - -enum { /* INTR_CTRL_INT_MASK_0 */ WCD937X_IRQ_MBHC_BUTTON_PRESS_DET = 0, WCD937X_IRQ_MBHC_BUTTON_RELEASE_DET, @@ -645,9 +621,4 @@ enum wcd937x_rx_sdw_channels { WCD937X_DSD_L, }; -enum { - WCD937X_SDW_DIR_RX, - WCD937X_SDW_DIR_TX, -}; - #endif diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index a1f04010da95..c995bcc59ead 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -21,7 +21,7 @@ #define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) -static struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = { +static const struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD938X_HPH_L, WCD938X_HPH_PORT, BIT(0)), WCD_SDW_CH(WCD938X_HPH_R, WCD938X_HPH_PORT, BIT(1)), WCD_SDW_CH(WCD938X_CLSH, WCD938X_CLSH_PORT, BIT(0)), @@ -32,7 +32,7 @@ static struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD938X_DSD_R, WCD938X_DSD_PORT, BIT(1)), }; -static struct wcd938x_sdw_ch_info wcd938x_sdw_tx_ch_info[] = { +static const struct wcd938x_sdw_ch_info wcd938x_sdw_tx_ch_info[] = { WCD_SDW_CH(WCD938X_ADC1, WCD938X_ADC_1_2_PORT, BIT(0)), WCD_SDW_CH(WCD938X_ADC2, WCD938X_ADC_1_2_PORT, BIT(1)), WCD_SDW_CH(WCD938X_ADC3, WCD938X_ADC_3_4_PORT, BIT(0)), diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 73387152ce02..12b32d5dc580 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -221,7 +221,7 @@ struct wcd938x_mbhc_zdet_param { u16 btn7; }; -static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { +static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD938X_ANA_MBHC_MECH, 0x80), WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD938X_ANA_MBHC_MECH, 0x40), WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD938X_ANA_MBHC_MECH, 0x20), @@ -418,7 +418,7 @@ static int wcd938x_io_init(struct wcd938x_priv *wcd938x) } -static int wcd938x_sdw_connect_port(struct wcd938x_sdw_ch_info *ch_info, +static int wcd938x_sdw_connect_port(const struct wcd938x_sdw_ch_info *ch_info, struct sdw_port_config *port_config, u8 enable) { @@ -3027,7 +3027,7 @@ static irqreturn_t wcd938x_wd_handle_irq(int irq, void *data) return IRQ_HANDLED; } -static struct irq_chip wcd_irq_chip = { +static const struct irq_chip wcd_irq_chip = { .name = "WCD938x", }; diff --git a/sound/soc/codecs/wcd938x.h b/sound/soc/codecs/wcd938x.h index 0d332cb555ac..b2ad98026ae2 100644 --- a/sound/soc/codecs/wcd938x.h +++ b/sound/soc/codecs/wcd938x.h @@ -642,10 +642,6 @@ enum wcd938x_rx_sdw_channels { WCD938X_DSD_R, WCD938X_DSD_L, }; -enum { - WCD938X_SDW_DIR_RX, - WCD938X_SDW_DIR_TX, -}; struct wcd938x_priv; struct wcd938x_sdw_priv { @@ -653,10 +649,9 @@ struct wcd938x_sdw_priv { struct sdw_stream_config sconfig; struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WCD938X_MAX_SWR_PORTS]; - struct wcd938x_sdw_ch_info *ch_info; + const struct wcd938x_sdw_ch_info *ch_info; bool port_enable[WCD938X_MAX_SWR_CH_IDS]; int active_ports; - int num_ports; bool is_tx; struct wcd938x_priv *wcd938x; struct irq_domain *slave_irq; diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c index 8acb5651c5bc..94b1e99a3ca0 100644 --- a/sound/soc/codecs/wcd939x-sdw.c +++ b/sound/soc/codecs/wcd939x-sdw.c @@ -23,7 +23,7 @@ #define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) -static struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = { +static const struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD939X_HPH_L, WCD939X_HPH_PORT, BIT(0)), WCD_SDW_CH(WCD939X_HPH_R, WCD939X_HPH_PORT, BIT(1)), WCD_SDW_CH(WCD939X_CLSH, WCD939X_CLSH_PORT, BIT(0)), @@ -36,7 +36,7 @@ static struct wcd939x_sdw_ch_info wcd939x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD939X_HIFI_PCM_R, WCD939X_HIFI_PCM_PORT, BIT(1)), }; -static struct wcd939x_sdw_ch_info wcd939x_sdw_tx_ch_info[] = { +static const struct wcd939x_sdw_ch_info wcd939x_sdw_tx_ch_info[] = { WCD_SDW_CH(WCD939X_ADC1, WCD939X_ADC_1_4_PORT, BIT(0)), WCD_SDW_CH(WCD939X_ADC2, WCD939X_ADC_1_4_PORT, BIT(1)), WCD_SDW_CH(WCD939X_ADC3, WCD939X_ADC_1_4_PORT, BIT(2)), diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 72d8a6a35052..66af035bd0e5 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -220,7 +220,7 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); -static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { +static const struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD939X_ANA_MBHC_MECH, 0x80), WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD939X_ANA_MBHC_MECH, 0x40), WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD939X_ANA_MBHC_MECH, 0x20), @@ -291,7 +291,7 @@ static const struct regmap_irq wcd939x_irqs[WCD939X_NUM_IRQS] = { REGMAP_IRQ_REG(WCD939X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08), }; -static struct regmap_irq_chip wcd939x_regmap_irq_chip = { +static const struct regmap_irq_chip wcd939x_regmap_irq_chip = { .name = "wcd939x", .irqs = wcd939x_irqs, .num_irqs = ARRAY_SIZE(wcd939x_irqs), @@ -414,7 +414,7 @@ static int wcd939x_io_init(struct snd_soc_component *component) return 0; } -static int wcd939x_sdw_connect_port(struct wcd939x_sdw_ch_info *ch_info, +static int wcd939x_sdw_connect_port(const struct wcd939x_sdw_ch_info *ch_info, struct sdw_port_config *port_config, u8 enable) { @@ -2957,7 +2957,7 @@ static irqreturn_t wcd939x_wd_handle_irq(int irq, void *data) * \- regmap_irq_thread() * \- handle_nested_irq(i) */ -static struct irq_chip wcd_irq_chip = { +static const struct irq_chip wcd_irq_chip = { .name = "WCD939x", }; diff --git a/sound/soc/codecs/wcd939x.h b/sound/soc/codecs/wcd939x.h index 756f497a337c..1571c2120cfc 100644 --- a/sound/soc/codecs/wcd939x.h +++ b/sound/soc/codecs/wcd939x.h @@ -903,21 +903,15 @@ enum wcd939x_rx_sdw_channels { WCD939X_HIFI_PCM_R, }; -enum { - WCD939X_SDW_DIR_RX, - WCD939X_SDW_DIR_TX, -}; - struct wcd939x_priv; struct wcd939x_sdw_priv { struct sdw_slave *sdev; struct sdw_stream_config sconfig; struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WCD939X_MAX_SWR_PORTS]; - struct wcd939x_sdw_ch_info *ch_info; + const struct wcd939x_sdw_ch_info *ch_info; bool port_enable[WCD939X_MAX_SWR_CH_IDS]; int active_ports; - int num_ports; bool is_tx; struct wcd939x_priv *wcd939x; struct irq_domain *slave_irq; diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 5ddc0c2fe53f..eb67689dcd6e 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -559,6 +559,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->pdev = pdev; + cpu_np = of_parse_phandle(np, "audio-cpu", 0); /* Give a chance to old DT binding */ if (!cpu_np) @@ -787,7 +789,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } /* Initialize sound card */ - priv->pdev = pdev; priv->card.dev = &pdev->dev; priv->card.owner = THIS_MODULE; ret = snd_soc_of_parse_card_name(&priv->card, "model"); diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index 14e94270911c..4fa208d6a032 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -50,4 +50,5 @@ int imx_pcm_dma_init(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(imx_pcm_dma_init); +MODULE_DESCRIPTION("Freescale i.MX PCM DMA interface"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c index e4967540a2e1..e9e5e235a8a6 100644 --- a/sound/soc/generic/test-component.c +++ b/sound/soc/generic/test-component.c @@ -189,7 +189,7 @@ static int test_dai_bespoke_trigger(struct snd_pcm_substream *substream, return 0; } -static u64 test_dai_formats = +static const u64 test_dai_formats = /* * Select below from Sound Card, not auto * SND_SOC_POSSIBLE_DAIFMT_BP_FP diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 35381a835c93..5cda527020c7 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1545,8 +1545,8 @@ static int avs_route_load(struct snd_soc_component *comp, int index, { struct snd_soc_acpi_mach *mach = dev_get_platdata(comp->card->dev); size_t len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN; - char buf[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int ssp_port, tdm_slot; + char *buf; /* See parse_link_formatted_string() for dynamic naming when(s). */ if (!avs_mach_singular_ssp(mach)) @@ -1557,13 +1557,24 @@ static int avs_route_load(struct snd_soc_component *comp, int index, return 0; tdm_slot = avs_mach_ssp_tdm(mach, ssp_port); + buf = devm_kzalloc(comp->card->dev, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; avs_ssp_sprint(buf, len, route->source, ssp_port, tdm_slot); - strscpy((char *)route->source, buf, len); + route->source = buf; + + buf = devm_kzalloc(comp->card->dev, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; avs_ssp_sprint(buf, len, route->sink, ssp_port, tdm_slot); - strscpy((char *)route->sink, buf, len); + route->sink = buf; + if (route->control) { + buf = devm_kzalloc(comp->card->dev, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; avs_ssp_sprint(buf, len, route->control, ssp_port, tdm_slot); - strscpy((char *)route->control, buf, len); + route->control = buf; } return 0; diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index b41a1147f1c3..a64d1989e28a 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -613,6 +613,17 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ARCHOS 101 CESIUM"), + }, + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | + BYT_RT5640_JD_NOT_INV | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ARCHOS 140 CESIUM"), }, .driver_data = (void *)(BYT_RT5640_IN1_MAP | diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index 48252fa9e39e..8e0ae3635a35 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -293,7 +293,7 @@ static const struct snd_soc_acpi_adr_device rt1318_1_single_adr[] = { .adr = 0x000130025D131801, .num_endpoints = 1, .endpoints = &single_endpoint, - .name_prefix = "rt1318" + .name_prefix = "rt1318-1" } }; diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index acaf81fd6c9b..f848e14b091a 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -31,7 +31,7 @@ struct mt8183_da7219_max98357_priv { static struct snd_soc_jack_pin mt8183_da7219_max98357_jack_pins[] = { { - .pin = "Headphone", + .pin = "Headphones", .mask = SND_JACK_HEADPHONE, }, { @@ -626,7 +626,7 @@ static struct snd_soc_codec_conf mt6358_codec_conf[] = { }; static const struct snd_kcontrol_new mt8183_da7219_max98357_snd_controls[] = { - SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headphones"), SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Speakers"), SOC_DAPM_PIN_SWITCH("Line Out"), @@ -634,7 +634,7 @@ static const struct snd_kcontrol_new mt8183_da7219_max98357_snd_controls[] = { static const struct snd_soc_dapm_widget mt8183_da7219_max98357_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_SPK("Line Out", NULL), @@ -680,7 +680,7 @@ static struct snd_soc_codec_conf mt8183_da7219_rt1015_codec_conf[] = { }; static const struct snd_kcontrol_new mt8183_da7219_rt1015_snd_controls[] = { - SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headphones"), SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Left Spk"), SOC_DAPM_PIN_SWITCH("Right Spk"), @@ -689,7 +689,7 @@ static const struct snd_kcontrol_new mt8183_da7219_rt1015_snd_controls[] = { static const struct snd_soc_dapm_widget mt8183_da7219_rt1015_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Left Spk", NULL), SND_SOC_DAPM_SPK("Right Spk", NULL), diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c index df2e4be992d2..9bb08cadeb18 100644 --- a/sound/soc/mxs/mxs-pcm.c +++ b/sound/soc/mxs/mxs-pcm.c @@ -43,4 +43,5 @@ int mxs_pcm_platform_register(struct device *dev) } EXPORT_SYMBOL_GPL(mxs_pcm_platform_register); +MODULE_DESCRIPTION("MXS ASoC PCM driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index ba28ec9dff86..9c98a35ad099 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -146,14 +146,17 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); int rc; - if (!dai_data->is_port_started[dai->id]) - return; - rc = q6apm_graph_stop(dai_data->graph[dai->id]); - if (rc < 0) - dev_err(dai->dev, "fail to close APM port (%d)\n", rc); + if (dai_data->is_port_started[dai->id]) { + rc = q6apm_graph_stop(dai_data->graph[dai->id]); + dai_data->is_port_started[dai->id] = false; + if (rc < 0) + dev_err(dai->dev, "fail to close APM port (%d)\n", rc); + } - q6apm_graph_close(dai_data->graph[dai->id]); - dai_data->is_port_started[dai->id] = false; + if (dai_data->graph[dai->id]) { + q6apm_graph_close(dai_data->graph[dai->id]); + dai_data->graph[dai->id] = NULL; + } } static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) @@ -168,8 +171,10 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s q6apm_graph_stop(dai_data->graph[dai->id]); dai_data->is_port_started[dai->id] = false; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { q6apm_graph_close(dai_data->graph[dai->id]); + dai_data->graph[dai->id] = NULL; + } } /** @@ -188,26 +193,29 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s cfg->direction = substream->stream; rc = q6apm_graph_media_format_pcm(dai_data->graph[dai->id], cfg); - if (rc) { dev_err(dai->dev, "Failed to set media format %d\n", rc); - return rc; + goto err; } rc = q6apm_graph_prepare(dai_data->graph[dai->id]); if (rc) { dev_err(dai->dev, "Failed to prepare Graph %d\n", rc); - return rc; + goto err; } rc = q6apm_graph_start(dai_data->graph[dai->id]); if (rc < 0) { dev_err(dai->dev, "fail to start APM port %x\n", dai->id); - return rc; + goto err; } dai_data->is_port_started[dai->id] = true; return 0; +err: + q6apm_graph_close(dai_data->graph[dai->id]); + dai_data->graph[dai->id] = NULL; + return rc; } static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) diff --git a/sound/soc/qcom/sdw.c b/sound/soc/qcom/sdw.c index eaa8bb016e50..f2eda2ff46c0 100644 --- a/sound/soc/qcom/sdw.c +++ b/sound/soc/qcom/sdw.c @@ -160,4 +160,5 @@ int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream, return 0; } EXPORT_SYMBOL_GPL(qcom_snd_sdw_hw_free); +MODULE_DESCRIPTION("Qualcomm ASoC SoundWire helper functions"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 9fa020ef7eab..ee517d7b5b7b 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -655,8 +655,17 @@ static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, int err; if (i2s_tdm->is_master_mode) { - struct clk *mclk = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - i2s_tdm->mclk_tx : i2s_tdm->mclk_rx; + struct clk *mclk; + + if (i2s_tdm->clk_trcm == TRCM_TX) { + mclk = i2s_tdm->mclk_tx; + } else if (i2s_tdm->clk_trcm == TRCM_RX) { + mclk = i2s_tdm->mclk_rx; + } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + mclk = i2s_tdm->mclk_tx; + } else { + mclk = i2s_tdm->mclk_rx; + } err = clk_set_rate(mclk, DEFAULT_MCLK_FS * params_rate(params)); if (err) diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 84601ba43b7d..087e379aa3bc 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1713,7 +1713,7 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream, * SND_SOC_DAIFMT_CBC_CFC * SND_SOC_DAIFMT_CBP_CFP */ -static u64 fsi_dai_formats = +static const u64 fsi_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J | SND_SOC_POSSIBLE_DAIFMT_NB_NF | diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 6bc7027ed4db..63b3c8bf0fde 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1061,7 +1061,7 @@ static int rsnd_soc_dai_prepare(struct snd_pcm_substream *substream, return rsnd_dai_call(prepare, io, priv); } -static u64 rsnd_soc_dai_formats[] = { +static const u64 rsnd_soc_dai_formats[] = { /* * 1st Priority * diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index ea3bc9318412..a63e942fdc0b 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -318,6 +318,12 @@ static int dmaengine_copy(struct snd_soc_component *component, return 0; } +static int dmaengine_pcm_sync_stop(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + return snd_dmaengine_pcm_sync_stop(substream); +} + static const struct snd_soc_component_driver dmaengine_pcm_component = { .name = SND_DMAENGINE_PCM_DRV_NAME, .probe_order = SND_SOC_COMP_ORDER_LATE, @@ -327,6 +333,7 @@ static const struct snd_soc_component_driver dmaengine_pcm_component = { .trigger = dmaengine_pcm_trigger, .pointer = dmaengine_pcm_pointer, .pcm_construct = dmaengine_pcm_new, + .sync_stop = dmaengine_pcm_sync_stop, }; static const struct snd_soc_component_driver dmaengine_pcm_component_process = { @@ -339,6 +346,7 @@ static const struct snd_soc_component_driver dmaengine_pcm_component_process = { .pointer = dmaengine_pcm_pointer, .copy = dmaengine_copy, .pcm_construct = dmaengine_pcm_new, + .sync_stop = dmaengine_pcm_sync_stop, }; static const char * const dmaengine_pcm_dma_channel_names[] = { diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index b00ec01361c2..4b166294602f 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1021,6 +1021,7 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_hdr *hdr) { struct snd_soc_dapm_context *dapm = &tplg->comp->dapm; + const size_t maxlen = SNDRV_CTL_ELEM_ID_NAME_MAXLEN; struct snd_soc_tplg_dapm_graph_elem *elem; struct snd_soc_dapm_route *route; int count, i; @@ -1044,31 +1045,27 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, tplg->pos += sizeof(struct snd_soc_tplg_dapm_graph_elem); /* validate routes */ - if (strnlen(elem->source, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { + if ((strnlen(elem->source, maxlen) == maxlen) || + (strnlen(elem->sink, maxlen) == maxlen) || + (strnlen(elem->control, maxlen) == maxlen)) { ret = -EINVAL; break; } - if (strnlen(elem->sink, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { - ret = -EINVAL; - break; - } - if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { - ret = -EINVAL; + + route->source = devm_kstrdup(tplg->dev, elem->source, GFP_KERNEL); + route->sink = devm_kstrdup(tplg->dev, elem->sink, GFP_KERNEL); + if (!route->source || !route->sink) { + ret = -ENOMEM; break; } - route->source = elem->source; - route->sink = elem->sink; - - /* set to NULL atm for tplg users */ - route->connected = NULL; - if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0) - route->control = NULL; - else - route->control = elem->control; + if (strnlen(elem->control, maxlen) != 0) { + route->control = devm_kstrdup(tplg->dev, elem->control, GFP_KERNEL); + if (!route->control) { + ret = -ENOMEM; + break; + } + } /* add route dobj to dobj_list */ route->dobj.type = SND_SOC_DOBJ_GRAPH; diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 5dea5609baf4..11ba89c6b83c 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -163,7 +163,7 @@ static const struct snd_soc_component_driver dummy_codec = { * SND_SOC_POSSIBLE_DAIFMT_CBC_CFP * SND_SOC_POSSIBLE_DAIFMT_CBC_CFC */ -static u64 dummy_dai_formats = +static const u64 dummy_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | SND_SOC_POSSIBLE_DAIFMT_LEFT_J | diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index ce675c22a5ab..c61d298ea6b3 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -379,7 +379,7 @@ static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream, sdev = widget_to_sdev(w); if (sdev->dspless_mode_selected) - goto skip_tlv; + return 0; /* get stream_id */ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); @@ -423,7 +423,6 @@ static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream, dma_config->dma_stream_channel_map.device_count = 1; dma_config->dma_priv_config_size = 0; -skip_tlv: return 0; } @@ -525,6 +524,9 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, return ret; } + if (sdev->dspless_mode_selected) + return 0; + ipc4_copier = widget_to_copier(w); dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id]; dma_config = &dma_config_tlv->dma_config; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 881eec38c2e2..9a52781bf8d8 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -485,7 +485,7 @@ sink_prepare: if (ret < 0) { /* unprepare the source widget */ if (widget_ops[widget->id].ipc_unprepare && - swidget && swidget->prepared) { + swidget && swidget->prepared && swidget->use_count == 0) { widget_ops[widget->id].ipc_unprepare(swidget); swidget->prepared = false; } diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 1e760c315521..2b1ed91a736c 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -1472,10 +1472,11 @@ static int davinci_mcasp_hw_rule_min_periodsize( { struct snd_interval *period_size = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + u8 numevt = *((u8 *)rule->private); struct snd_interval frames; snd_interval_any(&frames); - frames.min = 64; + frames.min = numevt; frames.integer = 1; return snd_interval_refine(period_size, &frames); @@ -1490,6 +1491,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, u32 max_channels = 0; int i, dir, ret; int tdm_slots = mcasp->tdm_slots; + u8 *numevt; /* Do not allow more then one stream per direction */ if (mcasp->substreams[substream->stream]) @@ -1589,9 +1591,12 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, return ret; } + numevt = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + &mcasp->txnumevt : + &mcasp->rxnumevt; snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - davinci_mcasp_hw_rule_min_periodsize, NULL, + davinci_mcasp_hw_rule_min_periodsize, numevt, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); return 0; diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c index 639bc83f4263..cf43ac19c4a6 100644 --- a/sound/soc/ti/omap-hdmi.c +++ b/sound/soc/ti/omap-hdmi.c @@ -354,11 +354,7 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) if (!card) return -ENOMEM; - card->name = devm_kasprintf(dev, GFP_KERNEL, - "HDMI %s", dev_name(ad->dssdev)); - if (!card->name) - return -ENOMEM; - + card->name = "HDMI"; card->owner = THIS_MODULE; card->dai_link = devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL); |