diff options
author | Takashi Iwai <tiwai@suse.de> | 2022-05-23 16:03:04 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2022-05-23 16:03:04 +0200 |
commit | 0163717ed5dec4fc3aaf937baa9f66f21ca11c1d (patch) | |
tree | b0618a944385d1d35bd829c9aa09eddb5c9633fa | |
parent | ALSA: ctxfi: fix typo in comment (diff) | |
parent | ASoC: SOF: Introduce generic (in)firmware tracing infrastructure (diff) | |
download | linux-0163717ed5dec4fc3aaf937baa9f66f21ca11c1d.tar.xz linux-0163717ed5dec4fc3aaf937baa9f66f21ca11c1d.zip |
Merge tag 'asoc-v5.19' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v5.19
This is quite a big update, partly due to the addition of some larger
drivers (more of which is to follow since at least the AVS driver is
still a work in progress) and partly due to Charles' work sorting out
our handling of endianness. As has been the case recently it's much
more about drivers than the core.
- Overhaul of endianness specification for data formats, avoiding
needless restrictions due to CODECs.
- Initial stages of Intel AVS driver merge.
- Introduction of v4 IPC mechanism for SOF.
- TDM mode support for AK4613.
- Support for Analog Devices ADAU1361, Cirrus Logic CS35L45, Maxim
MAX98396, MediaTek MT8186, NXP i.MX8 micfil and SAI interfaces,
nVidia Tegra186 ASRC, and Texas Instruments TAS2764 and TAS2780
521 files changed, 23013 insertions, 7589 deletions
diff --git a/Documentation/devicetree/bindings/dsp/mediatek,mt8195-dsp.yaml b/Documentation/devicetree/bindings/dsp/mediatek,mt8195-dsp.yaml new file mode 100644 index 000000000000..b7e68b0dfa13 --- /dev/null +++ b/Documentation/devicetree/bindings/dsp/mediatek,mt8195-dsp.yaml @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dsp/mediatek,mt8195-dsp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek mt8195 DSP core + +maintainers: + - YC Hung <yc.hung@mediatek.com> + +description: | + Some boards from mt8195 contain a DSP core used for + advanced pre- and post- audio processing. + +properties: + compatible: + const: mediatek,mt8195-dsp + + reg: + items: + - description: Address and size of the DSP Cfg registers + - description: Address and size of the DSP SRAM + + reg-names: + items: + - const: cfg + - const: sram + + clocks: + items: + - description: mux for audio dsp clock + - description: 26M clock + - description: mux for audio dsp local bus + - description: default audio dsp local bus clock source + - description: clock gate for audio dsp clock + - description: mux for audio dsp access external bus + + clock-names: + items: + - const: adsp_sel + - const: clk26m_ck + - const: audio_local_bus + - const: mainpll_d7_d2 + - const: scp_adsp_audiodsp + - const: audio_h + + power-domains: + maxItems: 1 + + mboxes: + items: + - description: ipc reply between host and audio DSP. + - description: ipc request between host and audio DSP. + + mbox-names: + items: + - const: mbox0 + - const: mbox1 + + memory-region: + items: + - description: dma buffer between host and DSP. + - description: DSP system memory. + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - memory-region + - power-domains + - mbox-names + - mboxes + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + dsp@10803000 { + compatible = "mediatek,mt8195-dsp"; + reg = <0x10803000 0x1000>, + <0x10840000 0x40000>; + reg-names = "cfg", "sram"; + clocks = <&topckgen 10>, //CLK_TOP_ADSP + <&clk26m>, + <&topckgen 107>, //CLK_TOP_AUDIO_LOCAL_BUS + <&topckgen 136>, //CLK_TOP_MAINPLL_D7_D2 + <&scp_adsp 0>, //CLK_SCP_ADSP_AUDIODSP + <&topckgen 34>; //CLK_TOP_AUDIO_H + clock-names = "adsp_sel", + "clk26m_ck", + "audio_local_bus", + "mainpll_d7_d2", + "scp_adsp_audiodsp", + "audio_h"; + memory-region = <&adsp_dma_mem_reserved>, + <&adsp_mem_reserved>; + power-domains = <&spm 6>; //MT8195_POWER_DOMAIN_ADSP + mbox-names = "mbox0", "mbox1"; + mboxes = <&adsp_mailbox0>, <&adsp_mailbox1>; + }; diff --git a/Documentation/devicetree/bindings/sound/adi,max98396.yaml b/Documentation/devicetree/bindings/sound/adi,max98396.yaml new file mode 100644 index 000000000000..ec4c10c2598a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/adi,max98396.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/adi,max98396.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices MAX98396 Speaker Amplifier Device Tree Bindings + +maintainers: + - Ryan Lee <ryans.lee@analog.com> + +description: + The MAX98396 is a mono Class-DG speaker amplifier with I/V sense. + The device provides a PCM interface for audio data and a standard + I2C interface for control data communication. + The MAX98397 is a variant of MAX98396 with wide input supply range. + +properties: + compatible: + enum: + - adi,max98396 + - adi,max98397 + reg: + maxItems: 1 + description: I2C address of the device. + + adi,vmon-slot-no: + description: slot number of the voltage sense monitor + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 15 + default: 0 + + adi,imon-slot-no: + description: slot number of the current sense monitor + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 15 + default: 0 + + adi,spkfb-slot-no: + description: slot number of speaker DSP monitor + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 15 + default: 0 + + adi,interleave-mode: + description: + For cases where a single combined channel for the I/V sense data + is not sufficient, the device can also be configured to share + a single data output channel on alternating frames. + In this configuration, the current and voltage data will be frame + interleaved on a single output channel. + type: boolean + + reset-gpios: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + max98396: amplifier@39 { + compatible = "adi,max98396"; + reg = <0x39>; + adi,vmon-slot-no = <0>; + adi,imon-slot-no = <1>; + reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml new file mode 100644 index 000000000000..184a1344ea76 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs35l45.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS35L45 Speaker Amplifier + +maintainers: + - Ricardo Rivera-Matos <rriveram@opensource.cirrus.com> + - Richard Fitzgerald <rf@opensource.cirrus.com> + +description: | + CS35L45 is a Boosted Mono Class D Amplifier with DSP + Speaker Protection and Adaptive Battery Management. + +properties: + compatible: + enum: + - cirrus,cs35l45 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 1 + + reset-gpios: + maxItems: 1 + + vdd-a-supply: + description: voltage regulator phandle for the VDD_A supply + + vdd-batt-supply: + description: voltage regulator phandle for the VDD_BATT supply + + spi-max-frequency: + maximum: 5000000 + + cirrus,asp-sdout-hiz-ctrl: + description: + Audio serial port SDOUT Hi-Z control. Sets the Hi-Z + configuration for SDOUT pin of amplifier. Logical OR of + CS35L45_ASP_TX_HIZ_xxx values. + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 3 + default: 2 + +required: + - compatible + - reg + - "#sound-dai-cells" + +additionalProperties: false + +examples: + - | + #include <dt-bindings/sound/cs35l45.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + cs35l45: cs35l45@2 { + #sound-dai-cells = <1>; + compatible = "cirrus,cs35l45"; + reg = <2>; + spi-max-frequency = <5000000>; + vdd-a-supply = <&dummy_vreg>; + vdd-batt-supply = <&dummy_vreg>; + reset-gpios = <&gpio 110 0>; + cirrus,asp-sdout-hiz-ctrl = <(CS35L45_ASP_TX_HIZ_UNUSED | + CS35L45_ASP_TX_HIZ_DISABLED)>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/fsl,micfil.txt b/Documentation/devicetree/bindings/sound/fsl,micfil.txt index 53e227b15277..1ea05d4996c7 100644 --- a/Documentation/devicetree/bindings/sound/fsl,micfil.txt +++ b/Documentation/devicetree/bindings/sound/fsl,micfil.txt @@ -6,6 +6,7 @@ microphone bitstream in a configurable output sampling rate. Required properties: - compatible : Compatible list, contains "fsl,imx8mm-micfil" + or "fsl,imx8mp-micfil" - reg : Offset and length of the register set for the device. diff --git a/Documentation/devicetree/bindings/sound/maxim,max98390.yaml b/Documentation/devicetree/bindings/sound/maxim,max98390.yaml index fea9a1b6619a..deaa6886c42f 100644 --- a/Documentation/devicetree/bindings/sound/maxim,max98390.yaml +++ b/Documentation/devicetree/bindings/sound/maxim,max98390.yaml @@ -29,6 +29,9 @@ properties: minimum: 1 maximum: 8388607 + reset-gpios: + maxItems: 1 + required: - compatible - reg @@ -37,6 +40,7 @@ additionalProperties: false examples: - | + #include <dt-bindings/gpio/gpio.h> i2c { #address-cells = <1>; #size-cells = <0>; @@ -45,5 +49,6 @@ examples: reg = <0x38>; maxim,temperature_calib = <1024>; maxim,r0_calib = <100232>; + reset-gpios = <&gpio 9 GPIO_ACTIVE_LOW>; }; }; diff --git a/Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml b/Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml index 5a5b765b859a..4fa179909c62 100644 --- a/Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml +++ b/Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml @@ -18,6 +18,7 @@ properties: enum: - mediatek,mt8192_mt6359_rt1015_rt5682 - mediatek,mt8192_mt6359_rt1015p_rt5682 + - mediatek,mt8192_mt6359_rt1015p_rt5682s mediatek,platform: $ref: "/schemas/types.yaml#/definitions/phandle" @@ -27,11 +28,33 @@ properties: $ref: "/schemas/types.yaml#/definitions/phandle" description: The phandle of HDMI codec. + headset-codec: + type: object + properties: + sound-dai: + $ref: /schemas/types.yaml#/definitions/phandle + required: + - sound-dai + + speaker-codecs: + type: object + properties: + sound-dai: + minItems: 1 + maxItems: 2 + items: + maxItems: 1 + $ref: /schemas/types.yaml#/definitions/phandle-array + required: + - sound-dai + additionalProperties: false required: - compatible - mediatek,platform + - headset-codec + - speaker-codecs examples: - | @@ -44,6 +67,15 @@ examples: "aud_clk_mosi_on"; pinctrl-0 = <&aud_clk_mosi_off>; pinctrl-1 = <&aud_clk_mosi_on>; + + headset-codec { + sound-dai = <&rt5682>; + }; + + speaker-codecs { + sound-dai = <&rt1015_l>, + <&rt1015_r>; + }; }; ... diff --git a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml b/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml deleted file mode 100644 index cf6ad7933e23..000000000000 --- a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1011-rt5682.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/sound/mt8195-mt6359-rt1011-rt5682.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Mediatek MT8195 with MT6359, RT1011 and RT5682 ASoC sound card driver - -maintainers: - - Trevor Wu <trevor.wu@mediatek.com> - -description: - This binding describes the MT8195 sound card with RT1011 and RT5682. - -properties: - compatible: - const: mediatek,mt8195_mt6359_rt1011_rt5682 - - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name - - mediatek,platform: - $ref: "/schemas/types.yaml#/definitions/phandle" - description: The phandle of MT8195 ASoC platform. - - mediatek,dptx-codec: - $ref: "/schemas/types.yaml#/definitions/phandle" - description: The phandle of MT8195 Display Port Tx codec node. - - mediatek,hdmi-codec: - $ref: "/schemas/types.yaml#/definitions/phandle" - description: The phandle of MT8195 HDMI codec node. - -additionalProperties: false - -required: - - compatible - - mediatek,platform - -examples: - - | - - sound: mt8195-sound { - compatible = "mediatek,mt8195_mt6359_rt1011_rt5682"; - mediatek,platform = <&afe>; - pinctrl-names = "default"; - pinctrl-0 = <&aud_pins_default>; - }; - -... diff --git a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml b/Documentation/devicetree/bindings/sound/mt8195-mt6359.yaml index 8f177e02ad35..ad3447ff8b2c 100644 --- a/Documentation/devicetree/bindings/sound/mt8195-mt6359-rt1019-rt5682.yaml +++ b/Documentation/devicetree/bindings/sound/mt8195-mt6359.yaml @@ -1,10 +1,10 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/sound/mt8195-mt6359-rt1019-rt5682.yaml# +$id: http://devicetree.org/schemas/sound/mt8195-mt6359.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Mediatek MT8195 with MT6359, RT1019 and RT5682 ASoC sound card driver +title: MediaTek MT8195 ASoC sound card driver maintainers: - Trevor Wu <trevor.wu@mediatek.com> @@ -14,7 +14,10 @@ description: properties: compatible: - const: mediatek,mt8195_mt6359_rt1019_rt5682 + enum: + - mediatek,mt8195_mt6359_rt1019_rt5682 + - mediatek,mt8195_mt6359_rt1011_rt5682 + - mediatek,mt8195_mt6359_max98390_rt5682 model: $ref: /schemas/types.yaml#/definitions/string diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra186-asrc.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra186-asrc.yaml new file mode 100644 index 000000000000..520d0d063d1a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra186-asrc.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra186-asrc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Tegra186 ASRC Device Tree Bindings + +description: | + Asynchronous Sample Rate Converter (ASRC) converts the sampling frequency + of the input signal from one frequency to another. It can handle over a + wide range of sample rate ratios (freq_in/freq_out) from 1:24 to 24:1. + ASRC has two modes of operation. One where ratio can be programmed in SW + and the other where it gets the information from ratio estimator module. + + It supports sample rate conversions in the range of 8 to 192 kHz and + supports 6 streams upto 12 total channels. The input data size can be + 16, 24 and 32 bits. + +maintainers: + - Jon Hunter <jonathanh@nvidia.com> + - Mohan Kumar <mkumard@nvidia.com> + - Sameer Pujar <spujar@nvidia.com> + +allOf: + - $ref: name-prefix.yaml# + +properties: + $nodename: + pattern: "^asrc@[0-9a-f]*$" + + compatible: + oneOf: + - const: nvidia,tegra186-asrc + - items: + - enum: + - nvidia,tegra234-asrc + - nvidia,tegra194-asrc + - const: nvidia,tegra186-asrc + + reg: + maxItems: 1 + + sound-name-prefix: + pattern: "^ASRC[1-9]$" + + ports: + $ref: /schemas/graph.yaml#/properties/ports + description: | + ASRC has seven input ports and six output ports. Accordingly ACIF + (Audio Client Interfaces) port nodes are defined to represent the + ASRC inputs (port 0 to 6) and outputs (port 7 to 12). These are + connected to corresponding ports on AHUB (Audio Hub). Additional + input (port 6) is for receiving ratio information from estimator. + + patternProperties: + '^port@[0-6]': + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + description: ASRC ACIF input ports + '^port@[7-9]|1[1-2]': + $ref: audio-graph-port.yaml# + unevaluatedProperties: false + description: ASRC ACIF output ports + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + + asrc@2910000 { + compatible = "nvidia,tegra186-asrc"; + reg = <0x2910000 0x2000>; + sound-name-prefix = "ASRC1"; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml index 4727f1e42c53..6df6f858038c 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml @@ -106,6 +106,10 @@ patternProperties: type: object $ref: nvidia,tegra210-mixer.yaml# + '^asrc@[0-9a-f]+$': + type: object + $ref: nvidia,tegra186-asrc.yaml# + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml index 2c81efb5fa37..e9a533080b32 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml @@ -38,8 +38,8 @@ properties: maxItems: 7 clock-names: - minItems: 3 - maxItems: 7 + minItems: 1 + maxItems: 10 interrupts: minItems: 2 @@ -62,6 +62,9 @@ properties: power-domains: maxItems: 1 + power-domain-names: + maxItems: 1 + '#sound-dai-cells': const: 1 @@ -192,15 +195,19 @@ allOf: oneOf: - items: #for I2S - const: aon_cc_audio_hm_h + - const: audio_cc_ext_mclk0 - const: core_cc_sysnoc_mport_core + - const: core_cc_ext_if0_ibit - const: core_cc_ext_if1_ibit - items: #for Soundwire - const: aon_cc_audio_hm_h + - const: audio_cc_codec_mem - const: audio_cc_codec_mem0 - const: audio_cc_codec_mem1 - const: audio_cc_codec_mem2 + - const: aon_cc_va_mem0 - items: #for HDMI - - const: aon_cc_audio_hm_h + - const: core_cc_sysnoc_mport_core reg-names: anyOf: @@ -228,6 +235,10 @@ allOf: - const: lpass-irq-hdmi - const: lpass-irq-vaif - const: lpass-irq-rxtxif + power-domain-names: + allOf: + - items: + - const: lcx required: - iommus diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml index 6127df5836c2..a6905bcf89d2 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml @@ -28,12 +28,17 @@ properties: maxItems: 5 clock-names: - items: - - const: mclk - - const: npl - - const: macro - - const: dcodec - - const: fsgen + oneOf: + - items: #for ADSP based platforms + - const: mclk + - const: npl + - const: macro + - const: dcodec + - const: fsgen + - items: #for ADSP bypass based platforms + - const: mclk + - const: npl + - const: fsgen clock-output-names: items: diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml index 3f0f99cb18ca..324595a62ae8 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml @@ -28,12 +28,17 @@ properties: maxItems: 5 clock-names: - items: - - const: mclk - - const: npl - - const: macro - - const: dcodec - - const: fsgen + oneOf: + - items: #for ADSP based platforms + - const: mclk + - const: npl + - const: macro + - const: dcodec + - const: fsgen + - items: #for ADSP bypass based platforms + - const: mclk + - const: npl + - const: fsgen clock-output-names: items: diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml index 9868a5ebe27e..7b4cc84eda8c 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml @@ -28,10 +28,13 @@ properties: maxItems: 3 clock-names: - items: - - const: mclk - - const: core - - const: dcodec + oneOf: + - items: #for ADSP based platforms + - const: mclk + - const: core + - const: dcodec + - items: #for ADSP bypass based platforms + - const: mclk clock-output-names: items: diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml index 7bf1a5fffcd2..51547190f709 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wcd938x.yaml @@ -36,6 +36,9 @@ properties: vdd-io-supply: description: A reference to the 1.8V I/O supply + vdd-mic-bias-supply: + description: A reference to the 3.8V mic bias supply + qcom,tx-device: $ref: /schemas/types.yaml#/definitions/phandle-array description: A reference to Soundwire tx device phandle diff --git a/Documentation/devicetree/bindings/sound/realtek,rt1015p.yaml b/Documentation/devicetree/bindings/sound/realtek,rt1015p.yaml index fdb7f295ef2d..1d73204451b1 100644 --- a/Documentation/devicetree/bindings/sound/realtek,rt1015p.yaml +++ b/Documentation/devicetree/bindings/sound/realtek,rt1015p.yaml @@ -25,6 +25,9 @@ properties: 0 means shut down; 1 means power on. maxItems: 1 + "#sound-dai-cells": + const: 0 + required: - compatible diff --git a/Documentation/devicetree/bindings/sound/rt5682.txt b/Documentation/devicetree/bindings/sound/rt5682.txt index cd8c53d8497e..c5f2b8febcee 100644 --- a/Documentation/devicetree/bindings/sound/rt5682.txt +++ b/Documentation/devicetree/bindings/sound/rt5682.txt @@ -46,6 +46,8 @@ Optional properties: - realtek,dmic-clk-driving-high : Set the high driving of the DMIC clock out. +- #sound-dai-cells: Should be set to '<0>'. + Pins on the device (for linking into audio routes) for RT5682: * DMIC L1 diff --git a/Documentation/devicetree/bindings/sound/tas2764.yaml b/Documentation/devicetree/bindings/sound/tas27xx.yaml index 5bf8c76ecda1..66a0df8850ea 100644 --- a/Documentation/devicetree/bindings/sound/tas2764.yaml +++ b/Documentation/devicetree/bindings/sound/tas27xx.yaml @@ -1,25 +1,26 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -# Copyright (C) 2020 Texas Instruments Incorporated +# Copyright (C) 2020-2022 Texas Instruments Incorporated %YAML 1.2 --- -$id: "http://devicetree.org/schemas/sound/tas2764.yaml#" +$id: "http://devicetree.org/schemas/sound/tas27xx.yaml#" $schema: "http://devicetree.org/meta-schemas/core.yaml#" -title: Texas Instruments TAS2764 Smart PA +title: Texas Instruments TAS2764/TAS2780 Smart PA maintainers: - - Dan Murphy <dmurphy@ti.com> + - Shenghao Ding <shenghao-ding@ti.com> description: | - The TAS2764 is a mono, digital input Class-D audio amplifier optimized for - efficiently driving high peak power into small loudspeakers. - Integrated speaker voltage and current sense provides for - real time monitoring of loudspeaker behavior. + The TAS2764/TAS2780 is a mono, digital input Class-D audio amplifier + optimized for efficiently driving high peak power into small + loudspeakers. Integrated speaker voltage and current sense provides + for real time monitoring of loudspeaker behavior. properties: compatible: enum: - ti,tas2764 + - ti,tas2780 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8731.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8731.yaml new file mode 100644 index 000000000000..e7220e8b49f0 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wlf,wm8731.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/wlf,wm8731.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Wolfson Microelectromics WM8731 audio CODEC + +maintainers: + - patches@opensource.cirrus.com + +description: | + Wolfson Microelectronics WM8731 audio CODEC + + Pins on the device (for linking into audio routes): + * LOUT: Left Channel Line Output + * ROUT: Right Channel Line Output + * LHPOUT: Left Channel Headphone Output + * RHPOUT: Right Channel Headphone Output + * LLINEIN: Left Channel Line Input + * RLINEIN: Right Channel Line Input + * MICIN: Microphone Input + +properties: + compatible: + enum: + - wlf,wm8731 + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 0 + + clocks: + description: Clock provider for MCLK pin. + maxItems: 1 + + clock-names: + items: + - const: mclk + + AVDD-supply: + description: Analog power supply regulator on the AVDD pin. + + HPVDD-supply: + description: Headphone power supply regulator on the HPVDD pin. + + DBVDD-supply: + description: Digital buffer supply regulator for the DBVDD pin. + + DCVDD-supply: + description: Digital core supply regulator for the DCVDD pin. + + spi-max-frequency: true + +additionalProperties: false + +required: + - reg + - compatible + - AVDD-supply + - HPVDD-supply + - DBVDD-supply + - DCVDD-supply + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + wm8731_i2c: codec@0 { + compatible = "wlf,wm8731"; + reg = <0>; + spi-max-frequency = <12500000>; + + AVDD-supply = <&avdd_reg>; + HPVDD-supply = <&hpvdd_reg>; + DCVDD-supply = <&dcvdd_reg>; + DBVDD-supply = <&dbvdd_reg>; + }; + }; + - | + + i2c { + #address-cells = <1>; + #size-cells = <0>; + wm8731_spi: codec@1b { + compatible = "wlf,wm8731"; + reg = <0x1b>; + + AVDD-supply = <&avdd_reg>; + HPVDD-supply = <&hpvdd_reg>; + DCVDD-supply = <&dcvdd_reg>; + DBVDD-supply = <&dbvdd_reg>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8940.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8940.yaml new file mode 100644 index 000000000000..8aadcbeed502 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wlf,wm8940.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/wlf,wm8940.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Wolfson WM8940 Codec Device Tree Bindings + +maintainers: + - patches@opensource.cirrus.com + +properties: + '#sound-dai-cells': + const: 0 + + compatible: + const: wlf,wm8940 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 526000 + +required: + - '#sound-dai-cells' + - compatible + - reg + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + codec@0 { + #sound-dai-cells = <0>; + compatible = "wlf,wm8940"; + reg = <0>; + spi-max-frequency = <500000>; + }; + }; + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + codec@1a { + #sound-dai-cells = <0>; + compatible = "wlf,wm8940"; + reg = <0x1a>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/wm8731.txt b/Documentation/devicetree/bindings/sound/wm8731.txt deleted file mode 100644 index f660d9bb0e69..000000000000 --- a/Documentation/devicetree/bindings/sound/wm8731.txt +++ /dev/null @@ -1,27 +0,0 @@ -WM8731 audio CODEC - -This device supports both I2C and SPI (configured with pin strapping -on the board). - -Required properties: - - - compatible : "wlf,wm8731" - - - reg : the I2C address of the device for I2C, the chip select - number for SPI. - -Example: - -wm8731: codec@1a { - compatible = "wlf,wm8731"; - reg = <0x1a>; -}; - -Available audio endpoints for an audio-routing table: - * LOUT: Left Channel Line Output - * ROUT: Right Channel Line Output - * LHPOUT: Left Channel Headphone Output - * RHPOUT: Right Channel Headphone Output - * LLINEIN: Left Channel Line Input - * RLINEIN: Right Channel Line Input - * MICIN: Microphone Input diff --git a/MAINTAINERS b/MAINTAINERS index 61d9f114c37f..15674926a506 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4688,10 +4688,12 @@ CIRRUS LOGIC AUDIO CODEC DRIVERS M: James Schulman <james.schulman@cirrus.com> M: David Rhodes <david.rhodes@cirrus.com> M: Lucas Tanure <tanureal@opensource.cirrus.com> +M: Richard Fitzgerald <rf@opensource.cirrus.com> L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: patches@opensource.cirrus.com S: Maintained F: Documentation/devicetree/bindings/sound/cirrus,cs* +F: include/dt-bindings/sound/cs* F: sound/pci/hda/cs* F: sound/soc/codecs/cs* diff --git a/arch/arm/configs/multi_v5_defconfig b/arch/arm/configs/multi_v5_defconfig index 70241ad2b550..f8d301150732 100644 --- a/arch/arm/configs/multi_v5_defconfig +++ b/arch/arm/configs/multi_v5_defconfig @@ -211,7 +211,8 @@ CONFIG_SND_ATMEL_SOC_WM8904=m CONFIG_SND_AT91_SOC_SAM9X5_WM8731=m CONFIG_SND_KIRKWOOD_SOC=y CONFIG_SND_SOC_ALC5623=y -CONFIG_SND_SOC_WM8731=y +CONFIG_SND_SOC_WM8731_I2C=y +CONFIG_SND_SOC_WM8731_SPI=y CONFIG_SND_SIMPLE_CARD=y CONFIG_HID_DRAGONRISE=y CONFIG_HID_GYRATION=y diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 2ddc31e64db0..3bffe3ecbd1b 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -25,7 +25,7 @@ #include <linux/of_dma.h> #include <asm/irq.h> -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> #include "dmaengine.h" #define IMXDMA_MAX_CHAN_DESCRIPTORS 16 diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 70c0aa931ddf..95367a8a81a5 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -14,6 +14,7 @@ #include <linux/iopoll.h> #include <linux/module.h> #include <linux/types.h> +#include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/mm.h> #include <linux/interrupt.h> @@ -35,7 +36,7 @@ #include <linux/workqueue.h> #include <asm/irq.h> -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> @@ -73,6 +74,7 @@ #define SDMA_CHNENBL0_IMX35 0x200 #define SDMA_CHNENBL0_IMX31 0x080 #define SDMA_CHNPRI_0 0x100 +#define SDMA_DONE0_CONFIG 0x1000 /* * Buffer descriptor status values. @@ -180,6 +182,12 @@ BIT(DMA_MEM_TO_DEV) | \ BIT(DMA_DEV_TO_DEV)) +#define SDMA_WATERMARK_LEVEL_N_FIFOS GENMASK(15, 12) +#define SDMA_WATERMARK_LEVEL_SW_DONE BIT(23) + +#define SDMA_DONE0_CONFIG_DONE_SEL BIT(7) +#define SDMA_DONE0_CONFIG_DONE_DIS BIT(6) + /** * struct sdma_script_start_addrs - SDMA script start pointers * @@ -441,6 +449,9 @@ struct sdma_channel { struct work_struct terminate_worker; struct list_head terminated; bool is_ram_script; + unsigned int n_fifos_src; + unsigned int n_fifos_dst; + bool sw_done; }; #define IMX_DMA_SG_LOOP BIT(0) @@ -778,6 +789,14 @@ static void sdma_event_enable(struct sdma_channel *sdmac, unsigned int event) val = readl_relaxed(sdma->regs + chnenbl); __set_bit(channel, &val); writel_relaxed(val, sdma->regs + chnenbl); + + /* Set SDMA_DONEx_CONFIG is sw_done enabled */ + if (sdmac->sw_done) { + val = readl_relaxed(sdma->regs + SDMA_DONE0_CONFIG); + val |= SDMA_DONE0_CONFIG_DONE_SEL; + val &= ~SDMA_DONE0_CONFIG_DONE_DIS; + writel_relaxed(val, sdma->regs + SDMA_DONE0_CONFIG); + } } static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event) @@ -940,7 +959,7 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id) /* * sets the pc of SDMA script according to the peripheral type */ -static void sdma_get_pc(struct sdma_channel *sdmac, +static int sdma_get_pc(struct sdma_channel *sdmac, enum sdma_peripheral_type peripheral_type) { struct sdma_engine *sdma = sdmac->sdma; @@ -1038,14 +1057,22 @@ static void sdma_get_pc(struct sdma_channel *sdmac, case IMX_DMATYPE_IPU_MEMORY: emi_2_per = sdma->script_addrs->ext_mem_2_ipu_addr; break; - default: + case IMX_DMATYPE_MULTI_SAI: + per_2_emi = sdma->script_addrs->sai_2_mcu_addr; + emi_2_per = sdma->script_addrs->mcu_2_sai_addr; break; + default: + dev_err(sdma->dev, "Unsupported transfer type %d\n", + peripheral_type); + return -EINVAL; } sdmac->pc_from_device = per_2_emi; sdmac->pc_to_device = emi_2_per; sdmac->device_to_device = per_2_per; sdmac->pc_to_pc = emi_2_emi; + + return 0; } static int sdma_load_context(struct sdma_channel *sdmac) @@ -1210,9 +1237,26 @@ static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac) sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_CONT; } +static void sdma_set_watermarklevel_for_sais(struct sdma_channel *sdmac) +{ + unsigned int n_fifos; + + if (sdmac->sw_done) + sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_SW_DONE; + + if (sdmac->direction == DMA_DEV_TO_MEM) + n_fifos = sdmac->n_fifos_src; + else + n_fifos = sdmac->n_fifos_dst; + + sdmac->watermark_level |= + FIELD_PREP(SDMA_WATERMARK_LEVEL_N_FIFOS, n_fifos); +} + static int sdma_config_channel(struct dma_chan *chan) { struct sdma_channel *sdmac = to_sdma_chan(chan); + int ret; sdma_disable_channel(chan); @@ -1233,7 +1277,9 @@ static int sdma_config_channel(struct dma_chan *chan) break; } - sdma_get_pc(sdmac, sdmac->peripheral_type); + ret = sdma_get_pc(sdmac, sdmac->peripheral_type); + if (ret) + return ret; if ((sdmac->peripheral_type != IMX_DMATYPE_MEMORY) && (sdmac->peripheral_type != IMX_DMATYPE_DSP)) { @@ -1243,6 +1289,10 @@ static int sdma_config_channel(struct dma_chan *chan) sdmac->peripheral_type == IMX_DMATYPE_ASRC) sdma_set_watermarklevel_for_p2p(sdmac); } else { + if (sdmac->peripheral_type == + IMX_DMATYPE_MULTI_SAI) + sdma_set_watermarklevel_for_sais(sdmac); + __set_bit(sdmac->event_id0, sdmac->event_mask); } @@ -1349,7 +1399,9 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan) mem_data.dma_request2 = 0; data = &mem_data; - sdma_get_pc(sdmac, IMX_DMATYPE_MEMORY); + ret = sdma_get_pc(sdmac, IMX_DMATYPE_MEMORY); + if (ret) + return ret; } switch (data->priority) { @@ -1698,9 +1750,23 @@ static int sdma_config(struct dma_chan *chan, struct dma_slave_config *dmaengine_cfg) { struct sdma_channel *sdmac = to_sdma_chan(chan); + struct sdma_engine *sdma = sdmac->sdma; memcpy(&sdmac->slave_config, dmaengine_cfg, sizeof(*dmaengine_cfg)); + if (dmaengine_cfg->peripheral_config) { + struct sdma_peripheral_config *sdmacfg = dmaengine_cfg->peripheral_config; + if (dmaengine_cfg->peripheral_size != sizeof(struct sdma_peripheral_config)) { + dev_err(sdma->dev, "Invalid peripheral size %zu, expected %zu\n", + dmaengine_cfg->peripheral_size, + sizeof(struct sdma_peripheral_config)); + return -EINVAL; + } + sdmac->n_fifos_src = sdmacfg->n_fifos_src; + sdmac->n_fifos_dst = sdmacfg->n_fifos_dst; + sdmac->sw_done = sdmacfg->sw_done; + } + /* Set ENBLn earlier to make sure dma request triggered after that */ if (sdmac->event_id0 >= sdmac->sdma->drvdata->num_events) return -EINVAL; diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index e5cfb01353d8..f7bc8306631b 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -203,6 +203,15 @@ config INTEL_STRATIX10_RSU Say Y here if you want Intel RSU support. +config MTK_ADSP_IPC + tristate "MTK ADSP IPC Protocol driver" + depends on MTK_ADSP_MBOX + help + Say yes here to add support for the MediaTek ADSP IPC + between host AP (Linux) and the firmware running on ADSP. + ADSP exists on some mtk processors. + Client might use shared memory to exchange information with ADSP. + config QCOM_SCM tristate diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 4e58cb474a68..1be0e8295222 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_INTEL_STRATIX10_RSU) += stratix10-rsu.o obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o +obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o obj-$(CONFIG_QCOM_SCM) += qcom-scm.o diff --git a/drivers/firmware/mtk-adsp-ipc.c b/drivers/firmware/mtk-adsp-ipc.c new file mode 100644 index 000000000000..cb255a99170c --- /dev/null +++ b/drivers/firmware/mtk-adsp-ipc.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 MediaTek Corporation. All rights reserved. + * Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com> + */ + +#include <linux/firmware/mediatek/mtk-adsp-ipc.h> +#include <linux/kernel.h> +#include <linux/mailbox_client.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +/* + * mtk_adsp_ipc_send - send ipc cmd to MTK ADSP + * + * @ipc: ADSP IPC handle + * @idx: index of the mailbox channel + * @msg: IPC cmd (reply or request) + * + * Returns zero for success from mbox_send_message + * negative value for error + */ +int mtk_adsp_ipc_send(struct mtk_adsp_ipc *ipc, unsigned int idx, uint32_t msg) +{ + struct mtk_adsp_chan *adsp_chan; + int ret; + + if (idx >= MTK_ADSP_MBOX_NUM) + return -EINVAL; + + adsp_chan = &ipc->chans[idx]; + ret = mbox_send_message(adsp_chan->ch, &msg); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(mtk_adsp_ipc_send); + +/* + * mtk_adsp_ipc_recv - recv callback used by MTK ADSP mailbox + * + * @c: mbox client + * @msg: message received + * + * Users of ADSP IPC will need to privde handle_reply and handle_request + * callbacks. + */ +static void mtk_adsp_ipc_recv(struct mbox_client *c, void *msg) +{ + struct mtk_adsp_chan *chan = container_of(c, struct mtk_adsp_chan, cl); + struct device *dev = c->dev; + + switch (chan->idx) { + case MTK_ADSP_MBOX_REPLY: + chan->ipc->ops->handle_reply(chan->ipc); + break; + case MTK_ADSP_MBOX_REQUEST: + chan->ipc->ops->handle_request(chan->ipc); + break; + default: + dev_err(dev, "wrong mbox chan %d\n", chan->idx); + break; + } +} + +static int mtk_adsp_ipc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_adsp_ipc *adsp_ipc; + struct mtk_adsp_chan *adsp_chan; + struct mbox_client *cl; + char *chan_name; + int ret; + int i, j; + + device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); + + adsp_ipc = devm_kzalloc(dev, sizeof(*adsp_ipc), GFP_KERNEL); + if (!adsp_ipc) + return -ENOMEM; + + for (i = 0; i < MTK_ADSP_MBOX_NUM; i++) { + chan_name = kasprintf(GFP_KERNEL, "mbox%d", i); + if (!chan_name) { + ret = -ENOMEM; + goto out; + } + + adsp_chan = &adsp_ipc->chans[i]; + cl = &adsp_chan->cl; + cl->dev = dev->parent; + cl->tx_block = false; + cl->knows_txdone = false; + cl->tx_prepare = NULL; + cl->rx_callback = mtk_adsp_ipc_recv; + + adsp_chan->ipc = adsp_ipc; + adsp_chan->idx = i; + adsp_chan->ch = mbox_request_channel_byname(cl, chan_name); + if (IS_ERR(adsp_chan->ch)) { + ret = PTR_ERR(adsp_chan->ch); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to request mbox chan %d ret %d\n", + i, ret); + goto out_free; + } + + dev_dbg(dev, "request mbox chan %s\n", chan_name); + kfree(chan_name); + } + + adsp_ipc->dev = dev; + dev_set_drvdata(dev, adsp_ipc); + dev_dbg(dev, "MTK ADSP IPC initialized\n"); + + return 0; + +out_free: + kfree(chan_name); +out: + for (j = 0; j < i; j++) { + adsp_chan = &adsp_ipc->chans[j]; + mbox_free_channel(adsp_chan->ch); + } + + return ret; +} + +static int mtk_adsp_ipc_remove(struct platform_device *pdev) +{ + struct mtk_adsp_ipc *adsp_ipc = dev_get_drvdata(&pdev->dev); + struct mtk_adsp_chan *adsp_chan; + int i; + + for (i = 0; i < MTK_ADSP_MBOX_NUM; i++) { + adsp_chan = &adsp_ipc->chans[i]; + mbox_free_channel(adsp_chan->ch); + } + + return 0; +} + +static struct platform_driver mtk_adsp_ipc_driver = { + .driver = { + .name = "mtk-adsp-ipc", + }, + .probe = mtk_adsp_ipc_probe, + .remove = mtk_adsp_ipc_remove, +}; +builtin_platform_driver(mtk_adsp_ipc_driver); + +MODULE_AUTHOR("Allen-KH Cheng <allen-kh.cheng@mediatek.com>"); +MODULE_DESCRIPTION("MTK ADSP IPC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 40b6878bea6c..de04b5afef2e 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -39,7 +39,7 @@ #include <asm/irq.h> #include <linux/platform_data/mmc-mxcmmc.h> -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> #define DRIVER_NAME "mxc-mmc" #define MXCMCI_TIMEOUT_MS 10000 diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 4c601294f8fa..19b1f3d881b0 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -20,7 +20,7 @@ #include <linux/of_device.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/spi/spi.h> diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index b2dd0a4d2446..a944c787f53f 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -24,7 +24,7 @@ #include <linux/of_device.h> #include <linux/property.h> -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> #define DRIVER_NAME "spi_imx" diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index b589cf6b1d03..db0b600ee5d1 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -702,8 +702,9 @@ static int gbaudio_init_jack(struct gbaudio_module_info *module, headset->pin = module->jack_name; headset->mask = module->jack_mask; - ret = snd_soc_card_jack_new(card, module->jack_name, module->jack_mask, - &module->headset.jack, headset, 1); + ret = snd_soc_card_jack_new_pins(card, module->jack_name, + module->jack_mask, + &module->headset.jack, headset, 1); if (ret) { dev_err(module->dev, "Failed to create new jack\n"); return ret; @@ -725,9 +726,10 @@ static int gbaudio_init_jack(struct gbaudio_module_info *module, button->pin = module->button_name; button->mask = module->button_mask; - ret = snd_soc_card_jack_new(card, module->button_name, - module->button_mask, &module->button.jack, - button, 1); + ret = snd_soc_card_jack_new_pins(card, module->button_name, + module->button_mask, + &module->button.jack, + button, 1); if (ret) { dev_err(module->dev, "Failed to create button jack\n"); goto free_jacks; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index fd38e6ed4fda..f8b5400e6267 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -30,7 +30,7 @@ #include <linux/dma-mapping.h> #include <asm/irq.h> -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> #include "serial_mctrl_gpio.h" diff --git a/drivers/video/fbdev/mx3fb.c b/drivers/video/fbdev/mx3fb.c index fabb271337ed..b945b68984b9 100644 --- a/drivers/video/fbdev/mx3fb.c +++ b/drivers/video/fbdev/mx3fb.c @@ -26,7 +26,7 @@ #include <linux/dma/ipu-dma.h> #include <linux/backlight.h> -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> #include <linux/platform_data/video-mx3fb.h> #include <asm/io.h> diff --git a/include/dt-bindings/sound/cs35l45.h b/include/dt-bindings/sound/cs35l45.h new file mode 100644 index 000000000000..076da4b2c28d --- /dev/null +++ b/include/dt-bindings/sound/cs35l45.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * cs35l45.h -- CS35L45 ALSA SoC audio driver DT bindings header + * + * Copyright 2022 Cirrus Logic, Inc. + */ + +#ifndef DT_CS35L45_H +#define DT_CS35L45_H + +/* + * cirrus,asp-sdout-hiz-ctrl + * + * TX_HIZ_UNUSED: TX pin high-impedance during unused slots. + * TX_HIZ_DISABLED: TX pin high-impedance when all channels disabled. + */ +#define CS35L45_ASP_TX_HIZ_UNUSED 0x1 +#define CS35L45_ASP_TX_HIZ_DISABLED 0x2 + +#endif /* DT_CS35L45_H */ diff --git a/include/linux/platform_data/dma-imx.h b/include/linux/dma/imx-dma.h index 281adbb26e6b..8887762360d4 100644 --- a/include/linux/platform_data/dma-imx.h +++ b/include/linux/dma/imx-dma.h @@ -3,8 +3,8 @@ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved. */ -#ifndef __ASM_ARCH_MXC_DMA_H__ -#define __ASM_ARCH_MXC_DMA_H__ +#ifndef __LINUX_DMA_IMX_H +#define __LINUX_DMA_IMX_H #include <linux/scatterlist.h> #include <linux/device.h> @@ -39,6 +39,7 @@ enum sdma_peripheral_type { IMX_DMATYPE_SSI_DUAL, /* SSI Dual FIFO */ IMX_DMATYPE_ASRC_SP, /* Shared ASRC */ IMX_DMATYPE_SAI, /* SAI */ + IMX_DMATYPE_MULTI_SAI, /* MULTI FIFOs For Audio */ }; enum imx_dma_prio { @@ -65,4 +66,23 @@ static inline int imx_dma_is_general_purpose(struct dma_chan *chan) !strcmp(chan->device->dev->driver->name, "imx-dma"); } -#endif +/** + * struct sdma_peripheral_config - SDMA config for audio + * @n_fifos_src: Number of FIFOs for recording + * @n_fifos_dst: Number of FIFOs for playback + * @sw_done: Use software done. Needed for PDM (micfil) + * + * Some i.MX Audio devices (SAI, micfil) have multiple successive FIFO + * registers. For multichannel recording/playback the SAI/micfil have + * one FIFO register per channel and the SDMA engine has to read/write + * the next channel from/to the next register and wrap around to the + * first register when all channels are handled. The number of active + * channels must be communicated to the SDMA engine using this struct. + */ +struct sdma_peripheral_config { + int n_fifos_src; + int n_fifos_dst; + bool sw_done; +}; + +#endif /* __LINUX_DMA_IMX_H */ diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h index 38b4da3ddfe4..30055706cce2 100644 --- a/include/linux/firmware/cirrus/cs_dsp.h +++ b/include/linux/firmware/cirrus/cs_dsp.h @@ -68,36 +68,36 @@ struct cs_dsp_alg_region { /** * struct cs_dsp_coeff_ctl - Describes a coefficient control + * @list: List node for internal use + * @dsp: DSP instance associated with this control + * @cache: Cached value of the control * @fw_name: Name of the firmware * @subname: Name of the control parsed from the WMFW * @subname_len: Length of subname - * @alg_region: Logical region associated with this control - * @dsp: DSP instance associated with this control - * @enabled: Flag indicating whether control is enabled - * @list: List node for internal use - * @cache: Cached value of the control * @offset: Offset of control within alg_region in words * @len: Length of the cached value in bytes - * @set: Flag indicating the value has been written by the user - * @flags: Bitfield of WMFW_CTL_FLAG_ control flags defined in wmfw.h * @type: One of the WMFW_CTL_TYPE_ control types defined in wmfw.h + * @flags: Bitfield of WMFW_CTL_FLAG_ control flags defined in wmfw.h + * @set: Flag indicating the value has been written by the user + * @enabled: Flag indicating whether control is enabled + * @alg_region: Logical region associated with this control * @priv: For use by the client */ struct cs_dsp_coeff_ctl { + struct list_head list; + struct cs_dsp *dsp; + void *cache; const char *fw_name; /* Subname is needed to match with firmware */ const char *subname; unsigned int subname_len; - struct cs_dsp_alg_region alg_region; - struct cs_dsp *dsp; - unsigned int enabled:1; - struct list_head list; - void *cache; unsigned int offset; size_t len; - unsigned int set:1; - unsigned int flags; unsigned int type; + unsigned int flags; + unsigned int set:1; + unsigned int enabled:1; + struct cs_dsp_alg_region alg_region; void *priv; }; diff --git a/include/linux/firmware/mediatek/mtk-adsp-ipc.h b/include/linux/firmware/mediatek/mtk-adsp-ipc.h new file mode 100644 index 000000000000..28fd313340b8 --- /dev/null +++ b/include/linux/firmware/mediatek/mtk-adsp-ipc.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022 MediaTek Inc. + */ + +#ifndef MTK_ADSP_IPC_H +#define MTK_ADSP_IPC_H + +#include <linux/device.h> +#include <linux/types.h> +#include <linux/mailbox_controller.h> +#include <linux/mailbox_client.h> + +#define MTK_ADSP_IPC_REQ 0 +#define MTK_ADSP_IPC_RSP 1 +#define MTK_ADSP_IPC_OP_REQ 0x1 +#define MTK_ADSP_IPC_OP_RSP 0x2 + +enum { + MTK_ADSP_MBOX_REPLY, + MTK_ADSP_MBOX_REQUEST, + MTK_ADSP_MBOX_NUM, +}; + +struct mtk_adsp_ipc; + +struct mtk_adsp_ipc_ops { + void (*handle_reply)(struct mtk_adsp_ipc *ipc); + void (*handle_request)(struct mtk_adsp_ipc *ipc); +}; + +struct mtk_adsp_chan { + struct mtk_adsp_ipc *ipc; + struct mbox_client cl; + struct mbox_chan *ch; + char *name; + int idx; +}; + +struct mtk_adsp_ipc { + struct mtk_adsp_chan chans[MTK_ADSP_MBOX_NUM]; + struct device *dev; + struct mtk_adsp_ipc_ops *ops; + void *private_data; +}; + +static inline void mtk_adsp_ipc_set_data(struct mtk_adsp_ipc *ipc, void *data) +{ + if (!ipc) + return; + + ipc->private_data = data; +} + +static inline void *mtk_adsp_ipc_get_data(struct mtk_adsp_ipc *ipc) +{ + if (!ipc) + return NULL; + + return ipc->private_data; +} + +int mtk_adsp_ipc_send(struct mtk_adsp_ipc *ipc, unsigned int idx, uint32_t op); + +#endif /* MTK_ADSP_IPC_H */ diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index dd70fb8dd860..8972fa697622 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -537,7 +537,6 @@ #define CS35L41_MAX_CACHE_REG 36 #define CS35L41_OTP_SIZE_WORDS 32 -#define CS35L41_NUM_OTP_ELEM 100 #define CS35L41_NUM_SUPPLIES 2 diff --git a/include/sound/intel-dsp-config.h b/include/sound/intel-dsp-config.h index d4609077c258..34c975910574 100644 --- a/include/sound/intel-dsp-config.h +++ b/include/sound/intel-dsp-config.h @@ -15,7 +15,8 @@ enum { SND_INTEL_DSP_DRIVER_LEGACY, SND_INTEL_DSP_DRIVER_SST, SND_INTEL_DSP_DRIVER_SOF, - SND_INTEL_DSP_DRIVER_LAST = SND_INTEL_DSP_DRIVER_SOF + SND_INTEL_DSP_DRIVER_AVS, + SND_INTEL_DSP_DRIVER_LAST = SND_INTEL_DSP_DRIVER_AVS }; #if IS_ENABLED(CONFIG_SND_INTEL_DSP_CONFIG) diff --git a/include/sound/intel-nhlt.h b/include/sound/intel-nhlt.h index 6fb2d5e378fd..3d5cf201cd80 100644 --- a/include/sound/intel-nhlt.h +++ b/include/sound/intel-nhlt.h @@ -25,8 +25,6 @@ enum nhlt_device_type { NHLT_DEVICE_INVALID }; -#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT) - struct wav_fmt { u16 fmt_tag; u16 channels; @@ -126,6 +124,8 @@ enum { NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf, }; +#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT) + struct nhlt_acpi_table *intel_nhlt_init(struct device *dev); void intel_nhlt_free(struct nhlt_acpi_table *addr); @@ -143,8 +143,6 @@ intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, #else -struct nhlt_acpi_table; - static inline struct nhlt_acpi_table *intel_nhlt_init(struct device *dev) { return NULL; diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index d33cf8df14b1..b38fd25c5729 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -156,6 +156,7 @@ struct snd_soc_acpi_link_adr { * @links: array of link _ADR descriptors, null terminated. * @drv_name: machine driver name * @fw_filename: firmware file name. Used when SOF is not enabled. + * @tplg_filename: topology file name. Used when SOF is not enabled. * @board: board name * @machine_quirk: pointer to quirk, usually based on DMI information when * ACPI ID alone is not sufficient, wrong or misleading @@ -174,6 +175,7 @@ struct snd_soc_acpi_mach { const struct snd_soc_acpi_link_adr *links; const char *drv_name; const char *fw_filename; + const char *tplg_filename; const char *board; struct snd_soc_acpi_mach * (*machine_quirk)(void *arg); const void *quirk_data; diff --git a/include/sound/soc-card.h b/include/sound/soc-card.h index 4f2cc4fb56b7..df08573bd80c 100644 --- a/include/sound/soc-card.h +++ b/include/sound/soc-card.h @@ -16,8 +16,11 @@ enum snd_soc_card_subclass { struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, const char *name); int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, - struct snd_soc_jack *jack, - struct snd_soc_jack_pin *pins, unsigned int num_pins); + struct snd_soc_jack *jack); +int snd_soc_card_jack_new_pins(struct snd_soc_card *card, const char *id, + int type, struct snd_soc_jack *jack, + struct snd_soc_jack_pin *pins, + unsigned int num_pins); int snd_soc_card_suspend_pre(struct snd_soc_card *card); int snd_soc_card_suspend_post(struct snd_soc_card *card); diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 766dc6f009c0..5a764c3099d3 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -169,6 +169,15 @@ struct snd_soc_component_driver { unsigned int idle_bias_on:1; unsigned int suspend_bias_off:1; unsigned int use_pmdown_time:1; /* care pmdown_time at stop */ + /* + * Indicates that the component does not care about the endianness of + * PCM audio data and the core will ensure that both LE and BE variants + * of each used format are present. Typically this is because the + * component sits behind a bus that abstracts away the endian of the + * original data, ie. one for which the transmission endian is defined + * (I2S/SLIMbus/SoundWire), or the concept of endian doesn't exist (PDM, + * analogue). + */ unsigned int endianness:1; unsigned int non_legacy_dai_naming:1; diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 75b92d883976..5b689c663290 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -103,6 +103,8 @@ struct snd_soc_dpcm_runtime { int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */ int be_start; /* refcount protected by BE stream pcm lock */ + int be_pause; /* refcount protected by BE stream pcm lock */ + bool fe_pause; /* used to track STOP after PAUSE */ }; #define for_each_dpcm_fe(be, stream, _dpcm) \ diff --git a/include/sound/soc.h b/include/sound/soc.h index 7a1650b303f1..f20f5f890794 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -176,6 +176,8 @@ .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_R_S_VALUE(reg_left, reg_right, xshift, \ xmin, xmax, xsign_bit, xinvert) } +#define SOC_SINGLE_S_TLV(xname, xreg, xshift, xmin, xmax, xsign_bit, xinvert, tlv_array) \ + SOC_DOUBLE_R_S_TLV(xname, xreg, xreg, xshift, xmin, xmax, xsign_bit, xinvert, tlv_array) #define SOC_SINGLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ @@ -280,6 +282,23 @@ .get = xhandler_get, .put = xhandler_put, \ .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ xmax, xinvert) } +#define SOC_DOUBLE_R_S_EXT_TLV(xname, reg_left, reg_right, xshift, xmin, xmax, \ + xsign_bit, xinvert, xhandler_get, xhandler_put, \ + tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_DOUBLE_R_S_VALUE(reg_left, reg_right, xshift, \ + xmin, xmax, xsign_bit, xinvert) } +#define SOC_SINGLE_S_EXT_TLV(xname, xreg, xshift, xmin, xmax, \ + xsign_bit, xinvert, xhandler_get, xhandler_put, \ + tlv_array) \ + SOC_DOUBLE_R_S_EXT_TLV(xname, xreg, xreg, xshift, xmin, xmax, \ + xsign_bit, xinvert, xhandler_get, xhandler_put, \ + tlv_array) #define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_bool_ext, \ @@ -485,6 +504,8 @@ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params); int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots); int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms); +int snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params, + int tdm_width, int tdm_slots, int slot_multiple); /* set runtime hw params */ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, @@ -1238,7 +1259,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); int snd_soc_of_parse_aux_devs(struct snd_soc_card *card, const char *propname); -unsigned int snd_soc_daifmt_clock_provider_fliped(unsigned int dai_fmt); +unsigned int snd_soc_daifmt_clock_provider_flipped(unsigned int dai_fmt); unsigned int snd_soc_daifmt_clock_provider_from_bitmap(unsigned int bit_frame); unsigned int snd_soc_daifmt_parse_format(struct device_node *np, const char *prefix); @@ -1263,6 +1284,10 @@ int snd_soc_of_get_dai_link_codecs(struct device *dev, struct device_node *of_node, struct snd_soc_dai_link *dai_link); void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link); +int snd_soc_of_get_dai_link_cpus(struct device *dev, + struct device_node *of_node, + struct snd_soc_dai_link *dai_link); +void snd_soc_of_put_dai_link_cpus(struct snd_soc_dai_link *dai_link); int snd_soc_add_pcm_runtime(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); diff --git a/include/sound/sof.h b/include/sound/sof.h index 7cdfc954df12..1a82a0db5e7f 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -16,6 +16,7 @@ #include <sound/soc-acpi.h> struct snd_sof_dsp_ops; +struct snd_sof_dev; /** * enum sof_fw_state - DSP firmware state definitions @@ -47,6 +48,13 @@ enum sof_dsp_power_states { SOF_DSP_PM_D3, }; +/* Definitions for multiple IPCs */ +enum sof_ipc_type { + SOF_IPC, + SOF_INTEL_IPC4, + SOF_IPC_TYPE_COUNT +}; + /* * SOF Platform data. */ @@ -83,6 +91,8 @@ struct snd_sof_pdata { const struct snd_soc_acpi_mach *machine; void *hw_pdata; + + enum sof_ipc_type ipc_type; }; /* @@ -115,14 +125,19 @@ struct sof_dev_desc { /* defaults for no codec mode */ const char *nocodec_tplg_filename; + /* information on supported IPCs */ + unsigned int ipc_supported_mask; + enum sof_ipc_type ipc_default; + /* defaults paths for firmware and topology files */ - const char *default_fw_path; - const char *default_tplg_path; + const char *default_fw_path[SOF_IPC_TYPE_COUNT]; + const char *default_tplg_path[SOF_IPC_TYPE_COUNT]; /* default firmware name */ - const char *default_fw_filename; + const char *default_fw_filename[SOF_IPC_TYPE_COUNT]; - const struct snd_sof_dsp_ops *ops; + struct snd_sof_dsp_ops *ops; + int (*ops_init)(struct snd_sof_dev *sdev); }; int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd); diff --git a/include/sound/sof/ext_manifest4.h b/include/sound/sof/ext_manifest4.h new file mode 100644 index 000000000000..ec97edcbbfc3 --- /dev/null +++ b/include/sound/sof/ext_manifest4.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2022 Intel Corporation. All rights reserved. + */ + +/* + * Extended manifest is a place to store metadata about firmware, known during + * compilation time - for example firmware version or used compiler. + * Given information are read on host side before firmware startup. + * This part of output binary is not signed. + */ + +#ifndef __SOF_FIRMWARE_EXT_MANIFEST4_H__ +#define __SOF_FIRMWARE_EXT_MANIFEST4_H__ + +#include <linux/uuid.h> + +/* In ASCII $AE1 */ +#define SOF_EXT_MAN4_MAGIC_NUMBER 0x31454124 + +#define MAX_MODULE_NAME_LEN 8 +#define MAX_FW_BINARY_NAME 8 +#define DEFAULT_HASH_SHA256_LEN 32 +#define SOF_MAN4_FW_HDR_OFFSET 0x2000 +#define SOF_MAN4_FW_HDR_OFFSET_CAVS_1_5 0x284 + +/********************************************************************* + * extended manifest (struct sof_ext_manifest4_hdr) + *------------------- + * css_manifest hdr + *------------------- + * offset reserved for future + *------------------- + * fw_hdr (struct sof_man4_fw_binary_header) + *------------------- + * module_entry[0] (struct sof_man4_module) + *------------------- + * module_entry[1] + *------------------- + * ... + *------------------- + * module_entry[n] + *------------------- + * module_config[0] (struct sof_man4_module_config) + *------------------- + * module_config[1] + *------------------- + * ... + *------------------- + * module_config[m] + *------------------- + * FW content + *------------------- + *********************************************************************/ + +struct sof_ext_manifest4_hdr { + uint32_t id; + uint32_t len; /* length of extension manifest */ + uint16_t version_major; /* header version */ + uint16_t version_minor; + uint32_t num_module_entries; +} __packed; + +struct sof_man4_fw_binary_header { + /* This part must be unchanged to be backward compatible with SPT-LP ROM */ + uint32_t id; + uint32_t len; /* sizeof(sof_man4_fw_binary_header) in bytes */ + uint8_t name[MAX_FW_BINARY_NAME]; + uint32_t preload_page_count; /* number of pages of preloaded image */ + uint32_t fw_image_flags; + uint32_t feature_mask; + uint16_t major_version; /* Firmware version */ + uint16_t minor_version; + uint16_t hotfix_version; + uint16_t build_version; + uint32_t num_module_entries; + + /* This part may change to contain any additional data for BaseFw that is skipped by ROM */ + uint32_t hw_buf_base_addr; + uint32_t hw_buf_length; + uint32_t load_offset; /* This value is used by ROM */ +} __packed; + +struct sof_man4_segment_desc { + uint32_t flags; + uint32_t v_base_addr; + uint32_t file_offset; +} __packed; + +struct sof_man4_module { + uint32_t id; + uint8_t name[MAX_MODULE_NAME_LEN]; + guid_t uuid; + uint32_t type; + uint8_t hash[DEFAULT_HASH_SHA256_LEN]; + uint32_t entry_point; + uint16_t cfg_offset; + uint16_t cfg_count; + uint32_t affinity_mask; + uint16_t instance_max_count; + uint16_t instance_stack_size; + struct sof_man4_segment_desc segments[3]; +} __packed; + +struct sof_man4_module_config { + uint32_t par[4]; /* module parameters */ + uint32_t is_bytes; /* actual size of instance .bss (bytes) */ + uint32_t cps; /* cycles per second */ + uint32_t ibs; /* input buffer size (bytes) */ + uint32_t obs; /* output buffer size (bytes) */ + uint32_t module_flags; /* flags, reserved for future use */ + uint32_t cpc; /* cycles per single run */ + uint32_t obls; /* output block size, reserved for future use */ +} __packed; + +#endif /* __SOF_FIRMWARE_EXT_MANIFEST4_H__ */ diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h new file mode 100644 index 000000000000..b8b8e5b5e3e1 --- /dev/null +++ b/include/sound/sof/ipc4/header.h @@ -0,0 +1,460 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2022 Intel Corporation. All rights reserved. + */ + +#ifndef __INCLUDE_SOUND_SOF_IPC4_HEADER_H__ +#define __INCLUDE_SOUND_SOF_IPC4_HEADER_H__ + +#include <linux/types.h> +#include <uapi/sound/sof/abi.h> + +/* maximum message size for mailbox Tx/Rx */ +#define SOF_IPC4_MSG_MAX_SIZE 4096 + +/** \addtogroup sof_uapi uAPI + * SOF uAPI specification. + * @{ + */ + +/** + * struct sof_ipc4_msg - Placeholder of an IPC4 message + * @header_u64: IPC4 header as single u64 number + * @primary: Primary, mandatory part of the header + * @extension: Extended part of the header, if not used it should be + * set to 0 + * @data_size: Size of data in bytes pointed by @data_ptr + * @data_ptr: Pointer to the optional payload of a message + */ +struct sof_ipc4_msg { + union { + u64 header_u64; + struct { + u32 primary; + u32 extension; + }; + }; + + size_t data_size; + void *data_ptr; +}; + +/** + * struct sof_ipc4_tuple - Generic type/ID and parameter tuple + * @type: type/ID + * @size: size of the @value array in bytes + * @value: value for the given type + */ +struct sof_ipc4_tuple { + uint32_t type; + uint32_t size; + uint32_t value[]; +} __packed; + +/* + * IPC4 messages have two 32 bit identifier made up as follows :- + * + * header - msg type, msg id, msg direction ... + * extension - extra params such as msg data size in mailbox + * + * These are sent at the start of the IPC message in the mailbox. Messages + * should not be sent in the doorbell (special exceptions for firmware). + */ + +/* + * IPC4 primary header bit allocation for messages + * bit 0-23: message type specific + * bit 24-28: type: enum sof_ipc4_global_msg if target is SOF_IPC4_FW_GEN_MSG + * enum sof_ipc4_module_type if target is SOF_IPC4_MODULE_MSG + * bit 29: response - sof_ipc4_msg_dir + * bit 30: target - enum sof_ipc4_msg_target + * bit 31: reserved, unused + */ + +/* Value of target field - must fit into 1 bit */ +enum sof_ipc4_msg_target { + /* Global FW message */ + SOF_IPC4_FW_GEN_MSG, + + /* Module message */ + SOF_IPC4_MODULE_MSG +}; + +/* Value of type field - must fit into 5 bits */ +enum sof_ipc4_global_msg { + SOF_IPC4_GLB_BOOT_CONFIG, + SOF_IPC4_GLB_ROM_CONTROL, + SOF_IPC4_GLB_IPCGATEWAY_CMD, + + /* 3 .. 12: RESERVED - do not use */ + + SOF_IPC4_GLB_PERF_MEASUREMENTS_CMD = 13, + SOF_IPC4_GLB_CHAIN_DMA, + + SOF_IPC4_GLB_LOAD_MULTIPLE_MODULES, + SOF_IPC4_GLB_UNLOAD_MULTIPLE_MODULES, + + /* pipeline settings */ + SOF_IPC4_GLB_CREATE_PIPELINE, + SOF_IPC4_GLB_DELETE_PIPELINE, + SOF_IPC4_GLB_SET_PIPELINE_STATE, + SOF_IPC4_GLB_GET_PIPELINE_STATE, + SOF_IPC4_GLB_GET_PIPELINE_CONTEXT_SIZE, + SOF_IPC4_GLB_SAVE_PIPELINE, + SOF_IPC4_GLB_RESTORE_PIPELINE, + + /* Loads library (using Code Load or HD/A Host Output DMA) */ + SOF_IPC4_GLB_LOAD_LIBRARY, + + /* 25: RESERVED - do not use */ + + SOF_IPC4_GLB_INTERNAL_MESSAGE = 26, + + /* Notification (FW to SW driver) */ + SOF_IPC4_GLB_NOTIFICATION, + + /* 28 .. 31: RESERVED - do not use */ + + SOF_IPC4_GLB_TYPE_LAST, +}; + +/* Value of response field - must fit into 1 bit */ +enum sof_ipc4_msg_dir { + SOF_IPC4_MSG_REQUEST, + SOF_IPC4_MSG_REPLY, +}; + +enum sof_ipc4_pipeline_state { + SOF_IPC4_PIPE_INVALID_STATE, + SOF_IPC4_PIPE_UNINITIALIZED, + SOF_IPC4_PIPE_RESET, + SOF_IPC4_PIPE_PAUSED, + SOF_IPC4_PIPE_RUNNING, + SOF_IPC4_PIPE_EOS +}; + +/* Generic message fields (bit 24-30) */ + +/* encoded to header's msg_tgt field */ +#define SOF_IPC4_MSG_TARGET_SHIFT 30 +#define SOF_IPC4_MSG_TARGET_MASK BIT(30) +#define SOF_IPC4_MSG_TARGET(x) ((x) << SOF_IPC4_MSG_TARGET_SHIFT) +#define SOF_IPC4_MSG_IS_MODULE_MSG(x) ((x) & SOF_IPC4_MSG_TARGET_MASK ? 1 : 0) + +/* encoded to header's rsp field */ +#define SOF_IPC4_MSG_DIR_SHIFT 29 +#define SOF_IPC4_MSG_DIR_MASK BIT(29) +#define SOF_IPC4_MSG_DIR(x) ((x) << SOF_IPC4_MSG_DIR_SHIFT) + +/* encoded to header's type field */ +#define SOF_IPC4_MSG_TYPE_SHIFT 24 +#define SOF_IPC4_MSG_TYPE_MASK GENMASK(28, 24) +#define SOF_IPC4_MSG_TYPE_SET(x) (((x) << SOF_IPC4_MSG_TYPE_SHIFT) & \ + SOF_IPC4_MSG_TYPE_MASK) +#define SOF_IPC4_MSG_TYPE_GET(x) (((x) & SOF_IPC4_MSG_TYPE_MASK) >> \ + SOF_IPC4_MSG_TYPE_SHIFT) + +/* Global message type specific field definitions */ + +/* pipeline creation ipc msg */ +#define SOF_IPC4_GLB_PIPE_INSTANCE_SHIFT 16 +#define SOF_IPC4_GLB_PIPE_INSTANCE_MASK GENMASK(23, 16) +#define SOF_IPC4_GLB_PIPE_INSTANCE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_INSTANCE_SHIFT) + +#define SOF_IPC4_GLB_PIPE_PRIORITY_SHIFT 11 +#define SOF_IPC4_GLB_PIPE_PRIORITY_MASK GENMASK(15, 11) +#define SOF_IPC4_GLB_PIPE_PRIORITY(x) ((x) << SOF_IPC4_GLB_PIPE_PRIORITY_SHIFT) + +#define SOF_IPC4_GLB_PIPE_MEM_SIZE_SHIFT 0 +#define SOF_IPC4_GLB_PIPE_MEM_SIZE_MASK GENMASK(10, 0) +#define SOF_IPC4_GLB_PIPE_MEM_SIZE(x) ((x) << SOF_IPC4_GLB_PIPE_MEM_SIZE_SHIFT) + +#define SOF_IPC4_GLB_PIPE_EXT_LP_SHIFT 0 +#define SOF_IPC4_GLB_PIPE_EXT_LP_MASK BIT(0) +#define SOF_IPC4_GLB_PIPE_EXT_LP(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_LP_SHIFT) + +/* pipeline set state ipc msg */ +#define SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT 16 +#define SOF_IPC4_GLB_PIPE_STATE_ID_MASK GENMASK(23, 16) +#define SOF_IPC4_GLB_PIPE_STATE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT) + +#define SOF_IPC4_GLB_PIPE_STATE_SHIFT 0 +#define SOF_IPC4_GLB_PIPE_STATE_MASK GENMASK(15, 0) +#define SOF_IPC4_GLB_PIPE_STATE(x) ((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT) + +enum sof_ipc4_channel_config { + /* one channel only. */ + SOF_IPC4_CHANNEL_CONFIG_MONO, + /* L & R. */ + SOF_IPC4_CHANNEL_CONFIG_STEREO, + /* L, R & LFE; PCM only. */ + SOF_IPC4_CHANNEL_CONFIG_2_POINT_1, + /* L, C & R; MP3 & AAC only. */ + SOF_IPC4_CHANNEL_CONFIG_3_POINT_0, + /* L, C, R & LFE; PCM only. */ + SOF_IPC4_CHANNEL_CONFIG_3_POINT_1, + /* L, R, Ls & Rs; PCM only. */ + SOF_IPC4_CHANNEL_CONFIG_QUATRO, + /* L, C, R & Cs; MP3 & AAC only. */ + SOF_IPC4_CHANNEL_CONFIG_4_POINT_0, + /* L, C, R, Ls & Rs. */ + SOF_IPC4_CHANNEL_CONFIG_5_POINT_0, + /* L, C, R, Ls, Rs & LFE. */ + SOF_IPC4_CHANNEL_CONFIG_5_POINT_1, + /* one channel replicated in two. */ + SOF_IPC4_CHANNEL_CONFIG_DUAL_MONO, + /* Stereo (L,R) in 4 slots, 1st stream: [ L, R, -, - ] */ + SOF_IPC4_CHANNEL_CONFIG_I2S_DUAL_STEREO_0, + /* Stereo (L,R) in 4 slots, 2nd stream: [ -, -, L, R ] */ + SOF_IPC4_CHANNEL_CONFIG_I2S_DUAL_STEREO_1, + /* L, C, R, Ls, Rs & LFE., LS, RS */ + SOF_IPC4_CHANNEL_CONFIG_7_POINT_1, +}; + +enum sof_ipc4_interleaved_style { + SOF_IPC4_CHANNELS_INTERLEAVED, + SOF_IPC4_CHANNELS_NONINTERLEAVED, +}; + +enum sof_ipc4_sample_type { + SOF_IPC4_MSB_INTEGER, /* integer with Most Significant Byte first */ + SOF_IPC4_LSB_INTEGER, /* integer with Least Significant Byte first */ +}; + +struct sof_ipc4_audio_format { + uint32_t sampling_frequency; + uint32_t bit_depth; + uint32_t ch_map; + uint32_t ch_cfg; /* sof_ipc4_channel_config */ + uint32_t interleaving_style; + uint32_t fmt_cfg; /* channels_count valid_bit_depth s_type */ +} __packed __aligned(4); + +#define SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT_SHIFT 0 +#define SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT_MASK GENMASK(7, 0) +#define SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(x) \ + ((x) & SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT_MASK) +#define SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH_SHIFT 8 +#define SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH_MASK GENMASK(15, 8) +#define SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(x) \ + (((x) & SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH_MASK) >> \ + SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH_SHIFT) +#define SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE_SHIFT 16 +#define SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE_MASK GENMASK(23, 16) +#define SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE(x) \ + (((x) & SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE_MASK) >> \ + SOF_IPC4_AUDIO_FORMAT_CFG_SAMPLE_TYPE_SHIFT) + +/* Module message type specific field definitions */ + +enum sof_ipc4_module_type { + SOF_IPC4_MOD_INIT_INSTANCE, + SOF_IPC4_MOD_CONFIG_GET, + SOF_IPC4_MOD_CONFIG_SET, + SOF_IPC4_MOD_LARGE_CONFIG_GET, + SOF_IPC4_MOD_LARGE_CONFIG_SET, + SOF_IPC4_MOD_BIND, + SOF_IPC4_MOD_UNBIND, + SOF_IPC4_MOD_SET_DX, + SOF_IPC4_MOD_SET_D0IX, + SOF_IPC4_MOD_ENTER_MODULE_RESTORE, + SOF_IPC4_MOD_EXIT_MODULE_RESTORE, + SOF_IPC4_MOD_DELETE_INSTANCE, + + SOF_IPC4_MOD_TYPE_LAST, +}; + +struct sof_ipc4_base_module_cfg { + uint32_t cpc; /* the max count of Cycles Per Chunk processing */ + uint32_t ibs; /* input Buffer Size (in bytes) */ + uint32_t obs; /* output Buffer Size (in bytes) */ + uint32_t is_pages; /* number of physical pages used */ + struct sof_ipc4_audio_format audio_fmt; +} __packed __aligned(4); + +/* common module ipc msg */ +#define SOF_IPC4_MOD_INSTANCE_SHIFT 16 +#define SOF_IPC4_MOD_INSTANCE_MASK GENMASK(23, 16) +#define SOF_IPC4_MOD_INSTANCE(x) ((x) << SOF_IPC4_MOD_INSTANCE_SHIFT) + +#define SOF_IPC4_MOD_ID_SHIFT 0 +#define SOF_IPC4_MOD_ID_MASK GENMASK(15, 0) +#define SOF_IPC4_MOD_ID(x) ((x) << SOF_IPC4_MOD_ID_SHIFT) + +/* init module ipc msg */ +#define SOF_IPC4_MOD_EXT_PARAM_SIZE_SHIFT 0 +#define SOF_IPC4_MOD_EXT_PARAM_SIZE_MASK GENMASK(15, 0) +#define SOF_IPC4_MOD_EXT_PARAM_SIZE(x) ((x) << SOF_IPC4_MOD_EXT_PARAM_SIZE_SHIFT) + +#define SOF_IPC4_MOD_EXT_PPL_ID_SHIFT 16 +#define SOF_IPC4_MOD_EXT_PPL_ID_MASK GENMASK(23, 16) +#define SOF_IPC4_MOD_EXT_PPL_ID(x) ((x) << SOF_IPC4_MOD_EXT_PPL_ID_SHIFT) + +#define SOF_IPC4_MOD_EXT_CORE_ID_SHIFT 24 +#define SOF_IPC4_MOD_EXT_CORE_ID_MASK GENMASK(27, 24) +#define SOF_IPC4_MOD_EXT_CORE_ID(x) ((x) << SOF_IPC4_MOD_EXT_CORE_ID_SHIFT) + +#define SOF_IPC4_MOD_EXT_DOMAIN_SHIFT 28 +#define SOF_IPC4_MOD_EXT_DOMAIN_MASK BIT(28) +#define SOF_IPC4_MOD_EXT_DOMAIN(x) ((x) << SOF_IPC4_MOD_EXT_DOMAIN_SHIFT) + +/* bind/unbind module ipc msg */ +#define SOF_IPC4_MOD_EXT_DST_MOD_ID_SHIFT 0 +#define SOF_IPC4_MOD_EXT_DST_MOD_ID_MASK GENMASK(15, 0) +#define SOF_IPC4_MOD_EXT_DST_MOD_ID(x) ((x) << SOF_IPC4_MOD_EXT_DST_MOD_ID_SHIFT) + +#define SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE_SHIFT 16 +#define SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE_MASK GENMASK(23, 16) +#define SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(x) ((x) << SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE_SHIFT) + +#define SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID_SHIFT 24 +#define SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID_MASK GENMASK(26, 24) +#define SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(x) ((x) << SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID_SHIFT) + +#define SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID_SHIFT 27 +#define SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID_MASK GENMASK(29, 27) +#define SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(x) ((x) << SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID_SHIFT) + +#define MOD_ENABLE_LOG 6 +#define MOD_SYSTEM_TIME 20 + +/* set module large config */ +#define SOF_IPC4_MOD_EXT_MSG_SIZE_SHIFT 0 +#define SOF_IPC4_MOD_EXT_MSG_SIZE_MASK GENMASK(19, 0) +#define SOF_IPC4_MOD_EXT_MSG_SIZE(x) ((x) << SOF_IPC4_MOD_EXT_MSG_SIZE_SHIFT) + +#define SOF_IPC4_MOD_EXT_MSG_PARAM_ID_SHIFT 20 +#define SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK GENMASK(27, 20) +#define SOF_IPC4_MOD_EXT_MSG_PARAM_ID(x) ((x) << SOF_IPC4_MOD_EXT_MSG_PARAM_ID_SHIFT) + +#define SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK_SHIFT 28 +#define SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK_MASK BIT(28) +#define SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(x) ((x) << SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK_SHIFT) + +#define SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_SHIFT 29 +#define SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK BIT(29) +#define SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(x) ((x) << SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_SHIFT) + +/* Init instance messagees */ +#define SOF_IPC4_MOD_INIT_BASEFW_MOD_ID 0 +#define SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID 0 + +enum sof_ipc4_base_fw_params { + SOF_IPC4_FW_PARAM_ENABLE_LOGS = 6, + SOF_IPC4_FW_PARAM_FW_CONFIG, + SOF_IPC4_FW_PARAM_HW_CONFIG_GET, + SOF_IPC4_FW_PARAM_MODULES_INFO_GET, + SOF_IPC4_FW_PARAM_LIBRARIES_INFO_GET = 16, + SOF_IPC4_FW_PARAM_SYSTEM_TIME = 20, +}; + +enum sof_ipc4_fw_config_params { + SOF_IPC4_FW_CFG_FW_VERSION, + SOF_IPC4_FW_CFG_MEMORY_RECLAIMED, + SOF_IPC4_FW_CFG_SLOW_CLOCK_FREQ_HZ, + SOF_IPC4_FW_CFG_FAST_CLOCK_FREQ_HZ, + SOF_IPC4_FW_CFG_DMA_BUFFER_CONFIG, + SOF_IPC4_FW_CFG_ALH_SUPPORT_LEVEL, + SOF_IPC4_FW_CFG_DL_MAILBOX_BYTES, + SOF_IPC4_FW_CFG_UL_MAILBOX_BYTES, + SOF_IPC4_FW_CFG_TRACE_LOG_BYTES, + SOF_IPC4_FW_CFG_MAX_PPL_COUNT, + SOF_IPC4_FW_CFG_MAX_ASTATE_COUNT, + SOF_IPC4_FW_CFG_MAX_MODULE_PIN_COUNT, + SOF_IPC4_FW_CFG_MODULES_COUNT, + SOF_IPC4_FW_CFG_MAX_MOD_INST_COUNT, + SOF_IPC4_FW_CFG_MAX_LL_TASKS_PER_PRI_COUNT, + SOF_IPC4_FW_CFG_LL_PRI_COUNT, + SOF_IPC4_FW_CFG_MAX_DP_TASKS_COUNT, + SOF_IPC4_FW_CFG_MAX_LIBS_COUNT, + SOF_IPC4_FW_CFG_SCHEDULER_CONFIG, + SOF_IPC4_FW_CFG_XTAL_FREQ_HZ, + SOF_IPC4_FW_CFG_CLOCKS_CONFIG, + SOF_IPC4_FW_CFG_RESERVED, + SOF_IPC4_FW_CFG_POWER_GATING_POLICY, + SOF_IPC4_FW_CFG_ASSERT_MODE, +}; + +struct sof_ipc4_fw_version { + uint16_t major; + uint16_t minor; + uint16_t hotfix; + uint16_t build; +} __packed; + +/* Reply messages */ + +/* + * IPC4 primary header bit allocation for replies + * bit 0-23: status + * bit 24-28: type: enum sof_ipc4_global_msg if target is SOF_IPC4_FW_GEN_MSG + * enum sof_ipc4_module_type if target is SOF_IPC4_MODULE_MSG + * bit 29: response - sof_ipc4_msg_dir + * bit 30: target - enum sof_ipc4_msg_target + * bit 31: reserved, unused + */ + +#define SOF_IPC4_REPLY_STATUS GENMASK(23, 0) + +/* Notification messages */ + +/* + * IPC4 primary header bit allocation for notifications + * bit 0-15: notification type specific + * bit 16-23: enum sof_ipc4_notification_type + * bit 24-28: SOF_IPC4_GLB_NOTIFICATION + * bit 29: response - sof_ipc4_msg_dir + * bit 30: target - enum sof_ipc4_msg_target + * bit 31: reserved, unused + */ + +#define SOF_IPC4_MSG_IS_NOTIFICATION(x) (SOF_IPC4_MSG_TYPE_GET(x) == \ + SOF_IPC4_GLB_NOTIFICATION) + +#define SOF_IPC4_NOTIFICATION_TYPE_SHIFT 16 +#define SOF_IPC4_NOTIFICATION_TYPE_MASK GENMASK(23, 16) +#define SOF_IPC4_NOTIFICATION_TYPE_GET(x) (((x) & SOF_IPC4_NOTIFICATION_TYPE_MASK) >> \ + SOF_IPC4_NOTIFICATION_TYPE_SHIFT) + +/* Value of notification type field - must fit into 8 bits */ +enum sof_ipc4_notification_type { + /* Phrase detected (notification from WoV module) */ + SOF_IPC4_NOTIFY_PHRASE_DETECTED = 4, + /* Event from a resource (pipeline or module instance) */ + SOF_IPC4_NOTIFY_RESOURCE_EVENT, + /* Debug log buffer status changed */ + SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS, + /* Timestamp captured at the link */ + SOF_IPC4_NOTIFY_TIMESTAMP_CAPTURED, + /* FW complete initialization */ + SOF_IPC4_NOTIFY_FW_READY, + /* Audio classifier result (ACA) */ + SOF_IPC4_NOTIFY_FW_AUD_CLASS_RESULT, + /* Exception caught by DSP FW */ + SOF_IPC4_NOTIFY_EXCEPTION_CAUGHT, + /* 11 is skipped by the existing cavs firmware */ + /* Custom module notification */ + SOF_IPC4_NOTIFY_MODULE_NOTIFICATION = 12, + /* 13 is reserved - do not use */ + /* Probe notify data available */ + SOF_IPC4_NOTIFY_PROBE_DATA_AVAILABLE = 14, + /* AM module notifications */ + SOF_IPC4_NOTIFY_ASYNC_MSG_SRVC_MESSAGE, + + SOF_IPC4_NOTIFY_TYPE_LAST, +}; + +struct sof_ipc4_notify_resource_data { + uint32_t resource_type; + uint32_t resource_id; + uint32_t event_type; + uint32_t reserved; + uint32_t data[6]; +} __packed __aligned(4); + +/** @}*/ + +#endif diff --git a/include/sound/sof/stream.h b/include/sound/sof/stream.h index 58a0d49977d6..1db3bbc3e65d 100644 --- a/include/sound/sof/stream.h +++ b/include/sound/sof/stream.h @@ -85,8 +85,9 @@ struct sof_ipc_stream_params { uint32_t host_period_bytes; uint16_t no_stream_position; /**< 1 means don't send stream position */ + uint8_t cont_update_posn; /**< 1 means continuous update stream position */ - uint16_t reserved[3]; + uint8_t reserved[5]; uint16_t chmap[SOF_IPC_MAX_CHANNELS]; /**< channel map - SOF_CHMAP_ */ } __packed; diff --git a/include/uapi/sound/intel/avs/tokens.h b/include/uapi/sound/intel/avs/tokens.h new file mode 100644 index 000000000000..754f02b2f444 --- /dev/null +++ b/include/uapi/sound/intel/avs/tokens.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright(c) 2021 Intel Corporation. All rights reserved. + * + * Authors: Cezary Rojewski <cezary.rojewski@intel.com> + * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> + */ + +#ifndef __UAPI_SOUND_INTEL_AVS_TOKENS_H +#define __UAPI_SOUND_INTEL_AVS_TOKENS_H + +enum avs_tplg_token { + /* struct avs_tplg */ + AVS_TKN_MANIFEST_NAME_STRING = 1, + AVS_TKN_MANIFEST_VERSION_U32 = 2, + AVS_TKN_MANIFEST_NUM_LIBRARIES_U32 = 3, + AVS_TKN_MANIFEST_NUM_AFMTS_U32 = 4, + AVS_TKN_MANIFEST_NUM_MODCFGS_BASE_U32 = 5, + AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32 = 6, + AVS_TKN_MANIFEST_NUM_PPLCFGS_U32 = 7, + AVS_TKN_MANIFEST_NUM_BINDINGS_U32 = 8, + + /* struct avs_tplg_library */ + AVS_TKN_LIBRARY_ID_U32 = 101, + AVS_TKN_LIBRARY_NAME_STRING = 102, + + /* struct avs_audio_format */ + AVS_TKN_AFMT_ID_U32 = 201, + AVS_TKN_AFMT_SAMPLE_RATE_U32 = 202, + AVS_TKN_AFMT_BIT_DEPTH_U32 = 203, + AVS_TKN_AFMT_CHANNEL_MAP_U32 = 204, + AVS_TKN_AFMT_CHANNEL_CFG_U32 = 205, + AVS_TKN_AFMT_INTERLEAVING_U32 = 206, + AVS_TKN_AFMT_NUM_CHANNELS_U32 = 207, + AVS_TKN_AFMT_VALID_BIT_DEPTH_U32 = 208, + AVS_TKN_AFMT_SAMPLE_TYPE_U32 = 209, + + /* struct avs_tplg_modcfg_base */ + AVS_TKN_MODCFG_BASE_ID_U32 = 301, + AVS_TKN_MODCFG_BASE_CPC_U32 = 302, + AVS_TKN_MODCFG_BASE_IBS_U32 = 303, + AVS_TKN_MODCFG_BASE_OBS_U32 = 304, + AVS_TKN_MODCFG_BASE_PAGES_U32 = 305, + + /* struct avs_tplg_modcfg_ext */ + AVS_TKN_MODCFG_EXT_ID_U32 = 401, + AVS_TKN_MODCFG_EXT_TYPE_UUID = 402, + AVS_TKN_MODCFG_CPR_OUT_AFMT_ID_U32 = 403, + AVS_TKN_MODCFG_CPR_FEATURE_MASK_U32 = 404, + AVS_TKN_MODCFG_CPR_DMA_TYPE_U32 = 405, + AVS_TKN_MODCFG_CPR_DMABUFF_SIZE_U32 = 406, + AVS_TKN_MODCFG_CPR_VINDEX_U8 = 407, + AVS_TKN_MODCFG_CPR_BLOB_FMT_ID_U32 = 408, + AVS_TKN_MODCFG_MICSEL_OUT_AFMT_ID_U32 = 409, + AVS_TKN_MODCFG_INTELWOV_CPC_LP_MODE_U32 = 410, + AVS_TKN_MODCFG_SRC_OUT_FREQ_U32 = 411, + AVS_TKN_MODCFG_MUX_REF_AFMT_ID_U32 = 412, + AVS_TKN_MODCFG_MUX_OUT_AFMT_ID_U32 = 413, + AVS_TKN_MODCFG_AEC_REF_AFMT_ID_U32 = 414, + AVS_TKN_MODCFG_AEC_OUT_AFMT_ID_U32 = 415, + AVS_TKN_MODCFG_AEC_CPC_LP_MODE_U32 = 416, + AVS_TKN_MODCFG_ASRC_OUT_FREQ_U32 = 417, + AVS_TKN_MODCFG_ASRC_MODE_U8 = 418, + AVS_TKN_MODCFG_ASRC_DISABLE_JITTER_U8 = 419, + AVS_TKN_MODCFG_UPDOWN_MIX_OUT_CHAN_CFG_U32 = 420, + AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_SELECT_U32 = 421, + AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_0_S32 = 422, + AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_1_S32 = 423, + AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_2_S32 = 424, + AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_3_S32 = 425, + AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_4_S32 = 426, + AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_5_S32 = 427, + AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_6_S32 = 428, + AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_7_S32 = 429, + AVS_TKN_MODCFG_UPDOWN_MIX_CHAN_MAP_U32 = 430, + AVS_TKN_MODCFG_EXT_NUM_INPUT_PINS_U16 = 431, + AVS_TKN_MODCFG_EXT_NUM_OUTPUT_PINS_U16 = 432, + + /* struct avs_tplg_pplcfg */ + AVS_TKN_PPLCFG_ID_U32 = 1401, + AVS_TKN_PPLCFG_REQ_SIZE_U16 = 1402, + AVS_TKN_PPLCFG_PRIORITY_U8 = 1403, + AVS_TKN_PPLCFG_LOW_POWER_BOOL = 1404, + AVS_TKN_PPLCFG_ATTRIBUTES_U16 = 1405, + AVS_TKN_PPLCFG_TRIGGER_U32 = 1406, + + /* struct avs_tplg_binding */ + AVS_TKN_BINDING_ID_U32 = 1501, + AVS_TKN_BINDING_TARGET_TPLG_NAME_STRING = 1502, + AVS_TKN_BINDING_TARGET_PATH_TMPL_ID_U32 = 1503, + AVS_TKN_BINDING_TARGET_PPL_ID_U32 = 1504, + AVS_TKN_BINDING_TARGET_MOD_ID_U32 = 1505, + AVS_TKN_BINDING_TARGET_MOD_PIN_U8 = 1506, + AVS_TKN_BINDING_MOD_ID_U32 = 1507, + AVS_TKN_BINDING_MOD_PIN_U8 = 1508, + AVS_TKN_BINDING_IS_SINK_U8 = 1509, + + /* struct avs_tplg_pipeline */ + AVS_TKN_PPL_ID_U32 = 1601, + AVS_TKN_PPL_PPLCFG_ID_U32 = 1602, + AVS_TKN_PPL_NUM_BINDING_IDS_U32 = 1603, + AVS_TKN_PPL_BINDING_ID_U32 = 1604, + + /* struct avs_tplg_module */ + AVS_TKN_MOD_ID_U32 = 1701, + AVS_TKN_MOD_MODCFG_BASE_ID_U32 = 1702, + AVS_TKN_MOD_IN_AFMT_ID_U32 = 1703, + AVS_TKN_MOD_CORE_ID_U8 = 1704, + AVS_TKN_MOD_PROC_DOMAIN_U8 = 1705, + AVS_TKN_MOD_MODCFG_EXT_ID_U32 = 1706, + + /* struct avs_tplg_path_template */ + AVS_TKN_PATH_TMPL_ID_U32 = 1801, + + /* struct avs_tplg_path */ + AVS_TKN_PATH_ID_U32 = 1901, + AVS_TKN_PATH_FE_FMT_ID_U32 = 1902, + AVS_TKN_PATH_BE_FMT_ID_U32 = 1903, + + /* struct avs_tplg_pin_format */ + AVS_TKN_PIN_FMT_INDEX_U32 = 2201, + AVS_TKN_PIN_FMT_IOBS_U32 = 2202, + AVS_TKN_PIN_FMT_AFMT_ID_U32 = 2203, +}; + +#endif diff --git a/include/uapi/sound/sof/abi.h b/include/uapi/sound/sof/abi.h index e052653a6e4c..0e7dccdc25fd 100644 --- a/include/uapi/sound/sof/abi.h +++ b/include/uapi/sound/sof/abi.h @@ -26,8 +26,8 @@ /* SOF ABI version major, minor and patch numbers */ #define SOF_ABI_MAJOR 3 -#define SOF_ABI_MINOR 19 -#define SOF_ABI_PATCH 1 +#define SOF_ABI_MINOR 21 +#define SOF_ABI_PATCH 0 /* SOF ABI version number. Format within 32bit word is MMmmmppp */ #define SOF_ABI_MAJOR_SHIFT 24 diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 5dcf77af07af..7d4747b6bab2 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -14,7 +14,7 @@ menuconfig SND_SOC If you want ASoC support, you should say Y here and also to the specific driver for your SoC platform below. - + ASoC provides power efficient ALSA support for embedded battery powered SoC based systems like PDA's, Phones and Personal Media Players. @@ -55,6 +55,13 @@ config SND_SOC_TOPOLOGY_KUNIT_TEST userspace applications such as pulseaudio, to prevent unnecessary problems. +config SND_SOC_UTILS_KUNIT_TEST + tristate "KUnit tests for SoC utils" + depends on KUNIT + default KUNIT_ALL_TESTS + help + If you want to perform tests on ALSA SoC utils library say Y here. + config SND_SOC_ACPI tristate diff --git a/sound/soc/Makefile b/sound/soc/Makefile index a7b37c06dc43..d4528962ac34 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -12,6 +12,11 @@ ifneq ($(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST),) obj-$(CONFIG_SND_SOC_TOPOLOGY_KUNIT_TEST) := soc-topology-test.o endif +ifneq ($(CONFIG_SND_SOC_UTILS_KUNIT_TEST),) +# snd-soc-test-objs := soc-utils-test.o +obj-$(CONFIG_SND_SOC_UTILS_KUNIT_TEST) := soc-utils-test.o +endif + ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) snd-soc-core-objs += soc-generic-dmaengine-pcm.o endif diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index 3bf86c2424ae..ef1b4cefc273 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -71,7 +71,7 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &cz_jack, NULL, 0); + &cz_jack); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; @@ -151,7 +151,7 @@ static int cz_rt5682_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &cz_jack, NULL, 0); + &cz_jack); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c index a79a46646d50..532aa98a2241 100644 --- a/sound/soc/amd/acp-rt5645.c +++ b/sound/soc/amd/acp-rt5645.c @@ -80,7 +80,7 @@ static int cz_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &cz_jack, NULL, 0); + &cz_jack); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index 626e4a5cb06a..9dae2719084c 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -42,7 +42,7 @@ config SND_AMD_ASOC_RENOIR config SND_SOC_AMD_MACH_COMMON tristate - depends on X86 && PCI && I2C && GPIOLIB + depends on X86 && PCI && I2C select CLK_FIXED_FCH select SND_SOC_RT5682_I2C select SND_SOC_DMIC @@ -54,14 +54,14 @@ config SND_SOC_AMD_MACH_COMMON config SND_SOC_AMD_LEGACY_MACH tristate "AMD Legacy Machine Driver Support" - depends on X86 && PCI && I2C && GPIOLIB + depends on X86 && PCI && I2C select SND_SOC_AMD_MACH_COMMON help This option enables legacy sound card support for ACP audio. config SND_SOC_AMD_SOF_MACH tristate "AMD SOF Machine Driver Support" - depends on X86 && PCI && I2C && GPIOLIB + depends on X86 && PCI && I2C select SND_SOC_AMD_MACH_COMMON help This option enables SOF sound card support for ACP audio. diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c index 5d276365d644..7f04a048ca3a 100644 --- a/sound/soc/amd/acp/acp-legacy-mach.c +++ b/sound/soc/amd/acp/acp-legacy-mach.c @@ -27,7 +27,6 @@ static struct acp_card_drvdata rt5682_rt1019_data = { .hs_codec_id = RT5682, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, - .gpio_spkr_en = EN_SPKR_GPIO_GB, }; static struct acp_card_drvdata rt5682s_max_data = { @@ -37,7 +36,6 @@ static struct acp_card_drvdata rt5682s_max_data = { .hs_codec_id = RT5682S, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, - .gpio_spkr_en = EN_SPKR_GPIO_NONE, }; static struct acp_card_drvdata rt5682s_rt1019_data = { @@ -47,7 +45,6 @@ static struct acp_card_drvdata rt5682s_rt1019_data = { .hs_codec_id = RT5682S, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, - .gpio_spkr_en = EN_SPKR_GPIO_NONE, }; static const struct snd_kcontrol_new acp_controls[] = { @@ -62,16 +59,15 @@ static const struct snd_kcontrol_new acp_controls[] = { static const struct snd_soc_dapm_widget acp_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_SPK("Spk", event_spkr_handler), - SND_SOC_DAPM_SPK("Left Spk", event_spkr_handler), - SND_SOC_DAPM_SPK("Right Spk", event_spkr_handler), + SND_SOC_DAPM_SPK("Spk", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), }; static int acp_asoc_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; struct device *dev = &pdev->dev; - unsigned int spkr_gpio; int ret; if (!pdev->id_entry) @@ -89,20 +85,9 @@ static int acp_asoc_probe(struct platform_device *pdev) card->controls = acp_controls; card->num_controls = ARRAY_SIZE(acp_controls); card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; - spkr_gpio = ((struct acp_card_drvdata *)(card->drvdata))->gpio_spkr_en; acp_legacy_dai_links_create(card); - if (gpio_is_valid(spkr_gpio)) { - ret = devm_gpio_request(dev, spkr_gpio, "spkren"); - if (ret) { - dev_err(dev, "(%s) gpio request failed: %d\n", - __func__, ret); - return ret; - } - gpio_direction_output(spkr_gpio, 0); - } - ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, @@ -131,6 +116,7 @@ static const struct platform_device_id board_ids[] = { }; static struct platform_driver acp_asoc_audio = { .driver = { + .pm = &snd_soc_pm_ops, .name = "acp_mach", }, .probe = acp_asoc_probe, diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index caa202f7864e..6ae454bf60af 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -71,31 +71,6 @@ static const struct snd_soc_dapm_route rt5682_map[] = { { "IN1P", NULL, "Headset Mic" }, }; -int event_spkr_handler(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - struct snd_soc_dapm_context *dapm = w->dapm; - struct snd_soc_card *card = dapm->card; - struct acp_card_drvdata *drvdata = snd_soc_card_get_drvdata(card); - - if (!gpio_is_valid(drvdata->gpio_spkr_en)) - return 0; - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - gpio_set_value(drvdata->gpio_spkr_en, 1); - break; - case SND_SOC_DAPM_PRE_PMD: - gpio_set_value(drvdata->gpio_spkr_en, 0); - break; - default: - dev_warn(card->dev, "%s invalid setting\n", __func__); - break; - } - return 0; -} -EXPORT_SYMBOL_NS_GPL(event_spkr_handler, SND_SOC_AMD_MACH); - /* Define card ops for RT5682 CODEC */ static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd) { @@ -145,7 +120,7 @@ static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pco_jack, NULL, 0); + &pco_jack); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; @@ -266,7 +241,7 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pco_jack, NULL, 0); + &pco_jack); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h index c855f50d6b34..5dc47cfbff10 100644 --- a/sound/soc/amd/acp/acp-mach.h +++ b/sound/soc/amd/acp/acp-mach.h @@ -17,11 +17,6 @@ #include <linux/input.h> #include <linux/module.h> #include <sound/soc.h> -#include <linux/gpio.h> -#include <linux/gpio/consumer.h> - -#define EN_SPKR_GPIO_GB 0x11F -#define EN_SPKR_GPIO_NONE -EINVAL enum be_id { HEADSET_BE_ID = 0, @@ -54,11 +49,9 @@ struct acp_card_drvdata { unsigned int dai_fmt; struct clk *wclk; struct clk *bclk; - unsigned int gpio_spkr_en; }; int acp_sofdsp_dai_links_create(struct snd_soc_card *card); int acp_legacy_dai_links_create(struct snd_soc_card *card); -int event_spkr_handler(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event); + #endif diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 3346677949e3..d1531cdab110 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -27,7 +27,6 @@ static struct acp_card_drvdata sof_rt5682_rt1019_data = { .hs_codec_id = RT5682, .amp_codec_id = RT1019, .dmic_codec_id = DMIC, - .gpio_spkr_en = EN_SPKR_GPIO_GB, }; static struct acp_card_drvdata sof_rt5682_max_data = { @@ -37,7 +36,6 @@ static struct acp_card_drvdata sof_rt5682_max_data = { .hs_codec_id = RT5682, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, - .gpio_spkr_en = EN_SPKR_GPIO_NONE, }; static struct acp_card_drvdata sof_rt5682s_rt1019_data = { @@ -56,7 +54,6 @@ static struct acp_card_drvdata sof_rt5682s_max_data = { .hs_codec_id = RT5682S, .amp_codec_id = MAX98360A, .dmic_codec_id = DMIC, - .gpio_spkr_en = EN_SPKR_GPIO_NONE, }; static const struct snd_kcontrol_new acp_controls[] = { @@ -70,16 +67,15 @@ static const struct snd_kcontrol_new acp_controls[] = { static const struct snd_soc_dapm_widget acp_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_SPK("Spk", event_spkr_handler), - SND_SOC_DAPM_SPK("Left Spk", event_spkr_handler), - SND_SOC_DAPM_SPK("Right Spk", event_spkr_handler), + SND_SOC_DAPM_SPK("Spk", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), }; static int acp_sof_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; struct device *dev = &pdev->dev; - unsigned int spkr_gpio; int ret; if (!pdev->id_entry) @@ -97,20 +93,9 @@ static int acp_sof_probe(struct platform_device *pdev) card->controls = acp_controls; card->num_controls = ARRAY_SIZE(acp_controls); card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; - spkr_gpio = ((struct acp_card_drvdata *)(card->drvdata))->gpio_spkr_en; acp_sofdsp_dai_links_create(card); - if (gpio_is_valid(spkr_gpio)) { - ret = devm_gpio_request(dev, spkr_gpio, "spkren"); - if (ret) { - dev_err(dev, "(%s) gpio request failed: %d\n", - __func__, ret); - return ret; - } - gpio_direction_output(spkr_gpio, 0); - } - ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, @@ -144,6 +129,7 @@ static const struct platform_device_id board_ids[] = { static struct platform_driver acp_asoc_audio = { .driver = { .name = "sof_mach", + .pm = &snd_soc_pm_ops, }, .probe = acp_sof_probe, .id_table = board_ids, diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c index dad70436d063..0543dda75b99 100644 --- a/sound/soc/amd/acp3x-rt5682-max9836.c +++ b/sound/soc/amd/acp3x-rt5682-max9836.c @@ -90,7 +90,7 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pco_jack, NULL, 0); + &pco_jack); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c index 1551546c3050..727de46860b1 100644 --- a/sound/soc/amd/vangogh/acp5x-mach.c +++ b/sound/soc/amd/vangogh/acp5x-mach.c @@ -17,10 +17,8 @@ #include <linux/clk.h> #include <linux/gpio.h> #include <linux/gpio/consumer.h> -#include <linux/module.h> #include <linux/i2c.h> #include <linux/input.h> -#include <linux/io.h> #include <linux/acpi.h> #include <linux/dmi.h> @@ -61,10 +59,10 @@ static int acp5x_8821_init(struct snd_soc_pcm_runtime *rtd) * Headset buttons map to the google Reference headset. * These can be configured by userspace. */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_BTN_0, - &vg_headset, acp5x_nau8821_jack_pins, - ARRAY_SIZE(acp5x_nau8821_jack_pins)); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &vg_headset, acp5x_nau8821_jack_pins, + ARRAY_SIZE(acp5x_nau8821_jack_pins)); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 9a767f47b89f..f06e6c1a7799 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -12,6 +12,7 @@ #include <sound/pcm_params.h> #include <linux/io.h> #include <linux/dmi.h> +#include <linux/acpi.h> #include "acp6x.h" @@ -45,108 +46,126 @@ static struct snd_soc_card acp6x_card = { static const struct dmi_system_id yc_acp_quirk_table[] = { { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21D2"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21D3"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21D4"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21D5"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21CF"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21CG"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21CQ"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21CR"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21AW"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21AX"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21BN"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21BQ"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21CH"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21CJ"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21CK"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21CL"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21D8"), } }, { + .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21D9"), @@ -157,18 +176,33 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { static int acp6x_probe(struct platform_device *pdev) { + const struct dmi_system_id *dmi_id; struct acp6x_pdm *machine = NULL; struct snd_soc_card *card; + struct acpi_device *adev; int ret; - const struct dmi_system_id *dmi_id; + /* check the parent device's firmware node has _DSD or not */ + adev = ACPI_COMPANION(pdev->dev.parent); + if (adev) { + const union acpi_object *obj; + + if (!acpi_dev_get_property(adev, "AcpDmicConnected", ACPI_TYPE_INTEGER, &obj) && + obj->integer.value == 1) + platform_set_drvdata(pdev, &acp6x_card); + } + + /* check for any DMI overrides */ dmi_id = dmi_first_match(yc_acp_quirk_table); - if (!dmi_id) + if (dmi_id) + platform_set_drvdata(pdev, dmi_id->driver_data); + + card = platform_get_drvdata(pdev); + if (!card) return -ENODEV; - card = &acp6x_card; + dev_info(&pdev->dev, "Enabling ACP DMIC support via %s", dmi_id ? "DMI" : "ACPI"); acp6x_card.dev = &pdev->dev; - platform_set_drvdata(pdev, card); snd_soc_card_set_drvdata(card, machine); ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { diff --git a/sound/soc/amd/yc/pci-acp6x.c b/sound/soc/amd/yc/pci-acp6x.c index 7e9a9a9d8ddd..20f7a99783f2 100644 --- a/sound/soc/amd/yc/pci-acp6x.c +++ b/sound/soc/amd/yc/pci-acp6x.c @@ -154,9 +154,14 @@ static int snd_acp6x_probe(struct pci_dev *pci, irqflags = IRQF_SHARED; /* Yellow Carp device check */ - if (pci->revision != 0x60) + switch (pci->revision) { + case 0x60: + case 0x6f: + break; + default: + dev_err(&pci->dev, "acp6x pci device not found\n"); return -ENODEV; - + } if (pci_enable_device(pci)) { dev_err(&pci->dev, "pci_enable_device failed\n"); return -ENODEV; diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 795c0b0b527a..5d59e00be823 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -42,9 +42,9 @@ config SND_ATMEL_SOC_SSC_DMA config SND_AT91_SOC_SAM9G20_WM8731 tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" depends on ARCH_AT91 || COMPILE_TEST - depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI + depends on ATMEL_SSC && I2C select SND_ATMEL_SOC_SSC_PDC - select SND_SOC_WM8731 + select SND_SOC_WM8731_I2C help Say Y if you want to add support for SoC audio on WM8731-based AT91sam9g20 evaluation board. diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c index a9f9f449c48c..74b7b2611aa7 100644 --- a/sound/soc/atmel/atmel-classd.c +++ b/sound/soc/atmel/atmel-classd.c @@ -458,7 +458,6 @@ static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = { .num_controls = ARRAY_SIZE(atmel_classd_snd_controls), .idle_bias_on = 1, .use_pmdown_time = 1, - .endianness = 1, }; /* ASoC sound card */ diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c index 42117de299e7..ea34efac2fff 100644 --- a/sound/soc/atmel/atmel-pdmic.c +++ b/sound/soc/atmel/atmel-pdmic.c @@ -481,7 +481,6 @@ static const struct snd_soc_component_driver atmel_pdmic_cpu_dai_component = { .num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls), .idle_bias_on = 1, .use_pmdown_time = 1, - .endianness = 1, }; /* ASoC sound card */ diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 0d639a33ad96..4d25fb61c652 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -127,8 +127,8 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev) ret = atmel_ssc_set_audio(0); if (ret) { - dev_err(&pdev->dev, "ssc channel is not valid\n"); - return -EINVAL; + dev_err(&pdev->dev, "ssc channel is not valid: %d\n", ret); + return ret; } card->dev = &pdev->dev; @@ -148,7 +148,8 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev) codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); if (!codec_np) { dev_err(&pdev->dev, "codec info missing\n"); - return -EINVAL; + ret = -EINVAL; + goto err; } at91sam9g20ek_dai.codecs->of_node = codec_np; @@ -159,7 +160,8 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev) if (!cpu_np) { dev_err(&pdev->dev, "dai and pcm info missing\n"); of_node_put(codec_np); - return -EINVAL; + ret = -EINVAL; + goto err; } at91sam9g20ek_dai.cpus->of_node = cpu_np; at91sam9g20ek_dai.platforms->of_node = cpu_np; @@ -169,10 +171,12 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev) ret = snd_soc_register_card(card); if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card() failed\n"); + dev_err_probe(&pdev->dev, ret, + "snd_soc_register_card() failed: %d\n", ret); + goto err; } - return ret; + return 0; err: atmel_ssc_put_audio(0); diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig index 38de7c0efbc7..8a78809e8754 100644 --- a/sound/soc/au1x/Kconfig +++ b/sound/soc/au1x/Kconfig @@ -58,7 +58,7 @@ config SND_SOC_DB1200 select SND_SOC_AC97_CODEC select SND_SOC_WM9712 select SND_SOC_AU1XPSC_I2S - select SND_SOC_WM8731 + select SND_SOC_WM8731_I2C help Select this option to enable audio (AC97 and I2S) on the Alchemy/AMD/RMI/NetLogic Db1200, Db1550 and Db1300 evaluation boards. diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index f46a22660103..6165db92a629 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -65,6 +65,8 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS35L36 imply SND_SOC_CS35L41_SPI imply SND_SOC_CS35L41_I2C + imply SND_SOC_CS35L45_I2C + imply SND_SOC_CS35L45_SPI imply SND_SOC_CS42L42 imply SND_SOC_CS42L51_I2C imply SND_SOC_CS42L52 @@ -127,6 +129,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_MAX98373_I2C imply SND_SOC_MAX98373_SDW imply SND_SOC_MAX98390 + imply SND_SOC_MAX98396 imply SND_SOC_MAX9850 imply SND_SOC_MAX9860 imply SND_SOC_MAX9759 @@ -168,6 +171,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT1011 imply SND_SOC_RT1015 imply SND_SOC_RT1015P + imply SND_SOC_RT1016 imply SND_SOC_RT1019 imply SND_SOC_RT1305 imply SND_SOC_RT1308 @@ -265,7 +269,8 @@ config SND_SOC_ALL_CODECS imply SND_SOC_WM8711 imply SND_SOC_WM8727 imply SND_SOC_WM8728 - imply SND_SOC_WM8731 + imply SND_SOC_WM8731_I2C + imply SND_SOC_WM8731_SPI imply SND_SOC_WM8737 imply SND_SOC_WM8741 imply SND_SOC_WM8750 @@ -655,6 +660,34 @@ config SND_SOC_CS35L41_I2C select SND_SOC_CS35L41 select REGMAP_I2C +config SND_SOC_CS35L45_TABLES + tristate + +config SND_SOC_CS35L45 + tristate + +config SND_SOC_CS35L45_SPI + tristate "Cirrus Logic CS35L45 CODEC (SPI)" + depends on SPI_MASTER + select REGMAP + select REGMAP_SPI + select SND_SOC_CS35L45_TABLES + select SND_SOC_CS35L45 + help + Enable support for Cirrus Logic CS35L45 smart speaker amplifier + with SPI control. + +config SND_SOC_CS35L45_I2C + tristate "Cirrus Logic CS35L45 CODEC (I2C)" + depends on I2C + select REGMAP + select REGMAP_I2C + select SND_SOC_CS35L45_TABLES + select SND_SOC_CS35L45 + help + Enable support for Cirrus Logic CS35L45 smart speaker amplifier + with I2C control. + config SND_SOC_CS42L42 tristate "Cirrus Logic CS42L42 CODEC" depends on I2C @@ -953,7 +986,6 @@ config SND_SOC_MAX98095 config SND_SOC_MAX98357A tristate "Maxim MAX98357A CODEC" - depends on GPIOLIB config SND_SOC_MAX98371 tristate @@ -1015,6 +1047,15 @@ config SND_SOC_MAX98390 tristate "Maxim Integrated MAX98390 Speaker Amplifier" depends on I2C +config SND_SOC_MAX98396 + tristate "Analog Devices MAX98396 Speaker Amplifier" + depends on I2C + help + Enable support for Analog Devices MAX98396 audio + amplifier. The device provides a PCM interface for + audio data and a standard I2C interface for control + data communication. + config SND_SOC_MAX9850 tristate depends on I2C @@ -1213,7 +1254,10 @@ config SND_SOC_RT1015 config SND_SOC_RT1015P tristate - depends on GPIOLIB + +config SND_SOC_RT1016 + tristate + depends on I2C config SND_SOC_RT1019 tristate @@ -1753,8 +1797,19 @@ config SND_SOC_WM8728 depends on SND_SOC_I2C_AND_SPI config SND_SOC_WM8731 - tristate "Wolfson Microelectronics WM8731 CODEC" - depends on SND_SOC_I2C_AND_SPI + tristate + +config SND_SOC_WM8731_I2C + tristate "Wolfson Microelectronics WM8731 CODEC with I2C" + depends on I2C + select REGMAP + select SND_SOC_WM8731 + +config SND_SOC_WM8731_SPI + tristate "Wolfson Microelectronics WM8731 CODEC with SPI" + depends on SPI + select REGMAP + select SND_SOC_WM8731 config SND_SOC_WM8737 tristate "Wolfson Microelectronics WM8737 ADC" @@ -1811,7 +1866,7 @@ config SND_SOC_WM8904 depends on I2C config SND_SOC_WM8940 - tristate + tristate "Wolfson Microelectronics WM8940 codec" depends on I2C config SND_SOC_WM8955 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 8637e9e869e3..28dc4edfd01f 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -60,6 +60,10 @@ snd-soc-cs35l41-lib-objs := cs35l41-lib.o snd-soc-cs35l41-objs := cs35l41.o snd-soc-cs35l41-spi-objs := cs35l41-spi.o snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o +snd-soc-cs35l45-tables-objs := cs35l45-tables.o +snd-soc-cs35l45-objs := cs35l45.o +snd-soc-cs35l45-spi-objs := cs35l45-spi.o +snd-soc-cs35l45-i2c-objs := cs35l45-i2c.o snd-soc-cs42l42-objs := cs42l42.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o @@ -135,6 +139,7 @@ snd-soc-max98373-objs := max98373.o snd-soc-max98373-i2c-objs := max98373-i2c.o snd-soc-max98373-sdw-objs := max98373-sdw.o snd-soc-max98390-objs := max98390.o +snd-soc-max98396-objs := max98396.o snd-soc-max9850-objs := max9850.o snd-soc-max9860-objs := max9860.o snd-soc-mc13783-objs := mc13783.o @@ -181,6 +186,7 @@ snd-soc-rl6347a-objs := rl6347a.o snd-soc-rt1011-objs := rt1011.o snd-soc-rt1015-objs := rt1015.o snd-soc-rt1015p-objs := rt1015p.o +snd-soc-rt1016-objs := rt1016.o snd-soc-rt1019-objs := rt1019.o snd-soc-rt1305-objs := rt1305.o snd-soc-rt1308-objs := rt1308.o @@ -290,6 +296,8 @@ snd-soc-wm8711-objs := wm8711.o snd-soc-wm8727-objs := wm8727.o snd-soc-wm8728-objs := wm8728.o snd-soc-wm8731-objs := wm8731.o +snd-soc-wm8731-i2c-objs := wm8731-i2c.o +snd-soc-wm8731-spi-objs := wm8731-spi.o snd-soc-wm8737-objs := wm8737.o snd-soc-wm8741-objs := wm8741.o snd-soc-wm8750-objs := wm8750.o @@ -404,6 +412,10 @@ obj-$(CONFIG_SND_SOC_CS35L41) += snd-soc-cs35l41.o obj-$(CONFIG_SND_SOC_CS35L41_LIB) += snd-soc-cs35l41-lib.o obj-$(CONFIG_SND_SOC_CS35L41_SPI) += snd-soc-cs35l41-spi.o obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o +obj-$(CONFIG_SND_SOC_CS35L45_TABLES) += snd-soc-cs35l45-tables.o +obj-$(CONFIG_SND_SOC_CS35L45) += snd-soc-cs35l45.o +obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o +obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o @@ -474,6 +486,7 @@ obj-$(CONFIG_SND_SOC_MAX98373) += snd-soc-max98373.o obj-$(CONFIG_SND_SOC_MAX98373_I2C) += snd-soc-max98373-i2c.o obj-$(CONFIG_SND_SOC_MAX98373_SDW) += snd-soc-max98373-sdw.o obj-$(CONFIG_SND_SOC_MAX98390) += snd-soc-max98390.o +obj-$(CONFIG_SND_SOC_MAX98396) += snd-soc-max98396.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o @@ -520,6 +533,7 @@ obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o obj-$(CONFIG_SND_SOC_RT1015) += snd-soc-rt1015.o obj-$(CONFIG_SND_SOC_RT1015P) += snd-soc-rt1015p.o +obj-$(CONFIG_SND_SOC_RT1016) += snd-soc-rt1016.o obj-$(CONFIG_SND_SOC_RT1019) += snd-soc-rt1019.o obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o @@ -632,6 +646,8 @@ obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o +obj-$(CONFIG_SND_SOC_WM8731_I2C) += snd-soc-wm8731-i2c.o +obj-$(CONFIG_SND_SOC_WM8731_SPI) += snd-soc-wm8731-spi.o obj-$(CONFIG_SND_SOC_WM8737) += snd-soc-wm8737.o obj-$(CONFIG_SND_SOC_WM8741) += snd-soc-wm8741.o obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c index 3d509a65e4ca..4cb8d87f9011 100644 --- a/sound/soc/codecs/ad193x-i2c.c +++ b/sound/soc/codecs/ad193x-i2c.c @@ -20,10 +20,10 @@ static const struct i2c_device_id ad193x_id[] = { }; MODULE_DEVICE_TABLE(i2c, ad193x_id); -static int ad193x_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ad193x_i2c_probe(struct i2c_client *client) { struct regmap_config config; + const struct i2c_device_id *id = i2c_match_id(ad193x_id, client); config = ad193x_regmap_config; config.val_bits = 8; @@ -38,7 +38,7 @@ static struct i2c_driver ad193x_i2c_driver = { .driver = { .name = "ad193x", }, - .probe = ad193x_i2c_probe, + .probe_new = ad193x_i2c_probe, .id_table = ad193x_id, }; module_i2c_driver(ad193x_i2c_driver); diff --git a/sound/soc/codecs/adau1372-i2c.c b/sound/soc/codecs/adau1372-i2c.c index fc87a76ff1ee..8ed0ffdedbc9 100644 --- a/sound/soc/codecs/adau1372-i2c.c +++ b/sound/soc/codecs/adau1372-i2c.c @@ -14,7 +14,7 @@ #include "adau1372.h" -static int adau1372_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int adau1372_i2c_probe(struct i2c_client *client) { return adau1372_probe(&client->dev, devm_regmap_init_i2c(client, &adau1372_regmap_config), NULL); @@ -30,7 +30,7 @@ static struct i2c_driver adau1372_i2c_driver = { .driver = { .name = "adau1372", }, - .probe = adau1372_i2c_probe, + .probe_new = adau1372_i2c_probe, .id_table = adau1372_i2c_ids, }; module_i2c_driver(adau1372_i2c_driver); diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c index 1faa4c426365..a9f89e8565ec 100644 --- a/sound/soc/codecs/adau1372.c +++ b/sound/soc/codecs/adau1372.c @@ -859,6 +859,7 @@ static const struct snd_soc_component_driver adau1372_driver = { .num_dapm_widgets = ARRAY_SIZE(adau1372_dapm_widgets), .dapm_routes = adau1372_dapm_routes, .num_dapm_routes = ARRAY_SIZE(adau1372_dapm_routes), + .endianness = 1, }; static const struct snd_soc_dai_ops adau1372_dai_ops = { diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 46128aaceae9..a9032b5c8d78 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -1473,8 +1473,7 @@ static const struct snd_soc_component_driver adau1373_component_driver = { .non_legacy_dai_naming = 1, }; -static int adau1373_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adau1373_i2c_probe(struct i2c_client *client) { struct adau1373 *adau1373; int ret; @@ -1508,7 +1507,7 @@ static struct i2c_driver adau1373_i2c_driver = { .driver = { .name = "adau1373", }, - .probe = adau1373_i2c_probe, + .probe_new = adau1373_i2c_probe, .id_table = adau1373_i2c_id, }; diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index dba9af753188..98768e5300f0 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -785,8 +785,7 @@ static const struct regmap_config adau1701_regmap = { .reg_read = adau1701_reg_read, }; -static int adau1701_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adau1701_i2c_probe(struct i2c_client *client) { struct adau1701 *adau1701; struct device *dev = &client->dev; @@ -878,7 +877,7 @@ static struct i2c_driver adau1701_i2c_driver = { .name = "adau1701", .of_match_table = of_match_ptr(adau1701_dt_ids), }, - .probe = adau1701_i2c_probe, + .probe_new = adau1701_i2c_probe, .id_table = adau1701_i2c_id, }; diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c index c8fce37e5cfa..0683caf86aea 100644 --- a/sound/soc/codecs/adau1761-i2c.c +++ b/sound/soc/codecs/adau1761-i2c.c @@ -14,10 +14,12 @@ #include "adau1761.h" -static int adau1761_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id adau1761_i2c_ids[]; + +static int adau1761_i2c_probe(struct i2c_client *client) { struct regmap_config config; + const struct i2c_device_id *id = i2c_match_id(adau1761_i2c_ids, client); config = adau1761_regmap_config; config.val_bits = 8; @@ -59,7 +61,7 @@ static struct i2c_driver adau1761_i2c_driver = { .name = "adau1761", .of_match_table = of_match_ptr(adau1761_i2c_dt_ids), }, - .probe = adau1761_i2c_probe, + .probe_new = adau1761_i2c_probe, .remove = adau1761_i2c_remove, .id_table = adau1761_i2c_ids, }; diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index fb006fc81653..8f887227981f 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -556,8 +556,6 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = { { "Left DAC", NULL, "Interpolator Resync Clock" }, { "Right DAC", NULL, "Interpolator Resync Clock" }, - { "DSP", NULL, "Digital Clock 0" }, - { "Slew Clock", NULL, "Digital Clock 0" }, { "Right Playback Mixer", NULL, "Slew Clock" }, { "Left Playback Mixer", NULL, "Slew Clock" }, @@ -569,6 +567,56 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = { { "Digital Clock 1", NULL, "SYSCLK" }, }; +static const struct snd_soc_dapm_route adau1761_dapm_dsp_routes[] = { + { "DSP", NULL, "Digital Clock 0" }, +}; + +static int adau1761_compatibility_probe(struct device *dev) +{ + struct adau *adau = dev_get_drvdata(dev); + struct regmap *regmap = adau->regmap; + int val, ret = 0; + + /* Only consider compatibility mode when ADAU1361 was specified. */ + if (adau->type != ADAU1361) + return 0; + + regcache_cache_bypass(regmap, true); + + /* + * This will enable the core clock and bypass the PLL, + * so that we can access the registers for probing purposes + * (without having to set up the PLL). + */ + regmap_write(regmap, ADAU17X1_CLOCK_CONTROL, + ADAU17X1_CLOCK_CONTROL_SYSCLK_EN); + + /* + * ADAU17X1_SERIAL_SAMPLING_RATE doesn't exist in non-DSP chips; + * reading it results in zero at all times, and write is a no-op. + * Use this register to probe for ADAU1761. + */ + regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 1); + ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val); + if (ret) + goto exit; + if (val != 1) + goto exit; + regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 0); + ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val); + if (ret) + goto exit; + if (val != 0) + goto exit; + + adau->type = ADAU1761_AS_1361; +exit: + /* Disable core clock after probing. */ + regmap_write(regmap, ADAU17X1_CLOCK_CONTROL, 0); + regcache_cache_bypass(regmap, false); + return ret; +} + static int adau1761_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { @@ -823,7 +871,11 @@ static int adau1761_component_probe(struct snd_soc_component *component) if (ret) return ret; - if (adau->type == ADAU1761) { + /* + * If we've got an ADAU1761, or an ADAU1761 operating as an + * ADAU1361, we need these non-DSP related DAPM widgets and routes. + */ + if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361) { ret = snd_soc_dapm_new_controls(dapm, adau1761_dapm_widgets, ARRAY_SIZE(adau1761_dapm_widgets)); if (ret) @@ -834,7 +886,29 @@ static int adau1761_component_probe(struct snd_soc_component *component) if (ret) return ret; } - + /* + * These routes are DSP related and only used when we have a + * bona fide ADAU1761. + */ + if (adau->type == ADAU1761) { + ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_dsp_routes, + ARRAY_SIZE(adau1761_dapm_dsp_routes)); + if (ret) + return ret; + } + /* + * In the ADAU1761, by default, the AIF is routed to the DSP, whereas + * for the ADAU1361, the AIF is permanently routed to the ADC and DAC. + * Thus, if we have an ADAU1761 masquerading as an ADAU1361, + * we need to explicitly route the AIF to the ADC and DAC. + * For the ADAU1761, this is normally done by set_tdm_slot, but this + * function is not necessarily called during stream setup, so set up + * the compatible AIF routings here from the start. + */ + if (adau->type == ADAU1761_AS_1361) { + regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE, 0x01); + regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x01); + } ret = adau17x1_add_routes(component); if (ret < 0) return ret; @@ -919,6 +993,10 @@ int adau1761_probe(struct device *dev, struct regmap *regmap, if (ret) return ret; + ret = adau1761_compatibility_probe(dev); + if (ret) + return ret; + /* Enable cache only mode as we could miss writes before bias level * reaches standby and the core clock is enabled */ regcache_cache_only(regmap, true); diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c index 1c476429ad99..e046de0ebcc7 100644 --- a/sound/soc/codecs/adau1781-i2c.c +++ b/sound/soc/codecs/adau1781-i2c.c @@ -14,10 +14,12 @@ #include "adau1781.h" -static int adau1781_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id adau1781_i2c_ids[]; + +static int adau1781_i2c_probe(struct i2c_client *client) { struct regmap_config config; + const struct i2c_device_id *id = i2c_match_id(adau1781_i2c_ids, client); config = adau1781_regmap_config; config.val_bits = 8; @@ -55,7 +57,7 @@ static struct i2c_driver adau1781_i2c_driver = { .name = "adau1781", .of_match_table = of_match_ptr(adau1781_i2c_dt_ids), }, - .probe = adau1781_i2c_probe, + .probe_new = adau1781_i2c_probe, .remove = adau1781_i2c_remove, .id_table = adau1781_i2c_ids, }; diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index af05463af4ac..c0f44ecef606 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -334,6 +334,17 @@ static bool adau17x1_has_dsp(struct adau *adau) } } +/* Chip has a DSP but we're pretending it doesn't. */ +static bool adau17x1_has_disused_dsp(struct adau *adau) +{ + switch (adau->type) { + case ADAU1761_AS_1361: + return true; + default: + return false; + } +} + static bool adau17x1_has_safeload(struct adau *adau) { switch (adau->type) { @@ -516,10 +527,11 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0, ADAU17X1_CONVERTER0_CONVSR_MASK, div); - if (adau17x1_has_dsp(adau)) { + + if (adau17x1_has_dsp(adau) || adau17x1_has_disused_dsp(adau)) regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div); + if (adau17x1_has_dsp(adau)) regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div); - } if (adau->sigmadsp) { ret = adau17x1_setup_firmware(component, params_rate(params)); @@ -663,7 +675,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai, switch (slot_width * slots) { case 32: - if (adau->type == ADAU1761) + if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361) return -EINVAL; ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32; @@ -738,7 +750,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai, regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1, ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1); - if (!adau17x1_has_dsp(adau)) + if (!adau17x1_has_dsp(adau) && !adau17x1_has_disused_dsp(adau)) return 0; if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) { diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h index 98a3b6f5bc96..5e58abfffc3d 100644 --- a/sound/soc/codecs/adau17x1.h +++ b/sound/soc/codecs/adau17x1.h @@ -10,6 +10,7 @@ enum adau17x1_type { ADAU1361, ADAU1761, + ADAU1761_AS_1361, ADAU1381, ADAU1781, }; diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c index 82a49c85d536..9f137a0634d5 100644 --- a/sound/soc/codecs/adau1977-i2c.c +++ b/sound/soc/codecs/adau1977-i2c.c @@ -14,10 +14,12 @@ #include "adau1977.h" -static int adau1977_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id adau1977_i2c_ids[]; + +static int adau1977_i2c_probe(struct i2c_client *client) { struct regmap_config config; + const struct i2c_device_id *id = i2c_match_id(adau1977_i2c_ids, client); config = adau1977_regmap_config; config.val_bits = 8; @@ -40,7 +42,7 @@ static struct i2c_driver adau1977_i2c_driver = { .driver = { .name = "adau1977", }, - .probe = adau1977_i2c_probe, + .probe_new = adau1977_i2c_probe, .id_table = adau1977_i2c_ids, }; module_i2c_driver(adau1977_i2c_driver); diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c index aa7afb3b826d..afed48401b25 100644 --- a/sound/soc/codecs/adau7118-i2c.c +++ b/sound/soc/codecs/adau7118-i2c.c @@ -48,8 +48,7 @@ static const struct regmap_config adau7118_regmap_config = { .volatile_reg = adau7118_volatile, }; -static int adau7118_probe_i2c(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int adau7118_probe_i2c(struct i2c_client *i2c) { struct regmap *map; @@ -79,7 +78,7 @@ static struct i2c_driver adau7118_driver = { .name = "adau7118", .of_match_table = adau7118_of_match, }, - .probe = adau7118_probe_i2c, + .probe_new = adau7118_probe_i2c, .id_table = adau7118_id, }; module_i2c_driver(adau7118_driver); diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c index 0f565b851ea5..bf181bbaabed 100644 --- a/sound/soc/codecs/adav803.c +++ b/sound/soc/codecs/adav803.c @@ -19,8 +19,7 @@ static const struct i2c_device_id adav803_id[] = { }; MODULE_DEVICE_TABLE(i2c, adav803_id); -static int adav803_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adav803_probe(struct i2c_client *client) { return adav80x_bus_probe(&client->dev, devm_regmap_init_i2c(client, &adav80x_regmap_config)); @@ -30,7 +29,7 @@ static struct i2c_driver adav803_driver = { .driver = { .name = "adav803", }, - .probe = adav803_probe, + .probe_new = adav803_probe, .id_table = adav803_id, }; module_i2c_driver(adav803_driver); diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c index 2e6bafd2a821..5c4a78c16733 100644 --- a/sound/soc/codecs/ak4118.c +++ b/sound/soc/codecs/ak4118.c @@ -356,8 +356,7 @@ static const struct regmap_config ak4118_regmap = { .max_register = AK4118_REG_MAX - 1, }; -static int ak4118_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ak4118_i2c_probe(struct i2c_client *i2c) { struct ak4118_priv *ak4118; int ret; @@ -416,7 +415,7 @@ static struct i2c_driver ak4118_i2c_driver = { .of_match_table = of_match_ptr(ak4118_of_match), }, .id_table = ak4118_id_table, - .probe = ak4118_i2c_probe, + .probe_new = ak4118_i2c_probe, }; module_i2c_driver(ak4118_i2c_driver); diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 91e7a57c43da..cc803e730c6e 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -405,8 +405,7 @@ static const struct snd_soc_component_driver soc_component_dev_ak4535 = { .non_legacy_dai_naming = 1, }; -static int ak4535_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ak4535_i2c_probe(struct i2c_client *i2c) { struct ak4535_priv *ak4535; int ret; @@ -441,7 +440,7 @@ static struct i2c_driver ak4535_i2c_driver = { .driver = { .name = "ak4535", }, - .probe = ak4535_i2c_probe, + .probe_new = ak4535_i2c_probe, .id_table = ak4535_i2c_id, }; diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 034195c83bd7..55e773f92122 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -10,11 +10,97 @@ // Based on ak4535.c by Richard Purdie // Based on wm8753.c by Liam Girdwood +/* + * +-------+ + * |AK4613 | + * SDTO1 <-| | + * | | + * SDTI1 ->| | + * SDTI2 ->| | + * SDTI3 ->| | + * +-------+ + * + * +---+ + * clk | |___________________________________________... + * + * [TDM512] + * SDTO1 [L1][R1][L2][R2] + * SDTI1 [L1][R1][L2][R2][L3][R3][L4][R4][L5][R5][L6][R6] + * + * [TDM256] + * SDTO1 [L1][R1][L2][R2] + * SDTI1 [L1][R1][L2][R2][L3][R3][L4][R4] + * SDTI2 [L5][R5][L6][R6] + * + * [TDM128] + * SDTO1 [L1][R1][L2][R2] + * SDTI1 [L1][R1][L2][R2] + * SDTI2 [L3][R3][L4][R4] + * SDTI3 [L5][R5][L6][R6] + * + * [STEREO] + * Playback 2ch : SDTI1 + * Capture 2ch : SDTO1 + * + * [TDM512] + * Playback 12ch : SDTI1 + * Capture 4ch : SDTO1 + * + * [TDM256] + * Playback 12ch : SDTI1 + SDTI2 + * Playback 8ch : SDTI1 + * Capture 4ch : SDTO1 + * + * [TDM128] + * Playback 12ch : SDTI1 + SDTI2 + SDTI3 + * Playback 8ch : SDTI1 + SDTI2 + * Playback 4ch : SDTI1 + * Capture 4ch : SDTO1 + * + * + * !!! NOTE !!! + * + * Renesas is the only user of ak4613 on upstream so far, + * but the chip connection is like below. + * Thus, Renesas can't test all connection case. + * Tested TDM is very limited. + * + * +-----+ +-----------+ + * | SoC | | AK4613 | + * | |<-----|SDTO1 IN1|<-- Mic + * | | | IN2| + * | | | | + * | |----->|SDTI1 OUT1|--> Headphone + * +-----+ |SDTI2 OUT2| + * |SDTI3 OUT3| + * | OUT4| + * | OUT5| + * | OUT6| + * +-----------+ + * + * Renesas SoC can handle [2, 6,8] channels. + * Ak4613 can handle [2,4, 8,12] channels. + * + * Because of above HW connection and available channels number, + * Renesas could test are ... + * + * [STEREO] Playback 2ch : SDTI1 + * Capture 2ch : SDTO1 + * [TDM256] Playback 8ch : SDTI1 (*) + * + * (*) it used 8ch data between SoC <-> AK4613 on TDM256 mode, + * but could confirm is only first 2ch because only 1 + * Headphone is connected. + * + * see + * AK4613_ENABLE_TDM_TEST + */ #include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/slab.h> #include <linux/of_device.h> +#include <linux/of_graph.h> #include <linux/module.h> #include <linux/regmap.h> #include <sound/soc.h> @@ -78,29 +164,74 @@ /* OCTRL */ #define OCTRL_MASK (0x3F) -struct ak4613_formats { - unsigned int width; - unsigned int fmt; -}; +/* + * configs + * + * 0x000000BA + * + * B : AK4613_CONFIG_SDTI_x + * A : AK4613_CONFIG_MODE_x + */ +#define AK4613_CONFIG_SET(priv, x) priv->configs |= AK4613_CONFIG_##x +#define AK4613_CONFIG_GET(priv, x) (priv->configs & AK4613_CONFIG_##x##_MASK) + +/* + * AK4613_CONFIG_SDTI_x + * + * It indicates how many SDTIx is connected. + */ +#define AK4613_CONFIG_SDTI_MASK (0xF << 4) +#define AK4613_CONFIG_SDTI(x) (((x) & 0xF) << 4) +#define AK4613_CONFIG_SDTI_set(priv, x) AK4613_CONFIG_SET(priv, SDTI(x)) +#define AK4613_CONFIG_SDTI_get(priv) ((AK4613_CONFIG_GET(priv, SDTI) >> 4) & 0xF) + +/* + * AK4613_CONFIG_MODE_x + * + * Same as Ctrl1 :: TDM1/TDM0 + * No shift is requested + * see + * AK4613_CTRL1_TO_MODE() + * Table 11/12/13/14 + */ +#define AK4613_CONFIG_MODE_MASK (0xF) +#define AK4613_CONFIG_MODE_STEREO (0x0) +#define AK4613_CONFIG_MODE_TDM512 (0x1) +#define AK4613_CONFIG_MODE_TDM256 (0x2) +#define AK4613_CONFIG_MODE_TDM128 (0x3) + +/* + * !!!! FIXME !!!! + * + * Because of testable HW limitation, TDM256 8ch TDM was only tested. + * This driver uses AK4613_ENABLE_TDM_TEST instead of new DT property so far. + * Don't hesitate to update driver, you don't need to care compatible + * with Renesas. + * + * #define AK4613_ENABLE_TDM_TEST + */ struct ak4613_interface { - struct ak4613_formats capture; - struct ak4613_formats playback; + unsigned int width; + unsigned int fmt; + u8 dif; }; struct ak4613_priv { struct mutex lock; - const struct ak4613_interface *iface; - struct snd_pcm_hw_constraint_list constraint; + struct snd_pcm_hw_constraint_list constraint_rates; + struct snd_pcm_hw_constraint_list constraint_channels; struct work_struct dummy_write_work; struct snd_soc_component *component; unsigned int rate; unsigned int sysclk; unsigned int fmt; + unsigned int configs; + int cnt; + u8 ctrl1; u8 oc; u8 ic; - int cnt; }; /* @@ -137,14 +268,24 @@ static const struct reg_default ak4613_reg[] = { { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 }, }; -#define AUDIO_IFACE_TO_VAL(fmts) ((fmts - ak4613_iface) << 3) -#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt } +/* + * CTRL1 register + * see + * Table 11/12/13/14 + */ +#define AUDIO_IFACE(_dif, _width, _fmt) \ + { \ + .dif = _dif, \ + .width = _width, \ + .fmt = SND_SOC_DAIFMT_##_fmt,\ + } static const struct ak4613_interface ak4613_iface[] = { - /* capture */ /* playback */ - /* [0] - [2] are not supported */ - [3] = { AUDIO_IFACE(24, LEFT_J), AUDIO_IFACE(24, LEFT_J) }, - [4] = { AUDIO_IFACE(24, I2S), AUDIO_IFACE(24, I2S) }, + /* It doesn't support asymmetric format */ + + AUDIO_IFACE(0x03, 24, LEFT_J), + AUDIO_IFACE(0x04, 24, I2S), }; +#define AK4613_CTRL1_TO_MODE(priv) ((priv)->ctrl1 >> 6) /* AK4613_CONFIG_MODE_x */ static const struct regmap_config ak4613_regmap_cfg = { .reg_bits = 8, @@ -250,13 +391,14 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, priv->cnt = 0; } if (!priv->cnt) - priv->iface = NULL; + priv->ctrl1 = 0; mutex_unlock(&priv->lock); } static void ak4613_hw_constraints(struct ak4613_priv *priv, - struct snd_pcm_runtime *runtime) + struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; static const unsigned int ak4613_rates[] = { 32000, 44100, @@ -267,10 +409,36 @@ static void ak4613_hw_constraints(struct ak4613_priv *priv, 176400, 192000, }; - struct snd_pcm_hw_constraint_list *constraint = &priv->constraint; +#define AK4613_CHANNEL_2 0 +#define AK4613_CHANNEL_4 1 +#define AK4613_CHANNEL_8 2 +#define AK4613_CHANNEL_12 3 +#define AK4613_CHANNEL_NONE -1 + static const unsigned int ak4613_channels[] = { + [AK4613_CHANNEL_2] = 2, + [AK4613_CHANNEL_4] = 4, + [AK4613_CHANNEL_8] = 8, + [AK4613_CHANNEL_12] = 12, + }; +#define MODE_MAX 4 +#define SDTx_MAX 4 +#define MASK(x) (1 << AK4613_CHANNEL_##x) + static const int mask_list[MODE_MAX][SDTx_MAX] = { + /* SDTO SDTIx1 SDTIx2 SDTIx3 */ + [AK4613_CONFIG_MODE_STEREO] = { MASK(2), MASK(2), MASK(2), MASK(2)}, + [AK4613_CONFIG_MODE_TDM512] = { MASK(4), MASK(12), MASK(12), MASK(12)}, + [AK4613_CONFIG_MODE_TDM256] = { MASK(4), MASK(8), MASK(8)|MASK(12), MASK(8)|MASK(12)}, + [AK4613_CONFIG_MODE_TDM128] = { MASK(4), MASK(4), MASK(4)|MASK(8), MASK(4)|MASK(8)|MASK(12)}, + }; + struct snd_pcm_hw_constraint_list *constraint; + unsigned int mask; + unsigned int mode; unsigned int fs; + int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + int sdti_num; int i; + constraint = &priv->constraint_rates; constraint->list = ak4613_rates; constraint->mask = 0; constraint->count = 0; @@ -296,6 +464,41 @@ static void ak4613_hw_constraints(struct ak4613_priv *priv, snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, constraint); + + + sdti_num = AK4613_CONFIG_SDTI_get(priv); + if (WARN_ON(sdti_num >= SDTx_MAX)) + return; + + if (priv->cnt) { + /* + * If it was already working, + * the constraint is same as working mode. + */ + mode = AK4613_CTRL1_TO_MODE(priv); + mask = 0; /* no default */ + } else { + /* + * It is not yet working, + * the constraint is based on board configs. + * STEREO mask is default + */ + mode = AK4613_CONFIG_GET(priv, MODE); + mask = mask_list[AK4613_CONFIG_MODE_STEREO][is_play * sdti_num]; + } + + if (WARN_ON(mode >= MODE_MAX)) + return; + + /* add each mode mask */ + mask |= mask_list[mode][is_play * sdti_num]; + + constraint = &priv->constraint_channels; + constraint->list = ak4613_channels; + constraint->mask = mask; + constraint->count = sizeof(ak4613_channels); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, constraint); } static int ak4613_dai_startup(struct snd_pcm_substream *substream, @@ -304,9 +507,10 @@ static int ak4613_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); + mutex_lock(&priv->lock); + ak4613_hw_constraints(priv, substream); priv->cnt++; - - ak4613_hw_constraints(priv, substream->runtime); + mutex_unlock(&priv->lock); return 0; } @@ -322,13 +526,13 @@ static int ak4613_dai_set_sysclk(struct snd_soc_dai *codec_dai, return 0; } -static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int format) { struct snd_soc_component *component = dai->component; struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); + unsigned int fmt; - fmt &= SND_SOC_DAIFMT_FORMAT_MASK; - + fmt = format & SND_SOC_DAIFMT_FORMAT_MASK; switch (fmt) { case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_I2S: @@ -338,24 +542,20 @@ static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - return 0; -} - -static bool ak4613_dai_fmt_matching(const struct ak4613_interface *iface, - int is_play, - unsigned int fmt, unsigned int width) -{ - const struct ak4613_formats *fmts; - - fmts = (is_play) ? &iface->playback : &iface->capture; - - if (fmts->fmt != fmt) - return false; - - if (fmts->width != width) - return false; + fmt = format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; + switch (fmt) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + /* + * SUPPORTME + * + * "clock provider" is not yet supperted + */ + return -EINVAL; + } - return true; + return 0; } static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, @@ -364,14 +564,12 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); - const struct ak4613_interface *iface; struct device *dev = component->dev; unsigned int width = params_width(params); unsigned int fmt = priv->fmt; unsigned int rate; - int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int i, ret; - u8 fmt_ctrl, ctrl2; + u8 ctrl2; rate = params_rate(params); switch (rate) { @@ -397,40 +595,51 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, /* * FIXME * - * It doesn't support TDM at this point + * It doesn't have full TDM suppert yet */ - fmt_ctrl = NO_FMT; ret = -EINVAL; - iface = NULL; mutex_lock(&priv->lock); - if (priv->iface) { - if (ak4613_dai_fmt_matching(priv->iface, is_play, fmt, width)) - iface = priv->iface; + if (priv->cnt > 1) { + /* + * If it was already working, use current priv->ctrl1 + */ + ret = 0; } else { + /* + * It is not yet working, + */ + unsigned int channel = params_channels(params); + u8 tdm; + + /* STEREO or TDM */ + if (channel == 2) + tdm = AK4613_CONFIG_MODE_STEREO; + else + tdm = AK4613_CONFIG_GET(priv, MODE); + for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { - if (!ak4613_dai_fmt_matching(ak4613_iface + i, - is_play, - fmt, width)) - continue; - iface = ak4613_iface + i; - break; + const struct ak4613_interface *iface = ak4613_iface + i; + + if ((iface->fmt == fmt) && (iface->width == width)) { + /* + * Ctrl1 + * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | + * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE| + * < tdm > < iface->dif > + */ + priv->ctrl1 = (tdm << 6) | (iface->dif << 3); + ret = 0; + break; + } } } - - if ((priv->iface == NULL) || - (priv->iface == iface)) { - priv->iface = iface; - ret = 0; - } mutex_unlock(&priv->lock); if (ret < 0) goto hw_params_end; - fmt_ctrl = AUDIO_IFACE_TO_VAL(iface); - - snd_soc_component_update_bits(component, CTRL1, FMT_MASK, fmt_ctrl); + snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1); snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2); snd_soc_component_update_bits(component, ICTRL, ICTRL_MASK, priv->ic); @@ -574,14 +783,14 @@ static struct snd_soc_dai_driver ak4613_dai = { .playback = { .stream_name = "Playback", .channels_min = 2, - .channels_max = 2, + .channels_max = 12, .rates = AK4613_PCM_RATE, .formats = AK4613_PCM_FMTBIT, }, .capture = { .stream_name = "Capture", .channels_min = 2, - .channels_max = 2, + .channels_max = 4, .rates = AK4613_PCM_RATE, .formats = AK4613_PCM_FMTBIT, }, @@ -626,6 +835,7 @@ static void ak4613_parse_of(struct ak4613_priv *priv, { struct device_node *np = dev->of_node; char prop[32]; + int sdti_num; int i; /* Input 1 - 2 */ @@ -641,10 +851,32 @@ static void ak4613_parse_of(struct ak4613_priv *priv, if (!of_get_property(np, prop, NULL)) priv->oc |= 1 << i; } + + /* + * enable TDM256 test + * + * !!! FIXME !!! + * + * It should be configured by DT or other way + * if it was full supported. + * But it is using ifdef style for now for test + * purpose. + */ +#if defined(AK4613_ENABLE_TDM_TEST) + AK4613_CONFIG_SET(priv, MODE_TDM256); +#endif + + /* + * connected STDI + */ + sdti_num = of_graph_get_endpoint_count(np); + if (WARN_ON((sdti_num > 3) || (sdti_num < 1))) + return; + + AK4613_CONFIG_SDTI_set(priv, sdti_num); } -static int ak4613_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ak4613_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct device_node *np = dev->of_node; @@ -655,8 +887,11 @@ static int ak4613_i2c_probe(struct i2c_client *i2c, regmap_cfg = NULL; if (np) regmap_cfg = of_device_get_match_data(dev); - else + else { + const struct i2c_device_id *id = + i2c_match_id(ak4613_i2c_id, i2c); regmap_cfg = (const struct regmap_config *)id->driver_data; + } if (!regmap_cfg) return -EINVAL; @@ -667,7 +902,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c, ak4613_parse_of(priv, dev); - priv->iface = NULL; + priv->ctrl1 = 0; priv->cnt = 0; priv->sysclk = 0; INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write); @@ -694,7 +929,7 @@ static struct i2c_driver ak4613_i2c_driver = { .name = "ak4613-codec", .of_match_table = ak4613_of_match, }, - .probe = ak4613_i2c_probe, + .probe_new = ak4613_i2c_probe, .remove = ak4613_i2c_remove, .id_table = ak4613_i2c_id, }; diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 04aef0e72aa5..d8d9cc712d67 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -548,8 +548,7 @@ static const struct regmap_config ak4641_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int ak4641_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ak4641_i2c_probe(struct i2c_client *i2c) { struct ak4641_platform_data *pdata = i2c->dev.platform_data; struct ak4641_priv *ak4641; @@ -632,7 +631,7 @@ static struct i2c_driver ak4641_i2c_driver = { .driver = { .name = "ak4641", }, - .probe = ak4641_i2c_probe, + .probe_new = ak4641_i2c_probe, .remove = ak4641_i2c_remove, .id_table = ak4641_i2c_id, }; diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index c284dcc5af76..3c20ff5595eb 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -630,8 +630,8 @@ static struct clk *ak4642_of_parse_mcko(struct device *dev) #endif static const struct of_device_id ak4642_of_match[]; -static int ak4642_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static const struct i2c_device_id ak4642_i2c_id[]; +static int ak4642_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct device_node *np = dev->of_node; @@ -651,6 +651,8 @@ static int ak4642_i2c_probe(struct i2c_client *i2c, if (of_id) drvdata = of_id->data; } else { + const struct i2c_device_id *id = + i2c_match_id(ak4642_i2c_id, i2c); drvdata = (const struct ak4642_drvdata *)id->driver_data; } @@ -697,7 +699,7 @@ static struct i2c_driver ak4642_i2c_driver = { .name = "ak4642-codec", .of_match_table = ak4642_of_match, }, - .probe = ak4642_i2c_probe, + .probe_new = ak4642_i2c_probe, .id_table = ak4642_i2c_id, }; diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index e9d1251c4265..60edcbe56014 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -629,8 +629,7 @@ static const struct regmap_config ak4671_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int ak4671_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ak4671_i2c_probe(struct i2c_client *client) { struct regmap *regmap; int ret; @@ -657,7 +656,7 @@ static struct i2c_driver ak4671_i2c_driver = { .driver = { .name = "ak4671-codec", }, - .probe = ak4671_i2c_probe, + .probe_new = ak4671_i2c_probe, .id_table = ak4671_i2c_id, }; diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index b10357a6d655..8e6235d2c544 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -968,14 +968,21 @@ static const struct regmap_config alc5623_regmap = { .cache_type = REGCACHE_RBTREE, }; +static const struct i2c_device_id alc5623_i2c_table[] = { + {"alc5621", 0x21}, + {"alc5622", 0x22}, + {"alc5623", 0x23}, + {} +}; +MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table); + /* * ALC5623 2 wire address is determined by A1 pin * state during powerup. * low = 0x1a * high = 0x1b */ -static int alc5623_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int alc5623_i2c_probe(struct i2c_client *client) { struct alc5623_platform_data *pdata; struct alc5623_priv *alc5623; @@ -983,6 +990,7 @@ static int alc5623_i2c_probe(struct i2c_client *client, unsigned int vid1, vid2; int ret; u32 val32; + const struct i2c_device_id *id; alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv), GFP_KERNEL); @@ -1009,6 +1017,8 @@ static int alc5623_i2c_probe(struct i2c_client *client, } vid2 >>= 8; + id = i2c_match_id(alc5623_i2c_table, client); + if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) { dev_err(&client->dev, "unknown or wrong codec\n"); dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n", @@ -1060,14 +1070,6 @@ static int alc5623_i2c_probe(struct i2c_client *client, return ret; } -static const struct i2c_device_id alc5623_i2c_table[] = { - {"alc5621", 0x21}, - {"alc5622", 0x22}, - {"alc5623", 0x23}, - {} -}; -MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table); - #ifdef CONFIG_OF static const struct of_device_id alc5623_of_match[] = { { .compatible = "realtek,alc5623", }, @@ -1082,7 +1084,7 @@ static struct i2c_driver alc5623_i2c_driver = { .name = "alc562x-codec", .of_match_table = of_match_ptr(alc5623_of_match), }, - .probe = alc5623_i2c_probe, + .probe_new = alc5623_i2c_probe, .id_table = alc5623_i2c_table, }; diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index 6d7af3736a91..641bdfddae16 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1092,18 +1092,24 @@ static const struct regmap_config alc5632_regmap = { .cache_type = REGCACHE_RBTREE, }; +static const struct i2c_device_id alc5632_i2c_table[] = { + {"alc5632", 0x5c}, + {} +}; +MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table); + /* * alc5632 2 wire address is determined by A1 pin * state during powerup. * low = 0x1a * high = 0x1b */ -static int alc5632_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int alc5632_i2c_probe(struct i2c_client *client) { struct alc5632_priv *alc5632; int ret, ret1, ret2; unsigned int vid1, vid2; + const struct i2c_device_id *id; alc5632 = devm_kzalloc(&client->dev, sizeof(struct alc5632_priv), GFP_KERNEL); @@ -1129,6 +1135,8 @@ static int alc5632_i2c_probe(struct i2c_client *client, vid2 >>= 8; + id = i2c_match_id(alc5632_i2c_table, client); + if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) { dev_err(&client->dev, "Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2); @@ -1161,12 +1169,6 @@ static int alc5632_i2c_probe(struct i2c_client *client, return ret; } -static const struct i2c_device_id alc5632_i2c_table[] = { - {"alc5632", 0x5c}, - {} -}; -MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table); - #ifdef CONFIG_OF static const struct of_device_id alc5632_of_match[] = { { .compatible = "realtek,alc5632", }, @@ -1181,7 +1183,7 @@ static struct i2c_driver alc5632_i2c_driver = { .name = "alc5632", .of_match_table = of_match_ptr(alc5632_of_match), }, - .probe = alc5632_i2c_probe, + .probe_new = alc5632_i2c_probe, .id_table = alc5632_i2c_table, }; diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 9b92e1a0d1a3..8b0a9c788a26 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -232,11 +232,11 @@ static int i2s_rx_hw_params(struct snd_pcm_substream *substream, if (params_rate(params) != 48000) return -EINVAL; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24; break; default: @@ -387,6 +387,7 @@ static const struct snd_soc_component_driver i2s_rx_component_driver = { .num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets), .dapm_routes = i2s_rx_dapm_routes, .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes), + .endianness = 1, }; static void *wov_map_shm(struct cros_ec_codec_priv *priv, diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index 933e3d627e5f..badfc55bc5fa 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -346,8 +346,7 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client, return 0; } -static int cs35l32_i2c_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) +static int cs35l32_i2c_probe(struct i2c_client *i2c_client) { struct cs35l32_private *cs35l32; struct cs35l32_platform_data *pdata = @@ -576,7 +575,7 @@ static struct i2c_driver cs35l32_i2c_driver = { .of_match_table = cs35l32_of_match, }, .id_table = cs35l32_id, - .probe = cs35l32_i2c_probe, + .probe_new = cs35l32_i2c_probe, .remove = cs35l32_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c index 2a6f5e46d031..47dc0f6d90a2 100644 --- a/sound/soc/codecs/cs35l33.c +++ b/sound/soc/codecs/cs35l33.c @@ -1116,8 +1116,7 @@ static int cs35l33_of_get_pdata(struct device *dev, return 0; } -static int cs35l33_i2c_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) +static int cs35l33_i2c_probe(struct i2c_client *i2c_client) { struct cs35l33_private *cs35l33; struct cs35l33_pdata *pdata = dev_get_platdata(&i2c_client->dev); @@ -1286,7 +1285,7 @@ static struct i2c_driver cs35l33_i2c_driver = { }, .id_table = cs35l33_id, - .probe = cs35l33_i2c_probe, + .probe_new = cs35l33_i2c_probe, .remove = cs35l33_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index ed678241c22b..50d509a06071 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -994,8 +994,7 @@ static const char * const cs35l34_core_supplies[] = { "VP", }; -static int cs35l34_i2c_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) +static int cs35l34_i2c_probe(struct i2c_client *i2c_client) { struct cs35l34_private *cs35l34; struct cs35l34_platform_data *pdata = @@ -1217,7 +1216,7 @@ static struct i2c_driver cs35l34_i2c_driver = { }, .id_table = cs35l34_id, - .probe = cs35l34_i2c_probe, + .probe_new = cs35l34_i2c_probe, .remove = cs35l34_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index 961a3e07e70f..6b70afb70a67 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -1466,8 +1466,7 @@ static const struct reg_sequence cs35l35_errata_patch[] = { { 0x7F, 0x00 }, }; -static int cs35l35_i2c_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) +static int cs35l35_i2c_probe(struct i2c_client *i2c_client) { struct cs35l35_private *cs35l35; struct device *dev = &i2c_client->dev; @@ -1658,7 +1657,7 @@ static struct i2c_driver cs35l35_i2c_driver = { .of_match_table = cs35l35_of_match, }, .id_table = cs35l35_id, - .probe = cs35l35_i2c_probe, + .probe_new = cs35l35_i2c_probe, .remove = cs35l35_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c index d83c1b318c1c..920190daa4d1 100644 --- a/sound/soc/codecs/cs35l36.c +++ b/sound/soc/codecs/cs35l36.c @@ -1700,8 +1700,7 @@ static const struct reg_sequence cs35l36_revb0_errata_patch[] = { { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 }, }; -static int cs35l36_i2c_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) +static int cs35l36_i2c_probe(struct i2c_client *i2c_client) { struct cs35l36_private *cs35l36; struct device *dev = &i2c_client->dev; @@ -1804,7 +1803,7 @@ static int cs35l36_i2c_probe(struct i2c_client *i2c_client, if (ret < 0) { dev_err(&i2c_client->dev, "Failed to read otp_id Register %d\n", ret); - return ret; + goto err; } if ((l37_id_reg & CS35L36_OTP_REV_MASK) == CS35L36_OTP_REV_L37) @@ -1947,7 +1946,7 @@ static struct i2c_driver cs35l36_i2c_driver = { .of_match_table = cs35l36_of_match, }, .id_table = cs35l36_id, - .probe = cs35l36_i2c_probe, + .probe_new = cs35l36_i2c_probe, .remove = cs35l36_i2c_remove, }; module_i2c_driver(cs35l36_i2c_driver); diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c index 5ff0f00a2de4..37c703c08fd5 100644 --- a/sound/soc/codecs/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -29,8 +29,7 @@ static const struct i2c_device_id cs35l41_id_i2c[] = { MODULE_DEVICE_TABLE(i2c, cs35l41_id_i2c); -static int cs35l41_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int cs35l41_i2c_probe(struct i2c_client *client) { struct cs35l41_private *cs35l41; struct device *dev = &client->dev; @@ -91,7 +90,7 @@ static struct i2c_driver cs35l41_i2c_driver = { .acpi_match_table = ACPI_PTR(cs35l41_acpi_match), }, .id_table = cs35l41_id_i2c, - .probe = cs35l41_i2c_probe, + .probe_new = cs35l41_i2c_probe, .remove = cs35l41_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index 3edb09000efa..6d3070ea9e06 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -423,7 +423,7 @@ static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) } } -static const struct cs35l41_otp_packed_element_t otp_map_1[CS35L41_NUM_OTP_ELEM] = { +static const struct cs35l41_otp_packed_element_t otp_map_1[] = { /* addr shift size */ { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/ { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/ @@ -526,7 +526,7 @@ static const struct cs35l41_otp_packed_element_t otp_map_1[CS35L41_NUM_OTP_ELEM] { 0x00017044, 0, 24 }, /*LOT_NUMBER*/ }; -static const struct cs35l41_otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] = { +static const struct cs35l41_otp_packed_element_t otp_map_2[] = { /* addr shift size */ { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/ { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/ @@ -691,35 +691,35 @@ static const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[] = { { .id = 0x01, .map = otp_map_1, - .num_elements = CS35L41_NUM_OTP_ELEM, + .num_elements = ARRAY_SIZE(otp_map_1), .bit_offset = 16, .word_offset = 2, }, { .id = 0x02, .map = otp_map_2, - .num_elements = CS35L41_NUM_OTP_ELEM, + .num_elements = ARRAY_SIZE(otp_map_2), .bit_offset = 16, .word_offset = 2, }, { .id = 0x03, .map = otp_map_2, - .num_elements = CS35L41_NUM_OTP_ELEM, + .num_elements = ARRAY_SIZE(otp_map_2), .bit_offset = 16, .word_offset = 2, }, { .id = 0x06, .map = otp_map_2, - .num_elements = CS35L41_NUM_OTP_ELEM, + .num_elements = ARRAY_SIZE(otp_map_2), .bit_offset = 16, .word_offset = 2, }, { .id = 0x08, .map = otp_map_1, - .num_elements = CS35L41_NUM_OTP_ELEM, + .num_elements = ARRAY_SIZE(otp_map_1), .bit_offset = 16, .word_offset = 2, }, @@ -842,7 +842,7 @@ int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap) word_offset = otp_map_match->word_offset; for (i = 0; i < otp_map_match->num_elements; i++) { - dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d otp_map[i].size = %d\n", + dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d, otp_map[i].size = %u\n", bit_offset, word_offset, bit_sum % 32, otp_map[i].size); if (bit_offset + otp_map[i].size - 1 >= 32) { otp_val = (otp_mem[word_offset] & diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 5f0eca229dd3..3e68a07a3c8e 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1025,6 +1025,8 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l41 = { .controls = cs35l41_aud_controls, .num_controls = ARRAY_SIZE(cs35l41_aud_controls), .set_sysclk = cs35l41_component_set_sysclk, + + .endianness = 1, }; static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_hw_cfg *hw_cfg) diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c new file mode 100644 index 000000000000..38a4dbc9e9fe --- /dev/null +++ b/sound/soc/codecs/cs35l45-i2c.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// +// cs35l45-i2c.c -- CS35L45 I2C driver +// +// Copyright 2019-2022 Cirrus Logic, Inc. +// +// Author: James Schulman <james.schulman@cirrus.com> + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include "cs35l45.h" + +static int cs35l45_i2c_probe(struct i2c_client *client) +{ + struct cs35l45_private *cs35l45; + struct device *dev = &client->dev; + int ret; + + cs35l45 = devm_kzalloc(dev, sizeof(struct cs35l45_private), GFP_KERNEL); + if (!cs35l45) + return -ENOMEM; + + i2c_set_clientdata(client, cs35l45); + cs35l45->regmap = devm_regmap_init_i2c(client, &cs35l45_i2c_regmap); + if (IS_ERR(cs35l45->regmap)) { + ret = PTR_ERR(cs35l45->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + cs35l45->dev = dev; + + return cs35l45_probe(cs35l45); +} + +static int cs35l45_i2c_remove(struct i2c_client *client) +{ + struct cs35l45_private *cs35l45 = i2c_get_clientdata(client); + + return cs35l45_remove(cs35l45); +} + +static const struct of_device_id cs35l45_of_match[] = { + { .compatible = "cirrus,cs35l45" }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l45_of_match); + +static const struct i2c_device_id cs35l45_id_i2c[] = { + { "cs35l45", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs35l45_id_i2c); + +static struct i2c_driver cs35l45_i2c_driver = { + .driver = { + .name = "cs35l45", + .of_match_table = cs35l45_of_match, + .pm = &cs35l45_pm_ops, + }, + .id_table = cs35l45_id_i2c, + .probe_new = cs35l45_i2c_probe, + .remove = cs35l45_i2c_remove, +}; +module_i2c_driver(cs35l45_i2c_driver); + +MODULE_DESCRIPTION("I2C CS35L45 driver"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_CS35L45); +MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES); diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c new file mode 100644 index 000000000000..baaf6e0f4fb9 --- /dev/null +++ b/sound/soc/codecs/cs35l45-spi.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// +// cs35l45-spi.c -- CS35L45 SPI driver +// +// Copyright 2019-2022 Cirrus Logic, Inc. +// +// Author: James Schulman <james.schulman@cirrus.com> + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "cs35l45.h" + +static int cs35l45_spi_probe(struct spi_device *spi) +{ + struct cs35l45_private *cs35l45; + struct device *dev = &spi->dev; + int ret; + + cs35l45 = devm_kzalloc(dev, sizeof(struct cs35l45_private), GFP_KERNEL); + if (cs35l45 == NULL) + return -ENOMEM; + + spi_set_drvdata(spi, cs35l45); + cs35l45->regmap = devm_regmap_init_spi(spi, &cs35l45_spi_regmap); + if (IS_ERR(cs35l45->regmap)) { + ret = PTR_ERR(cs35l45->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + cs35l45->dev = dev; + + return cs35l45_probe(cs35l45); +} + +static void cs35l45_spi_remove(struct spi_device *spi) +{ + struct cs35l45_private *cs35l45 = spi_get_drvdata(spi); + + cs35l45_remove(cs35l45); +} + +static const struct of_device_id cs35l45_of_match[] = { + { .compatible = "cirrus,cs35l45" }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l45_of_match); + +static const struct spi_device_id cs35l45_id_spi[] = { + { "cs35l45", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, cs35l45_id_spi); + +static struct spi_driver cs35l45_spi_driver = { + .driver = { + .name = "cs35l45", + .of_match_table = cs35l45_of_match, + .pm = &cs35l45_pm_ops, + }, + .id_table = cs35l45_id_spi, + .probe = cs35l45_spi_probe, + .remove = cs35l45_spi_remove, +}; +module_spi_driver(cs35l45_spi_driver); + +MODULE_DESCRIPTION("SPI CS35L45 driver"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_CS35L45); +MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES); diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c new file mode 100644 index 000000000000..5a2c2e684ef9 --- /dev/null +++ b/sound/soc/codecs/cs35l45-tables.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// +// cs35l45-tables.c -- CS35L45 ALSA SoC audio driver +// +// Copyright 2019-2022 Cirrus Logic, Inc. +// +// Author: James Schulman <james.schulman@cirrus.com> + +#include <linux/module.h> +#include <linux/regmap.h> + +#include "cs35l45.h" + +static const struct reg_sequence cs35l45_patch[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00000044, 0x00000055 }, + { 0x00000044, 0x000000AA }, + { 0x00006480, 0x0830500A }, + { 0x00007C60, 0x1000850B }, + { CS35L45_BOOST_OV_CFG, 0x007000D0 }, + { CS35L45_LDPM_CONFIG, 0x0001B636 }, + { 0x00002C08, 0x00000009 }, + { 0x00006850, 0x0A30FFC4 }, + { 0x00003820, 0x00040100 }, + { 0x00003824, 0x00000000 }, + { 0x00007CFC, 0x62870004 }, + { 0x00007C60, 0x1001850B }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000000 }, + { CS35L45_BOOST_CCM_CFG, 0xF0000003 }, + { CS35L45_BOOST_DCM_CFG, 0x08710220 }, + { CS35L45_ERROR_RELEASE, 0x00200000 }, +}; + +int cs35l45_apply_patch(struct cs35l45_private *cs35l45) +{ + return regmap_register_patch(cs35l45->regmap, cs35l45_patch, + ARRAY_SIZE(cs35l45_patch)); +} +EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45_TABLES); + +static const struct reg_default cs35l45_defaults[] = { + { CS35L45_BLOCK_ENABLES, 0x00003323 }, + { CS35L45_BLOCK_ENABLES2, 0x00000010 }, + { CS35L45_REFCLK_INPUT, 0x00000510 }, + { CS35L45_GLOBAL_SAMPLE_RATE, 0x00000003 }, + { CS35L45_ASP_ENABLES1, 0x00000000 }, + { CS35L45_ASP_CONTROL1, 0x00000028 }, + { CS35L45_ASP_CONTROL2, 0x18180200 }, + { CS35L45_ASP_CONTROL3, 0x00000002 }, + { CS35L45_ASP_FRAME_CONTROL1, 0x03020100 }, + { CS35L45_ASP_FRAME_CONTROL2, 0x00000004 }, + { CS35L45_ASP_FRAME_CONTROL5, 0x00000100 }, + { CS35L45_ASP_DATA_CONTROL1, 0x00000018 }, + { CS35L45_ASP_DATA_CONTROL5, 0x00000018 }, + { CS35L45_DACPCM1_INPUT, 0x00000008 }, + { CS35L45_ASPTX1_INPUT, 0x00000018 }, + { CS35L45_ASPTX2_INPUT, 0x00000019 }, + { CS35L45_ASPTX3_INPUT, 0x00000020 }, + { CS35L45_ASPTX4_INPUT, 0x00000028 }, + { CS35L45_ASPTX5_INPUT, 0x00000048 }, + { CS35L45_AMP_PCM_CONTROL, 0x00100000 }, +}; + +static bool cs35l45_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L45_DEVID ... CS35L45_OTPID: + case CS35L45_SFT_RESET: + case CS35L45_GLOBAL_ENABLES: + case CS35L45_BLOCK_ENABLES: + case CS35L45_BLOCK_ENABLES2: + case CS35L45_ERROR_RELEASE: + case CS35L45_REFCLK_INPUT: + case CS35L45_GLOBAL_SAMPLE_RATE: + case CS35L45_ASP_ENABLES1: + case CS35L45_ASP_CONTROL1: + case CS35L45_ASP_CONTROL2: + case CS35L45_ASP_CONTROL3: + case CS35L45_ASP_FRAME_CONTROL1: + case CS35L45_ASP_FRAME_CONTROL2: + case CS35L45_ASP_FRAME_CONTROL5: + case CS35L45_ASP_DATA_CONTROL1: + case CS35L45_ASP_DATA_CONTROL5: + case CS35L45_DACPCM1_INPUT: + case CS35L45_ASPTX1_INPUT: + case CS35L45_ASPTX2_INPUT: + case CS35L45_ASPTX3_INPUT: + case CS35L45_ASPTX4_INPUT: + case CS35L45_ASPTX5_INPUT: + case CS35L45_AMP_PCM_CONTROL: + case CS35L45_AMP_PCM_HPF_TST: + case CS35L45_IRQ1_EINT_4: + return true; + default: + return false; + } +} + +static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L45_DEVID ... CS35L45_OTPID: + case CS35L45_SFT_RESET: + case CS35L45_GLOBAL_ENABLES: + case CS35L45_ERROR_RELEASE: + case CS35L45_AMP_PCM_HPF_TST: /* not cachable */ + case CS35L45_IRQ1_EINT_4: + return true; + default: + return false; + } +} + +const struct regmap_config cs35l45_i2c_regmap = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L45_LASTREG, + .reg_defaults = cs35l45_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults), + .volatile_reg = cs35l45_volatile_reg, + .readable_reg = cs35l45_readable_reg, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_NS_GPL(cs35l45_i2c_regmap, SND_SOC_CS35L45_TABLES); + +const struct regmap_config cs35l45_spi_regmap = { + .reg_bits = 32, + .val_bits = 32, + .pad_bits = 16, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L45_LASTREG, + .reg_defaults = cs35l45_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults), + .volatile_reg = cs35l45_volatile_reg, + .readable_reg = cs35l45_readable_reg, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_NS_GPL(cs35l45_spi_regmap, SND_SOC_CS35L45_TABLES); + +static const struct { + u8 cfg_id; + u32 freq; +} cs35l45_pll_refclk_freq[] = { + { 0x0C, 128000 }, + { 0x0F, 256000 }, + { 0x11, 384000 }, + { 0x12, 512000 }, + { 0x15, 768000 }, + { 0x17, 1024000 }, + { 0x19, 1411200 }, + { 0x1B, 1536000 }, + { 0x1C, 2116800 }, + { 0x1D, 2048000 }, + { 0x1E, 2304000 }, + { 0x1F, 2822400 }, + { 0x21, 3072000 }, + { 0x23, 4233600 }, + { 0x24, 4096000 }, + { 0x25, 4608000 }, + { 0x26, 5644800 }, + { 0x27, 6000000 }, + { 0x28, 6144000 }, + { 0x29, 6350400 }, + { 0x2A, 6912000 }, + { 0x2D, 7526400 }, + { 0x2E, 8467200 }, + { 0x2F, 8192000 }, + { 0x30, 9216000 }, + { 0x31, 11289600 }, + { 0x33, 12288000 }, + { 0x37, 16934400 }, + { 0x38, 18432000 }, + { 0x39, 22579200 }, + { 0x3B, 24576000 }, +}; + +unsigned int cs35l45_get_clk_freq_id(unsigned int freq) +{ + int i; + + if (freq == 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(cs35l45_pll_refclk_freq); ++i) { + if (cs35l45_pll_refclk_freq[i].freq == freq) + return cs35l45_pll_refclk_freq[i].cfg_id; + } + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(cs35l45_get_clk_freq_id, SND_SOC_CS35L45_TABLES); + +MODULE_DESCRIPTION("ASoC CS35L45 driver tables"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c new file mode 100644 index 000000000000..2367c1a4c10e --- /dev/null +++ b/sound/soc/codecs/cs35l45.c @@ -0,0 +1,690 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// +// cs35l45.c - CS35L45 ALSA SoC audio driver +// +// Copyright 2019-2022 Cirrus Logic, Inc. +// +// Author: James Schulman <james.schulman@cirrus.com> + +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "cs35l45.h" + +static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component); + + dev_dbg(cs35l45->dev, "%s event : %x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES, + CS35L45_GLOBAL_EN_MASK); + + usleep_range(CS35L45_POST_GLOBAL_EN_US, CS35L45_POST_GLOBAL_EN_US + 100); + break; + case SND_SOC_DAPM_PRE_PMD: + usleep_range(CS35L45_PRE_GLOBAL_DIS_US, CS35L45_PRE_GLOBAL_DIS_US + 100); + + regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES, 0); + break; + default: + break; + } + + return 0; +} + +static const char * const cs35l45_asp_tx_txt[] = { + "Zero", "ASP_RX1", "ASP_RX2", + "VMON", "IMON", "ERR_VOL", + "VDD_BATTMON", "VDD_BSTMON", + "Interpolator", "IL_TARGET", +}; + +static const unsigned int cs35l45_asp_tx_val[] = { + CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2, + CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL, + CS35L45_PCM_SRC_VDD_BATTMON, CS35L45_PCM_SRC_VDD_BSTMON, + CS35L45_PCM_SRC_INTERPOLATOR, CS35L45_PCM_SRC_IL_TARGET, +}; + +static const struct soc_enum cs35l45_asp_tx_enums[] = { + SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX1_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, + cs35l45_asp_tx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX2_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, + cs35l45_asp_tx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX3_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, + cs35l45_asp_tx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX4_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, + cs35l45_asp_tx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX5_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, + cs35l45_asp_tx_val), +}; + +static const char * const cs35l45_dac_txt[] = { + "Zero", "ASP_RX1", "ASP_RX2" +}; + +static const unsigned int cs35l45_dac_val[] = { + CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2 +}; + +static const struct soc_enum cs35l45_dacpcm_enums[] = { + SOC_VALUE_ENUM_SINGLE(CS35L45_DACPCM1_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dac_txt), cs35l45_dac_txt, + cs35l45_dac_val), +}; + +static const struct snd_kcontrol_new cs35l45_asp_muxes[] = { + SOC_DAPM_ENUM("ASP_TX1 Source", cs35l45_asp_tx_enums[0]), + SOC_DAPM_ENUM("ASP_TX2 Source", cs35l45_asp_tx_enums[1]), + SOC_DAPM_ENUM("ASP_TX3 Source", cs35l45_asp_tx_enums[2]), + SOC_DAPM_ENUM("ASP_TX4 Source", cs35l45_asp_tx_enums[3]), + SOC_DAPM_ENUM("ASP_TX5 Source", cs35l45_asp_tx_enums[4]), +}; + +static const struct snd_kcontrol_new cs35l45_dac_muxes[] = { + SOC_DAPM_ENUM("DACPCM1 Source", cs35l45_dacpcm_enums[0]), +}; + +static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("GLOBAL_EN", SND_SOC_NOPM, 0, 0, + cs35l45_global_en_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("ASP_EN", CS35L45_BLOCK_ENABLES2, CS35L45_ASP_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_SIGGEN("VMON_SRC"), + SND_SOC_DAPM_SIGGEN("IMON_SRC"), + SND_SOC_DAPM_SIGGEN("VDD_BATTMON_SRC"), + SND_SOC_DAPM_SIGGEN("VDD_BSTMON_SRC"), + SND_SOC_DAPM_SIGGEN("ERR_VOL"), + SND_SOC_DAPM_SIGGEN("AMP_INTP"), + SND_SOC_DAPM_SIGGEN("IL_TARGET"), + SND_SOC_DAPM_ADC("VMON", NULL, CS35L45_BLOCK_ENABLES, CS35L45_VMON_EN_SHIFT, 0), + SND_SOC_DAPM_ADC("IMON", NULL, CS35L45_BLOCK_ENABLES, CS35L45_IMON_EN_SHIFT, 0), + SND_SOC_DAPM_ADC("VDD_BATTMON", NULL, CS35L45_BLOCK_ENABLES, + CS35L45_VDD_BATTMON_EN_SHIFT, 0), + SND_SOC_DAPM_ADC("VDD_BSTMON", NULL, CS35L45_BLOCK_ENABLES, + CS35L45_VDD_BSTMON_EN_SHIFT, 0), + + SND_SOC_DAPM_AIF_IN("ASP_RX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASP_RX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX2_EN_SHIFT, 0), + + SND_SOC_DAPM_AIF_OUT("ASP_TX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP_TX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP_TX3", NULL, 2, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX3_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP_TX4", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX4_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP_TX5", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX5_EN_SHIFT, 0), + + SND_SOC_DAPM_MUX("ASP_TX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[0]), + SND_SOC_DAPM_MUX("ASP_TX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[1]), + SND_SOC_DAPM_MUX("ASP_TX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[2]), + SND_SOC_DAPM_MUX("ASP_TX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[3]), + SND_SOC_DAPM_MUX("ASP_TX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[4]), + + SND_SOC_DAPM_MUX("DACPCM1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]), + + SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("SPK"), +}; + +#define CS35L45_ASP_MUX_ROUTE(name) \ + { name" Source", "ASP_RX1", "ASP_RX1" }, \ + { name" Source", "ASP_RX2", "ASP_RX2" }, \ + { name" Source", "VMON", "VMON" }, \ + { name" Source", "IMON", "IMON" }, \ + { name" Source", "ERR_VOL", "ERR_VOL" }, \ + { name" Source", "VDD_BATTMON", "VDD_BATTMON" }, \ + { name" Source", "VDD_BSTMON", "VDD_BSTMON" }, \ + { name" Source", "Interpolator", "AMP_INTP" }, \ + { name" Source", "IL_TARGET", "IL_TARGET" } + +#define CS35L45_DAC_MUX_ROUTE(name) \ + { name" Source", "ASP_RX1", "ASP_RX1" }, \ + { name" Source", "ASP_RX2", "ASP_RX2" } + +static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = { + /* Feedback */ + { "VMON", NULL, "VMON_SRC" }, + { "IMON", NULL, "IMON_SRC" }, + { "VDD_BATTMON", NULL, "VDD_BATTMON_SRC" }, + { "VDD_BSTMON", NULL, "VDD_BSTMON_SRC" }, + + { "Capture", NULL, "ASP_TX1"}, + { "Capture", NULL, "ASP_TX2"}, + { "Capture", NULL, "ASP_TX3"}, + { "Capture", NULL, "ASP_TX4"}, + { "Capture", NULL, "ASP_TX5"}, + { "ASP_TX1", NULL, "ASP_TX1 Source"}, + { "ASP_TX2", NULL, "ASP_TX2 Source"}, + { "ASP_TX3", NULL, "ASP_TX3 Source"}, + { "ASP_TX4", NULL, "ASP_TX4 Source"}, + { "ASP_TX5", NULL, "ASP_TX5 Source"}, + + { "ASP_TX1", NULL, "ASP_EN" }, + { "ASP_TX2", NULL, "ASP_EN" }, + { "ASP_TX3", NULL, "ASP_EN" }, + { "ASP_TX4", NULL, "ASP_EN" }, + { "ASP_TX1", NULL, "GLOBAL_EN" }, + { "ASP_TX2", NULL, "GLOBAL_EN" }, + { "ASP_TX3", NULL, "GLOBAL_EN" }, + { "ASP_TX4", NULL, "GLOBAL_EN" }, + { "ASP_TX5", NULL, "GLOBAL_EN" }, + + CS35L45_ASP_MUX_ROUTE("ASP_TX1"), + CS35L45_ASP_MUX_ROUTE("ASP_TX2"), + CS35L45_ASP_MUX_ROUTE("ASP_TX3"), + CS35L45_ASP_MUX_ROUTE("ASP_TX4"), + CS35L45_ASP_MUX_ROUTE("ASP_TX5"), + + /* Playback */ + { "ASP_RX1", NULL, "Playback" }, + { "ASP_RX2", NULL, "Playback" }, + { "ASP_RX1", NULL, "ASP_EN" }, + { "ASP_RX2", NULL, "ASP_EN" }, + + { "AMP", NULL, "DACPCM1 Source"}, + { "AMP", NULL, "GLOBAL_EN"}, + + CS35L45_DAC_MUX_ROUTE("DACPCM1"), + + { "SPK", NULL, "AMP"}, +}; + +static const DECLARE_TLV_DB_SCALE(cs35l45_dig_pcm_vol_tlv, -10225, 25, true); + +static const struct snd_kcontrol_new cs35l45_controls[] = { + /* Ignore bit 0: it is beyond the resolution of TLV_DB_SCALE */ + SOC_SINGLE_S_TLV("Digital PCM Volume", + CS35L45_AMP_PCM_CONTROL, + CS35L45_AMP_VOL_PCM_SHIFT + 1, + -409, 48, + (CS35L45_AMP_VOL_PCM_WIDTH - 1) - 1, + 0, cs35l45_dig_pcm_vol_tlv), +}; + +static int cs35l45_set_pll(struct cs35l45_private *cs35l45, unsigned int freq) +{ + unsigned int val; + int freq_id; + + freq_id = cs35l45_get_clk_freq_id(freq); + if (freq_id < 0) { + dev_err(cs35l45->dev, "Invalid freq: %u\n", freq); + return -EINVAL; + } + + regmap_read(cs35l45->regmap, CS35L45_REFCLK_INPUT, &val); + val = (val & CS35L45_PLL_REFCLK_FREQ_MASK) >> CS35L45_PLL_REFCLK_FREQ_SHIFT; + if (val == freq_id) + return 0; + + regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK); + regmap_update_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, + CS35L45_PLL_REFCLK_FREQ_MASK, + freq_id << CS35L45_PLL_REFCLK_FREQ_SHIFT); + regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK); + regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK); + regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK); + + return 0; +} + +static int cs35l45_asp_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(codec_dai->component); + unsigned int asp_fmt, fsync_inv, bclk_inv; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + dev_err(cs35l45->dev, "Invalid DAI clocking\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + asp_fmt = CS35l45_ASP_FMT_DSP_A; + break; + case SND_SOC_DAIFMT_I2S: + asp_fmt = CS35L45_ASP_FMT_I2S; + break; + default: + dev_err(cs35l45->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + fsync_inv = 1; + bclk_inv = 0; + break; + case SND_SOC_DAIFMT_IB_NF: + fsync_inv = 0; + bclk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_IF: + fsync_inv = 1; + bclk_inv = 1; + break; + case SND_SOC_DAIFMT_NB_NF: + fsync_inv = 0; + bclk_inv = 0; + break; + default: + dev_warn(cs35l45->dev, "Invalid DAI clock polarity\n"); + return -EINVAL; + } + + regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2, + CS35L45_ASP_FMT_MASK | + CS35L45_ASP_FSYNC_INV_MASK | + CS35L45_ASP_BCLK_INV_MASK, + (asp_fmt << CS35L45_ASP_FMT_SHIFT) | + (fsync_inv << CS35L45_ASP_FSYNC_INV_SHIFT) | + (bclk_inv << CS35L45_ASP_BCLK_INV_SHIFT)); + + return 0; +} + +static int cs35l45_asp_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component); + unsigned int asp_width, asp_wl, global_fs, slot_multiple, asp_fmt; + int bclk; + + switch (params_rate(params)) { + case 44100: + global_fs = CS35L45_44P100_KHZ; + break; + case 48000: + global_fs = CS35L45_48P0_KHZ; + break; + case 88200: + global_fs = CS35L45_88P200_KHZ; + break; + case 96000: + global_fs = CS35L45_96P0_KHZ; + break; + default: + dev_warn(cs35l45->dev, "Unsupported sample rate (%d)\n", + params_rate(params)); + return -EINVAL; + } + + regmap_update_bits(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE, + CS35L45_GLOBAL_FS_MASK, + global_fs << CS35L45_GLOBAL_FS_SHIFT); + + asp_wl = params_width(params); + + if (cs35l45->slot_width) + asp_width = cs35l45->slot_width; + else + asp_width = params_width(params); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2, + CS35L45_ASP_WIDTH_RX_MASK, + asp_width << CS35L45_ASP_WIDTH_RX_SHIFT); + + regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL5, + CS35L45_ASP_WL_MASK, + asp_wl << CS35L45_ASP_WL_SHIFT); + } else { + regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2, + CS35L45_ASP_WIDTH_TX_MASK, + asp_width << CS35L45_ASP_WIDTH_TX_SHIFT); + + regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL1, + CS35L45_ASP_WL_MASK, + asp_wl << CS35L45_ASP_WL_SHIFT); + } + + if (cs35l45->sysclk_set) + return 0; + + /* I2S always has an even number of channels */ + regmap_read(cs35l45->regmap, CS35L45_ASP_CONTROL2, &asp_fmt); + asp_fmt = (asp_fmt & CS35L45_ASP_FMT_MASK) >> CS35L45_ASP_FMT_SHIFT; + if (asp_fmt == CS35L45_ASP_FMT_I2S) + slot_multiple = 2; + else + slot_multiple = 1; + + bclk = snd_soc_tdm_params_to_bclk(params, asp_width, + cs35l45->slot_count, slot_multiple); + + return cs35l45_set_pll(cs35l45, bclk); +} + +static int cs35l45_asp_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component); + + if (slot_width && ((slot_width < 16) || (slot_width > 128))) + return -EINVAL; + + cs35l45->slot_width = slot_width; + cs35l45->slot_count = slots; + + return 0; +} + +static int cs35l45_asp_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component); + int ret; + + if (clk_id != 0) { + dev_err(cs35l45->dev, "Invalid clk_id %d\n", clk_id); + return -EINVAL; + } + + cs35l45->sysclk_set = false; + if (freq == 0) + return 0; + + ret = cs35l45_set_pll(cs35l45, freq); + if (ret < 0) + return -EINVAL; + + cs35l45->sysclk_set = true; + + return 0; +} + +static int cs35l45_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component); + unsigned int global_fs, val, hpf_tune; + + if (mute) + return 0; + + regmap_read(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE, &global_fs); + global_fs = (global_fs & CS35L45_GLOBAL_FS_MASK) >> CS35L45_GLOBAL_FS_SHIFT; + switch (global_fs) { + case CS35L45_44P100_KHZ: + hpf_tune = CS35L45_HPF_44P1; + break; + case CS35L45_88P200_KHZ: + hpf_tune = CS35L45_HPF_88P2; + break; + default: + hpf_tune = CS35l45_HPF_DEFAULT; + break; + } + + regmap_read(cs35l45->regmap, CS35L45_AMP_PCM_HPF_TST, &val); + if (val != hpf_tune) { + struct reg_sequence hpf_override_seq[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00000044, 0x00000055 }, + { 0x00000044, 0x000000AA }, + { CS35L45_AMP_PCM_HPF_TST, hpf_tune }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000000 }, + }; + regmap_multi_reg_write(cs35l45->regmap, hpf_override_seq, + ARRAY_SIZE(hpf_override_seq)); + } + + return 0; +} + +static const struct snd_soc_dai_ops cs35l45_asp_dai_ops = { + .set_fmt = cs35l45_asp_set_fmt, + .hw_params = cs35l45_asp_hw_params, + .set_tdm_slot = cs35l45_asp_set_tdm_slot, + .set_sysclk = cs35l45_asp_set_sysclk, + .mute_stream = cs35l45_mute_stream, +}; + +static struct snd_soc_dai_driver cs35l45_dai[] = { + { + .name = "cs35l45", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS35L45_RATES, + .formats = CS35L45_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 5, + .rates = CS35L45_RATES, + .formats = CS35L45_FORMATS, + }, + .symmetric_rate = true, + .symmetric_sample_bits = true, + .ops = &cs35l45_asp_dai_ops, + }, +}; + +static const struct snd_soc_component_driver cs35l45_component = { + .dapm_widgets = cs35l45_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l45_dapm_widgets), + + .dapm_routes = cs35l45_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs35l45_dapm_routes), + + .controls = cs35l45_controls, + .num_controls = ARRAY_SIZE(cs35l45_controls), + + .name = "cs35l45", +}; + +static int __maybe_unused cs35l45_runtime_suspend(struct device *dev) +{ + struct cs35l45_private *cs35l45 = dev_get_drvdata(dev); + + regcache_cache_only(cs35l45->regmap, true); + + dev_dbg(cs35l45->dev, "Runtime suspended\n"); + + return 0; +} + +static int __maybe_unused cs35l45_runtime_resume(struct device *dev) +{ + struct cs35l45_private *cs35l45 = dev_get_drvdata(dev); + int ret; + + dev_dbg(cs35l45->dev, "Runtime resume\n"); + + regcache_cache_only(cs35l45->regmap, false); + ret = regcache_sync(cs35l45->regmap); + if (ret != 0) + dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret); + + /* Clear global error status */ + regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK); + regmap_set_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK); + regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK); + return ret; +} + +static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45) +{ + unsigned int val; + + if (device_property_read_u32(cs35l45->dev, + "cirrus,asp-sdout-hiz-ctrl", &val) == 0) { + regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL3, + CS35L45_ASP_DOUT_HIZ_CTRL_MASK, + val << CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT); + } + + return 0; +} + +static int cs35l45_initialize(struct cs35l45_private *cs35l45) +{ + struct device *dev = cs35l45->dev; + unsigned int dev_id[5]; + unsigned int sts; + int ret; + + ret = regmap_read_poll_timeout(cs35l45->regmap, CS35L45_IRQ1_EINT_4, sts, + (sts & CS35L45_OTP_BOOT_DONE_STS_MASK), + 1000, 5000); + if (ret < 0) { + dev_err(cs35l45->dev, "Timeout waiting for OTP boot\n"); + return ret; + } + + ret = regmap_bulk_read(cs35l45->regmap, CS35L45_DEVID, dev_id, ARRAY_SIZE(dev_id)); + if (ret) { + dev_err(cs35l45->dev, "Get Device ID failed: %d\n", ret); + return ret; + } + + switch (dev_id[0]) { + case 0x35A450: + break; + default: + dev_err(cs35l45->dev, "Bad DEVID 0x%x\n", dev_id[0]); + return -ENODEV; + } + + dev_info(cs35l45->dev, "Cirrus Logic CS35L45: REVID %02X OTPID %02X\n", + dev_id[1], dev_id[4]); + + regmap_write(cs35l45->regmap, CS35L45_IRQ1_EINT_4, + CS35L45_OTP_BOOT_DONE_STS_MASK | CS35L45_OTP_BUSY_MASK); + + ret = cs35l45_apply_patch(cs35l45); + if (ret < 0) { + dev_err(dev, "Failed to apply init patch %d\n", ret); + return ret; + } + + ret = cs35l45_apply_property_config(cs35l45); + if (ret < 0) + return ret; + + pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000); + pm_runtime_use_autosuspend(cs35l45->dev); + pm_runtime_set_active(cs35l45->dev); + pm_runtime_enable(cs35l45->dev); + + return 0; +} + +int cs35l45_probe(struct cs35l45_private *cs35l45) +{ + struct device *dev = cs35l45->dev; + int ret; + + cs35l45->vdd_batt = devm_regulator_get(dev, "vdd-batt"); + if (IS_ERR(cs35l45->vdd_batt)) + return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_batt), + "Failed to request vdd-batt\n"); + + cs35l45->vdd_a = devm_regulator_get(dev, "vdd-a"); + if (IS_ERR(cs35l45->vdd_a)) + return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_a), + "Failed to request vdd-a\n"); + + /* VDD_BATT must always be enabled before other supplies */ + ret = regulator_enable(cs35l45->vdd_batt); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable vdd-batt\n"); + + ret = regulator_enable(cs35l45->vdd_a); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable vdd-a\n"); + + /* If reset is shared only one instance can claim it */ + cs35l45->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs35l45->reset_gpio)) { + ret = PTR_ERR(cs35l45->reset_gpio); + cs35l45->reset_gpio = NULL; + if (ret == -EBUSY) { + dev_dbg(dev, "Reset line busy, assuming shared reset\n"); + } else { + dev_err_probe(dev, ret, "Failed to get reset GPIO\n"); + goto err; + } + } + + if (cs35l45->reset_gpio) { + usleep_range(CS35L45_RESET_HOLD_US, CS35L45_RESET_HOLD_US + 100); + gpiod_set_value_cansleep(cs35l45->reset_gpio, 1); + } + + usleep_range(CS35L45_RESET_US, CS35L45_RESET_US + 100); + + ret = cs35l45_initialize(cs35l45); + if (ret < 0) + goto err_reset; + + ret = devm_snd_soc_register_component(dev, &cs35l45_component, + cs35l45_dai, + ARRAY_SIZE(cs35l45_dai)); + if (ret < 0) + goto err_reset; + + return 0; + +err_reset: + gpiod_set_value_cansleep(cs35l45->reset_gpio, 0); +err: + regulator_disable(cs35l45->vdd_a); + regulator_disable(cs35l45->vdd_batt); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l45_probe, SND_SOC_CS35L45); + +int cs35l45_remove(struct cs35l45_private *cs35l45) +{ + pm_runtime_disable(cs35l45->dev); + + gpiod_set_value_cansleep(cs35l45->reset_gpio, 0); + regulator_disable(cs35l45->vdd_a); + /* VDD_BATT must be the last to power-off */ + regulator_disable(cs35l45->vdd_batt); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l45_remove, SND_SOC_CS35L45); + +const struct dev_pm_ops cs35l45_pm_ops = { + SET_RUNTIME_PM_OPS(cs35l45_runtime_suspend, cs35l45_runtime_resume, NULL) +}; +EXPORT_SYMBOL_NS_GPL(cs35l45_pm_ops, SND_SOC_CS35L45); + +MODULE_DESCRIPTION("ASoC CS35L45 driver"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES); diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h new file mode 100644 index 000000000000..4e266d19cd1c --- /dev/null +++ b/sound/soc/codecs/cs35l45.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * cs35l45.h - CS35L45 ALSA SoC audio driver + * + * Copyright 2019-2022 Cirrus Logic, Inc. + * + * Author: James Schulman <james.schulman@cirrus.com> + * + */ + +#ifndef CS35L45_H +#define CS35L45_H + +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#define CS35L45_DEVID 0x00000000 +#define CS35L45_REVID 0x00000004 +#define CS35L45_RELID 0x0000000C +#define CS35L45_OTPID 0x00000010 +#define CS35L45_SFT_RESET 0x00000020 +#define CS35L45_GLOBAL_ENABLES 0x00002014 +#define CS35L45_BLOCK_ENABLES 0x00002018 +#define CS35L45_BLOCK_ENABLES2 0x0000201C +#define CS35L45_ERROR_RELEASE 0x00002034 +#define CS35L45_REFCLK_INPUT 0x00002C04 +#define CS35L45_GLOBAL_SAMPLE_RATE 0x00002C0C +#define CS35L45_BOOST_CCM_CFG 0x00003808 +#define CS35L45_BOOST_DCM_CFG 0x0000380C +#define CS35L45_BOOST_OV_CFG 0x0000382C +#define CS35L45_ASP_ENABLES1 0x00004800 +#define CS35L45_ASP_CONTROL1 0x00004804 +#define CS35L45_ASP_CONTROL2 0x00004808 +#define CS35L45_ASP_CONTROL3 0x0000480C +#define CS35L45_ASP_FRAME_CONTROL1 0x00004810 +#define CS35L45_ASP_FRAME_CONTROL2 0x00004814 +#define CS35L45_ASP_FRAME_CONTROL5 0x00004820 +#define CS35L45_ASP_DATA_CONTROL1 0x00004830 +#define CS35L45_ASP_DATA_CONTROL5 0x00004840 +#define CS35L45_DACPCM1_INPUT 0x00004C00 +#define CS35L45_ASPTX1_INPUT 0x00004C20 +#define CS35L45_ASPTX2_INPUT 0x00004C24 +#define CS35L45_ASPTX3_INPUT 0x00004C28 +#define CS35L45_ASPTX4_INPUT 0x00004C2C +#define CS35L45_ASPTX5_INPUT 0x00004C30 +#define CS35L45_LDPM_CONFIG 0x00006404 +#define CS35L45_AMP_PCM_CONTROL 0x00007000 +#define CS35L45_AMP_PCM_HPF_TST 0x00007004 +#define CS35L45_IRQ1_EINT_4 0x0000E01C +#define CS35L45_LASTREG 0x0000E01C + +/* SFT_RESET */ +#define CS35L45_SOFT_RESET_TRIGGER 0x5A000000 + +/* GLOBAL_ENABLES */ +#define CS35L45_GLOBAL_EN_SHIFT 0 +#define CS35L45_GLOBAL_EN_MASK BIT(0) + +/* BLOCK_ENABLES */ +#define CS35L45_IMON_EN_SHIFT 13 +#define CS35L45_VMON_EN_SHIFT 12 +#define CS35L45_VDD_BSTMON_EN_SHIFT 9 +#define CS35L45_VDD_BATTMON_EN_SHIFT 8 +#define CS35L45_BST_EN_SHIFT 4 +#define CS35L45_BST_EN_MASK GENMASK(5, 4) + +#define CS35L45_BST_DISABLE_FET_ON 0x01 + +/* BLOCK_ENABLES2 */ +#define CS35L45_ASP_EN_SHIFT 27 + +/* ERROR_RELEASE */ +#define CS35L45_GLOBAL_ERR_RLS_MASK BIT(11) + +/* REFCLK_INPUT */ +#define CS35L45_PLL_FORCE_EN_SHIFT 16 +#define CS35L45_PLL_FORCE_EN_MASK BIT(16) +#define CS35L45_PLL_OPEN_LOOP_SHIFT 11 +#define CS35L45_PLL_OPEN_LOOP_MASK BIT(11) +#define CS35L45_PLL_REFCLK_FREQ_SHIFT 5 +#define CS35L45_PLL_REFCLK_FREQ_MASK GENMASK(10, 5) +#define CS35L45_PLL_REFCLK_EN_SHIFT 4 +#define CS35L45_PLL_REFCLK_EN_MASK BIT(4) +#define CS35L45_PLL_REFCLK_SEL_SHIFT 0 +#define CS35L45_PLL_REFCLK_SEL_MASK GENMASK(2, 0) + +#define CS35L45_PLL_REFCLK_SEL_BCLK 0x0 + +/* GLOBAL_SAMPLE_RATE */ +#define CS35L45_GLOBAL_FS_SHIFT 0 +#define CS35L45_GLOBAL_FS_MASK GENMASK(4, 0) + +#define CS35L45_48P0_KHZ 0x03 +#define CS35L45_96P0_KHZ 0x04 +#define CS35L45_44P100_KHZ 0x0B +#define CS35L45_88P200_KHZ 0x0C + +/* ASP_ENABLES_1 */ +#define CS35L45_ASP_RX2_EN_SHIFT 17 +#define CS35L45_ASP_RX1_EN_SHIFT 16 +#define CS35L45_ASP_TX5_EN_SHIFT 4 +#define CS35L45_ASP_TX4_EN_SHIFT 3 +#define CS35L45_ASP_TX3_EN_SHIFT 2 +#define CS35L45_ASP_TX2_EN_SHIFT 1 +#define CS35L45_ASP_TX1_EN_SHIFT 0 + +/* ASP_CONTROL2 */ +#define CS35L45_ASP_WIDTH_RX_SHIFT 24 +#define CS35L45_ASP_WIDTH_RX_MASK GENMASK(31, 24) +#define CS35L45_ASP_WIDTH_TX_SHIFT 16 +#define CS35L45_ASP_WIDTH_TX_MASK GENMASK(23, 16) +#define CS35L45_ASP_FMT_SHIFT 8 +#define CS35L45_ASP_FMT_MASK GENMASK(10, 8) +#define CS35L45_ASP_BCLK_INV_SHIFT 6 +#define CS35L45_ASP_BCLK_INV_MASK BIT(6) +#define CS35L45_ASP_FSYNC_INV_SHIFT 2 +#define CS35L45_ASP_FSYNC_INV_MASK BIT(2) + +#define CS35l45_ASP_FMT_DSP_A 0 +#define CS35L45_ASP_FMT_I2S 2 + +/* ASP_CONTROL3 */ +#define CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT 0 +#define CS35L45_ASP_DOUT_HIZ_CTRL_MASK GENMASK(1, 0) + +/* ASP_FRAME_CONTROL1 */ +#define CS35L45_ASP_TX4_SLOT_SHIFT 24 +#define CS35L45_ASP_TX4_SLOT_MASK GENMASK(29, 24) +#define CS35L45_ASP_TX3_SLOT_SHIFT 16 +#define CS35L45_ASP_TX3_SLOT_MASK GENMASK(21, 16) +#define CS35L45_ASP_TX2_SLOT_SHIFT 8 +#define CS35L45_ASP_TX2_SLOT_MASK GENMASK(13, 8) +#define CS35L45_ASP_TX1_SLOT_SHIFT 0 +#define CS35L45_ASP_TX1_SLOT_MASK GENMASK(5, 0) + +#define CS35L45_ASP_TX_ALL_SLOTS (CS35L45_ASP_TX4_SLOT_MASK | \ + CS35L45_ASP_TX3_SLOT_MASK | \ + CS35L45_ASP_TX2_SLOT_MASK | \ + CS35L45_ASP_TX1_SLOT_MASK) +/* ASP_FRAME_CONTROL5 */ +#define CS35L45_ASP_RX2_SLOT_SHIFT 8 +#define CS35L45_ASP_RX2_SLOT_MASK GENMASK(13, 8) +#define CS35L45_ASP_RX1_SLOT_SHIFT 0 +#define CS35L45_ASP_RX1_SLOT_MASK GENMASK(5, 0) + +#define CS35L45_ASP_RX_ALL_SLOTS (CS35L45_ASP_RX2_SLOT_MASK | \ + CS35L45_ASP_RX1_SLOT_MASK) + +/* ASP_DATA_CONTROL1 */ +/* ASP_DATA_CONTROL5 */ +#define CS35L45_ASP_WL_SHIFT 0 +#define CS35L45_ASP_WL_MASK GENMASK(5, 0) + +/* AMP_PCM_CONTROL */ +#define CS35L45_AMP_VOL_PCM_SHIFT 0 +#define CS35L45_AMP_VOL_PCM_WIDTH 11 + +/* AMP_PCM_HPF_TST */ +#define CS35l45_HPF_DEFAULT 0x00000000 +#define CS35L45_HPF_44P1 0x000108BD +#define CS35L45_HPF_88P2 0x0001045F + +/* IRQ1_EINT_4 */ +#define CS35L45_OTP_BOOT_DONE_STS_MASK BIT(1) +#define CS35L45_OTP_BUSY_MASK BIT(0) + +/* Mixer sources */ +#define CS35L45_PCM_SRC_MASK 0x7F +#define CS35L45_PCM_SRC_ZERO 0x00 +#define CS35L45_PCM_SRC_ASP_RX1 0x08 +#define CS35L45_PCM_SRC_ASP_RX2 0x09 +#define CS35L45_PCM_SRC_VMON 0x18 +#define CS35L45_PCM_SRC_IMON 0x19 +#define CS35L45_PCM_SRC_ERR_VOL 0x20 +#define CS35L45_PCM_SRC_CLASSH_TGT 0x21 +#define CS35L45_PCM_SRC_VDD_BATTMON 0x28 +#define CS35L45_PCM_SRC_VDD_BSTMON 0x29 +#define CS35L45_PCM_SRC_TEMPMON 0x3A +#define CS35L45_PCM_SRC_INTERPOLATOR 0x40 +#define CS35L45_PCM_SRC_IL_TARGET 0x48 + +#define CS35L45_RESET_HOLD_US 2000 +#define CS35L45_RESET_US 2000 +#define CS35L45_POST_GLOBAL_EN_US 5000 +#define CS35L45_PRE_GLOBAL_DIS_US 3000 + +#define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE| \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define CS35L45_RATES (SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000) + +struct cs35l45_private { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + struct regulator *vdd_batt; + struct regulator *vdd_a; + bool initialized; + bool sysclk_set; + u8 slot_width; + u8 slot_count; +}; + +extern const struct dev_pm_ops cs35l45_pm_ops; +extern const struct regmap_config cs35l45_i2c_regmap; +extern const struct regmap_config cs35l45_spi_regmap; +int cs35l45_apply_patch(struct cs35l45_private *cs43l45); +unsigned int cs35l45_get_clk_freq_id(unsigned int freq); +int cs35l45_probe(struct cs35l45_private *cs35l45); +int cs35l45_remove(struct cs35l45_private *cs35l45); + +#endif /* CS35L45_H */ diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c index 20126cc675b1..881c5ba70c0e 100644 --- a/sound/soc/codecs/cs4234.c +++ b/sound/soc/codecs/cs4234.c @@ -663,6 +663,7 @@ static const struct snd_soc_component_driver soc_component_cs4234 = { .non_legacy_dai_naming = 1, .idle_bias_on = 1, .suspend_bias_off = 1, + .endianness = 1, }; static const struct regmap_config cs4234_regmap = { @@ -731,7 +732,7 @@ static int cs4234_powerup(struct cs4234 *cs4234) return 0; } -static int cs4234_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) +static int cs4234_i2c_probe(struct i2c_client *i2c_client) { struct cs4234 *cs4234; struct device *dev = &i2c_client->dev; @@ -908,7 +909,7 @@ static struct i2c_driver cs4234_i2c_driver = { .pm = &cs4234_pm, .of_match_table = cs4234_of_match, }, - .probe = cs4234_i2c_probe, + .probe_new = cs4234_i2c_probe, .remove = cs4234_i2c_remove, }; module_i2c_driver(cs4234_i2c_driver); diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 4415fb364d4d..86bfa8d5ec78 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -568,8 +568,7 @@ static const struct regmap_config cs4265_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int cs4265_i2c_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) +static int cs4265_i2c_probe(struct i2c_client *i2c_client) { struct cs4265_private *cs4265; int ret; @@ -653,7 +652,7 @@ static struct i2c_driver cs4265_i2c_driver = { .of_match_table = cs4265_of_match, }, .id_table = cs4265_id, - .probe = cs4265_i2c_probe, + .probe_new = cs4265_i2c_probe, .remove = cs4265_i2c_remove, }; diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 2d239e983a83..531f63b01554 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -32,18 +32,9 @@ #include <linux/gpio/consumer.h> #include <linux/of_device.h> -/* - * The codec isn't really big-endian or little-endian, since the I2S - * interface requires data to be sent serially with the MSbit first. - * However, to support BE and LE I2S devices, we specify both here. That - * way, ALSA will always match the bit patterns. - */ -#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ - SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) +#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE) /* CS4270 registers addresses */ #define CS4270_CHIPID 0x01 /* Chip ID */ @@ -677,8 +668,7 @@ static int cs4270_i2c_remove(struct i2c_client *i2c_client) * This function is called whenever the I2C subsystem finds a device that * matches the device ID given via a prior call to i2c_add_driver(). */ -static int cs4270_i2c_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) +static int cs4270_i2c_probe(struct i2c_client *i2c_client) { struct cs4270_private *cs4270; unsigned int val; @@ -765,7 +755,7 @@ static struct i2c_driver cs4270_i2c_driver = { .of_match_table = cs4270_of_match, }, .id_table = cs4270_id, - .probe = cs4270_i2c_probe, + .probe_new = cs4270_i2c_probe, .remove = cs4270_i2c_remove, }; diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c index 0a174236f573..0e8a7cf0da50 100644 --- a/sound/soc/codecs/cs4271-i2c.c +++ b/sound/soc/codecs/cs4271-i2c.c @@ -11,8 +11,7 @@ #include <sound/soc.h> #include "cs4271.h" -static int cs4271_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int cs4271_i2c_probe(struct i2c_client *client) { struct regmap_config config; @@ -35,7 +34,7 @@ static struct i2c_driver cs4271_i2c_driver = { .name = "cs4271", .of_match_table = of_match_ptr(cs4271_dt_ids), }, - .probe = cs4271_i2c_probe, + .probe_new = cs4271_i2c_probe, .id_table = cs4271_i2c_id, }; module_i2c_driver(cs4271_i2c_driver); diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index c8409d50e934..4fade2388797 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -2194,8 +2194,7 @@ static int __maybe_unused cs42l42_resume(struct device *dev) return 0; } -static int cs42l42_i2c_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) +static int cs42l42_i2c_probe(struct i2c_client *i2c_client) { struct cs42l42_private *cs42l42; int ret, i, devid; @@ -2399,7 +2398,7 @@ static struct i2c_driver cs42l42_i2c_driver = { .acpi_match_table = ACPI_PTR(cs42l42_acpi_match), }, .id_table = cs42l42_id, - .probe = cs42l42_i2c_probe, + .probe_new = cs42l42_i2c_probe, .remove = cs42l42_i2c_remove, }; diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index 3cb21a2ba29f..3613fb12d623 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -19,8 +19,7 @@ static struct i2c_device_id cs42l51_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id); -static int cs42l51_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int cs42l51_i2c_probe(struct i2c_client *i2c) { struct regmap_config config; @@ -46,7 +45,7 @@ static struct i2c_driver cs42l51_i2c_driver = { .of_match_table = cs42l51_of_match, .pm = &cs42l51_pm_ops, }, - .probe = cs42l51_i2c_probe, + .probe_new = cs42l51_i2c_probe, .remove = cs42l51_i2c_remove, .id_table = cs42l51_i2c_id, }; diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index e9c3cb4e2bfc..aff618513c75 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -51,11 +51,8 @@ struct cs42l51_private { struct regmap *regmap; }; -#define CS42L51_FORMATS ( \ - SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) +#define CS42L51_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 80161151b3f2..9b182b585be4 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1086,8 +1086,7 @@ static const struct regmap_config cs42l52_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int cs42l52_i2c_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) +static int cs42l52_i2c_probe(struct i2c_client *i2c_client) { struct cs42l52_private *cs42l52; struct cs42l52_platform_data *pdata = dev_get_platdata(&i2c_client->dev); @@ -1226,7 +1225,7 @@ static struct i2c_driver cs42l52_i2c_driver = { .of_match_table = cs42l52_of_match, }, .id_table = cs42l52_id, - .probe = cs42l52_i2c_probe, + .probe_new = cs42l52_i2c_probe, }; module_i2c_driver(cs42l52_i2c_driver); diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 3cf8a0b4478c..dc23007336c5 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -1167,8 +1167,7 @@ static int cs42l56_handle_of_data(struct i2c_client *i2c_client, return 0; } -static int cs42l56_i2c_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) +static int cs42l56_i2c_probe(struct i2c_client *i2c_client) { struct cs42l56_private *cs42l56; struct cs42l56_platform_data *pdata = @@ -1246,7 +1245,7 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client, ret = regmap_read(cs42l56->regmap, CS42L56_CHIP_ID_1, ®); if (ret) { dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret); - return ret; + goto err_enable; } devid = reg & CS42L56_CHIP_ID_MASK; @@ -1350,7 +1349,7 @@ static struct i2c_driver cs42l56_i2c_driver = { .of_match_table = cs42l56_of_match, }, .id_table = cs42l56_id, - .probe = cs42l56_i2c_probe, + .probe_new = cs42l56_i2c_probe, .remove = cs42l56_i2c_remove, }; diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 018463f34e12..5a9166289f36 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1274,8 +1274,7 @@ static const struct regmap_config cs42l73_regmap = { .use_single_write = true, }; -static int cs42l73_i2c_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) +static int cs42l73_i2c_probe(struct i2c_client *i2c_client) { struct cs42l73_private *cs42l73; struct cs42l73_platform_data *pdata = dev_get_platdata(&i2c_client->dev); @@ -1386,7 +1385,7 @@ static struct i2c_driver cs42l73_i2c_driver = { .of_match_table = cs42l73_of_match, }, .id_table = cs42l73_id, - .probe = cs42l73_i2c_probe, + .probe_new = cs42l73_i2c_probe, }; diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c index 0214e3ab9da0..cb06a06d48b0 100644 --- a/sound/soc/codecs/cs42xx8-i2c.c +++ b/sound/soc/codecs/cs42xx8-i2c.c @@ -17,8 +17,7 @@ #include "cs42xx8.h" -static int cs42xx8_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int cs42xx8_i2c_probe(struct i2c_client *i2c) { int ret = cs42xx8_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config)); @@ -51,7 +50,7 @@ static struct i2c_driver cs42xx8_i2c_driver = { .pm = &cs42xx8_pm, .of_match_table = cs42xx8_of_match, }, - .probe = cs42xx8_i2c_probe, + .probe_new = cs42xx8_i2c_probe, .remove = cs42xx8_i2c_remove, .id_table = cs42xx8_i2c_id, }; diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c index 44b20c1ef851..a2bce0f9f247 100644 --- a/sound/soc/codecs/cs43130.c +++ b/sound/soc/codecs/cs43130.c @@ -712,30 +712,30 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk, case CS43130_ASP_PCM_DAI: case CS43130_ASP_DOP_DAI: regmap_write(cs43130->regmap, CS43130_ASP_DEN_1, - (clk_gen->den & CS43130_SP_M_LSB_DATA_MASK) >> + (clk_gen->v.denominator & CS43130_SP_M_LSB_DATA_MASK) >> CS43130_SP_M_LSB_DATA_SHIFT); regmap_write(cs43130->regmap, CS43130_ASP_DEN_2, - (clk_gen->den & CS43130_SP_M_MSB_DATA_MASK) >> + (clk_gen->v.denominator & CS43130_SP_M_MSB_DATA_MASK) >> CS43130_SP_M_MSB_DATA_SHIFT); regmap_write(cs43130->regmap, CS43130_ASP_NUM_1, - (clk_gen->num & CS43130_SP_N_LSB_DATA_MASK) >> + (clk_gen->v.numerator & CS43130_SP_N_LSB_DATA_MASK) >> CS43130_SP_N_LSB_DATA_SHIFT); regmap_write(cs43130->regmap, CS43130_ASP_NUM_2, - (clk_gen->num & CS43130_SP_N_MSB_DATA_MASK) >> + (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >> CS43130_SP_N_MSB_DATA_SHIFT); break; case CS43130_XSP_DOP_DAI: regmap_write(cs43130->regmap, CS43130_XSP_DEN_1, - (clk_gen->den & CS43130_SP_M_LSB_DATA_MASK) >> + (clk_gen->v.denominator & CS43130_SP_M_LSB_DATA_MASK) >> CS43130_SP_M_LSB_DATA_SHIFT); regmap_write(cs43130->regmap, CS43130_XSP_DEN_2, - (clk_gen->den & CS43130_SP_M_MSB_DATA_MASK) >> + (clk_gen->v.denominator & CS43130_SP_M_MSB_DATA_MASK) >> CS43130_SP_M_MSB_DATA_SHIFT); regmap_write(cs43130->regmap, CS43130_XSP_NUM_1, - (clk_gen->num & CS43130_SP_N_LSB_DATA_MASK) >> + (clk_gen->v.numerator & CS43130_SP_N_LSB_DATA_MASK) >> CS43130_SP_N_LSB_DATA_SHIFT); regmap_write(cs43130->regmap, CS43130_XSP_NUM_2, - (clk_gen->num & CS43130_SP_N_MSB_DATA_MASK) >> + (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >> CS43130_SP_N_MSB_DATA_SHIFT); break; default: @@ -2303,7 +2303,7 @@ static int cs43130_probe(struct snd_soc_component *component) } ret = snd_soc_card_jack_new(card, "Headphone", CS43130_JACK_MASK, - &cs43130->jack, NULL, 0); + &cs43130->jack); if (ret < 0) { dev_err(component->dev, "Cannot create jack\n"); return ret; @@ -2418,8 +2418,7 @@ static int cs43130_handle_device_data(struct i2c_client *i2c_client, return 0; } -static int cs43130_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int cs43130_i2c_probe(struct i2c_client *client) { struct cs43130_private *cs43130; int ret; @@ -2702,7 +2701,7 @@ static struct i2c_driver cs43130_i2c_driver = { .pm = &cs43130_runtime_pm, }, .id_table = cs43130_i2c_id, - .probe = cs43130_i2c_probe, + .probe_new = cs43130_i2c_probe, .remove = cs43130_i2c_remove, }; diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h index e62d671e95bb..1dd893674313 100644 --- a/sound/soc/codecs/cs43130.h +++ b/sound/soc/codecs/cs43130.h @@ -10,6 +10,8 @@ #ifndef __CS43130_H__ #define __CS43130_H__ +#include <linux/math.h> + /* CS43130 registers addresses */ /* all reg address is shifted by a byte for control byte to be LSB */ #define CS43130_FIRSTREG 0x010000 @@ -372,97 +374,96 @@ enum cs43130_dai_id { }; struct cs43130_clk_gen { - unsigned int mclk_int; - int fs; - u16 den; - u16 num; + unsigned int mclk_int; + int fs; + struct u16_fract v; }; /* frm_size = 16 */ static const struct cs43130_clk_gen cs43130_16_clk_gen[] = { - {22579200, 32000, 441, 10,}, - {22579200, 44100, 32, 1,}, - {22579200, 48000, 147, 5,}, - {22579200, 88200, 16, 1,}, - {22579200, 96000, 147, 10,}, - {22579200, 176400, 8, 1,}, - {22579200, 192000, 147, 20,}, - {22579200, 352800, 4, 1,}, - {22579200, 384000, 147, 40,}, - {24576000, 32000, 48, 1,}, - {24576000, 44100, 5120, 147,}, - {24576000, 48000, 32, 1,}, - {24576000, 88200, 2560, 147,}, - {24576000, 96000, 16, 1,}, - {24576000, 176400, 1280, 147,}, - {24576000, 192000, 8, 1,}, - {24576000, 352800, 640, 147,}, - {24576000, 384000, 4, 1,}, + { 22579200, 32000, .v = { 441, 10, }, }, + { 22579200, 44100, .v = { 32, 1, }, }, + { 22579200, 48000, .v = { 147, 5, }, }, + { 22579200, 88200, .v = { 16, 1, }, }, + { 22579200, 96000, .v = { 147, 10, }, }, + { 22579200, 176400, .v = { 8, 1, }, }, + { 22579200, 192000, .v = { 147, 20, }, }, + { 22579200, 352800, .v = { 4, 1, }, }, + { 22579200, 384000, .v = { 147, 40, }, }, + { 24576000, 32000, .v = { 48, 1, }, }, + { 24576000, 44100, .v = { 5120, 147, }, }, + { 24576000, 48000, .v = { 32, 1, }, }, + { 24576000, 88200, .v = { 2560, 147, }, }, + { 24576000, 96000, .v = { 16, 1, }, }, + { 24576000, 176400, .v = { 1280, 147, }, }, + { 24576000, 192000, .v = { 8, 1, }, }, + { 24576000, 352800, .v = { 640, 147, }, }, + { 24576000, 384000, .v = { 4, 1, }, }, }; /* frm_size = 32 */ static const struct cs43130_clk_gen cs43130_32_clk_gen[] = { - {22579200, 32000, 441, 20,}, - {22579200, 44100, 16, 1,}, - {22579200, 48000, 147, 10,}, - {22579200, 88200, 8, 1,}, - {22579200, 96000, 147, 20,}, - {22579200, 176400, 4, 1,}, - {22579200, 192000, 147, 40,}, - {22579200, 352800, 2, 1,}, - {22579200, 384000, 147, 80,}, - {24576000, 32000, 24, 1,}, - {24576000, 44100, 2560, 147,}, - {24576000, 48000, 16, 1,}, - {24576000, 88200, 1280, 147,}, - {24576000, 96000, 8, 1,}, - {24576000, 176400, 640, 147,}, - {24576000, 192000, 4, 1,}, - {24576000, 352800, 320, 147,}, - {24576000, 384000, 2, 1,}, + { 22579200, 32000, .v = { 441, 20, }, }, + { 22579200, 44100, .v = { 16, 1, }, }, + { 22579200, 48000, .v = { 147, 10, }, }, + { 22579200, 88200, .v = { 8, 1, }, }, + { 22579200, 96000, .v = { 147, 20, }, }, + { 22579200, 176400, .v = { 4, 1, }, }, + { 22579200, 192000, .v = { 147, 40, }, }, + { 22579200, 352800, .v = { 2, 1, }, }, + { 22579200, 384000, .v = { 147, 80, }, }, + { 24576000, 32000, .v = { 24, 1, }, }, + { 24576000, 44100, .v = { 2560, 147, }, }, + { 24576000, 48000, .v = { 16, 1, }, }, + { 24576000, 88200, .v = { 1280, 147, }, }, + { 24576000, 96000, .v = { 8, 1, }, }, + { 24576000, 176400, .v = { 640, 147, }, }, + { 24576000, 192000, .v = { 4, 1, }, }, + { 24576000, 352800, .v = { 320, 147, }, }, + { 24576000, 384000, .v = { 2, 1, }, }, }; /* frm_size = 48 */ static const struct cs43130_clk_gen cs43130_48_clk_gen[] = { - {22579200, 32000, 147, 100,}, - {22579200, 44100, 32, 3,}, - {22579200, 48000, 49, 5,}, - {22579200, 88200, 16, 3,}, - {22579200, 96000, 49, 10,}, - {22579200, 176400, 8, 3,}, - {22579200, 192000, 49, 20,}, - {22579200, 352800, 4, 3,}, - {22579200, 384000, 49, 40,}, - {24576000, 32000, 16, 1,}, - {24576000, 44100, 5120, 441,}, - {24576000, 48000, 32, 3,}, - {24576000, 88200, 2560, 441,}, - {24576000, 96000, 16, 3,}, - {24576000, 176400, 1280, 441,}, - {24576000, 192000, 8, 3,}, - {24576000, 352800, 640, 441,}, - {24576000, 384000, 4, 3,}, + { 22579200, 32000, .v = { 147, 100, }, }, + { 22579200, 44100, .v = { 32, 3, }, }, + { 22579200, 48000, .v = { 49, 5, }, }, + { 22579200, 88200, .v = { 16, 3, }, }, + { 22579200, 96000, .v = { 49, 10, }, }, + { 22579200, 176400, .v = { 8, 3, }, }, + { 22579200, 192000, .v = { 49, 20, }, }, + { 22579200, 352800, .v = { 4, 3, }, }, + { 22579200, 384000, .v = { 49, 40, }, }, + { 24576000, 32000, .v = { 16, 1, }, }, + { 24576000, 44100, .v = { 5120, 441, }, }, + { 24576000, 48000, .v = { 32, 3, }, }, + { 24576000, 88200, .v = { 2560, 441, }, }, + { 24576000, 96000, .v = { 16, 3, }, }, + { 24576000, 176400, .v = { 1280, 441, }, }, + { 24576000, 192000, .v = { 8, 3, }, }, + { 24576000, 352800, .v = { 640, 441, }, }, + { 24576000, 384000, .v = { 4, 3, }, }, }; /* frm_size = 64 */ static const struct cs43130_clk_gen cs43130_64_clk_gen[] = { - {22579200, 32000, 441, 40,}, - {22579200, 44100, 8, 1,}, - {22579200, 48000, 147, 20,}, - {22579200, 88200, 4, 1,}, - {22579200, 96000, 147, 40,}, - {22579200, 176400, 2, 1,}, - {22579200, 192000, 147, 80,}, - {22579200, 352800, 1, 1,}, - {24576000, 32000, 12, 1,}, - {24576000, 44100, 1280, 147,}, - {24576000, 48000, 8, 1,}, - {24576000, 88200, 640, 147,}, - {24576000, 96000, 4, 1,}, - {24576000, 176400, 320, 147,}, - {24576000, 192000, 2, 1,}, - {24576000, 352800, 160, 147,}, - {24576000, 384000, 1, 1,}, + { 22579200, 32000, .v = { 441, 40, }, }, + { 22579200, 44100, .v = { 8, 1, }, }, + { 22579200, 48000, .v = { 147, 20, }, }, + { 22579200, 88200, .v = { 4, 1, }, }, + { 22579200, 96000, .v = { 147, 40, }, }, + { 22579200, 176400, .v = { 2, 1, }, }, + { 22579200, 192000, .v = { 147, 80, }, }, + { 22579200, 352800, .v = { 1, 1, }, }, + { 24576000, 32000, .v = { 12, 1, }, }, + { 24576000, 44100, .v = { 1280, 147, }, }, + { 24576000, 48000, .v = { 8, 1, }, }, + { 24576000, 88200, .v = { 640, 147, }, }, + { 24576000, 96000, .v = { 4, 1, }, }, + { 24576000, 176400, .v = { 320, 147, }, }, + { 24576000, 192000, .v = { 2, 1, }, }, + { 24576000, 352800, .v = { 160, 147, }, }, + { 24576000, 384000, .v = { 1, 1, }, }, }; struct cs43130_bitwidth_map { diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c index 29d05e32d341..8ac043f1aae0 100644 --- a/sound/soc/codecs/cs4341.c +++ b/sound/soc/codecs/cs4341.c @@ -225,8 +225,7 @@ static int cs4341_probe(struct device *dev) } #if IS_ENABLED(CONFIG_I2C) -static int cs4341_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int cs4341_i2c_probe(struct i2c_client *i2c) { struct cs4341_priv *cs4341; @@ -260,7 +259,7 @@ static struct i2c_driver cs4341_i2c_driver = { .name = "cs4341-i2c", .of_match_table = of_match_ptr(cs4341_dt_ids), }, - .probe = cs4341_i2c_probe, + .probe_new = cs4341_i2c_probe, .id_table = cs4341_i2c_id, }; #endif diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c index 786c69a8ec4a..7069e9b54857 100644 --- a/sound/soc/codecs/cs4349.c +++ b/sound/soc/codecs/cs4349.c @@ -223,12 +223,9 @@ static const struct snd_soc_dapm_route cs4349_routes[] = { {"OutputB", NULL, "HiFi DAC"}, }; -#define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ - SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \ +#define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE) #define CS4349_PCM_RATES SNDRV_PCM_RATE_8000_192000 @@ -278,8 +275,7 @@ static const struct regmap_config cs4349_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int cs4349_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int cs4349_i2c_probe(struct i2c_client *client) { struct cs4349_private *cs4349; int ret; @@ -382,7 +378,7 @@ static struct i2c_driver cs4349_i2c_driver = { .pm = &cs4349_runtime_pm, }, .id_table = cs4349_i2c_id, - .probe = cs4349_i2c_probe, + .probe_new = cs4349_i2c_probe, .remove = cs4349_i2c_remove, }; diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c index f2087bd38dbc..703545273900 100644 --- a/sound/soc/codecs/cs53l30.c +++ b/sound/soc/codecs/cs53l30.c @@ -918,8 +918,7 @@ static struct regmap_config cs53l30_regmap = { .use_single_write = true, }; -static int cs53l30_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int cs53l30_i2c_probe(struct i2c_client *client) { const struct device_node *np = client->dev.of_node; struct device *dev = &client->dev; @@ -1125,7 +1124,7 @@ static struct i2c_driver cs53l30_i2c_driver = { .pm = &cs53l30_runtime_pm, }, .id_table = cs53l30_id, - .probe = cs53l30_i2c_probe, + .probe_new = cs53l30_i2c_probe, .remove = cs53l30_i2c_remove, }; diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 1f5c57fab1d8..b35debb5818d 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -1527,6 +1527,7 @@ static const struct snd_soc_component_driver soc_codec_driver_cx2072x = { .num_dapm_widgets = ARRAY_SIZE(cx2072x_dapm_widgets), .dapm_routes = cx2072x_intercon, .num_dapm_routes = ARRAY_SIZE(cx2072x_intercon), + .endianness = 1, }; /* @@ -1626,8 +1627,7 @@ static int __maybe_unused cx2072x_runtime_resume(struct device *dev) return clk_prepare_enable(cx2072x->mclk); } -static int cx2072x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int cx2072x_i2c_probe(struct i2c_client *i2c) { struct cx2072x_priv *cx2072x; unsigned int ven_id, rev_id; @@ -1710,7 +1710,7 @@ static struct i2c_driver cx2072x_i2c_driver = { .acpi_match_table = ACPI_PTR(cx2072x_acpi_match), .pm = &cx2072x_runtime_pm, }, - .probe = cx2072x_i2c_probe, + .probe_new = cx2072x_i2c_probe, .remove = cx2072x_i2c_remove, .id_table = cx2072x_i2c_id, }; diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index 8af344b2fdbf..3fa3042e4424 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -1206,8 +1206,7 @@ static const struct regmap_config da7210_regmap_config_i2c = { .cache_type = REGCACHE_RBTREE, }; -static int da7210_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int da7210_i2c_probe(struct i2c_client *i2c) { struct da7210_priv *da7210; int ret; @@ -1250,7 +1249,7 @@ static struct i2c_driver da7210_i2c_driver = { .driver = { .name = "da7210", }, - .probe = da7210_i2c_probe, + .probe_new = da7210_i2c_probe, .id_table = da7210_i2c_id, }; #endif diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 3ab89387b4e6..2e645dc60eda 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1946,8 +1946,7 @@ static const char *da7213_supply_names[DA7213_NUM_SUPPLIES] = { [DA7213_SUPPLY_VDDIO] = "VDDIO", }; -static int da7213_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int da7213_i2c_probe(struct i2c_client *i2c) { struct da7213_priv *da7213; int i, ret; @@ -2040,7 +2039,7 @@ static struct i2c_driver da7213_i2c_driver = { .acpi_match_table = ACPI_PTR(da7213_acpi_match), .pm = &da7213_pm, }, - .probe = da7213_i2c_probe, + .probe_new = da7213_i2c_probe, .id_table = da7213_i2c_id, }; diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index ea426d986d4c..a5d7c350a3de 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -3258,8 +3258,19 @@ static const struct regmap_config da7218_regmap_config = { * I2C layer */ -static int da7218_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static const struct i2c_device_id da7218_i2c_id[]; + +static inline int da7218_i2c_get_id(struct i2c_client *i2c) +{ + const struct i2c_device_id *id = i2c_match_id(da7218_i2c_id, i2c); + + if (id) + return (uintptr_t)id->driver_data; + else + return -EINVAL; +} + +static int da7218_i2c_probe(struct i2c_client *i2c) { struct da7218_priv *da7218; int ret; @@ -3273,7 +3284,7 @@ static int da7218_i2c_probe(struct i2c_client *i2c, if (i2c->dev.of_node) da7218->dev_id = da7218_of_get_id(&i2c->dev); else - da7218->dev_id = id->driver_data; + da7218->dev_id = da7218_i2c_get_id(i2c); if ((da7218->dev_id != DA7217_DEV_ID) && (da7218->dev_id != DA7218_DEV_ID)) { @@ -3311,7 +3322,7 @@ static struct i2c_driver da7218_i2c_driver = { .name = "da7218", .of_match_table = da7218_of_match, }, - .probe = da7218_i2c_probe, + .probe_new = da7218_i2c_probe, .id_table = da7218_i2c_id, }; diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index c7493549a9a5..7fdef38ed8cd 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -2655,8 +2655,7 @@ static const struct snd_soc_component_driver soc_component_dev_da7219 = { * I2C layer */ -static int da7219_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int da7219_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct da7219_priv *da7219; @@ -2711,7 +2710,7 @@ static struct i2c_driver da7219_i2c_driver = { .of_match_table = of_match_ptr(da7219_of_match), .acpi_match_table = ACPI_PTR(da7219_acpi_match), }, - .probe = da7219_i2c_probe, + .probe_new = da7219_i2c_probe, .remove = da7219_i2c_remove, .id_table = da7219_i2c_id, }; diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 42d6a3fc3af5..f14cddf23f42 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -1506,8 +1506,7 @@ static const struct snd_soc_component_driver soc_component_dev_da732x = { .non_legacy_dai_naming = 1, }; -static int da732x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int da732x_i2c_probe(struct i2c_client *i2c) { struct da732x_priv *da732x; unsigned int reg; @@ -1562,7 +1561,7 @@ static struct i2c_driver da732x_i2c_driver = { .driver = { .name = "da7320", }, - .probe = da732x_i2c_probe, + .probe_new = da732x_i2c_probe, .remove = da732x_i2c_remove, .id_table = da732x_i2c_id, }; diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index a9676b261129..9d8c8adc5d76 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1473,8 +1473,7 @@ static const struct regmap_config da9055_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -static int da9055_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int da9055_i2c_probe(struct i2c_client *i2c) { struct da9055_priv *da9055; struct da9055_platform_data *pdata = dev_get_platdata(&i2c->dev); @@ -1533,7 +1532,7 @@ static struct i2c_driver da9055_i2c_driver = { .name = "da9055-codec", .of_match_table = of_match_ptr(da9055_of_match), }, - .probe = da9055_i2c_probe, + .probe_new = da9055_i2c_probe, .id_table = da9055_i2c_id, }; diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c index 5d079d90fd3b..d1a30ca4571a 100644 --- a/sound/soc/codecs/dmic.c +++ b/sound/soc/codecs/dmic.c @@ -82,7 +82,10 @@ static struct snd_soc_dai_driver dmic_dai = { .rates = SNDRV_PCM_RATE_CONTINUOUS, .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE - | SNDRV_PCM_FMTBIT_S16_LE, + | SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_DSD_U8 + | SNDRV_PCM_FMTBIT_DSD_U16_LE + | SNDRV_PCM_FMTBIT_DSD_U32_LE, }, .ops = &dmic_dai_ops, }; diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index ff33eab6f9de..4407166bb338 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -789,8 +789,7 @@ static const struct regmap_config es8316_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int es8316_i2c_probe(struct i2c_client *i2c_client, - const struct i2c_device_id *id) +static int es8316_i2c_probe(struct i2c_client *i2c_client) { struct device *dev = &i2c_client->dev; struct es8316_priv *es8316; @@ -852,7 +851,7 @@ static struct i2c_driver es8316_i2c_driver = { .acpi_match_table = ACPI_PTR(es8316_acpi_match), .of_match_table = of_match_ptr(es8316_of_match), }, - .probe = es8316_i2c_probe, + .probe_new = es8316_i2c_probe, .id_table = es8316_i2c_id, }; module_i2c_driver(es8316_i2c_driver); diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c index 6b0df0d750dc..68072e99fcc7 100644 --- a/sound/soc/codecs/es8328-i2c.c +++ b/sound/soc/codecs/es8328-i2c.c @@ -29,8 +29,7 @@ static const struct of_device_id es8328_of_match[] = { }; MODULE_DEVICE_TABLE(of, es8328_of_match); -static int es8328_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int es8328_i2c_probe(struct i2c_client *i2c) { return es8328_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &es8328_regmap_config)); @@ -41,7 +40,7 @@ static struct i2c_driver es8328_i2c_driver = { .name = "es8328", .of_match_table = es8328_of_match, }, - .probe = es8328_i2c_probe, + .probe_new = es8328_i2c_probe, .id_table = es8328_id, }; diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index a9f61c7e44ee..8debcee59224 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -571,13 +571,14 @@ static const struct snd_soc_dapm_widget hdac_hda_dapm_widgets[] = { }; static const struct snd_soc_component_driver hdac_hda_codec = { - .probe = hdac_hda_codec_probe, - .remove = hdac_hda_codec_remove, - .idle_bias_on = false, - .dapm_widgets = hdac_hda_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(hdac_hda_dapm_widgets), - .dapm_routes = hdac_hda_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(hdac_hda_dapm_routes), + .probe = hdac_hda_codec_probe, + .remove = hdac_hda_codec_remove, + .dapm_widgets = hdac_hda_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(hdac_hda_dapm_widgets), + .dapm_routes = hdac_hda_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(hdac_hda_dapm_routes), + .idle_bias_on = false, + .endianness = 1, }; static int hdac_hda_dev_probe(struct hdac_device *hdev) diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index b07607a9ecea..b773466619b2 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -727,10 +727,8 @@ static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = { SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ SNDRV_PCM_RATE_192000) -#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\ - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) +#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE) /* * This list is only for formats allowed on the I2S bus. So there is @@ -740,12 +738,9 @@ static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = { * problems, we should add the video side driver an option to disable * them. */ -#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\ - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\ - SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) +#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) static struct snd_kcontrol_new hdmi_codec_controls[] = { { diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c index 1d86b6a0eb9d..39be31e1282e 100644 --- a/sound/soc/codecs/isabelle.c +++ b/sound/soc/codecs/isabelle.c @@ -1108,8 +1108,7 @@ static const struct regmap_config isabelle_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -static int isabelle_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int isabelle_i2c_probe(struct i2c_client *i2c) { struct regmap *isabelle_regmap; int ret = 0; @@ -1144,7 +1143,7 @@ static struct i2c_driver isabelle_i2c_driver = { .driver = { .name = "isabelle", }, - .probe = isabelle_i2c_probe, + .probe_new = isabelle_i2c_probe, .id_table = isabelle_i2c_id, }; diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c index 300b325e2fdd..dba161305de8 100644 --- a/sound/soc/codecs/lm4857.c +++ b/sound/soc/codecs/lm4857.c @@ -115,8 +115,7 @@ static const struct regmap_config lm4857_regmap_config = { .num_reg_defaults = ARRAY_SIZE(lm4857_default_regs), }; -static int lm4857_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int lm4857_i2c_probe(struct i2c_client *i2c) { struct regmap *regmap; @@ -138,7 +137,7 @@ static struct i2c_driver lm4857_i2c_driver = { .driver = { .name = "lm4857", }, - .probe = lm4857_i2c_probe, + .probe_new = lm4857_i2c_probe, .id_table = lm4857_i2c_id, }; diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index 973d781b4b6a..bd0078e4499b 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -1412,8 +1412,7 @@ static const struct regmap_config lm49453_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -static int lm49453_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int lm49453_i2c_probe(struct i2c_client *i2c) { struct lm49453_priv *lm49453; int ret = 0; @@ -1458,7 +1457,7 @@ static struct i2c_driver lm49453_i2c_driver = { .driver = { .name = "lm49453", }, - .probe = lm49453_i2c_probe, + .probe_new = lm49453_i2c_probe, .remove = lm49453_i2c_remove, .id_table = lm49453_i2c_id, }; diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c index 54426a90bc0b..54a8ba7ed3c2 100644 --- a/sound/soc/codecs/lochnagar-sc.c +++ b/sound/soc/codecs/lochnagar-sc.c @@ -212,12 +212,13 @@ static struct snd_soc_dai_driver lochnagar_sc_dai[] = { }; static const struct snd_soc_component_driver lochnagar_sc_driver = { - .non_legacy_dai_naming = 1, - .dapm_widgets = lochnagar_sc_widgets, .num_dapm_widgets = ARRAY_SIZE(lochnagar_sc_widgets), .dapm_routes = lochnagar_sc_routes, .num_dapm_routes = ARRAY_SIZE(lochnagar_sc_routes), + + .non_legacy_dai_naming = 1, + .endianness = 1, }; static int lochnagar_sc_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c index 6cede75ed3b5..1b9082d237c1 100644 --- a/sound/soc/codecs/lpass-macro-common.c +++ b/sound/soc/codecs/lpass-macro-common.c @@ -24,42 +24,45 @@ struct lpass_macro *lpass_macro_pds_init(struct device *dev) return ERR_PTR(-ENOMEM); l_pds->macro_pd = dev_pm_domain_attach_by_name(dev, "macro"); - if (IS_ERR_OR_NULL(l_pds->macro_pd)) - return NULL; - - ret = pm_runtime_get_sync(l_pds->macro_pd); - if (ret < 0) { - pm_runtime_put_noidle(l_pds->macro_pd); + if (IS_ERR_OR_NULL(l_pds->macro_pd)) { + ret = l_pds->macro_pd ? PTR_ERR(l_pds->macro_pd) : -ENODATA; goto macro_err; } + ret = pm_runtime_resume_and_get(l_pds->macro_pd); + if (ret < 0) + goto macro_sync_err; + l_pds->dcodec_pd = dev_pm_domain_attach_by_name(dev, "dcodec"); - if (IS_ERR_OR_NULL(l_pds->dcodec_pd)) + if (IS_ERR_OR_NULL(l_pds->dcodec_pd)) { + ret = l_pds->dcodec_pd ? PTR_ERR(l_pds->dcodec_pd) : -ENODATA; goto dcodec_err; + } - ret = pm_runtime_get_sync(l_pds->dcodec_pd); - if (ret < 0) { - pm_runtime_put_noidle(l_pds->dcodec_pd); + ret = pm_runtime_resume_and_get(l_pds->dcodec_pd); + if (ret < 0) goto dcodec_sync_err; - } return l_pds; dcodec_sync_err: dev_pm_domain_detach(l_pds->dcodec_pd, false); dcodec_err: pm_runtime_put(l_pds->macro_pd); -macro_err: +macro_sync_err: dev_pm_domain_detach(l_pds->macro_pd, false); +macro_err: return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(lpass_macro_pds_init); void lpass_macro_pds_exit(struct lpass_macro *pds) { - pm_runtime_put(pds->macro_pd); - dev_pm_domain_detach(pds->macro_pd, false); - pm_runtime_put(pds->dcodec_pd); - dev_pm_domain_detach(pds->dcodec_pd, false); + if (pds) { + pm_runtime_put(pds->macro_pd); + dev_pm_domain_detach(pds->macro_pd, false); + pm_runtime_put(pds->dcodec_pd); + dev_pm_domain_detach(pds->dcodec_pd, false); + } } EXPORT_SYMBOL_GPL(lpass_macro_pds_exit); diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c index 39dda1b03b3d..d711eb1da0a8 100644 --- a/sound/soc/codecs/max9768.c +++ b/sound/soc/codecs/max9768.c @@ -167,8 +167,7 @@ static const struct regmap_config max9768_i2c_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -static int max9768_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max9768_i2c_probe(struct i2c_client *client) { struct max9768 *max9768; struct max9768_pdata *pdata = client->dev.platform_data; @@ -215,7 +214,7 @@ static struct i2c_driver max9768_i2c_driver = { .driver = { .name = "max9768", }, - .probe = max9768_i2c_probe, + .probe_new = max9768_i2c_probe, .id_table = max9768_i2c_id, }; module_i2c_driver(max9768_i2c_driver); diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 429717d4ac5a..5ef2e1279ee7 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1737,11 +1737,18 @@ static const struct snd_soc_component_driver soc_component_dev_max98088 = { .non_legacy_dai_naming = 1, }; -static int max98088_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static const struct i2c_device_id max98088_i2c_id[] = { + { "max98088", MAX98088 }, + { "max98089", MAX98089 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98088_i2c_id); + +static int max98088_i2c_probe(struct i2c_client *i2c) { struct max98088_priv *max98088; int ret; + const struct i2c_device_id *id; max98088 = devm_kzalloc(&i2c->dev, sizeof(struct max98088_priv), GFP_KERNEL); @@ -1757,6 +1764,7 @@ static int max98088_i2c_probe(struct i2c_client *i2c, if (PTR_ERR(max98088->mclk) == -EPROBE_DEFER) return PTR_ERR(max98088->mclk); + id = i2c_match_id(max98088_i2c_id, i2c); max98088->devtype = id->driver_data; i2c_set_clientdata(i2c, max98088); @@ -1767,13 +1775,6 @@ static int max98088_i2c_probe(struct i2c_client *i2c, return ret; } -static const struct i2c_device_id max98088_i2c_id[] = { - { "max98088", MAX98088 }, - { "max98089", MAX98089 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max98088_i2c_id); - #if defined(CONFIG_OF) static const struct of_device_id max98088_of_match[] = { { .compatible = "maxim,max98088" }, @@ -1788,7 +1789,7 @@ static struct i2c_driver max98088_i2c_driver = { .name = "max98088", .of_match_table = of_match_ptr(max98088_of_match), }, - .probe = max98088_i2c_probe, + .probe_new = max98088_i2c_probe, .id_table = max98088_i2c_id, }; diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 62b41ca050a2..576277a82d41 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -393,9 +393,11 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol, struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int mask = (1 << fls(mc->max)) - 1; - unsigned int sel = ucontrol->value.integer.value[0]; + int sel_unchecked = ucontrol->value.integer.value[0]; + unsigned int sel; unsigned int val = snd_soc_component_read(component, mc->reg); unsigned int *select; + int change; switch (mc->reg) { case M98090_REG_MIC1_INPUT_LEVEL: @@ -413,9 +415,11 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol, val = (val >> mc->shift) & mask; - if (sel < 0 || sel > mc->max) + if (sel_unchecked < 0 || sel_unchecked > mc->max) return -EINVAL; + sel = sel_unchecked; + change = *select != sel; *select = sel; /* Setting a volume is only valid if it is already On */ @@ -430,7 +434,7 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol, mask << mc->shift, sel << mc->shift); - return *select != val; + return change; } static const char *max98090_perf_pwr_text[] = @@ -2532,8 +2536,14 @@ static const struct regmap_config max98090_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int max98090_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *i2c_id) +static const struct i2c_device_id max98090_i2c_id[] = { + { "max98090", MAX98090 }, + { "max98091", MAX98091 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98090_i2c_id); + +static int max98090_i2c_probe(struct i2c_client *i2c) { struct max98090_priv *max98090; const struct acpi_device_id *acpi_id; @@ -2555,7 +2565,9 @@ static int max98090_i2c_probe(struct i2c_client *i2c, return -EINVAL; } driver_data = acpi_id->driver_data; - } else if (i2c_id) { + } else { + const struct i2c_device_id *i2c_id = + i2c_match_id(max98090_i2c_id, i2c); driver_data = i2c_id->driver_data; } @@ -2662,13 +2674,6 @@ static const struct dev_pm_ops max98090_pm = { SET_SYSTEM_SLEEP_PM_OPS(NULL, max98090_resume) }; -static const struct i2c_device_id max98090_i2c_id[] = { - { "max98090", MAX98090 }, - { "max98091", MAX98091 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max98090_i2c_id); - #ifdef CONFIG_OF static const struct of_device_id max98090_of_match[] = { { .compatible = "maxim,max98090", }, @@ -2693,7 +2698,7 @@ static struct i2c_driver max98090_i2c_driver = { .of_match_table = of_match_ptr(max98090_of_match), .acpi_match_table = ACPI_PTR(max98090_acpi_match), }, - .probe = max98090_i2c_probe, + .probe_new = max98090_i2c_probe, .shutdown = max98090_i2c_shutdown, .remove = max98090_i2c_remove, .id_table = max98090_i2c_id, diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 4977b00ddf5f..7bca99fa61b5 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -2106,11 +2106,17 @@ static const struct snd_soc_component_driver soc_component_dev_max98095 = { .non_legacy_dai_naming = 1, }; -static int max98095_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static const struct i2c_device_id max98095_i2c_id[] = { + { "max98095", MAX98095 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98095_i2c_id); + +static int max98095_i2c_probe(struct i2c_client *i2c) { struct max98095_priv *max98095; int ret; + const struct i2c_device_id *id; max98095 = devm_kzalloc(&i2c->dev, sizeof(struct max98095_priv), GFP_KERNEL); @@ -2126,6 +2132,7 @@ static int max98095_i2c_probe(struct i2c_client *i2c, return ret; } + id = i2c_match_id(max98095_i2c_id, i2c); max98095->devtype = id->driver_data; i2c_set_clientdata(i2c, max98095); max98095->pdata = i2c->dev.platform_data; @@ -2136,12 +2143,6 @@ static int max98095_i2c_probe(struct i2c_client *i2c, return ret; } -static const struct i2c_device_id max98095_i2c_id[] = { - { "max98095", MAX98095 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max98095_i2c_id); - #ifdef CONFIG_OF static const struct of_device_id max98095_of_match[] = { { .compatible = "maxim,max98095", }, @@ -2155,7 +2156,7 @@ static struct i2c_driver max98095_i2c_driver = { .name = "max98095", .of_match_table = of_match_ptr(max98095_of_match), }, - .probe = max98095_i2c_probe, + .probe_new = max98095_i2c_probe, .id_table = max98095_i2c_id, }; diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c index 8d42f523e420..800f2bca6a0f 100644 --- a/sound/soc/codecs/max98371.c +++ b/sound/soc/codecs/max98371.c @@ -365,8 +365,7 @@ static const struct regmap_config max98371_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int max98371_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max98371_i2c_probe(struct i2c_client *i2c) { struct max98371_priv *max98371; int ret, reg; @@ -421,7 +420,7 @@ static struct i2c_driver max98371_i2c_driver = { .name = "max98371", .of_match_table = of_match_ptr(max98371_of_match), }, - .probe = max98371_i2c_probe, + .probe_new = max98371_i2c_probe, .id_table = max98371_i2c_id, }; diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c index ddb6436835d7..4fe065ece17c 100644 --- a/sound/soc/codecs/max98373-i2c.c +++ b/sound/soc/codecs/max98373-i2c.c @@ -516,8 +516,7 @@ static const struct regmap_config max98373_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int max98373_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max98373_i2c_probe(struct i2c_client *i2c) { int ret = 0; int reg = 0; @@ -622,7 +621,7 @@ static struct i2c_driver max98373_i2c_driver = { .acpi_match_table = ACPI_PTR(max98373_acpi_match), .pm = &max98373_pm, }, - .probe = max98373_i2c_probe, + .probe_new = max98373_i2c_probe, .id_table = max98373_i2c_id, }; diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 40fd6f363f35..2a6b1648c884 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -1014,14 +1014,14 @@ static void max98390_slot_config(struct i2c_client *i2c, max98390->i_l_slot = 1; } -static int max98390_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max98390_i2c_probe(struct i2c_client *i2c) { int ret = 0; int reg = 0; struct max98390_priv *max98390 = NULL; struct i2c_adapter *adapter = i2c->adapter; + struct gpio_desc *reset_gpio; ret = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE @@ -1073,6 +1073,17 @@ static int max98390_i2c_probe(struct i2c_client *i2c, return ret; } + reset_gpio = devm_gpiod_get_optional(&i2c->dev, + "reset", GPIOD_OUT_HIGH); + + /* Power on device */ + if (reset_gpio) { + usleep_range(1000, 2000); + /* bring out of reset */ + gpiod_set_value_cansleep(reset_gpio, 0); + usleep_range(1000, 2000); + } + /* Check Revision ID */ ret = regmap_read(max98390->regmap, MAX98390_R24FF_REV_ID, ®); @@ -1121,7 +1132,7 @@ static struct i2c_driver max98390_i2c_driver = { .acpi_match_table = ACPI_PTR(max98390_acpi_match), .pm = &max98390_pm, }, - .probe = max98390_i2c_probe, + .probe_new = max98390_i2c_probe, .id_table = max98390_i2c_id, }; diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c new file mode 100644 index 000000000000..56eb62bb041f --- /dev/null +++ b/sound/soc/codecs/max98396.c @@ -0,0 +1,1637 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022, Analog Devices Inc. + +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <linux/gpio.h> +#include <sound/tlv.h> +#include "max98396.h" + +static struct reg_default max98396_reg[] = { + {MAX98396_R2000_SW_RESET, 0x00}, + {MAX98396_R2001_INT_RAW1, 0x00}, + {MAX98396_R2002_INT_RAW2, 0x00}, + {MAX98396_R2003_INT_RAW3, 0x00}, + {MAX98396_R2004_INT_RAW4, 0x00}, + {MAX98396_R2006_INT_STATE1, 0x00}, + {MAX98396_R2007_INT_STATE2, 0x00}, + {MAX98396_R2008_INT_STATE3, 0x00}, + {MAX98396_R2009_INT_STATE4, 0x00}, + {MAX98396_R200B_INT_FLAG1, 0x00}, + {MAX98396_R200C_INT_FLAG2, 0x00}, + {MAX98396_R200D_INT_FLAG3, 0x00}, + {MAX98396_R200E_INT_FLAG4, 0x00}, + {MAX98396_R2010_INT_EN1, 0x02}, + {MAX98396_R2011_INT_EN2, 0x00}, + {MAX98396_R2012_INT_EN3, 0x00}, + {MAX98396_R2013_INT_EN4, 0x00}, + {MAX98396_R2015_INT_FLAG_CLR1, 0x00}, + {MAX98396_R2016_INT_FLAG_CLR2, 0x00}, + {MAX98396_R2017_INT_FLAG_CLR3, 0x00}, + {MAX98396_R2018_INT_FLAG_CLR4, 0x00}, + {MAX98396_R201F_IRQ_CTRL, 0x00}, + {MAX98396_R2020_THERM_WARN_THRESH, 0x46}, + {MAX98396_R2021_THERM_WARN_THRESH2, 0x46}, + {MAX98396_R2022_THERM_SHDN_THRESH, 0x64}, + {MAX98396_R2023_THERM_HYSTERESIS, 0x02}, + {MAX98396_R2024_THERM_FOLDBACK_SET, 0xC5}, + {MAX98396_R2027_THERM_FOLDBACK_EN, 0x01}, + {MAX98396_R2030_NOISEGATE_MODE_CTRL, 0x32}, + {MAX98396_R2033_NOISEGATE_MODE_EN, 0x00}, + {MAX98396_R2038_CLK_MON_CTRL, 0x00}, + {MAX98396_R2039_DATA_MON_CTRL, 0x00}, + {MAX98396_R203F_ENABLE_CTRLS, 0x0F}, + {MAX98396_R2040_PIN_CFG, 0x55}, + {MAX98396_R2041_PCM_MODE_CFG, 0xC0}, + {MAX98396_R2042_PCM_CLK_SETUP, 0x04}, + {MAX98396_R2043_PCM_SR_SETUP, 0x88}, + {MAX98396_R2044_PCM_TX_CTRL_1, 0x00}, + {MAX98396_R2045_PCM_TX_CTRL_2, 0x00}, + {MAX98396_R2046_PCM_TX_CTRL_3, 0x00}, + {MAX98396_R2047_PCM_TX_CTRL_4, 0x00}, + {MAX98396_R2048_PCM_TX_CTRL_5, 0x00}, + {MAX98396_R2049_PCM_TX_CTRL_6, 0x00}, + {MAX98396_R204A_PCM_TX_CTRL_7, 0x00}, + {MAX98396_R204B_PCM_TX_CTRL_8, 0x00}, + {MAX98396_R204C_PCM_TX_HIZ_CTRL_1, 0xFF}, + {MAX98396_R204D_PCM_TX_HIZ_CTRL_2, 0xFF}, + {MAX98396_R204E_PCM_TX_HIZ_CTRL_3, 0xFF}, + {MAX98396_R204F_PCM_TX_HIZ_CTRL_4, 0xFF}, + {MAX98396_R2050_PCM_TX_HIZ_CTRL_5, 0xFF}, + {MAX98396_R2051_PCM_TX_HIZ_CTRL_6, 0xFF}, + {MAX98396_R2052_PCM_TX_HIZ_CTRL_7, 0xFF}, + {MAX98396_R2053_PCM_TX_HIZ_CTRL_8, 0xFF}, + {MAX98396_R2055_PCM_RX_SRC1, 0x00}, + {MAX98396_R2056_PCM_RX_SRC2, 0x00}, + {MAX98396_R2058_PCM_BYPASS_SRC, 0x00}, + {MAX98396_R205D_PCM_TX_SRC_EN, 0x00}, + {MAX98396_R205E_PCM_RX_EN, 0x00}, + {MAX98396_R205F_PCM_TX_EN, 0x00}, + {MAX98396_R2070_ICC_RX_EN_A, 0x00}, + {MAX98396_R2071_ICC_RX_EN_B, 0x00}, + {MAX98396_R2072_ICC_TX_CTRL, 0x00}, + {MAX98396_R207F_ICC_EN, 0x00}, + {MAX98396_R2083_TONE_GEN_DC_CFG, 0x04}, + {MAX98396_R2084_TONE_GEN_DC_LVL1, 0x00}, + {MAX98396_R2085_TONE_GEN_DC_LVL2, 0x00}, + {MAX98396_R2086_TONE_GEN_DC_LVL3, 0x00}, + {MAX98396_R208F_TONE_GEN_EN, 0x00}, + {MAX98396_R2090_AMP_VOL_CTRL, 0x00}, + {MAX98396_R2091_AMP_PATH_GAIN, 0x0B}, + {MAX98396_R2092_AMP_DSP_CFG, 0x23}, + {MAX98396_R2093_SSM_CFG, 0x0D}, + {MAX98396_R2094_SPK_CLS_DG_THRESH, 0x12}, + {MAX98396_R2095_SPK_CLS_DG_HDR, 0x17}, + {MAX98396_R2096_SPK_CLS_DG_HOLD_TIME, 0x17}, + {MAX98396_R2097_SPK_CLS_DG_DELAY, 0x00}, + {MAX98396_R2098_SPK_CLS_DG_MODE, 0x00}, + {MAX98396_R2099_SPK_CLS_DG_VBAT_LVL, 0x03}, + {MAX98396_R209A_SPK_EDGE_CTRL, 0x00}, + {MAX98396_R209C_SPK_EDGE_CTRL1, 0x0A}, + {MAX98396_R209D_SPK_EDGE_CTRL2, 0xAA}, + {MAX98396_R209E_AMP_CLIP_GAIN, 0x00}, + {MAX98396_R209F_BYPASS_PATH_CFG, 0x00}, + {MAX98396_R20A0_AMP_SUPPLY_CTL, 0x00}, + {MAX98396_R20AF_AMP_EN, 0x00}, + {MAX98396_R20B0_ADC_SR, 0x30}, + {MAX98396_R20B1_ADC_PVDD_CFG, 0x00}, + {MAX98396_R20B2_ADC_VBAT_CFG, 0x00}, + {MAX98396_R20B3_ADC_THERMAL_CFG, 0x00}, + {MAX98396_R20B4_ADC_READBACK_CTRL1, 0x00}, + {MAX98396_R20B5_ADC_READBACK_CTRL2, 0x00}, + {MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0x00}, + {MAX98396_R20B7_ADC_PVDD_READBACK_LSB, 0x00}, + {MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0x00}, + {MAX98396_R20B9_ADC_VBAT_READBACK_LSB, 0x00}, + {MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0x00}, + {MAX98396_R20BB_ADC_TEMP_READBACK_LSB, 0x00}, + {MAX98396_R20BC_ADC_LO_PVDD_READBACK_MSB, 0x00}, + {MAX98396_R20BD_ADC_LO_PVDD_READBACK_LSB, 0x00}, + {MAX98396_R20BE_ADC_LO_VBAT_READBACK_MSB, 0x00}, + {MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB, 0x00}, + {MAX98396_R20C7_ADC_CFG, 0x00}, + {MAX98396_R20D0_DHT_CFG1, 0x00}, + {MAX98396_R20D1_LIMITER_CFG1, 0x08}, + {MAX98396_R20D2_LIMITER_CFG2, 0x00}, + {MAX98396_R20D3_DHT_CFG2, 0x14}, + {MAX98396_R20D4_DHT_CFG3, 0x02}, + {MAX98396_R20D5_DHT_CFG4, 0x04}, + {MAX98396_R20D6_DHT_HYSTERESIS_CFG, 0x07}, + {MAX98396_R20DF_DHT_EN, 0x00}, + {MAX98396_R20E0_IV_SENSE_PATH_CFG, 0x04}, + {MAX98396_R20E4_IV_SENSE_PATH_EN, 0x00}, + {MAX98396_R20E5_BPE_STATE, 0x00}, + {MAX98396_R20E6_BPE_L3_THRESH_MSB, 0x00}, + {MAX98396_R20E7_BPE_L3_THRESH_LSB, 0x00}, + {MAX98396_R20E8_BPE_L2_THRESH_MSB, 0x00}, + {MAX98396_R20E9_BPE_L2_THRESH_LSB, 0x00}, + {MAX98396_R20EA_BPE_L1_THRESH_MSB, 0x00}, + {MAX98396_R20EB_BPE_L1_THRESH_LSB, 0x00}, + {MAX98396_R20EC_BPE_L0_THRESH_MSB, 0x00}, + {MAX98396_R20ED_BPE_L0_THRESH_LSB, 0x00}, + {MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME, 0x00}, + {MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME, 0x00}, + {MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME, 0x00}, + {MAX98396_R20F1_BPE_L0_HOLD_TIME, 0x00}, + {MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20FA_BPE_L3_ATT_REL_RATE, 0x00}, + {MAX98396_R20FB_BPE_L2_ATT_REL_RATE, 0x00}, + {MAX98396_R20FC_BPE_L1_ATT_REL_RATE, 0x00}, + {MAX98396_R20FD_BPE_L0_ATT_REL_RATE, 0x00}, + {MAX98396_R20FE_BPE_L3_LIMITER_CFG, 0x00}, + {MAX98396_R20FF_BPE_L2_LIMITER_CFG, 0x00}, + {MAX98396_R2100_BPE_L1_LIMITER_CFG, 0x00}, + {MAX98396_R2101_BPE_L0_LIMITER_CFG, 0x00}, + {MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2106_BPE_THRESH_HYSTERESIS, 0x00}, + {MAX98396_R2107_BPE_INFINITE_HOLD_CLR, 0x00}, + {MAX98396_R2108_BPE_SUPPLY_SRC, 0x00}, + {MAX98396_R2109_BPE_LOW_STATE, 0x00}, + {MAX98396_R210A_BPE_LOW_GAIN, 0x00}, + {MAX98396_R210B_BPE_LOW_LIMITER, 0x00}, + {MAX98396_R210D_BPE_EN, 0x00}, + {MAX98396_R210E_AUTO_RESTART, 0x00}, + {MAX98396_R210F_GLOBAL_EN, 0x00}, + {MAX98396_R21FF_REVISION_ID, 0x00}, +}; + +static struct reg_default max98397_reg[] = { + {MAX98396_R2000_SW_RESET, 0x00}, + {MAX98396_R2001_INT_RAW1, 0x00}, + {MAX98396_R2002_INT_RAW2, 0x00}, + {MAX98396_R2003_INT_RAW3, 0x00}, + {MAX98396_R2004_INT_RAW4, 0x00}, + {MAX98396_R2006_INT_STATE1, 0x00}, + {MAX98396_R2007_INT_STATE2, 0x00}, + {MAX98396_R2008_INT_STATE3, 0x00}, + {MAX98396_R2009_INT_STATE4, 0x00}, + {MAX98396_R200B_INT_FLAG1, 0x00}, + {MAX98396_R200C_INT_FLAG2, 0x00}, + {MAX98396_R200D_INT_FLAG3, 0x00}, + {MAX98396_R200E_INT_FLAG4, 0x00}, + {MAX98396_R2010_INT_EN1, 0x02}, + {MAX98396_R2011_INT_EN2, 0x00}, + {MAX98396_R2012_INT_EN3, 0x00}, + {MAX98396_R2013_INT_EN4, 0x00}, + {MAX98396_R2015_INT_FLAG_CLR1, 0x00}, + {MAX98396_R2016_INT_FLAG_CLR2, 0x00}, + {MAX98396_R2017_INT_FLAG_CLR3, 0x00}, + {MAX98396_R2018_INT_FLAG_CLR4, 0x00}, + {MAX98396_R201F_IRQ_CTRL, 0x00}, + {MAX98396_R2020_THERM_WARN_THRESH, 0x46}, + {MAX98396_R2021_THERM_WARN_THRESH2, 0x46}, + {MAX98396_R2022_THERM_SHDN_THRESH, 0x64}, + {MAX98396_R2023_THERM_HYSTERESIS, 0x02}, + {MAX98396_R2024_THERM_FOLDBACK_SET, 0xC5}, + {MAX98396_R2027_THERM_FOLDBACK_EN, 0x01}, + {MAX98396_R2030_NOISEGATE_MODE_CTRL, 0x32}, + {MAX98396_R2033_NOISEGATE_MODE_EN, 0x00}, + {MAX98396_R2038_CLK_MON_CTRL, 0x00}, + {MAX98396_R2039_DATA_MON_CTRL, 0x00}, + {MAX98397_R203A_SPK_MON_THRESH, 0x03}, + {MAX98396_R203F_ENABLE_CTRLS, 0x0F}, + {MAX98396_R2040_PIN_CFG, 0x55}, + {MAX98396_R2041_PCM_MODE_CFG, 0xC0}, + {MAX98396_R2042_PCM_CLK_SETUP, 0x04}, + {MAX98396_R2043_PCM_SR_SETUP, 0x88}, + {MAX98396_R2044_PCM_TX_CTRL_1, 0x00}, + {MAX98396_R2045_PCM_TX_CTRL_2, 0x00}, + {MAX98396_R2046_PCM_TX_CTRL_3, 0x00}, + {MAX98396_R2047_PCM_TX_CTRL_4, 0x00}, + {MAX98396_R2048_PCM_TX_CTRL_5, 0x00}, + {MAX98396_R2049_PCM_TX_CTRL_6, 0x00}, + {MAX98396_R204A_PCM_TX_CTRL_7, 0x00}, + {MAX98396_R204B_PCM_TX_CTRL_8, 0x00}, + {MAX98397_R204C_PCM_TX_CTRL_9, 0x00}, + {MAX98397_R204D_PCM_TX_HIZ_CTRL_1, 0xFF}, + {MAX98397_R204E_PCM_TX_HIZ_CTRL_2, 0xFF}, + {MAX98397_R204F_PCM_TX_HIZ_CTRL_3, 0xFF}, + {MAX98397_R2050_PCM_TX_HIZ_CTRL_4, 0xFF}, + {MAX98397_R2051_PCM_TX_HIZ_CTRL_5, 0xFF}, + {MAX98397_R2052_PCM_TX_HIZ_CTRL_6, 0xFF}, + {MAX98397_R2053_PCM_TX_HIZ_CTRL_7, 0xFF}, + {MAX98397_R2054_PCM_TX_HIZ_CTRL_8, 0xFF}, + {MAX98397_R2056_PCM_RX_SRC1, 0x00}, + {MAX98397_R2057_PCM_RX_SRC2, 0x00}, + {MAX98396_R2058_PCM_BYPASS_SRC, 0x00}, + {MAX98396_R205D_PCM_TX_SRC_EN, 0x00}, + {MAX98396_R205E_PCM_RX_EN, 0x00}, + {MAX98396_R205F_PCM_TX_EN, 0x00}, + {MAX98397_R2060_PCM_TX_SUPPLY_SEL, 0x00}, + {MAX98396_R2070_ICC_RX_EN_A, 0x00}, + {MAX98396_R2071_ICC_RX_EN_B, 0x00}, + {MAX98396_R2072_ICC_TX_CTRL, 0x00}, + {MAX98396_R207F_ICC_EN, 0x00}, + {MAX98396_R2083_TONE_GEN_DC_CFG, 0x04}, + {MAX98396_R2084_TONE_GEN_DC_LVL1, 0x00}, + {MAX98396_R2085_TONE_GEN_DC_LVL2, 0x00}, + {MAX98396_R2086_TONE_GEN_DC_LVL3, 0x00}, + {MAX98396_R208F_TONE_GEN_EN, 0x00}, + {MAX98396_R2090_AMP_VOL_CTRL, 0x00}, + {MAX98396_R2091_AMP_PATH_GAIN, 0x12}, + {MAX98396_R2092_AMP_DSP_CFG, 0x22}, + {MAX98396_R2093_SSM_CFG, 0x08}, + {MAX98396_R2094_SPK_CLS_DG_THRESH, 0x12}, + {MAX98396_R2095_SPK_CLS_DG_HDR, 0x17}, + {MAX98396_R2096_SPK_CLS_DG_HOLD_TIME, 0x17}, + {MAX98396_R2097_SPK_CLS_DG_DELAY, 0x00}, + {MAX98396_R2098_SPK_CLS_DG_MODE, 0x00}, + {MAX98396_R2099_SPK_CLS_DG_VBAT_LVL, 0x03}, + {MAX98396_R209A_SPK_EDGE_CTRL, 0x00}, + {MAX98397_R209B_SPK_PATH_WB_ONLY, 0x00}, + {MAX98396_R209C_SPK_EDGE_CTRL1, 0x03}, + {MAX98396_R209D_SPK_EDGE_CTRL2, 0xFC}, + {MAX98396_R209E_AMP_CLIP_GAIN, 0x00}, + {MAX98396_R209F_BYPASS_PATH_CFG, 0x00}, + {MAX98396_R20AF_AMP_EN, 0x00}, + {MAX98396_R20B0_ADC_SR, 0x30}, + {MAX98396_R20B1_ADC_PVDD_CFG, 0x00}, + {MAX98396_R20B2_ADC_VBAT_CFG, 0x00}, + {MAX98396_R20B3_ADC_THERMAL_CFG, 0x00}, + {MAX98397_R20B4_ADC_VDDH_CFG, 0x00}, + {MAX98397_R20B5_ADC_READBACK_CTRL1, 0x00}, + {MAX98397_R20B6_ADC_READBACK_CTRL2, 0x00}, + {MAX98397_R20B7_ADC_PVDD_READBACK_MSB, 0x00}, + {MAX98397_R20B8_ADC_PVDD_READBACK_LSB, 0x00}, + {MAX98397_R20B9_ADC_VBAT_READBACK_MSB, 0x00}, + {MAX98397_R20BA_ADC_VBAT_READBACK_LSB, 0x00}, + {MAX98397_R20BB_ADC_TEMP_READBACK_MSB, 0x00}, + {MAX98397_R20BC_ADC_TEMP_READBACK_LSB, 0x00}, + {MAX98397_R20BD_ADC_VDDH__READBACK_MSB, 0x00}, + {MAX98397_R20BE_ADC_VDDH_READBACK_LSB, 0x00}, + {MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB, 0x00}, + {MAX98397_R20C3_ADC_LO_VDDH_READBACK_MSB, 0x00}, + {MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB, 0x00}, + {MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE, 0x04}, + {MAX98396_R20C7_ADC_CFG, 0x00}, + {MAX98396_R20D0_DHT_CFG1, 0x00}, + {MAX98396_R20D1_LIMITER_CFG1, 0x08}, + {MAX98396_R20D2_LIMITER_CFG2, 0x00}, + {MAX98396_R20D3_DHT_CFG2, 0x14}, + {MAX98396_R20D4_DHT_CFG3, 0x02}, + {MAX98396_R20D5_DHT_CFG4, 0x04}, + {MAX98396_R20D6_DHT_HYSTERESIS_CFG, 0x07}, + {MAX98396_R20DF_DHT_EN, 0x00}, + {MAX98396_R20E0_IV_SENSE_PATH_CFG, 0x04}, + {MAX98396_R20E4_IV_SENSE_PATH_EN, 0x00}, + {MAX98396_R20E5_BPE_STATE, 0x00}, + {MAX98396_R20E6_BPE_L3_THRESH_MSB, 0x00}, + {MAX98396_R20E7_BPE_L3_THRESH_LSB, 0x00}, + {MAX98396_R20E8_BPE_L2_THRESH_MSB, 0x00}, + {MAX98396_R20E9_BPE_L2_THRESH_LSB, 0x00}, + {MAX98396_R20EA_BPE_L1_THRESH_MSB, 0x00}, + {MAX98396_R20EB_BPE_L1_THRESH_LSB, 0x00}, + {MAX98396_R20EC_BPE_L0_THRESH_MSB, 0x00}, + {MAX98396_R20ED_BPE_L0_THRESH_LSB, 0x00}, + {MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME, 0x00}, + {MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME, 0x00}, + {MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME, 0x00}, + {MAX98396_R20F1_BPE_L0_HOLD_TIME, 0x00}, + {MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20FA_BPE_L3_ATT_REL_RATE, 0x00}, + {MAX98396_R20FB_BPE_L2_ATT_REL_RATE, 0x00}, + {MAX98396_R20FC_BPE_L1_ATT_REL_RATE, 0x00}, + {MAX98396_R20FD_BPE_L0_ATT_REL_RATE, 0x00}, + {MAX98396_R20FE_BPE_L3_LIMITER_CFG, 0x00}, + {MAX98396_R20FF_BPE_L2_LIMITER_CFG, 0x00}, + {MAX98396_R2100_BPE_L1_LIMITER_CFG, 0x00}, + {MAX98396_R2101_BPE_L0_LIMITER_CFG, 0x00}, + {MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2106_BPE_THRESH_HYSTERESIS, 0x00}, + {MAX98396_R2107_BPE_INFINITE_HOLD_CLR, 0x00}, + {MAX98396_R2108_BPE_SUPPLY_SRC, 0x00}, + {MAX98396_R2109_BPE_LOW_STATE, 0x00}, + {MAX98396_R210A_BPE_LOW_GAIN, 0x00}, + {MAX98396_R210B_BPE_LOW_LIMITER, 0x00}, + {MAX98396_R210D_BPE_EN, 0x00}, + {MAX98396_R210E_AUTO_RESTART, 0x00}, + {MAX98396_R210F_GLOBAL_EN, 0x00}, + {MAX98397_R22FF_REVISION_ID, 0x00}, +}; + +static void max98396_global_enable_onoff(struct regmap *regmap, bool onoff) +{ + regmap_write(regmap, MAX98396_R210F_GLOBAL_EN, onoff ? 1 : 0); + usleep_range(11000, 12000); +} + +static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component); + unsigned int format = 0; + unsigned int bclk_pol = 0; + int ret, status; + int reg; + bool update = false; + + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + format = MAX98396_PCM_MODE_CFG_LRCLKEDGE; + break; + case SND_SOC_DAIFMT_IB_NF: + bclk_pol = MAX98396_PCM_MODE_CFG_BCLKEDGE; + break; + case SND_SOC_DAIFMT_IB_IF: + bclk_pol = MAX98396_PCM_MODE_CFG_BCLKEDGE; + format = MAX98396_PCM_MODE_CFG_LRCLKEDGE; + break; + + default: + dev_err(component->dev, "DAI invert mode unsupported\n"); + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format |= MAX98396_PCM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format |= MAX98396_PCM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_DSP_A: + format |= MAX98396_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format |= MAX98396_PCM_FORMAT_TDM_MODE0; + break; + default: + return -EINVAL; + } + + ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status); + if (ret < 0) + return -EINVAL; + + if (status) { + ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, ®); + if (ret < 0) + return -EINVAL; + if (format != (reg & MAX98396_PCM_BCLKEDGE_BSEL_MASK)) { + update = true; + } else { + ret = regmap_read(max98396->regmap, + MAX98396_R2042_PCM_CLK_SETUP, ®); + if (ret < 0) + return -EINVAL; + if (bclk_pol != (reg & MAX98396_PCM_MODE_CFG_BCLKEDGE)) + update = true; + } + /* GLOBAL_EN OFF prior to pcm mode, clock configuration change */ + if (update) + max98396_global_enable_onoff(max98396->regmap, false); + } + + regmap_update_bits(max98396->regmap, + MAX98396_R2041_PCM_MODE_CFG, + MAX98396_PCM_BCLKEDGE_BSEL_MASK, + format); + + regmap_update_bits(max98396->regmap, + MAX98396_R2042_PCM_CLK_SETUP, + MAX98396_PCM_MODE_CFG_BCLKEDGE, + bclk_pol); + + if (status && update) + max98396_global_enable_onoff(max98396->regmap, true); + + return 0; +} + +/* BCLKs per LRCLK */ +static const int bclk_sel_table[] = { + 32, 48, 64, 96, 128, 192, 256, 384, 512, 320, +}; + +static int max98396_get_bclk_sel(int bclk) +{ + int i; + /* match BCLKs per LRCLK */ + for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) { + if (bclk_sel_table[i] == bclk) + return i + 2; + } + return 0; +} + +static int max98396_set_clock(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component); + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98396->ch_size; + int value; + + if (!max98396->tdm_mode) { + /* BCLK configuration */ + value = max98396_get_bclk_sel(blr_clk_ratio); + if (!value) { + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + regmap_update_bits(max98396->regmap, + MAX98396_R2042_PCM_CLK_SETUP, + MAX98396_PCM_CLK_SETUP_BSEL_MASK, + value); + } + + return 0; +} + +static int max98396_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 max98396_priv *max98396 = snd_soc_component_get_drvdata(component); + unsigned int sampling_rate = 0; + unsigned int chan_sz = 0; + int ret, reg; + int status; + bool update = false; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + goto err; + } + + max98396->ch_size = snd_pcm_format_width(params_format(params)); + + dev_dbg(component->dev, "format supported %d", + params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98396_PCM_SR_8000; + break; + case 11025: + sampling_rate = MAX98396_PCM_SR_11025; + break; + case 12000: + sampling_rate = MAX98396_PCM_SR_12000; + break; + case 16000: + sampling_rate = MAX98396_PCM_SR_16000; + break; + case 22050: + sampling_rate = MAX98396_PCM_SR_22050; + break; + case 24000: + sampling_rate = MAX98396_PCM_SR_24000; + break; + case 32000: + sampling_rate = MAX98396_PCM_SR_32000; + break; + case 44100: + sampling_rate = MAX98396_PCM_SR_44100; + break; + case 48000: + sampling_rate = MAX98396_PCM_SR_48000; + break; + case 88200: + sampling_rate = MAX98396_PCM_SR_88200; + break; + case 96000: + sampling_rate = MAX98396_PCM_SR_96000; + break; + case 192000: + sampling_rate = MAX98396_PCM_SR_192000; + break; + default: + dev_err(component->dev, "rate %d not supported\n", + params_rate(params)); + goto err; + } + + ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status); + if (ret < 0) + goto err; + + if (status) { + ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, ®); + if (ret < 0) + goto err; + if (chan_sz != (reg & MAX98396_PCM_MODE_CFG_CHANSZ_MASK)) { + update = true; + } else { + ret = regmap_read(max98396->regmap, MAX98396_R2043_PCM_SR_SETUP, ®); + if (ret < 0) + goto err; + if (sampling_rate != (reg & MAX98396_PCM_SR_MASK)) + update = true; + } + + /* GLOBAL_EN OFF prior to channel size and sampling rate change */ + if (update) + max98396_global_enable_onoff(max98396->regmap, false); + } + + /* set channel size */ + regmap_update_bits(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, + MAX98396_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98396->regmap, MAX98396_R2043_PCM_SR_SETUP, + MAX98396_PCM_SR_MASK, sampling_rate); + + /* set sampling rate of IV */ + if (max98396->interleave_mode && + sampling_rate > MAX98396_PCM_SR_16000) + regmap_update_bits(max98396->regmap, + MAX98396_R2043_PCM_SR_SETUP, + MAX98396_IVADC_SR_MASK, + (sampling_rate - 3) + << MAX98396_IVADC_SR_SHIFT); + else + regmap_update_bits(max98396->regmap, + MAX98396_R2043_PCM_SR_SETUP, + MAX98396_IVADC_SR_MASK, + sampling_rate << MAX98396_IVADC_SR_SHIFT); + + ret = max98396_set_clock(component, params); + + if (status && update) + max98396_global_enable_onoff(max98396->regmap, true); + + return ret; + +err: + return -EINVAL; +} + +static int max98396_dai_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 max98396_priv *max98396 = + snd_soc_component_get_drvdata(component); + int bsel; + unsigned int chan_sz = 0; + int ret, status; + int reg; + bool update = false; + + if (!tx_mask && !rx_mask && !slots && !slot_width) + max98396->tdm_mode = false; + else + max98396->tdm_mode = true; + + /* BCLK configuration */ + bsel = max98396_get_bclk_sel(slots * slot_width); + if (bsel == 0) { + dev_err(component->dev, "BCLK %d not supported\n", + slots * slot_width); + return -EINVAL; + } + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + slot_width); + return -EINVAL; + } + + ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status); + if (ret < 0) + return -EINVAL; + + if (status) { + ret = regmap_read(max98396->regmap, MAX98396_R2042_PCM_CLK_SETUP, ®); + if (ret < 0) + return -EINVAL; + if (bsel != (reg & MAX98396_PCM_CLK_SETUP_BSEL_MASK)) { + update = true; + } else { + ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, ®); + if (ret < 0) + return -EINVAL; + if (chan_sz != (reg & MAX98396_PCM_MODE_CFG_CHANSZ_MASK)) + update = true; + } + + /* GLOBAL_EN OFF prior to channel size and BCLK per LRCLK change */ + if (update) + max98396_global_enable_onoff(max98396->regmap, false); + } + + regmap_update_bits(max98396->regmap, + MAX98396_R2042_PCM_CLK_SETUP, + MAX98396_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + regmap_update_bits(max98396->regmap, + MAX98396_R2041_PCM_MODE_CFG, + MAX98396_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + if (max98396->device_id == CODEC_TYPE_MAX98396) { + regmap_update_bits(max98396->regmap, + MAX98396_R2056_PCM_RX_SRC2, + MAX98396_PCM_DMIX_CH0_SRC_MASK, + rx_mask); + regmap_update_bits(max98396->regmap, + MAX98396_R2056_PCM_RX_SRC2, + MAX98396_PCM_DMIX_CH1_SRC_MASK, + rx_mask << MAX98396_PCM_DMIX_CH1_SHIFT); + } else { + regmap_update_bits(max98396->regmap, + MAX98397_R2057_PCM_RX_SRC2, + MAX98396_PCM_DMIX_CH0_SRC_MASK, + rx_mask); + regmap_update_bits(max98396->regmap, + MAX98397_R2057_PCM_RX_SRC2, + MAX98396_PCM_DMIX_CH1_SRC_MASK, + rx_mask << MAX98396_PCM_DMIX_CH1_SHIFT); + } + + /* Tx slot Hi-Z configuration */ + if (max98396->device_id == CODEC_TYPE_MAX98396) { + regmap_write(max98396->regmap, + MAX98396_R2053_PCM_TX_HIZ_CTRL_8, + ~tx_mask & 0xFF); + regmap_write(max98396->regmap, + MAX98396_R2052_PCM_TX_HIZ_CTRL_7, + (~tx_mask & 0xFF00) >> 8); + } else { + regmap_write(max98396->regmap, + MAX98397_R2054_PCM_TX_HIZ_CTRL_8, + ~tx_mask & 0xFF); + regmap_write(max98396->regmap, + MAX98397_R2053_PCM_TX_HIZ_CTRL_7, + (~tx_mask & 0xFF00) >> 8); + } + + if (status && update) + max98396_global_enable_onoff(max98396->regmap, true); + + return 0; +} + +#define MAX98396_RATES SNDRV_PCM_RATE_8000_192000 + +#define MAX98396_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98396_dai_ops = { + .set_fmt = max98396_dai_set_fmt, + .hw_params = max98396_dai_hw_params, + .set_tdm_slot = max98396_dai_tdm_slot, +}; + +static int max98396_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 max98396_priv *max98396 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + max98396_global_enable_onoff(max98396->regmap, true); + break; + case SND_SOC_DAPM_PRE_PMD: + max98396_global_enable_onoff(max98396->regmap, false); + + max98396->tdm_mode = false; + break; + default: + return 0; + } + return 0; +} + +static bool max98396_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98396_R2001_INT_RAW1 ... MAX98396_R2004_INT_RAW4: + case MAX98396_R2006_INT_STATE1 ... MAX98396_R2009_INT_STATE4: + case MAX98396_R200B_INT_FLAG1 ... MAX98396_R200E_INT_FLAG4: + case MAX98396_R2010_INT_EN1 ... MAX98396_R2013_INT_EN4: + case MAX98396_R2015_INT_FLAG_CLR1 ... MAX98396_R2018_INT_FLAG_CLR4: + case MAX98396_R201F_IRQ_CTRL ... MAX98396_R2024_THERM_FOLDBACK_SET: + case MAX98396_R2027_THERM_FOLDBACK_EN: + case MAX98396_R2030_NOISEGATE_MODE_CTRL: + case MAX98396_R2033_NOISEGATE_MODE_EN: + case MAX98396_R2038_CLK_MON_CTRL ... MAX98396_R2039_DATA_MON_CTRL: + case MAX98396_R203F_ENABLE_CTRLS ... MAX98396_R2053_PCM_TX_HIZ_CTRL_8: + case MAX98396_R2055_PCM_RX_SRC1 ... MAX98396_R2056_PCM_RX_SRC2: + case MAX98396_R2058_PCM_BYPASS_SRC: + case MAX98396_R205D_PCM_TX_SRC_EN ... MAX98396_R205F_PCM_TX_EN: + case MAX98396_R2070_ICC_RX_EN_A... MAX98396_R2072_ICC_TX_CTRL: + case MAX98396_R207F_ICC_EN: + case MAX98396_R2083_TONE_GEN_DC_CFG ... MAX98396_R2086_TONE_GEN_DC_LVL3: + case MAX98396_R208F_TONE_GEN_EN ... MAX98396_R209A_SPK_EDGE_CTRL: + case MAX98396_R209C_SPK_EDGE_CTRL1 ... MAX98396_R20A0_AMP_SUPPLY_CTL: + case MAX98396_R20AF_AMP_EN ... MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB: + case MAX98396_R20C7_ADC_CFG: + case MAX98396_R20D0_DHT_CFG1 ... MAX98396_R20D6_DHT_HYSTERESIS_CFG: + case MAX98396_R20DF_DHT_EN: + case MAX98396_R20E0_IV_SENSE_PATH_CFG: + case MAX98396_R20E4_IV_SENSE_PATH_EN + ... MAX98396_R2106_BPE_THRESH_HYSTERESIS: + case MAX98396_R2108_BPE_SUPPLY_SRC ... MAX98396_R210B_BPE_LOW_LIMITER: + case MAX98396_R210D_BPE_EN ... MAX98396_R210F_GLOBAL_EN: + case MAX98396_R21FF_REVISION_ID: + return true; + default: + return false; + } +}; + +static bool max98396_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98396_R2000_SW_RESET: + case MAX98396_R2001_INT_RAW1 ... MAX98396_R200E_INT_FLAG4: + case MAX98396_R2041_PCM_MODE_CFG: + case MAX98396_R20B6_ADC_PVDD_READBACK_MSB + ... MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB: + case MAX98396_R20E5_BPE_STATE: + case MAX98396_R2109_BPE_LOW_STATE + ... MAX98396_R210B_BPE_LOW_LIMITER: + case MAX98396_R210F_GLOBAL_EN: + case MAX98396_R21FF_REVISION_ID: + return true; + default: + return false; + } +} + +static bool max98397_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98396_R2001_INT_RAW1 ... MAX98396_R2004_INT_RAW4: + case MAX98396_R2006_INT_STATE1 ... MAX98396_R2009_INT_STATE4: + case MAX98396_R200B_INT_FLAG1 ... MAX98396_R200E_INT_FLAG4: + case MAX98396_R2010_INT_EN1 ... MAX98396_R2013_INT_EN4: + case MAX98396_R2015_INT_FLAG_CLR1 ... MAX98396_R2018_INT_FLAG_CLR4: + case MAX98396_R201F_IRQ_CTRL ... MAX98396_R2024_THERM_FOLDBACK_SET: + case MAX98396_R2027_THERM_FOLDBACK_EN: + case MAX98396_R2030_NOISEGATE_MODE_CTRL: + case MAX98396_R2033_NOISEGATE_MODE_EN: + case MAX98396_R2038_CLK_MON_CTRL ... MAX98397_R203A_SPK_MON_THRESH: + case MAX98396_R203F_ENABLE_CTRLS ... MAX98397_R2054_PCM_TX_HIZ_CTRL_8: + case MAX98397_R2056_PCM_RX_SRC1... MAX98396_R2058_PCM_BYPASS_SRC: + case MAX98396_R205D_PCM_TX_SRC_EN ... MAX98397_R2060_PCM_TX_SUPPLY_SEL: + case MAX98396_R2070_ICC_RX_EN_A... MAX98396_R2072_ICC_TX_CTRL: + case MAX98396_R207F_ICC_EN: + case MAX98396_R2083_TONE_GEN_DC_CFG ... MAX98396_R2086_TONE_GEN_DC_LVL3: + case MAX98396_R208F_TONE_GEN_EN ... MAX98396_R209F_BYPASS_PATH_CFG: + case MAX98396_R20AF_AMP_EN ... MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE: + case MAX98396_R20C7_ADC_CFG: + case MAX98396_R20D0_DHT_CFG1 ... MAX98396_R20D6_DHT_HYSTERESIS_CFG: + case MAX98396_R20DF_DHT_EN: + case MAX98396_R20E0_IV_SENSE_PATH_CFG: + case MAX98396_R20E4_IV_SENSE_PATH_EN + ... MAX98396_R2106_BPE_THRESH_HYSTERESIS: + case MAX98396_R2108_BPE_SUPPLY_SRC ... MAX98396_R210B_BPE_LOW_LIMITER: + case MAX98396_R210D_BPE_EN ... MAX98396_R210F_GLOBAL_EN: + case MAX98397_R22FF_REVISION_ID: + return true; + default: + return false; + } +}; + +static bool max98397_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98396_R2001_INT_RAW1 ... MAX98396_R200E_INT_FLAG4: + case MAX98396_R2041_PCM_MODE_CFG: + case MAX98397_R20B7_ADC_PVDD_READBACK_MSB + ... MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB: + case MAX98396_R20E5_BPE_STATE: + case MAX98396_R2109_BPE_LOW_STATE + ... MAX98396_R210B_BPE_LOW_LIMITER: + case MAX98396_R210F_GLOBAL_EN: + case MAX98397_R22FF_REVISION_ID: + return true; + default: + return false; + } +} + +static const char * const max98396_op_mod_text[] = { + "DG", "PVDD", "VBAT", +}; + +static SOC_ENUM_SINGLE_DECL(max98396_op_mod_enum, + MAX98396_R2098_SPK_CLS_DG_MODE, + 0, max98396_op_mod_text); + +static DECLARE_TLV_DB_SCALE(max98396_digital_tlv, -6350, 50, 1); +static const DECLARE_TLV_DB_RANGE(max98396_spk_tlv, + 0, 0x11, TLV_DB_SCALE_ITEM(400, 100, 0), +); +static DECLARE_TLV_DB_RANGE(max98397_digital_tlv, + 0, 0x4A, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + 0x4B, 0xFF, TLV_DB_SCALE_ITEM(-9000, 50, 0), +); +static const DECLARE_TLV_DB_RANGE(max98397_spk_tlv, + 0, 0x15, TLV_DB_SCALE_ITEM(600, 100, 0), +); + +static int max98396_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component); + int reg, val; + + if (max98396->device_id == CODEC_TYPE_MAX98396) + reg = MAX98396_R2055_PCM_RX_SRC1; + else + reg = MAX98397_R2056_PCM_RX_SRC1; + + regmap_read(max98396->regmap, reg, &val); + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int max98396_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + int reg, val; + int change; + + if (item[0] >= e->items) + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + if (max98396->device_id == CODEC_TYPE_MAX98396) + reg = MAX98396_R2055_PCM_RX_SRC1; + else + reg = MAX98397_R2056_PCM_RX_SRC1; + + change = snd_soc_component_test_bits(component, reg, + MAX98396_PCM_RX_MASK, val); + + if (change) + regmap_update_bits(max98396->regmap, reg, + MAX98396_PCM_RX_MASK, val); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL); + + return change; +} + +static const char * const max98396_switch_text[] = { + "Left", "Right", "LeftRight"}; + +static SOC_ENUM_SINGLE_DECL(dai_sel_enum, SND_SOC_NOPM, 0, + max98396_switch_text); + +static const struct snd_kcontrol_new max98396_dai_mux = + SOC_DAPM_ENUM_EXT("DAI Sel Mux", dai_sel_enum, + max98396_mux_get, max98396_mux_put); + +static const struct snd_kcontrol_new max98396_vi_control = + SOC_DAPM_SINGLE("Switch", MAX98396_R205F_PCM_TX_EN, 0, 1, 0); + +static const struct snd_soc_dapm_widget max98396_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", + MAX98396_R20AF_AMP_EN, 0, 0, max98396_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, + &max98396_dai_mux), + SND_SOC_DAPM_OUTPUT("BE_OUT"), + SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0, + MAX98396_R20E4_IV_SENSE_PATH_EN, 0, 0), + SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0, + MAX98396_R20E4_IV_SENSE_PATH_EN, 1, 0), + SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0, + &max98396_vi_control), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON"), + SND_SOC_DAPM_SIGGEN("FBMON"), +}; + +static const char * const max98396_thermal_thresh_text[] = { + "50C", "51C", "52C", "53C", "54C", "55C", "56C", "57C", + "58C", "59C", "60C", "61C", "62C", "63C", "64C", "65C", + "66C", "67C", "68C", "69C", "70C", "71C", "72C", "73C", + "74C", "75C", "76C", "77C", "78C", "79C", "80C", "81C", + "82C", "83C", "84C", "85C", "86C", "87C", "88C", "89C", + "90C", "91C", "92C", "93C", "94C", "95C", "96C", "97C", + "98C", "99C", "100C", "101C", "102C", "103C", "104C", "105C", + "106C", "107C", "108C", "109C", "110C", "111C", "112C", "113C", + "114C", "115C", "116C", "117C", "118C", "119C", "120C", "121C", + "122C", "123C", "124C", "125C", "126C", "127C", "128C", "129C", + "130C", "131C", "132C", "133C", "134C", "135C", "136C", "137C", + "138C", "139C", "140C", "141C", "142C", "143C", "144C", "145C", + "146C", "147C", "148C", "149C", "150C" +}; + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_warn_thresh1_enum, + MAX98396_R2020_THERM_WARN_THRESH, 0, + max98396_thermal_thresh_text); + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_warn_thresh2_enum, + MAX98396_R2021_THERM_WARN_THRESH2, 0, + max98396_thermal_thresh_text); + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_shdn_thresh_enum, + MAX98396_R2022_THERM_SHDN_THRESH, 0, + max98396_thermal_thresh_text); + +static const char * const max98396_thermal_hyteresis_text[] = { + "2C", "5C", "7C", "10C" +}; + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_hysteresis_enum, + MAX98396_R2023_THERM_HYSTERESIS, 0, + max98396_thermal_hyteresis_text); + +static const char * const max98396_foldback_slope_text[] = { + "0.25", "0.5", "1.0", "2.0" +}; + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_slope1_enum, + MAX98396_R2024_THERM_FOLDBACK_SET, + MAX98396_THERM_FB_SLOPE1_SHIFT, + max98396_foldback_slope_text); + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_slope2_enum, + MAX98396_R2024_THERM_FOLDBACK_SET, + MAX98396_THERM_FB_SLOPE2_SHIFT, + max98396_foldback_slope_text); + +static const char * const max98396_foldback_reltime_text[] = { + "3ms", "10ms", "100ms", "300ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_reltime_enum, + MAX98396_R2024_THERM_FOLDBACK_SET, + MAX98396_THERM_FB_REL_SHIFT, + max98396_foldback_reltime_text); + +static const char * const max98396_foldback_holdtime_text[] = { + "0ms", "20ms", "40ms", "80ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_holdtime_enum, + MAX98396_R2024_THERM_FOLDBACK_SET, + MAX98396_THERM_FB_HOLD_SHIFT, + max98396_foldback_holdtime_text); + +static int max98396_adc_value_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component); + int ret; + u8 val[2]; + int reg = mc->reg; + + /* ADC value is not available if the device is powered down */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + goto exit; + + if (max98396->device_id == CODEC_TYPE_MAX98397) { + switch (mc->reg) { + case MAX98396_R20B6_ADC_PVDD_READBACK_MSB: + reg = MAX98397_R20B7_ADC_PVDD_READBACK_MSB; + break; + case MAX98396_R20B8_ADC_VBAT_READBACK_MSB: + reg = MAX98397_R20B9_ADC_VBAT_READBACK_MSB; + break; + case MAX98396_R20BA_ADC_TEMP_READBACK_MSB: + reg = MAX98397_R20BB_ADC_TEMP_READBACK_MSB; + break; + default: + goto exit; + } + } + + ret = regmap_raw_read(max98396->regmap, reg, &val, 2); + if (ret) + goto exit; + + /* ADC readback bits[8:0] rearrangement */ + ucontrol->value.integer.value[0] = (val[0] << 1) | (val[1] & 1); + return 0; + +exit: + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static const struct snd_kcontrol_new max98396_snd_controls[] = { + /* Volume */ + SOC_SINGLE_TLV("Digital Volume", MAX98396_R2090_AMP_VOL_CTRL, + 0, 0x7F, 1, max98396_digital_tlv), + SOC_SINGLE_TLV("Speaker Volume", MAX98396_R2091_AMP_PATH_GAIN, + 0, 0x11, 0, max98396_spk_tlv), + /* Volume Ramp Up/Down Enable*/ + SOC_SINGLE("Ramp Up Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0), + SOC_SINGLE("Ramp Down Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0), + /* Clock Monitor Enable */ + SOC_SINGLE("CLK Monitor Switch", MAX98396_R203F_ENABLE_CTRLS, + MAX98396_CTRL_CMON_EN_SHIFT, 1, 0), + /* Dither Enable */ + SOC_SINGLE("Dither Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_DITH_EN_SHIFT, 1, 0), + SOC_SINGLE("IV Dither Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_DITH_EN_SHIFT, 1, 0), + /* DC Blocker Enable */ + SOC_SINGLE("DC Blocker Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_DCBLK_EN_SHIFT, 1, 0), + SOC_SINGLE("IV DC Blocker Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_DCBLK_EN_SHIFT, 3, 0), + /* Speaker Safe Mode Enable */ + SOC_SINGLE("Safe Mode Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_SAFE_EN_SHIFT, 1, 0), + /* Wideband Filter Enable */ + SOC_SINGLE("WB Filter Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_WB_FLT_EN_SHIFT, 1, 0), + SOC_SINGLE("IV WB Filter Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_WB_FLT_EN_SHIFT, 1, 0), + /* Dynamic Headroom Tracking */ + SOC_SINGLE("DHT Switch", MAX98396_R20DF_DHT_EN, 0, 1, 0), + /* Brownout Protection Engine */ + SOC_SINGLE("BPE Switch", MAX98396_R210D_BPE_EN, 0, 1, 0), + SOC_SINGLE("BPE Limiter Switch", MAX98396_R210D_BPE_EN, 1, 1, 0), + /* Bypass Path Enable */ + SOC_SINGLE("Bypass Path Switch", + MAX98396_R205E_PCM_RX_EN, 1, 1, 0), + /* Speaker Operation Mode */ + SOC_ENUM("OP Mode", max98396_op_mod_enum), + /* Auto Restart functions */ + SOC_SINGLE("CMON Auto Restart Switch", MAX98396_R2038_CLK_MON_CTRL, + MAX98396_CLK_MON_AUTO_RESTART_SHIFT, 1, 0), + SOC_SINGLE("PVDD Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_PVDD_UVLO_RESTART_SHFT, 1, 0), + SOC_SINGLE("VBAT Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_VBAT_UVLO_RESTART_SHFT, 1, 0), + SOC_SINGLE("THERM Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_THEM_SHDN_RESTART_SHFT, 1, 0), + SOC_SINGLE("OVC Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_OVC_RESTART_SHFT, 1, 0), + /* Thermal Threshold */ + SOC_ENUM("THERM Thresh1", max98396_thermal_warn_thresh1_enum), + SOC_ENUM("THERM Thresh2", max98396_thermal_warn_thresh2_enum), + SOC_ENUM("THERM SHDN Thresh", max98396_thermal_shdn_thresh_enum), + SOC_ENUM("THERM Hysteresis", max98396_thermal_hysteresis_enum), + SOC_SINGLE("THERM Foldback Switch", + MAX98396_R2027_THERM_FOLDBACK_EN, 0, 1, 0), + SOC_ENUM("THERM Slope1", max98396_thermal_fb_slope1_enum), + SOC_ENUM("THERM Slope2", max98396_thermal_fb_slope2_enum), + SOC_ENUM("THERM Release", max98396_thermal_fb_reltime_enum), + SOC_ENUM("THERM Hold", max98396_thermal_fb_holdtime_enum), + /* ADC */ + SOC_SINGLE_EXT("ADC PVDD", MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0, 0x1FF, 0, + max98396_adc_value_get, NULL), + SOC_SINGLE_EXT("ADC VBAT", MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0, 0x1FF, 0, + max98396_adc_value_get, NULL), + SOC_SINGLE_EXT("ADC TEMP", MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0, 0x1FF, 0, + max98396_adc_value_get, NULL), +}; + +static const struct snd_kcontrol_new max98397_snd_controls[] = { + /* Volume */ + SOC_SINGLE_TLV("Digital Volume", MAX98396_R2090_AMP_VOL_CTRL, + 0, 0xFF, 1, max98397_digital_tlv), + SOC_SINGLE_TLV("Speaker Volume", MAX98396_R2091_AMP_PATH_GAIN, + 0, 0x15, 0, max98397_spk_tlv), + /* Volume Ramp Up/Down Enable*/ + SOC_SINGLE("Ramp Up Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0), + SOC_SINGLE("Ramp Down Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0), + /* Clock Monitor Enable */ + SOC_SINGLE("CLK Monitor Switch", MAX98396_R203F_ENABLE_CTRLS, + MAX98396_CTRL_CMON_EN_SHIFT, 1, 0), + /* Dither Enable */ + SOC_SINGLE("Dither Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_DITH_EN_SHIFT, 1, 0), + SOC_SINGLE("IV Dither Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_DITH_EN_SHIFT, 1, 0), + /* DC Blocker Enable */ + SOC_SINGLE("DC Blocker Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_DCBLK_EN_SHIFT, 1, 0), + SOC_SINGLE("IV DC Blocker Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_DCBLK_EN_SHIFT, 3, 0), + /* Speaker Safe Mode Enable */ + SOC_SINGLE("Safe Mode Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_SAFE_EN_SHIFT, 1, 0), + /* Wideband Filter Enable */ + SOC_SINGLE("WB Filter Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_WB_FLT_EN_SHIFT, 1, 0), + SOC_SINGLE("IV WB Filter Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_WB_FLT_EN_SHIFT, 1, 0), + /* Dynamic Headroom Tracking */ + SOC_SINGLE("DHT Switch", MAX98396_R20DF_DHT_EN, 0, 1, 0), + /* Brownout Protection Engine */ + SOC_SINGLE("BPE Switch", MAX98396_R210D_BPE_EN, 0, 1, 0), + SOC_SINGLE("BPE Limiter Switch", MAX98396_R210D_BPE_EN, 1, 1, 0), + /* Bypass Path Enable */ + SOC_SINGLE("Bypass Path Switch", + MAX98396_R205E_PCM_RX_EN, 1, 1, 0), + /* Speaker Operation Mode */ + SOC_ENUM("OP Mode", max98396_op_mod_enum), + /* Auto Restart functions */ + SOC_SINGLE("CMON Auto Restart Switch", MAX98396_R2038_CLK_MON_CTRL, + MAX98396_CLK_MON_AUTO_RESTART_SHIFT, 1, 0), + SOC_SINGLE("PVDD Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_PVDD_UVLO_RESTART_SHFT, 1, 0), + SOC_SINGLE("VBAT Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_VBAT_UVLO_RESTART_SHFT, 1, 0), + SOC_SINGLE("THERM Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_THEM_SHDN_RESTART_SHFT, 1, 0), + SOC_SINGLE("OVC Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_OVC_RESTART_SHFT, 1, 0), + /* Thermal Threshold */ + SOC_ENUM("THERM Thresh1", max98396_thermal_warn_thresh1_enum), + SOC_ENUM("THERM Thresh2", max98396_thermal_warn_thresh2_enum), + SOC_ENUM("THERM SHDN Thresh", max98396_thermal_shdn_thresh_enum), + SOC_ENUM("THERM Hysteresis", max98396_thermal_hysteresis_enum), + SOC_SINGLE("THERM Foldback Switch", + MAX98396_R2027_THERM_FOLDBACK_EN, 0, 1, 0), + SOC_ENUM("THERM Slope1", max98396_thermal_fb_slope1_enum), + SOC_ENUM("THERM Slope2", max98396_thermal_fb_slope2_enum), + SOC_ENUM("THERM Release", max98396_thermal_fb_reltime_enum), + SOC_ENUM("THERM Hold", max98396_thermal_fb_holdtime_enum), + /* ADC */ + SOC_SINGLE_EXT("ADC PVDD", MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0, 0x1FF, 0, + max98396_adc_value_get, NULL), + SOC_SINGLE_EXT("ADC VBAT", MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0, 0x1FF, 0, + max98396_adc_value_get, NULL), + SOC_SINGLE_EXT("ADC TEMP", MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0, 0x1FF, 0, + max98396_adc_value_get, NULL), +}; + +static const struct snd_soc_dapm_route max98396_audio_map[] = { + /* Plabyack */ + {"DAI Sel Mux", "Left", "Amp Enable"}, + {"DAI Sel Mux", "Right", "Amp Enable"}, + {"DAI Sel Mux", "LeftRight", "Amp Enable"}, + {"BE_OUT", NULL, "DAI Sel Mux"}, + /* Capture */ + { "VI Sense", "Switch", "VMON" }, + { "VI Sense", "Switch", "IMON" }, + { "Voltage Sense", NULL, "VI Sense" }, + { "Current Sense", NULL, "VI Sense" }, +}; + +static struct snd_soc_dai_driver max98396_dai[] = { + { + .name = "max98396-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98396_RATES, + .formats = MAX98396_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98396_RATES, + .formats = MAX98396_FORMATS, + }, + .ops = &max98396_dai_ops, + } +}; + +static struct snd_soc_dai_driver max98397_dai[] = { + { + .name = "max98397-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98396_RATES, + .formats = MAX98396_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98396_RATES, + .formats = MAX98396_FORMATS, + }, + .ops = &max98396_dai_ops, + } +}; + +static void max98396_reset(struct max98396_priv *max98396, struct device *dev) +{ + int ret, reg, count; + + /* Software Reset */ + ret = regmap_write(max98396->regmap, + MAX98396_R2000_SW_RESET, 1); + if (ret) + dev_err(dev, "Reset command failed. (ret:%d)\n", ret); + + count = 0; + while (count < 3) { + usleep_range(5000, 6000); + /* Software Reset Verification */ + ret = regmap_read(max98396->regmap, + GET_REG_ADDR_REV_ID(max98396->device_id), ®); + if (!ret) { + dev_info(dev, "Reset completed (retry:%d)\n", count); + return; + } + count++; + } + dev_err(dev, "Reset failed. (ret:%d)\n", ret); +} + +static int max98396_probe(struct snd_soc_component *component) +{ + struct max98396_priv *max98396 = + snd_soc_component_get_drvdata(component); + + /* Software Reset */ + max98396_reset(max98396, component->dev); + + /* L/R mix configuration */ + if (max98396->device_id == CODEC_TYPE_MAX98396) { + regmap_write(max98396->regmap, + MAX98396_R2055_PCM_RX_SRC1, 0x02); + regmap_write(max98396->regmap, + MAX98396_R2056_PCM_RX_SRC2, 0x10); + } else { + regmap_write(max98396->regmap, + MAX98397_R2056_PCM_RX_SRC1, 0x02); + regmap_write(max98396->regmap, + MAX98397_R2057_PCM_RX_SRC2, 0x10); + } + /* Enable DC blocker */ + regmap_update_bits(max98396->regmap, + MAX98396_R2092_AMP_DSP_CFG, 1, 1); + /* Enable IV Monitor DC blocker */ + regmap_update_bits(max98396->regmap, + MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_DCBLK_EN_MASK, + MAX98396_IV_SENSE_DCBLK_EN_MASK); + /* Configure default data output sources */ + regmap_write(max98396->regmap, + MAX98396_R205D_PCM_TX_SRC_EN, 3); + /* Enable Wideband Filter */ + regmap_update_bits(max98396->regmap, + MAX98396_R2092_AMP_DSP_CFG, 0x40, 0x40); + /* Enable IV Wideband Filter */ + regmap_update_bits(max98396->regmap, + MAX98396_R20E0_IV_SENSE_PATH_CFG, 8, 8); + + /* Enable Bypass Source */ + regmap_write(max98396->regmap, + MAX98396_R2058_PCM_BYPASS_SRC, + max98396->bypass_slot); + /* Voltage, current slot configuration */ + regmap_write(max98396->regmap, + MAX98396_R2044_PCM_TX_CTRL_1, + max98396->v_slot); + regmap_write(max98396->regmap, + MAX98396_R2045_PCM_TX_CTRL_2, + max98396->i_slot); + + if (max98396->v_slot < 8) + if (max98396->device_id == CODEC_TYPE_MAX98396) + regmap_update_bits(max98396->regmap, + MAX98396_R2053_PCM_TX_HIZ_CTRL_8, + 1 << max98396->v_slot, 0); + else + regmap_update_bits(max98396->regmap, + MAX98397_R2054_PCM_TX_HIZ_CTRL_8, + 1 << max98396->v_slot, 0); + else + if (max98396->device_id == CODEC_TYPE_MAX98396) + regmap_update_bits(max98396->regmap, + MAX98396_R2052_PCM_TX_HIZ_CTRL_7, + 1 << (max98396->v_slot - 8), 0); + else + regmap_update_bits(max98396->regmap, + MAX98397_R2053_PCM_TX_HIZ_CTRL_7, + 1 << (max98396->v_slot - 8), 0); + + if (max98396->i_slot < 8) + if (max98396->device_id == CODEC_TYPE_MAX98396) + regmap_update_bits(max98396->regmap, + MAX98396_R2053_PCM_TX_HIZ_CTRL_8, + 1 << max98396->i_slot, 0); + else + regmap_update_bits(max98396->regmap, + MAX98397_R2054_PCM_TX_HIZ_CTRL_8, + 1 << max98396->i_slot, 0); + else + if (max98396->device_id == CODEC_TYPE_MAX98396) + regmap_update_bits(max98396->regmap, + MAX98396_R2052_PCM_TX_HIZ_CTRL_7, + 1 << (max98396->i_slot - 8), 0); + else + regmap_update_bits(max98396->regmap, + MAX98397_R2053_PCM_TX_HIZ_CTRL_7, + 1 << (max98396->i_slot - 8), 0); + + /* Set interleave mode */ + if (max98396->interleave_mode) + regmap_update_bits(max98396->regmap, + MAX98396_R2041_PCM_MODE_CFG, + MAX98396_PCM_TX_CH_INTERLEAVE_MASK, + MAX98396_PCM_TX_CH_INTERLEAVE_MASK); + + regmap_update_bits(max98396->regmap, + MAX98396_R2038_CLK_MON_CTRL, + MAX98396_CLK_MON_AUTO_RESTART_MASK, + MAX98396_CLK_MON_AUTO_RESTART_MASK); + + /* Speaker Amplifier PCM RX Enable by default */ + regmap_update_bits(max98396->regmap, + MAX98396_R205E_PCM_RX_EN, + MAX98396_PCM_RX_EN_MASK, 1); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max98396_suspend(struct device *dev) +{ + struct max98396_priv *max98396 = dev_get_drvdata(dev); + + regcache_cache_only(max98396->regmap, true); + regcache_mark_dirty(max98396->regmap); + return 0; +} + +static int max98396_resume(struct device *dev) +{ + struct max98396_priv *max98396 = dev_get_drvdata(dev); + + regcache_cache_only(max98396->regmap, false); + max98396_reset(max98396, dev); + regcache_sync(max98396->regmap); + return 0; +} +#endif + +static const struct dev_pm_ops max98396_pm = { + SET_SYSTEM_SLEEP_PM_OPS(max98396_suspend, max98396_resume) +}; + +static const struct snd_soc_component_driver soc_codec_dev_max98396 = { + .probe = max98396_probe, + .controls = max98396_snd_controls, + .num_controls = ARRAY_SIZE(max98396_snd_controls), + .dapm_widgets = max98396_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98396_dapm_widgets), + .dapm_routes = max98396_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98396_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct snd_soc_component_driver soc_codec_dev_max98397 = { + .probe = max98396_probe, + .controls = max98397_snd_controls, + .num_controls = ARRAY_SIZE(max98397_snd_controls), + .dapm_widgets = max98396_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98396_dapm_widgets), + .dapm_routes = max98396_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98396_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config max98396_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98396_R21FF_REVISION_ID, + .reg_defaults = max98396_reg, + .num_reg_defaults = ARRAY_SIZE(max98396_reg), + .readable_reg = max98396_readable_register, + .volatile_reg = max98396_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct regmap_config max98397_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98397_R22FF_REVISION_ID, + .reg_defaults = max98397_reg, + .num_reg_defaults = ARRAY_SIZE(max98397_reg), + .readable_reg = max98397_readable_register, + .volatile_reg = max98397_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static void max98396_read_device_property(struct device *dev, + struct max98396_priv *max98396) +{ + int value; + + if (!device_property_read_u32(dev, "adi,vmon-slot-no", &value)) + max98396->v_slot = value & 0xF; + else + max98396->v_slot = 0; + + if (!device_property_read_u32(dev, "adi,imon-slot-no", &value)) + max98396->i_slot = value & 0xF; + else + max98396->i_slot = 1; + + if (!device_property_read_u32(dev, "adi,bypass-slot-no", &value)) + max98396->bypass_slot = value & 0xF; + else + max98396->bypass_slot = 0; +} + +static int max98396_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max98396_priv *max98396 = NULL; + int ret, reg; + + max98396 = devm_kzalloc(&i2c->dev, sizeof(*max98396), GFP_KERNEL); + + if (!max98396) { + ret = -ENOMEM; + return ret; + } + i2c_set_clientdata(i2c, max98396); + + max98396->device_id = id->driver_data; + + /* regmap initialization */ + if (max98396->device_id == CODEC_TYPE_MAX98396) + max98396->regmap = devm_regmap_init_i2c(i2c, &max98396_regmap); + + else + max98396->regmap = devm_regmap_init_i2c(i2c, &max98397_regmap); + + if (IS_ERR(max98396->regmap)) { + ret = PTR_ERR(max98396->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + return ret; + } + + /* update interleave mode info */ + if (device_property_read_bool(&i2c->dev, "adi,interleave_mode")) + max98396->interleave_mode = true; + else + max98396->interleave_mode = false; + + /* voltage/current slot & gpio configuration */ + max98396_read_device_property(&i2c->dev, max98396); + + /* Reset the Device */ + max98396->reset_gpio = devm_gpiod_get_optional(&i2c->dev, + "reset", GPIOD_OUT_HIGH); + if (IS_ERR(max98396->reset_gpio)) { + ret = PTR_ERR(max98396->reset_gpio); + dev_err(&i2c->dev, "Unable to request GPIO pin: %d.\n", ret); + return ret; + } + + if (max98396->reset_gpio) { + usleep_range(5000, 6000); + gpiod_set_value_cansleep(max98396->reset_gpio, 0); + /* Wait for the hw reset done */ + usleep_range(5000, 6000); + } + + ret = regmap_read(max98396->regmap, + GET_REG_ADDR_REV_ID(max98396->device_id), ®); + if (ret < 0) { + dev_err(&i2c->dev, "%s: failed to read revision of the device.\n", id->name); + return ret; + } + dev_info(&i2c->dev, "%s revision ID: 0x%02X\n", id->name, reg); + + /* codec registration */ + if (max98396->device_id == CODEC_TYPE_MAX98396) + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_max98396, + max98396_dai, + ARRAY_SIZE(max98396_dai)); + else + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_max98397, + max98397_dai, + ARRAY_SIZE(max98397_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id max98396_i2c_id[] = { + { "max98396", CODEC_TYPE_MAX98396}, + { "max98397", CODEC_TYPE_MAX98397}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98396_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id max98396_of_match[] = { + { .compatible = "adi,max98396", }, + { .compatible = "adi,max98397", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98396_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id max98396_acpi_match[] = { + { "ADS8396", 0 }, + { "ADS8397", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98396_acpi_match); +#endif + +static struct i2c_driver max98396_i2c_driver = { + .driver = { + .name = "max98396", + .of_match_table = of_match_ptr(max98396_of_match), + .acpi_match_table = ACPI_PTR(max98396_acpi_match), + .pm = &max98396_pm, + }, + .probe = max98396_i2c_probe, + .id_table = max98396_i2c_id, +}; + +module_i2c_driver(max98396_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98396 driver"); +MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98396.h b/sound/soc/codecs/max98396.h new file mode 100644 index 000000000000..694411038597 --- /dev/null +++ b/sound/soc/codecs/max98396.h @@ -0,0 +1,305 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * max98396.h -- MAX98396 ALSA SoC audio driver header + * + * Copyright(c) 2022, Analog Devices Inc. + */ + +#ifndef _MAX98396_H +#define _MAX98396_H + +#define MAX98396_R2000_SW_RESET 0x2000 +#define MAX98396_R2001_INT_RAW1 0x2001 +#define MAX98396_R2002_INT_RAW2 0x2002 +#define MAX98396_R2003_INT_RAW3 0x2003 +#define MAX98396_R2004_INT_RAW4 0x2004 +#define MAX98396_R2006_INT_STATE1 0x2006 +#define MAX98396_R2007_INT_STATE2 0x2007 +#define MAX98396_R2008_INT_STATE3 0x2008 +#define MAX98396_R2009_INT_STATE4 0x2009 +#define MAX98396_R200B_INT_FLAG1 0x200B +#define MAX98396_R200C_INT_FLAG2 0x200C +#define MAX98396_R200D_INT_FLAG3 0x200D +#define MAX98396_R200E_INT_FLAG4 0x200E +#define MAX98396_R2010_INT_EN1 0x2010 +#define MAX98396_R2011_INT_EN2 0x2011 +#define MAX98396_R2012_INT_EN3 0x2012 +#define MAX98396_R2013_INT_EN4 0x2013 +#define MAX98396_R2015_INT_FLAG_CLR1 0x2015 +#define MAX98396_R2016_INT_FLAG_CLR2 0x2016 +#define MAX98396_R2017_INT_FLAG_CLR3 0x2017 +#define MAX98396_R2018_INT_FLAG_CLR4 0x2018 +#define MAX98396_R201F_IRQ_CTRL 0x201F +#define MAX98396_R2020_THERM_WARN_THRESH 0x2020 +#define MAX98396_R2021_THERM_WARN_THRESH2 0x2021 +#define MAX98396_R2022_THERM_SHDN_THRESH 0x2022 +#define MAX98396_R2023_THERM_HYSTERESIS 0x2023 +#define MAX98396_R2024_THERM_FOLDBACK_SET 0x2024 +#define MAX98396_R2027_THERM_FOLDBACK_EN 0x2027 +#define MAX98396_R2030_NOISEGATE_MODE_CTRL 0x2030 +#define MAX98396_R2033_NOISEGATE_MODE_EN 0x2033 +#define MAX98396_R2038_CLK_MON_CTRL 0x2038 +#define MAX98396_R2039_DATA_MON_CTRL 0x2039 +#define MAX98396_R203F_ENABLE_CTRLS 0x203F +#define MAX98396_R2040_PIN_CFG 0x2040 +#define MAX98396_R2041_PCM_MODE_CFG 0x2041 +#define MAX98396_R2042_PCM_CLK_SETUP 0x2042 +#define MAX98396_R2043_PCM_SR_SETUP 0x2043 +#define MAX98396_R2044_PCM_TX_CTRL_1 0x2044 +#define MAX98396_R2045_PCM_TX_CTRL_2 0x2045 +#define MAX98396_R2046_PCM_TX_CTRL_3 0x2046 +#define MAX98396_R2047_PCM_TX_CTRL_4 0x2047 +#define MAX98396_R2048_PCM_TX_CTRL_5 0x2048 +#define MAX98396_R2049_PCM_TX_CTRL_6 0x2049 +#define MAX98396_R204A_PCM_TX_CTRL_7 0x204A +#define MAX98396_R204B_PCM_TX_CTRL_8 0x204B +#define MAX98396_R204C_PCM_TX_HIZ_CTRL_1 0x204C +#define MAX98396_R204D_PCM_TX_HIZ_CTRL_2 0x204D +#define MAX98396_R204E_PCM_TX_HIZ_CTRL_3 0x204E +#define MAX98396_R204F_PCM_TX_HIZ_CTRL_4 0x204F +#define MAX98396_R2050_PCM_TX_HIZ_CTRL_5 0x2050 +#define MAX98396_R2051_PCM_TX_HIZ_CTRL_6 0x2051 +#define MAX98396_R2052_PCM_TX_HIZ_CTRL_7 0x2052 +#define MAX98396_R2053_PCM_TX_HIZ_CTRL_8 0x2053 +#define MAX98396_R2055_PCM_RX_SRC1 0x2055 +#define MAX98396_R2056_PCM_RX_SRC2 0x2056 +#define MAX98396_R2058_PCM_BYPASS_SRC 0x2058 +#define MAX98396_R205D_PCM_TX_SRC_EN 0x205D +#define MAX98396_R205E_PCM_RX_EN 0x205E +#define MAX98396_R205F_PCM_TX_EN 0x205F +#define MAX98396_R2070_ICC_RX_EN_A 0x2070 +#define MAX98396_R2071_ICC_RX_EN_B 0x2071 +#define MAX98396_R2072_ICC_TX_CTRL 0x2072 +#define MAX98396_R207F_ICC_EN 0x207F +#define MAX98396_R2083_TONE_GEN_DC_CFG 0x2083 +#define MAX98396_R2084_TONE_GEN_DC_LVL1 0x2084 +#define MAX98396_R2085_TONE_GEN_DC_LVL2 0x2085 +#define MAX98396_R2086_TONE_GEN_DC_LVL3 0x2086 +#define MAX98396_R208F_TONE_GEN_EN 0x208F +#define MAX98396_R2090_AMP_VOL_CTRL 0x2090 +#define MAX98396_R2091_AMP_PATH_GAIN 0x2091 +#define MAX98396_R2092_AMP_DSP_CFG 0x2092 +#define MAX98396_R2093_SSM_CFG 0x2093 +#define MAX98396_R2094_SPK_CLS_DG_THRESH 0x2094 +#define MAX98396_R2095_SPK_CLS_DG_HDR 0x2095 +#define MAX98396_R2096_SPK_CLS_DG_HOLD_TIME 0x2096 +#define MAX98396_R2097_SPK_CLS_DG_DELAY 0x2097 +#define MAX98396_R2098_SPK_CLS_DG_MODE 0x2098 +#define MAX98396_R2099_SPK_CLS_DG_VBAT_LVL 0x2099 +#define MAX98396_R209A_SPK_EDGE_CTRL 0x209A +#define MAX98396_R209C_SPK_EDGE_CTRL1 0x209C +#define MAX98396_R209D_SPK_EDGE_CTRL2 0x209D +#define MAX98396_R209E_AMP_CLIP_GAIN 0x209E +#define MAX98396_R209F_BYPASS_PATH_CFG 0x209F +#define MAX98396_R20A0_AMP_SUPPLY_CTL 0x20A0 +#define MAX98396_R20AF_AMP_EN 0x20AF +#define MAX98396_R20B0_ADC_SR 0x20B0 +#define MAX98396_R20B1_ADC_PVDD_CFG 0x20B1 +#define MAX98396_R20B2_ADC_VBAT_CFG 0x20B2 +#define MAX98396_R20B3_ADC_THERMAL_CFG 0x20B3 +#define MAX98396_R20B4_ADC_READBACK_CTRL1 0x20B4 +#define MAX98396_R20B5_ADC_READBACK_CTRL2 0x20B5 +#define MAX98396_R20B6_ADC_PVDD_READBACK_MSB 0x20B6 +#define MAX98396_R20B7_ADC_PVDD_READBACK_LSB 0x20B7 +#define MAX98396_R20B8_ADC_VBAT_READBACK_MSB 0x20B8 +#define MAX98396_R20B9_ADC_VBAT_READBACK_LSB 0x20B9 +#define MAX98396_R20BA_ADC_TEMP_READBACK_MSB 0x20BA +#define MAX98396_R20BB_ADC_TEMP_READBACK_LSB 0x20BB +#define MAX98396_R20BC_ADC_LO_PVDD_READBACK_MSB 0x20BC +#define MAX98396_R20BD_ADC_LO_PVDD_READBACK_LSB 0x20BD +#define MAX98396_R20BE_ADC_LO_VBAT_READBACK_MSB 0x20BE +#define MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB 0x20BF +#define MAX98396_R20C7_ADC_CFG 0x20C7 +#define MAX98396_R20D0_DHT_CFG1 0x20D0 +#define MAX98396_R20D1_LIMITER_CFG1 0x20D1 +#define MAX98396_R20D2_LIMITER_CFG2 0x20D2 +#define MAX98396_R20D3_DHT_CFG2 0x20D3 +#define MAX98396_R20D4_DHT_CFG3 0x20D4 +#define MAX98396_R20D5_DHT_CFG4 0x20D5 +#define MAX98396_R20D6_DHT_HYSTERESIS_CFG 0x20D6 +#define MAX98396_R20DF_DHT_EN 0x20DF +#define MAX98396_R20E0_IV_SENSE_PATH_CFG 0x20E0 +#define MAX98396_R20E4_IV_SENSE_PATH_EN 0x20E4 +#define MAX98396_R20E5_BPE_STATE 0x20E5 +#define MAX98396_R20E6_BPE_L3_THRESH_MSB 0x20E6 +#define MAX98396_R20E7_BPE_L3_THRESH_LSB 0x20E7 +#define MAX98396_R20E8_BPE_L2_THRESH_MSB 0x20E8 +#define MAX98396_R20E9_BPE_L2_THRESH_LSB 0x20E9 +#define MAX98396_R20EA_BPE_L1_THRESH_MSB 0x20EA +#define MAX98396_R20EB_BPE_L1_THRESH_LSB 0x20EB +#define MAX98396_R20EC_BPE_L0_THRESH_MSB 0x20EC +#define MAX98396_R20ED_BPE_L0_THRESH_LSB 0x20ED +#define MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME 0x20EE +#define MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME 0x20EF +#define MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME 0x20F0 +#define MAX98396_R20F1_BPE_L0_HOLD_TIME 0x20F1 +#define MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP 0x20F2 +#define MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP 0x20F3 +#define MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP 0x20F4 +#define MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP 0x20F5 +#define MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN 0x20F6 +#define MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN 0x20F7 +#define MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN 0x20F8 +#define MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN 0x20F9 +#define MAX98396_R20FA_BPE_L3_ATT_REL_RATE 0x20FA +#define MAX98396_R20FB_BPE_L2_ATT_REL_RATE 0x20FB +#define MAX98396_R20FC_BPE_L1_ATT_REL_RATE 0x20FC +#define MAX98396_R20FD_BPE_L0_ATT_REL_RATE 0x20FD +#define MAX98396_R20FE_BPE_L3_LIMITER_CFG 0x20FE +#define MAX98396_R20FF_BPE_L2_LIMITER_CFG 0x20FF +#define MAX98396_R2100_BPE_L1_LIMITER_CFG 0x2100 +#define MAX98396_R2101_BPE_L0_LIMITER_CFG 0x2101 +#define MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE 0x2102 +#define MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE 0x2103 +#define MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE 0x2104 +#define MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE 0x2105 +#define MAX98396_R2106_BPE_THRESH_HYSTERESIS 0x2106 +#define MAX98396_R2107_BPE_INFINITE_HOLD_CLR 0x2107 +#define MAX98396_R2108_BPE_SUPPLY_SRC 0x2108 +#define MAX98396_R2109_BPE_LOW_STATE 0x2109 +#define MAX98396_R210A_BPE_LOW_GAIN 0x210A +#define MAX98396_R210B_BPE_LOW_LIMITER 0x210B +#define MAX98396_R210D_BPE_EN 0x210D +#define MAX98396_R210E_AUTO_RESTART 0x210E +#define MAX98396_R210F_GLOBAL_EN 0x210F +#define MAX98396_R21FF_REVISION_ID 0x21FF + +/* MAX98927 Registers */ +#define MAX98397_R203A_SPK_MON_THRESH 0x203A +#define MAX98397_R204C_PCM_TX_CTRL_9 0x204C +#define MAX98397_R204D_PCM_TX_HIZ_CTRL_1 0x204D +#define MAX98397_R204E_PCM_TX_HIZ_CTRL_2 0x204E +#define MAX98397_R204F_PCM_TX_HIZ_CTRL_3 0x204F +#define MAX98397_R2050_PCM_TX_HIZ_CTRL_4 0x2050 +#define MAX98397_R2051_PCM_TX_HIZ_CTRL_5 0x2051 +#define MAX98397_R2052_PCM_TX_HIZ_CTRL_6 0x2052 +#define MAX98397_R2053_PCM_TX_HIZ_CTRL_7 0x2053 +#define MAX98397_R2054_PCM_TX_HIZ_CTRL_8 0x2054 +#define MAX98397_R2056_PCM_RX_SRC1 0x2056 +#define MAX98397_R2057_PCM_RX_SRC2 0x2057 +#define MAX98397_R2060_PCM_TX_SUPPLY_SEL 0x2060 +#define MAX98397_R209B_SPK_PATH_WB_ONLY 0x209B +#define MAX98397_R20B4_ADC_VDDH_CFG 0x20B4 +#define MAX98397_R20B5_ADC_READBACK_CTRL1 0x20B5 +#define MAX98397_R20B6_ADC_READBACK_CTRL2 0x20B6 +#define MAX98397_R20B7_ADC_PVDD_READBACK_MSB 0x20B7 +#define MAX98397_R20B8_ADC_PVDD_READBACK_LSB 0x20B8 +#define MAX98397_R20B9_ADC_VBAT_READBACK_MSB 0x20B9 +#define MAX98397_R20BA_ADC_VBAT_READBACK_LSB 0x20BA +#define MAX98397_R20BB_ADC_TEMP_READBACK_MSB 0x20BB +#define MAX98397_R20BC_ADC_TEMP_READBACK_LSB 0x20BC +#define MAX98397_R20BD_ADC_VDDH__READBACK_MSB 0x20BD +#define MAX98397_R20BE_ADC_VDDH_READBACK_LSB 0x20BE +#define MAX98397_R20BF_ADC_LO_PVDD_READBACK_MSB 0x20BF +#define MAX98397_R20C0_ADC_LO_PVDD_READBACK_LSB 0x20C0 +#define MAX98397_R20C1_ADC_LO_VBAT_READBACK_MSB 0x20C1 +#define MAX98397_R20C2_ADC_LO_VBAT_READBACK_LSB 0x20C2 +#define MAX98397_R20C3_ADC_LO_VDDH_READBACK_MSB 0x20C3 +#define MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB 0x20C4 +#define MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE 0x20C5 +#define MAX98397_R22FF_REVISION_ID 0x22FF + +#define GET_REG_ADDR_REV_ID(x)\ + ((x) > 0 ? MAX98397_R22FF_REVISION_ID : MAX98396_R21FF_REVISION_ID) + +/* MAX98396_R2024_THERM_FOLDBACK_SET */ +#define MAX98396_THERM_FB_SLOPE1_SHIFT (0) +#define MAX98396_THERM_FB_SLOPE2_SHIFT (2) +#define MAX98396_THERM_FB_REL_SHIFT (4) +#define MAX98396_THERM_FB_HOLD_SHIFT (6) + +/* MAX98396_R2038_CLK_MON_CTRL */ +#define MAX98396_CLK_MON_AUTO_RESTART_MASK (0x1 << 0) +#define MAX98396_CLK_MON_AUTO_RESTART_SHIFT (0) + +/* MAX98396_R203F_ENABLE_CTRLS */ +#define MAX98396_CTRL_CMON_EN_SHIFT (0) + +/* MAX98396_R2041_PCM_MODE_CFG */ +#define MAX98396_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3) +#define MAX98396_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2) +#define MAX98396_PCM_FORMAT_I2S (0x0 << 3) +#define MAX98396_PCM_FORMAT_LJ (0x1 << 3) +#define MAX98396_PCM_FORMAT_TDM_MODE0 (0x3 << 3) +#define MAX98396_PCM_FORMAT_TDM_MODE1 (0x4 << 3) +#define MAX98396_PCM_FORMAT_TDM_MODE2 (0x5 << 3) +#define MAX98396_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) +#define MAX98396_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) +#define MAX98396_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) +#define MAX98396_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6) +#define MAX98396_PCM_MODE_CFG_LRCLKEDGE (0x1 << 1) + +/* MAX98396_R2042_PCM_CLK_SETUP */ +#define MAX98396_PCM_MODE_CFG_BCLKEDGE (0x1 << 4) +#define MAX98396_PCM_CLK_SETUP_BSEL_MASK (0xF << 0) +#define MAX98396_PCM_BCLKEDGE_BSEL_MASK (0x1F) + +/* MAX98396_R2043_PCM_SR_SETUP */ +#define MAX98396_PCM_SR_SHIFT (0) +#define MAX98396_IVADC_SR_SHIFT (4) +#define MAX98396_PCM_SR_MASK (0xF << MAX98396_PCM_SR_SHIFT) +#define MAX98396_IVADC_SR_MASK (0xF << MAX98396_IVADC_SR_SHIFT) +#define MAX98396_PCM_SR_8000 (0) +#define MAX98396_PCM_SR_11025 (1) +#define MAX98396_PCM_SR_12000 (2) +#define MAX98396_PCM_SR_16000 (3) +#define MAX98396_PCM_SR_22050 (4) +#define MAX98396_PCM_SR_24000 (5) +#define MAX98396_PCM_SR_32000 (6) +#define MAX98396_PCM_SR_44100 (7) +#define MAX98396_PCM_SR_48000 (8) +#define MAX98396_PCM_SR_88200 (9) +#define MAX98396_PCM_SR_96000 (10) +#define MAX98396_PCM_SR_176400 (11) +#define MAX98396_PCM_SR_192000 (12) + +/* MAX98396_R2055_PCM_RX_SRC1 */ +#define MAX98396_PCM_RX_MASK (0x3 << 0) + +/* MAX98396_R2056_PCM_RX_SRC2 */ +#define MAX98396_PCM_DMIX_CH1_SHIFT (0xF << 0) +#define MAX98396_PCM_DMIX_CH0_SRC_MASK (0xF << 0) +#define MAX98396_PCM_DMIX_CH1_SRC_MASK (0xF << MAX98396_PCM_DMIX_CH1_SHIFT) + +/* MAX98396_R205E_PCM_RX_EN */ +#define MAX98396_PCM_RX_EN_MASK (0x1 << 0) +#define MAX98396_PCM_RX_BYP_EN_MASK (0x1 << 1) + +/* MAX98396_R2092_AMP_DSP_CFG */ +#define MAX98396_DSP_SPK_DCBLK_EN_SHIFT (0) +#define MAX98396_DSP_SPK_DITH_EN_SHIFT (1) +#define MAX98396_DSP_SPK_INVERT_SHIFT (2) +#define MAX98396_DSP_SPK_VOL_RMPUP_SHIFT (3) +#define MAX98396_DSP_SPK_VOL_RMPDN_SHIFT (4) +#define MAX98396_DSP_SPK_SAFE_EN_SHIFT (5) +#define MAX98396_DSP_SPK_WB_FLT_EN_SHIFT (6) + +/* MAX98396_R20E0_IV_SENSE_PATH_CFG */ +#define MAX98396_IV_SENSE_DCBLK_EN_MASK (0x3 << 0) +#define MAX98396_IV_SENSE_DCBLK_EN_SHIFT (0) +#define MAX98396_IV_SENSE_DITH_EN_SHIFT (2) +#define MAX98396_IV_SENSE_WB_FLT_EN_SHIFT (3) + +/* MAX98396_R210E_AUTO_RESTART_BEHAVIOR */ +#define MAX98396_PVDD_UVLO_RESTART_SHFT (0) +#define MAX98396_VBAT_UVLO_RESTART_SHFT (1) +#define MAX98396_THEM_SHDN_RESTART_SHFT (2) +#define MAX98396_OVC_RESTART_SHFT (3) + +enum { + CODEC_TYPE_MAX98396, + CODEC_TYPE_MAX98397, +}; + +struct max98396_priv { + struct regmap *regmap; + struct gpio_desc *reset_gpio; + unsigned int v_slot; + unsigned int i_slot; + unsigned int bypass_slot; + bool interleave_mode; + unsigned int ch_size; + bool tdm_mode; + int device_id; +}; +#endif diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index e073f0e029be..9ca6fc254883 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -299,8 +299,7 @@ static const struct snd_soc_component_driver soc_component_dev_max9850 = { .non_legacy_dai_naming = 1, }; -static int max9850_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max9850_i2c_probe(struct i2c_client *i2c) { struct max9850_priv *max9850; int ret; @@ -331,7 +330,7 @@ static struct i2c_driver max9850_i2c_driver = { .driver = { .name = "max9850", }, - .probe = max9850_i2c_probe, + .probe_new = max9850_i2c_probe, .id_table = max9850_i2c_id, }; diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c index a5aa124c4a2e..0daa2a3dd621 100644 --- a/sound/soc/codecs/max98504.c +++ b/sound/soc/codecs/max98504.c @@ -291,6 +291,7 @@ static const struct snd_soc_component_driver max98504_component_driver = { .num_dapm_widgets = ARRAY_SIZE(max98504_dapm_widgets), .dapm_routes = max98504_dapm_routes, .num_dapm_routes = ARRAY_SIZE(max98504_dapm_routes), + .endianness = 1, }; static const struct regmap_config max98504_regmap = { @@ -304,8 +305,7 @@ static const struct regmap_config max98504_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int max98504_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max98504_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device_node *node = dev->of_node; @@ -371,7 +371,7 @@ static struct i2c_driver max98504_i2c_driver = { .name = "max98504", .of_match_table = of_match_ptr(max98504_of_match), }, - .probe = max98504_i2c_probe, + .probe_new = max98504_i2c_probe, .id_table = max98504_i2c_id, }; module_i2c_driver(max98504_i2c_driver); diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c index bb8649cd421c..f0f085ecab55 100644 --- a/sound/soc/codecs/max98520.c +++ b/sound/soc/codecs/max98520.c @@ -677,7 +677,7 @@ static void max98520_power_on(struct max98520_priv *max98520, bool poweron) gpiod_set_value_cansleep(max98520->reset_gpio, !poweron); } -static int max98520_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +static int max98520_i2c_probe(struct i2c_client *i2c) { int ret; int reg = 0; @@ -757,7 +757,7 @@ static struct i2c_driver max98520_i2c_driver = { .of_match_table = of_match_ptr(max98520_of_match), .pm = &max98520_pm, }, - .probe = max98520_i2c_probe, + .probe_new = max98520_i2c_probe, .id_table = max98520_i2c_id, }; diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index c2b1151c75cc..eb628b7e84f5 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -613,8 +613,7 @@ static const struct regmap_config max9867_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int max9867_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max9867_i2c_probe(struct i2c_client *i2c) { struct max9867_priv *max9867; int ret, reg; @@ -662,7 +661,7 @@ static struct i2c_driver max9867_i2c_driver = { .name = "max9867", .of_match_table = of_match_ptr(max9867_of_match), }, - .probe = max9867_i2c_probe, + .probe_new = max9867_i2c_probe, .id_table = max9867_i2c_id, }; diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c index 71fede9224c4..3bf86328d5cd 100644 --- a/sound/soc/codecs/max9877.c +++ b/sound/soc/codecs/max9877.c @@ -133,8 +133,7 @@ static const struct regmap_config max9877_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int max9877_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max9877_i2c_probe(struct i2c_client *client) { struct regmap *regmap; int i; @@ -161,7 +160,7 @@ static struct i2c_driver max9877_i2c_driver = { .driver = { .name = "max9877", }, - .probe = max9877_i2c_probe, + .probe_new = max9877_i2c_probe, .id_table = max9877_i2c_id, }; diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c index f34fa274ae4f..63849ebcfd35 100644 --- a/sound/soc/codecs/max98925.c +++ b/sound/soc/codecs/max98925.c @@ -558,8 +558,7 @@ static const struct regmap_config max98925_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int max98925_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max98925_i2c_probe(struct i2c_client *i2c) { int ret, reg; u32 value; @@ -637,7 +636,7 @@ static struct i2c_driver max98925_i2c_driver = { .name = "max98925", .of_match_table = of_match_ptr(max98925_of_match), }, - .probe = max98925_i2c_probe, + .probe_new = max98925_i2c_probe, .id_table = max98925_i2c_id, }; diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index 1fbbc62bb0a2..56e0a87c7112 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -510,8 +510,7 @@ static const struct regmap_config max98926_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int max98926_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max98926_i2c_probe(struct i2c_client *i2c) { int ret, reg; u32 value; @@ -584,7 +583,7 @@ static struct i2c_driver max98926_i2c_driver = { .name = "max98926", .of_match_table = of_match_ptr(max98926_of_match), }, - .probe = max98926_i2c_probe, + .probe_new = max98926_i2c_probe, .id_table = max98926_i2c_id, }; diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index bf78d3c98514..b7cff76d7b5b 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -863,8 +863,7 @@ static void max98927_slot_config(struct i2c_client *i2c, max98927->i_l_slot = 1; } -static int max98927_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max98927_i2c_probe(struct i2c_client *i2c) { int ret = 0, value; @@ -977,7 +976,7 @@ static struct i2c_driver max98927_i2c_driver = { .acpi_match_table = ACPI_PTR(max98927_acpi_match), .pm = &max98927_pm, }, - .probe = max98927_i2c_probe, + .probe_new = max98927_i2c_probe, .remove = max98927_i2c_remove, .id_table = max98927_i2c_id, }; diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 0823527e4a75..de8fcbdd85be 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -550,8 +550,7 @@ static const struct regmap_config ml26124_i2c_regmap = { .write_flag_mask = 0x01, }; -static int ml26124_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ml26124_i2c_probe(struct i2c_client *i2c) { struct ml26124_priv *priv; int ret; @@ -583,7 +582,7 @@ static struct i2c_driver ml26124_i2c_driver = { .driver = { .name = "ml26124", }, - .probe = ml26124_i2c_probe, + .probe_new = ml26124_i2c_probe, .id_table = ml26124_i2c_id, }; diff --git a/sound/soc/codecs/mt6351.c b/sound/soc/codecs/mt6351.c index 5c0536eb1044..d2cf4847eead 100644 --- a/sound/soc/codecs/mt6351.c +++ b/sound/soc/codecs/mt6351.c @@ -282,12 +282,9 @@ static const struct snd_soc_dai_ops mt6351_codec_dai_ops = { .hw_params = mt6351_codec_dai_hw_params, }; -#define MT6351_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ - SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |\ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ - SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |\ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\ - SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE) +#define MT6351_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) static struct snd_soc_dai_driver mt6351_dai_driver[] = { { @@ -1448,6 +1445,7 @@ static const struct snd_soc_component_driver mt6351_soc_component_driver = { .num_dapm_widgets = ARRAY_SIZE(mt6351_dapm_widgets), .dapm_routes = mt6351_dapm_routes, .num_dapm_routes = ARRAY_SIZE(mt6351_dapm_routes), + .endianness = 1, }; static int mt6351_codec_driver_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c index 4c7b5d940799..60b209efe52d 100644 --- a/sound/soc/codecs/mt6358.c +++ b/sound/soc/codecs/mt6358.c @@ -2340,12 +2340,9 @@ static const struct snd_soc_dai_ops mt6358_codec_dai_ops = { .hw_params = mt6358_codec_dai_hw_params, }; -#define MT6358_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ - SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |\ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ - SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |\ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\ - SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE) +#define MT6358_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) static struct snd_soc_dai_driver mt6358_dai_driver[] = { { @@ -2433,6 +2430,7 @@ static const struct snd_soc_component_driver mt6358_soc_component_driver = { .num_dapm_widgets = ARRAY_SIZE(mt6358_dapm_widgets), .dapm_routes = mt6358_dapm_routes, .num_dapm_routes = ARRAY_SIZE(mt6358_dapm_routes), + .endianness = 1, }; static void mt6358_parse_dt(struct mt6358_priv *priv) diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c index f8532aa7e4aa..23709b180409 100644 --- a/sound/soc/codecs/mt6359.c +++ b/sound/soc/codecs/mt6359.c @@ -2576,12 +2576,9 @@ static const struct snd_soc_dai_ops mt6359_codec_dai_ops = { .shutdown = mt6359_codec_dai_shutdown, }; -#define MT6359_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ - SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |\ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ - SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |\ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\ - SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE) +#define MT6359_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) static struct snd_soc_dai_driver mt6359_dai_driver[] = { { @@ -2739,6 +2736,7 @@ static const struct snd_soc_component_driver mt6359_soc_component_driver = { .num_dapm_widgets = ARRAY_SIZE(mt6359_dapm_widgets), .dapm_routes = mt6359_dapm_routes, .num_dapm_routes = ARRAY_SIZE(mt6359_dapm_routes), + .endianness = 1, }; static int mt6359_parse_dt(struct mt6359_priv *priv) diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c index 3a881523c30f..ba11555796ad 100644 --- a/sound/soc/codecs/mt6660.c +++ b/sound/soc/codecs/mt6660.c @@ -323,6 +323,7 @@ static const struct snd_soc_component_driver mt6660_component_driver = { .num_dapm_routes = ARRAY_SIZE(mt6660_component_dapm_routes), .idle_bias_on = false, /* idle_bias_off = true */ + .endianness = 1, }; static int mt6660_component_aif_hw_params(struct snd_pcm_substream *substream, @@ -456,8 +457,7 @@ static int _mt6660_read_chip_revision(struct mt6660_chip *chip) return 0; } -static int mt6660_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mt6660_i2c_probe(struct i2c_client *client) { struct mt6660_chip *chip = NULL; int ret; @@ -567,7 +567,7 @@ static struct i2c_driver mt6660_i2c_driver = { .of_match_table = of_match_ptr(mt6660_of_id), .pm = &mt6660_dev_pm_ops, }, - .probe = mt6660_i2c_probe, + .probe_new = mt6660_i2c_probe, .remove = mt6660_i2c_remove, .id_table = mt6660_i2c_id, }; diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index ace96995fedc..347c715e22a4 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -823,8 +823,7 @@ static const struct regmap_config nau8540_regmap_config = { .num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults), }; -static int nau8540_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int nau8540_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct nau8540 *nau8540 = dev_get_platdata(dev); @@ -874,7 +873,7 @@ static struct i2c_driver nau8540_i2c_driver = { .name = "nau8540", .of_match_table = of_match_ptr(nau8540_of_ids), }, - .probe = nau8540_i2c_probe, + .probe_new = nau8540_i2c_probe, .id_table = nau8540_i2c_ids, }; module_i2c_driver(nau8540_i2c_driver); diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c index 13676b544f58..7b3b1e4ac246 100644 --- a/sound/soc/codecs/nau8810.c +++ b/sound/soc/codecs/nau8810.c @@ -869,8 +869,7 @@ static const struct snd_soc_component_driver nau8810_component_driver = { .non_legacy_dai_naming = 1, }; -static int nau8810_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int nau8810_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct nau8810 *nau8810 = dev_get_platdata(dev); @@ -916,7 +915,7 @@ static struct i2c_driver nau8810_i2c_driver = { .name = "nau8810", .of_match_table = of_match_ptr(nau8810_of_match), }, - .probe = nau8810_i2c_probe, + .probe_new = nau8810_i2c_probe, .id_table = nau8810_i2c_id, }; diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index d67dc27890a9..ce4e7f46bb06 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -1626,8 +1626,7 @@ static int nau8821_setup_irq(struct nau8821 *nau8821) return 0; } -static int nau8821_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int nau8821_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct nau8821 *nau8821 = dev_get_platdata(&i2c->dev); @@ -1703,7 +1702,7 @@ static struct i2c_driver nau8821_driver = { .of_match_table = of_match_ptr(nau8821_of_ids), .acpi_match_table = ACPI_PTR(nau8821_acpi_match), }, - .probe = nau8821_i2c_probe, + .probe_new = nau8821_i2c_probe, .remove = nau8821_i2c_remove, .id_table = nau8821_i2c_ids, }; diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index 58123390c7a3..66bbd8f4f1ad 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -1083,8 +1083,7 @@ static const struct regmap_config nau8822_regmap_config = { .num_reg_defaults = ARRAY_SIZE(nau8822_reg_defaults), }; -static int nau8822_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int nau8822_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct nau8822 *nau8822 = dev_get_platdata(dev); @@ -1141,7 +1140,7 @@ static struct i2c_driver nau8822_i2c_driver = { .name = "nau8822", .of_match_table = of_match_ptr(nau8822_of_match), }, - .probe = nau8822_i2c_probe, + .probe_new = nau8822_i2c_probe, .id_table = nau8822_i2c_id, }; module_i2c_driver(nau8822_i2c_driver); diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index d0dd1542f78a..2a7c93508535 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -1910,8 +1910,7 @@ const char *nau8824_components(void) } EXPORT_SYMBOL_GPL(nau8824_components); -static int nau8824_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int nau8824_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct nau8824 *nau8824 = dev_get_platdata(dev); @@ -1985,7 +1984,7 @@ static struct i2c_driver nau8824_i2c_driver = { .of_match_table = of_match_ptr(nau8824_of_ids), .acpi_match_table = ACPI_PTR(nau8824_acpi_match), }, - .probe = nau8824_i2c_probe, + .probe_new = nau8824_i2c_probe, .id_table = nau8824_i2c_ids, }; module_i2c_driver(nau8824_i2c_driver); diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 7734bc35ab21..20e45a337b8f 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -2613,8 +2613,7 @@ static int nau8825_setup_irq(struct nau8825 *nau8825) return 0; } -static int nau8825_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int nau8825_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev); @@ -2703,7 +2702,7 @@ static struct i2c_driver nau8825_driver = { .of_match_table = of_match_ptr(nau8825_of_ids), .acpi_match_table = ACPI_PTR(nau8825_acpi_match), }, - .probe = nau8825_i2c_probe, + .probe_new = nau8825_i2c_probe, .remove = nau8825_i2c_remove, .id_table = nau8825_i2c_ids, }; diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index 9eb65f94fc4d..20eb04c8a41a 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -299,8 +299,7 @@ static const struct i2c_device_id pcm1681_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id); -static int pcm1681_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pcm1681_i2c_probe(struct i2c_client *client) { int ret; struct pcm1681_private *priv; @@ -329,7 +328,7 @@ static struct i2c_driver pcm1681_i2c_driver = { .of_match_table = of_match_ptr(pcm1681_dt_ids), }, .id_table = pcm1681_i2c_id, - .probe = pcm1681_i2c_probe, + .probe_new = pcm1681_i2c_probe, }; module_i2c_driver(pcm1681_i2c_driver); diff --git a/sound/soc/codecs/pcm1789-i2c.c b/sound/soc/codecs/pcm1789-i2c.c index 7a6be45f8149..1d2f7480a6e4 100644 --- a/sound/soc/codecs/pcm1789-i2c.c +++ b/sound/soc/codecs/pcm1789-i2c.c @@ -12,8 +12,7 @@ #include "pcm1789.h" -static int pcm1789_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pcm1789_i2c_probe(struct i2c_client *client) { struct regmap *regmap; int ret; @@ -30,7 +29,9 @@ static int pcm1789_i2c_probe(struct i2c_client *client, static int pcm1789_i2c_remove(struct i2c_client *client) { - return pcm1789_common_exit(&client->dev); + pcm1789_common_exit(&client->dev); + + return 0; } #ifdef CONFIG_OF @@ -53,7 +54,7 @@ static struct i2c_driver pcm1789_i2c_driver = { .of_match_table = of_match_ptr(pcm1789_of_match), }, .id_table = pcm1789_i2c_ids, - .probe = pcm1789_i2c_probe, + .probe_new = pcm1789_i2c_probe, .remove = pcm1789_i2c_remove, }; diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c index 620dec172ce7..35788b57e11f 100644 --- a/sound/soc/codecs/pcm1789.c +++ b/sound/soc/codecs/pcm1789.c @@ -259,13 +259,11 @@ int pcm1789_common_init(struct device *dev, struct regmap *regmap) } EXPORT_SYMBOL_GPL(pcm1789_common_init); -int pcm1789_common_exit(struct device *dev) +void pcm1789_common_exit(struct device *dev) { struct pcm1789_private *priv = dev_get_drvdata(dev); flush_work(&priv->work); - - return 0; } EXPORT_SYMBOL_GPL(pcm1789_common_exit); diff --git a/sound/soc/codecs/pcm1789.h b/sound/soc/codecs/pcm1789.h index c446d789ed48..79439c8322b3 100644 --- a/sound/soc/codecs/pcm1789.h +++ b/sound/soc/codecs/pcm1789.h @@ -12,6 +12,6 @@ extern const struct regmap_config pcm1789_regmap_config; int pcm1789_common_init(struct device *dev, struct regmap *regmap); -int pcm1789_common_exit(struct device *dev); +void pcm1789_common_exit(struct device *dev); #endif diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c index 34a3d596f288..e20fe3a85af8 100644 --- a/sound/soc/codecs/pcm179x-i2c.c +++ b/sound/soc/codecs/pcm179x-i2c.c @@ -14,8 +14,7 @@ #include "pcm179x.h" -static int pcm179x_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pcm179x_i2c_probe(struct i2c_client *client) { struct regmap *regmap; int ret; @@ -50,7 +49,7 @@ static struct i2c_driver pcm179x_i2c_driver = { .of_match_table = of_match_ptr(pcm179x_of_match), }, .id_table = pcm179x_i2c_ids, - .probe = pcm179x_i2c_probe, + .probe_new = pcm179x_i2c_probe, }; module_i2c_driver(pcm179x_i2c_driver); diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c index f8382b74391d..932c8d41c3ea 100644 --- a/sound/soc/codecs/pcm186x-i2c.c +++ b/sound/soc/codecs/pcm186x-i2c.c @@ -22,9 +22,18 @@ static const struct of_device_id pcm186x_of_match[] = { }; MODULE_DEVICE_TABLE(of, pcm186x_of_match); -static int pcm186x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static const struct i2c_device_id pcm186x_i2c_id[] = { + { "pcm1862", PCM1862 }, + { "pcm1863", PCM1863 }, + { "pcm1864", PCM1864 }, + { "pcm1865", PCM1865 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id); + +static int pcm186x_i2c_probe(struct i2c_client *i2c) { + const struct i2c_device_id *id = i2c_match_id(pcm186x_i2c_id, i2c); const enum pcm186x_type type = (enum pcm186x_type)id->driver_data; int irq = i2c->irq; struct regmap *regmap; @@ -36,17 +45,8 @@ static int pcm186x_i2c_probe(struct i2c_client *i2c, return pcm186x_probe(&i2c->dev, type, irq, regmap); } -static const struct i2c_device_id pcm186x_i2c_id[] = { - { "pcm1862", PCM1862 }, - { "pcm1863", PCM1863 }, - { "pcm1864", PCM1864 }, - { "pcm1865", PCM1865 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id); - static struct i2c_driver pcm186x_i2c_driver = { - .probe = pcm186x_i2c_probe, + .probe_new = pcm186x_i2c_probe, .id_table = pcm186x_i2c_id, .driver = { .name = "pcm186x", diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c index 2c78dccb3f62..fda9d7ee3fe6 100644 --- a/sound/soc/codecs/pcm186x.c +++ b/sound/soc/codecs/pcm186x.c @@ -534,19 +534,14 @@ static int pcm186x_power_on(struct snd_soc_component *component) static int pcm186x_power_off(struct snd_soc_component *component) { struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component); - int ret; snd_soc_component_update_bits(component, PCM186X_POWER_CTRL, PCM186X_PWR_CTRL_PWRDN, PCM186X_PWR_CTRL_PWRDN); regcache_cache_only(priv->regmap, true); - ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies), + return regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); - if (ret) - return ret; - - return 0; } static int pcm186x_set_bias_level(struct snd_soc_component *component, diff --git a/sound/soc/codecs/pcm3060-i2c.c b/sound/soc/codecs/pcm3060-i2c.c index abcdeb922201..2388098eeca3 100644 --- a/sound/soc/codecs/pcm3060-i2c.c +++ b/sound/soc/codecs/pcm3060-i2c.c @@ -10,8 +10,7 @@ #include "pcm3060.h" -static int pcm3060_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int pcm3060_i2c_probe(struct i2c_client *i2c) { struct pcm3060_priv *priv; @@ -50,7 +49,7 @@ static struct i2c_driver pcm3060_i2c_driver = { #endif /* CONFIG_OF */ }, .id_table = pcm3060_i2c_id, - .probe = pcm3060_i2c_probe, + .probe_new = pcm3060_i2c_probe, }; module_i2c_driver(pcm3060_i2c_driver); diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c index 4e3bfb9fa444..586ec8c7246c 100644 --- a/sound/soc/codecs/pcm3060.c +++ b/sound/soc/codecs/pcm3060.c @@ -255,6 +255,7 @@ static const struct snd_soc_component_driver pcm3060_soc_comp_driver = { .num_dapm_widgets = ARRAY_SIZE(pcm3060_dapm_widgets), .dapm_routes = pcm3060_dapm_map, .num_dapm_routes = ARRAY_SIZE(pcm3060_dapm_map), + .endianness = 1, }; /* regmap */ diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c index 1f75933e74fa..c0fa0dc80e8f 100644 --- a/sound/soc/codecs/pcm3168a-i2c.c +++ b/sound/soc/codecs/pcm3168a-i2c.c @@ -15,8 +15,7 @@ #include "pcm3168a.h" -static int pcm3168a_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int pcm3168a_i2c_probe(struct i2c_client *i2c) { struct regmap *regmap; @@ -47,7 +46,7 @@ static const struct of_device_id pcm3168a_of_match[] = { MODULE_DEVICE_TABLE(of, pcm3168a_of_match); static struct i2c_driver pcm3168a_i2c_driver = { - .probe = pcm3168a_i2c_probe, + .probe_new = pcm3168a_i2c_probe, .remove = pcm3168a_i2c_remove, .id_table = pcm3168a_i2c_id, .driver = { diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 633f7ebe29a3..81754e141a55 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -13,8 +13,7 @@ #include "pcm512x.h" -static int pcm512x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int pcm512x_i2c_probe(struct i2c_client *i2c) { struct regmap *regmap; struct regmap_config config = pcm512x_regmap; @@ -68,7 +67,7 @@ MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match); #endif static struct i2c_driver pcm512x_i2c_driver = { - .probe = pcm512x_i2c_probe, + .probe_new = pcm512x_i2c_probe, .remove = pcm512x_i2c_remove, .id_table = pcm512x_i2c_id, .driver = { diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c index 758d439e8c7a..86b679cf7aef 100644 --- a/sound/soc/codecs/rk3328_codec.c +++ b/sound/soc/codecs/rk3328_codec.c @@ -481,7 +481,7 @@ static int rk3328_platform_probe(struct platform_device *pdev) ret = clk_prepare_enable(rk3328->pclk); if (ret < 0) { dev_err(&pdev->dev, "failed to enable acodec pclk\n"); - return ret; + goto err_unprepare_mclk; } base = devm_platform_ioremap_resource(pdev, 0); diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c index b62301a6281f..08dbaef84d4e 100644 --- a/sound/soc/codecs/rt1011.c +++ b/sound/soc/codecs/rt1011.c @@ -2433,8 +2433,7 @@ static int rt1011_parse_dp(struct rt1011_priv *rt1011, struct device *dev) return 0; } -static int rt1011_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt1011_i2c_probe(struct i2c_client *i2c) { struct rt1011_priv *rt1011; int ret; @@ -2485,7 +2484,7 @@ static struct i2c_driver rt1011_i2c_driver = { .of_match_table = of_match_ptr(rt1011_of_match), .acpi_match_table = ACPI_PTR(rt1011_acpi_match) }, - .probe = rt1011_i2c_probe, + .probe_new = rt1011_i2c_probe, .shutdown = rt1011_i2c_shutdown, .id_table = rt1011_i2c_id, }; diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 6a27dfacd81c..7a06f2654afb 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -1113,8 +1113,7 @@ static void rt1015_parse_dt(struct rt1015_priv *rt1015, struct device *dev) &rt1015->pdata.power_up_delay_ms); } -static int rt1015_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt1015_i2c_probe(struct i2c_client *i2c) { struct rt1015_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt1015_priv *rt1015; @@ -1172,7 +1171,7 @@ static struct i2c_driver rt1015_i2c_driver = { .of_match_table = of_match_ptr(rt1015_of_match), .acpi_match_table = ACPI_PTR(rt1015_acpi_match), }, - .probe = rt1015_i2c_probe, + .probe_new = rt1015_i2c_probe, .shutdown = rt1015_i2c_shutdown, .id_table = rt1015_i2c_id, }; diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c index 9845cdddcb4c..e31c4736627f 100644 --- a/sound/soc/codecs/rt1016.c +++ b/sound/soc/codecs/rt1016.c @@ -631,8 +631,7 @@ static const struct acpi_device_id rt1016_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt1016_acpi_match); #endif -static int rt1016_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt1016_i2c_probe(struct i2c_client *i2c) { struct rt1016_priv *rt1016; int ret; @@ -685,7 +684,7 @@ static struct i2c_driver rt1016_i2c_driver = { .of_match_table = of_match_ptr(rt1016_of_match), .acpi_match_table = ACPI_PTR(rt1016_acpi_match), }, - .probe = rt1016_i2c_probe, + .probe_new = rt1016_i2c_probe, .shutdown = rt1016_i2c_shutdown, .id_table = rt1016_i2c_id, }; diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c index 80b7ca0e4e1e..f3f15fbe85d0 100644 --- a/sound/soc/codecs/rt1019.c +++ b/sound/soc/codecs/rt1019.c @@ -515,7 +515,7 @@ static struct snd_soc_dai_driver rt1019_dai[] = { }; static const struct snd_soc_component_driver soc_component_dev_rt1019 = { - .probe = rt1019_probe, + .probe = rt1019_probe, .controls = rt1019_snd_controls, .num_controls = ARRAY_SIZE(rt1019_snd_controls), .dapm_widgets = rt1019_dapm_widgets, @@ -523,6 +523,7 @@ static const struct snd_soc_component_driver soc_component_dev_rt1019 = { .dapm_routes = rt1019_dapm_routes, .num_dapm_routes = ARRAY_SIZE(rt1019_dapm_routes), .non_legacy_dai_naming = 1, + .endianness = 1, }; static const struct regmap_config rt1019_regmap = { @@ -558,8 +559,7 @@ static const struct acpi_device_id rt1019_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt1019_acpi_match); #endif -static int rt1019_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt1019_i2c_probe(struct i2c_client *i2c) { struct rt1019_priv *rt1019; int ret; @@ -599,7 +599,7 @@ static struct i2c_driver rt1019_i2c_driver = { .of_match_table = of_match_ptr(rt1019_of_match), .acpi_match_table = ACPI_PTR(rt1019_acpi_match), }, - .probe = rt1019_i2c_probe, + .probe_new = rt1019_i2c_probe, .id_table = rt1019_i2c_id, }; module_i2c_driver(rt1019_i2c_driver); diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c index a9c473537a91..58d97e3c5087 100644 --- a/sound/soc/codecs/rt1305.c +++ b/sound/soc/codecs/rt1305.c @@ -1117,8 +1117,7 @@ static void rt1305_calibrate(struct rt1305_priv *rt1305) regcache_cache_bypass(rt1305->regmap, false); } -static int rt1305_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt1305_i2c_probe(struct i2c_client *i2c) { struct rt1305_priv *rt1305; int ret; @@ -1172,7 +1171,7 @@ static struct i2c_driver rt1305_i2c_driver = { .acpi_match_table = ACPI_PTR(rt1305_acpi_match) #endif }, - .probe = rt1305_i2c_probe, + .probe_new = rt1305_i2c_probe, .shutdown = rt1305_i2c_shutdown, .id_table = rt1305_i2c_id, }; diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 1ef836a68a56..1c11b42dd76e 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -615,6 +615,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1308 = { .num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets), .dapm_routes = rt1308_dapm_routes, .num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes), + .endianness = 1, }; static const struct snd_soc_dai_ops rt1308_aif_dai_ops = { diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h index c5ce75666dcc..6668e19d85d4 100644 --- a/sound/soc/codecs/rt1308-sdw.h +++ b/sound/soc/codecs/rt1308-sdw.h @@ -140,6 +140,7 @@ static const struct reg_default rt1308_reg_defaults[] = { { 0x3008, 0x02 }, { 0x300a, 0x00 }, { 0xc003 | (RT1308_DAC_SET << 4), 0x00 }, + { 0xc000 | (RT1308_POWER << 4), 0x00 }, { 0xc001 | (RT1308_POWER << 4), 0x00 }, { 0xc002 | (RT1308_POWER << 4), 0x00 }, }; diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c index c555b77b3c5c..eec2b1760408 100644 --- a/sound/soc/codecs/rt1308.c +++ b/sound/soc/codecs/rt1308.c @@ -814,8 +814,7 @@ static void rt1308_efuse(struct rt1308_priv *rt1308) regmap_write(rt1308->regmap, RT1308_PVDD_OFFSET_CTL, 0x10000000); } -static int rt1308_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt1308_i2c_probe(struct i2c_client *i2c) { struct rt1308_priv *rt1308; int ret; @@ -864,7 +863,7 @@ static struct i2c_driver rt1308_i2c_driver = { .of_match_table = of_match_ptr(rt1308_of_match), .acpi_match_table = ACPI_PTR(rt1308_acpi_match), }, - .probe = rt1308_i2c_probe, + .probe_new = rt1308_i2c_probe, .shutdown = rt1308_i2c_shutdown, .id_table = rt1308_i2c_id, }; diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index c66d7b20cb4d..60baa9ff1907 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -597,6 +597,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1316 = { .num_dapm_widgets = ARRAY_SIZE(rt1316_dapm_widgets), .dapm_routes = rt1316_dapm_routes, .num_dapm_routes = ARRAY_SIZE(rt1316_dapm_routes), + .endianness = 1, }; static const struct snd_soc_dai_ops rt1316_aif_dai_ops = { diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index 0d3773c576f8..ab093bdb5552 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1114,8 +1114,7 @@ static const struct acpi_device_id rt274_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt274_acpi_match); #endif -static int rt274_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt274_i2c_probe(struct i2c_client *i2c) { struct rt274_priv *rt274; @@ -1227,7 +1226,7 @@ static struct i2c_driver rt274_i2c_driver = { .of_match_table = of_match_ptr(rt274_of_match), #endif }, - .probe = rt274_i2c_probe, + .probe_new = rt274_i2c_probe, .remove = rt274_i2c_remove, .id_table = rt274_i2c_id, }; diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index caa720884e3e..ad8ea1fa7c23 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -1134,8 +1134,7 @@ static const struct dmi_system_id dmi_dell[] = { { } }; -static int rt286_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt286_i2c_probe(struct i2c_client *i2c) { struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt286_priv *rt286; @@ -1271,7 +1270,7 @@ static struct i2c_driver rt286_i2c_driver = { .name = "rt286", .acpi_match_table = ACPI_PTR(rt286_acpi_match), }, - .probe = rt286_i2c_probe, + .probe_new = rt286_i2c_probe, .remove = rt286_i2c_remove, .id_table = rt286_i2c_id, }; diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index c592c40a7ab3..c291786dc82d 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1176,8 +1176,7 @@ static const struct dmi_system_id force_combo_jack_table[] = { { } }; -static int rt298_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt298_i2c_probe(struct i2c_client *i2c) { struct rt298_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt298_priv *rt298; @@ -1314,7 +1313,7 @@ static struct i2c_driver rt298_i2c_driver = { .name = "rt298", .acpi_match_table = ACPI_PTR(rt298_acpi_match), }, - .probe = rt298_i2c_probe, + .probe_new = rt298_i2c_probe, .remove = rt298_i2c_remove, .id_table = rt298_i2c_id, }; diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 577680df7052..be8ece4630df 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -419,7 +419,7 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol, } } - return 0; + return 1; } static const struct snd_kcontrol_new rt5514_snd_controls[] = { @@ -1252,8 +1252,7 @@ static __maybe_unused int rt5514_i2c_resume(struct device *dev) return 0; } -static int rt5514_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5514_i2c_probe(struct i2c_client *i2c) { struct rt5514_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5514_priv *rt5514; @@ -1330,7 +1329,7 @@ static struct i2c_driver rt5514_i2c_driver = { .of_match_table = of_match_ptr(rt5514_of_match), .pm = &rt5514_i2_pm_ops, }, - .probe = rt5514_i2c_probe, + .probe_new = rt5514_i2c_probe, .id_table = rt5514_i2c_id, }; module_i2c_driver(rt5514_i2c_driver); diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index 8e6414468a87..37f1bf552eff 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -1337,8 +1337,7 @@ static const struct of_device_id rt5616_of_match[] = { MODULE_DEVICE_TABLE(of, rt5616_of_match); #endif -static int rt5616_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5616_i2c_probe(struct i2c_client *i2c) { struct rt5616_priv *rt5616; unsigned int val; @@ -1408,7 +1407,7 @@ static struct i2c_driver rt5616_i2c_driver = { .name = "rt5616", .of_match_table = of_match_ptr(rt5616_of_match), }, - .probe = rt5616_i2c_probe, + .probe_new = rt5616_i2c_probe, .remove = rt5616_i2c_remove, .shutdown = rt5616_i2c_shutdown, .id_table = rt5616_i2c_id, diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 38356ea2bd6e..c941e878471c 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1699,8 +1699,7 @@ static const struct regmap_config rt5631_regmap_config = { .use_single_write = true, }; -static int rt5631_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5631_i2c_probe(struct i2c_client *i2c) { struct rt5631_priv *rt5631; int ret; @@ -1732,7 +1731,7 @@ static struct i2c_driver rt5631_i2c_driver = { .name = "rt5631", .of_match_table = of_match_ptr(rt5631_i2c_dt_ids), }, - .probe = rt5631_i2c_probe, + .probe_new = rt5631_i2c_probe, .remove = rt5631_i2c_remove, .id_table = rt5631_i2c_id, }; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 30c2e7cb7ed2..12da2bea1a7b 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2927,8 +2927,7 @@ static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np) return 0; } -static int rt5640_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5640_i2c_probe(struct i2c_client *i2c) { struct rt5640_priv *rt5640; int ret; @@ -3006,7 +3005,7 @@ static struct i2c_driver rt5640_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5640_acpi_match), .of_match_table = of_match_ptr(rt5640_of_match), }, - .probe = rt5640_i2c_probe, + .probe_new = rt5640_i2c_probe, .id_table = rt5640_i2c_id, }; module_i2c_driver(rt5640_i2c_driver); diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 197c56047947..507aba8de3cc 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3855,8 +3855,7 @@ static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev) return 0; } -static int rt5645_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5645_i2c_probe(struct i2c_client *i2c) { struct rt5645_platform_data *pdata = NULL; const struct dmi_system_id *dmi_data; @@ -3944,7 +3943,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, ret = PTR_ERR(regmap); dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n", ret); - return ret; + goto err_enable; } /* @@ -3975,7 +3974,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, ret = PTR_ERR(rt5645->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); - return ret; + goto err_enable; } regmap_write(rt5645->regmap, RT5645_RESET, 0); @@ -4154,9 +4153,14 @@ static int rt5645_i2c_remove(struct i2c_client *i2c) if (i2c->irq) free_irq(i2c->irq, rt5645); + /* + * Since the rt5645_btn_check_callback() can queue jack_detect_work, + * the timer need to be delted first + */ + del_timer_sync(&rt5645->btn_check_timer); + cancel_delayed_work_sync(&rt5645->jack_detect_work); cancel_delayed_work_sync(&rt5645->rcclock_work); - del_timer_sync(&rt5645->btn_check_timer); regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies); @@ -4183,7 +4187,7 @@ static struct i2c_driver rt5645_i2c_driver = { .of_match_table = of_match_ptr(rt5645_of_match), .acpi_match_table = ACPI_PTR(rt5645_acpi_match), }, - .probe = rt5645_i2c_probe, + .probe_new = rt5645_i2c_probe, .remove = rt5645_i2c_remove, .shutdown = rt5645_i2c_shutdown, .id_table = rt5645_i2c_id, diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index f302c25688d1..d11d201b1d03 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -2209,8 +2209,7 @@ MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id); * Note this function MUST not look at device-properties, see the comment * above rt5651_apply_properties(). */ -static int rt5651_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5651_i2c_probe(struct i2c_client *i2c) { struct rt5651_priv *rt5651; int ret; @@ -2281,7 +2280,7 @@ static struct i2c_driver rt5651_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5651_acpi_match), .of_match_table = of_match_ptr(rt5651_of_match), }, - .probe = rt5651_i2c_probe, + .probe_new = rt5651_i2c_probe, .id_table = rt5651_i2c_id, }; module_i2c_driver(rt5651_i2c_driver); diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index e1503c2eee81..6efa90f46362 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -4093,8 +4093,7 @@ static void rt5659_intel_hd_header_probe_setup(struct rt5659_priv *rt5659) RT5659_IL_IRQ_MASK, RT5659_IL_IRQ_EN); } -static int rt5659_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5659_i2c_probe(struct i2c_client *i2c) { struct rt5659_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5659_priv *rt5659; @@ -4342,7 +4341,7 @@ static struct i2c_driver rt5659_i2c_driver = { .of_match_table = of_match_ptr(rt5659_of_match), .acpi_match_table = ACPI_PTR(rt5659_acpi_match), }, - .probe = rt5659_i2c_probe, + .probe_new = rt5659_i2c_probe, .shutdown = rt5659_i2c_shutdown, .id_table = rt5659_i2c_id, }; diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 3b50fb29864e..d5f9926625d2 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -1266,8 +1266,7 @@ static int rt5660_parse_dt(struct rt5660_priv *rt5660, struct device *dev) return 0; } -static int rt5660_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5660_i2c_probe(struct i2c_client *i2c) { struct rt5660_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5660_priv *rt5660; @@ -1343,7 +1342,7 @@ static struct i2c_driver rt5660_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5660_acpi_match), .of_match_table = of_match_ptr(rt5660_of_match), }, - .probe = rt5660_i2c_probe, + .probe_new = rt5660_i2c_probe, .id_table = rt5660_i2c_id, }; module_i2c_driver(rt5660_i2c_driver); diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 3a8fba101b20..e51eed8a79ab 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3490,8 +3490,7 @@ static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) return 0; } -static int rt5663_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5663_i2c_probe(struct i2c_client *i2c) { struct rt5663_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5663_priv *rt5663; @@ -3737,7 +3736,7 @@ static struct i2c_driver rt5663_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5663_acpi_match), .of_match_table = of_match_ptr(rt5663_of_match), }, - .probe = rt5663_i2c_probe, + .probe_new = rt5663_i2c_probe, .remove = rt5663_i2c_remove, .shutdown = rt5663_i2c_shutdown, .id_table = rt5663_i2c_id, diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 33e889802ff8..4a8d62e1dd2b 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -4757,8 +4757,7 @@ static void rt5665_calibrate_handler(struct work_struct *work) rt5665_calibrate(rt5665); } -static int rt5665_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5665_i2c_probe(struct i2c_client *i2c) { struct rt5665_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5665_priv *rt5665; @@ -4970,7 +4969,7 @@ static struct i2c_driver rt5665_i2c_driver = { .of_match_table = of_match_ptr(rt5665_of_match), .acpi_match_table = ACPI_PTR(rt5665_acpi_match), }, - .probe = rt5665_i2c_probe, + .probe_new = rt5665_i2c_probe, .shutdown = rt5665_i2c_shutdown, .id_table = rt5665_i2c_id, }; diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index 5b12cbf2ba21..01566f036ca1 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -2453,8 +2453,7 @@ static void rt5668_calibrate(struct rt5668_priv *rt5668) } -static int rt5668_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5668_i2c_probe(struct i2c_client *i2c) { struct rt5668_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5668_priv *rt5668; @@ -2620,7 +2619,7 @@ static struct i2c_driver rt5668_i2c_driver = { .of_match_table = of_match_ptr(rt5668_of_match), .acpi_match_table = ACPI_PTR(rt5668_acpi_match), }, - .probe = rt5668_i2c_probe, + .probe_new = rt5668_i2c_probe, .shutdown = rt5668_i2c_shutdown, .id_table = rt5668_i2c_id, }; diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index ce7684752bb0..8a97f6db04d5 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -3046,8 +3046,7 @@ const char *rt5670_components(void) } EXPORT_SYMBOL_GPL(rt5670_components); -static int rt5670_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5670_i2c_probe(struct i2c_client *i2c) { struct rt5670_priv *rt5670; int ret; @@ -3334,7 +3333,7 @@ static struct i2c_driver rt5670_i2c_driver = { .name = "rt5670", .acpi_match_table = ACPI_PTR(rt5670_acpi_match), }, - .probe = rt5670_i2c_probe, + .probe_new = rt5670_i2c_probe, .remove = rt5670_i2c_remove, .id_table = rt5670_i2c_id, }; diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index 20fc0f3766de..3f72f6093436 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -118,8 +118,7 @@ static void rt5682_i2c_disable_regulators(void *data) regulator_bulk_disable(ARRAY_SIZE(rt5682->supplies), rt5682->supplies); } -static int rt5682_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5682_i2c_probe(struct i2c_client *i2c) { struct rt5682_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5682_priv *rt5682; @@ -335,7 +334,7 @@ static struct i2c_driver rt5682_i2c_driver = { .acpi_match_table = rt5682_acpi_match, .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, - .probe = rt5682_i2c_probe, + .probe_new = rt5682_i2c_probe, .remove = rt5682_i2c_remove, .shutdown = rt5682_i2c_shutdown, .id_table = rt5682_i2c_id, diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index b55f3ac3a267..4d44eddee901 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -42,8 +42,8 @@ static const struct rt5682s_platform_data i2s_default_platform_data = { }; static const char *rt5682s_supply_names[RT5682S_NUM_SUPPLIES] = { - "AVDD", - "MICVDD", + [RT5682S_SUPPLY_AVDD] = "AVDD", + [RT5682S_SUPPLY_MICVDD] = "MICVDD", }; static const struct reg_sequence patch_list[] = { @@ -3022,12 +3022,21 @@ static struct snd_soc_dai_driver rt5682s_dai[] = { static void rt5682s_i2c_disable_regulators(void *data) { struct rt5682s_priv *rt5682s = data; + struct device *dev = regmap_get_device(rt5682s->regmap); + int ret; + + ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_AVDD].consumer); + if (ret) + dev_err(dev, "Failed to disable supply AVDD: %d\n", ret); - regulator_bulk_disable(ARRAY_SIZE(rt5682s->supplies), rt5682s->supplies); + usleep_range(1000, 1500); + + ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_MICVDD].consumer); + if (ret) + dev_err(dev, "Failed to disable supply MICVDD: %d\n", ret); } -static int rt5682s_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5682s_i2c_probe(struct i2c_client *i2c) { struct rt5682s_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5682s_priv *rt5682s; @@ -3068,9 +3077,16 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c, if (ret) return ret; - ret = regulator_bulk_enable(ARRAY_SIZE(rt5682s->supplies), rt5682s->supplies); + ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_MICVDD].consumer); + if (ret) { + dev_err(&i2c->dev, "Failed to enable supply MICVDD: %d\n", ret); + return ret; + } + usleep_range(1000, 1500); + + ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_AVDD].consumer); if (ret) { - dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + dev_err(&i2c->dev, "Failed to enable supply AVDD: %d\n", ret); return ret; } @@ -3211,7 +3227,7 @@ static struct i2c_driver rt5682s_i2c_driver = { .acpi_match_table = rt5682s_acpi_match, .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, - .probe = rt5682s_i2c_probe, + .probe_new = rt5682s_i2c_probe, .remove = rt5682s_i2c_remove, .shutdown = rt5682s_i2c_shutdown, .id_table = rt5682s_i2c_id, diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h index 397a2531b6f6..7353831c73dd 100644 --- a/sound/soc/codecs/rt5682s.h +++ b/sound/soc/codecs/rt5682s.h @@ -1434,7 +1434,11 @@ struct pll_calc_map { bool sel_ps; }; -#define RT5682S_NUM_SUPPLIES 2 +enum { + RT5682S_SUPPLY_AVDD, + RT5682S_SUPPLY_MICVDD, + RT5682S_NUM_SUPPLIES, +}; struct rt5682s_priv { struct snd_soc_component *component; diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index e61a8257bf64..af32295fa9b9 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -853,6 +853,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt700 = { .dapm_routes = rt700_audio_map, .num_dapm_routes = ARRAY_SIZE(rt700_audio_map), .set_jack = rt700_set_jack_detect, + .endianness = 1, }; static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index bdb1375f0338..57629c18db38 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -1208,6 +1208,7 @@ static const struct snd_soc_component_driver soc_sdca_dev_rt711 = { .num_dapm_routes = ARRAY_SIZE(rt711_sdca_audio_map), .set_jack = rt711_sdca_set_jack_detect, .remove = rt711_sdca_remove, + .endianness = 1, }; static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index ea25fd58d43a..9838fb4d5b9c 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -950,6 +950,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt711 = { .num_dapm_routes = ARRAY_SIZE(rt711_audio_map), .set_jack = rt711_set_jack_detect, .remove = rt711_remove, + .endianness = 1, }; static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index a5c673f43d82..0ecd2948f7aa 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -181,8 +181,6 @@ static int rt715_sdca_sdw_probe(struct sdw_slave *slave, { struct regmap *mbq_regmap, *regmap; - slave->ops = &rt715_sdca_slave_ops; - /* Regmap Initialization */ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt715_sdca_mbq_regmap); if (IS_ERR(mbq_regmap)) diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index bfa536bd7196..5857d0866307 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -765,6 +765,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = { .num_dapm_widgets = ARRAY_SIZE(rt715_sdca_dapm_widgets), .dapm_routes = rt715_sdca_audio_map, .num_dapm_routes = ARRAY_SIZE(rt715_sdca_audio_map), + .endianness = 1, }; static int rt715_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index a64d11a74751..418e006b19ef 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -745,6 +745,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt715 = { .num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets), .dapm_routes = rt715_audio_map, .num_dapm_routes = ARRAY_SIZE(rt715_audio_map), + .endianness = 1, }; static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c index 6e0d7cf0c8c9..da495bdc8415 100644 --- a/sound/soc/codecs/rt9120.c +++ b/sound/soc/codecs/rt9120.c @@ -7,6 +7,7 @@ #include <linux/i2c.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <sound/pcm.h> @@ -75,6 +76,7 @@ enum { struct rt9120_data { struct device *dev; struct regmap *regmap; + struct gpio_desc *pwdnn_gpio; int chip_idx; }; @@ -160,6 +162,8 @@ static int rt9120_codec_probe(struct snd_soc_component *comp) snd_soc_component_init_regmap(comp, data->regmap); + pm_runtime_get_sync(comp->dev); + /* Internal setting */ if (data->chip_idx == CHIP_IDX_RT9120S) { snd_soc_component_write(comp, RT9120_REG_INTERCFG, 0xde); @@ -167,6 +171,9 @@ static int rt9120_codec_probe(struct snd_soc_component *comp) } else snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x04); + pm_runtime_mark_last_busy(comp->dev); + pm_runtime_put(comp->dev); + return 0; } @@ -178,6 +185,7 @@ static const struct snd_soc_component_driver rt9120_component_driver = { .num_dapm_widgets = ARRAY_SIZE(rt9120_dapm_widgets), .dapm_routes = rt9120_dapm_routes, .num_dapm_routes = ARRAY_SIZE(rt9120_dapm_routes), + .endianness = 1, }; static int rt9120_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) @@ -337,6 +345,18 @@ static const struct regmap_access_table rt9120_wr_table = { .n_yes_ranges = ARRAY_SIZE(rt9120_wr_yes_ranges), }; +static bool rt9120_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00 ... 0x01: + case 0x10: + case 0x30 ... 0x40: + return true; + default: + return false; + } +} + static int rt9120_get_reg_size(unsigned int reg) { switch (reg) { @@ -371,7 +391,7 @@ static int rt9120_reg_read(void *context, unsigned int reg, unsigned int *val) *val = be32_to_cpup((__be32 *)raw); break; case 3: - *val = raw[0] << 16 | raw[1] << 8 | raw[0]; + *val = raw[0] << 16 | raw[1] << 8 | raw[2]; break; case 2: *val = be16_to_cpup((__be16 *)raw); @@ -396,14 +416,49 @@ static int rt9120_reg_write(void *context, unsigned int reg, unsigned int val) return i2c_smbus_write_i2c_block_data(i2c, reg, size, rawp + offs); } +static const struct reg_default rt9120_reg_defaults[] = { + { .reg = 0x02, .def = 0x02 }, + { .reg = 0x03, .def = 0xf2 }, + { .reg = 0x04, .def = 0x01 }, + { .reg = 0x05, .def = 0xc0 }, + { .reg = 0x06, .def = 0x28 }, + { .reg = 0x07, .def = 0x04 }, + { .reg = 0x08, .def = 0xff }, + { .reg = 0x09, .def = 0x01 }, + { .reg = 0x0a, .def = 0x01 }, + { .reg = 0x0b, .def = 0x00 }, + { .reg = 0x0c, .def = 0x04 }, + { .reg = 0x11, .def = 0x30 }, + { .reg = 0x12, .def = 0x08 }, + { .reg = 0x13, .def = 0x12 }, + { .reg = 0x14, .def = 0x09 }, + { .reg = 0x15, .def = 0x00 }, + { .reg = 0x20, .def = 0x7ff }, + { .reg = 0x21, .def = 0x180 }, + { .reg = 0x22, .def = 0x180 }, + { .reg = 0x23, .def = 0x00 }, + { .reg = 0x24, .def = 0x80 }, + { .reg = 0x25, .def = 0x180 }, + { .reg = 0x26, .def = 0x640 }, + { .reg = 0x27, .def = 0x180 }, + { .reg = 0x63, .def = 0x5e }, + { .reg = 0x65, .def = 0x66 }, + { .reg = 0x6c, .def = 0xe0 }, + { .reg = 0xf8, .def = 0x44 }, +}; + static const struct regmap_config rt9120_regmap_config = { .reg_bits = 8, .val_bits = 32, .max_register = RT9120_REG_DIGCFG, + .reg_defaults = rt9120_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt9120_reg_defaults), + .cache_type = REGCACHE_RBTREE, .reg_read = rt9120_reg_read, .reg_write = rt9120_reg_write, + .volatile_reg = rt9120_volatile_reg, .wr_table = &rt9120_wr_table, .rd_table = &rt9120_rd_table, }; @@ -449,7 +504,6 @@ static int rt9120_do_register_reset(struct rt9120_data *data) static int rt9120_probe(struct i2c_client *i2c) { struct rt9120_data *data; - struct gpio_desc *pwdnn_gpio; struct regulator *dvdd_supply; int dvdd_supply_volt, ret; @@ -460,12 +514,12 @@ static int rt9120_probe(struct i2c_client *i2c) data->dev = &i2c->dev; i2c_set_clientdata(i2c, data); - pwdnn_gpio = devm_gpiod_get_optional(&i2c->dev, "pwdnn", - GPIOD_OUT_HIGH); - if (IS_ERR(pwdnn_gpio)) { + data->pwdnn_gpio = devm_gpiod_get_optional(&i2c->dev, "pwdnn", + GPIOD_OUT_HIGH); + if (IS_ERR(data->pwdnn_gpio)) { dev_err(&i2c->dev, "Failed to initialize 'pwdnn' gpio\n"); - return PTR_ERR(pwdnn_gpio); - } else if (pwdnn_gpio) { + return PTR_ERR(data->pwdnn_gpio); + } else if (data->pwdnn_gpio) { dev_dbg(&i2c->dev, "'pwdnn' from low to high, wait chip on\n"); msleep(RT9120_CHIPON_WAITMS); } @@ -507,11 +561,55 @@ static int rt9120_probe(struct i2c_client *i2c) } } + pm_runtime_set_autosuspend_delay(&i2c->dev, 1000); + pm_runtime_use_autosuspend(&i2c->dev); + pm_runtime_set_active(&i2c->dev); + pm_runtime_mark_last_busy(&i2c->dev); + pm_runtime_enable(&i2c->dev); + return devm_snd_soc_register_component(&i2c->dev, &rt9120_component_driver, &rt9120_dai, 1); } +static int rt9120_remove(struct i2c_client *i2c) +{ + pm_runtime_disable(&i2c->dev); + pm_runtime_set_suspended(&i2c->dev); + return 0; +} + +static int __maybe_unused rt9120_runtime_suspend(struct device *dev) +{ + struct rt9120_data *data = dev_get_drvdata(dev); + + if (data->pwdnn_gpio) { + regcache_cache_only(data->regmap, true); + regcache_mark_dirty(data->regmap); + gpiod_set_value(data->pwdnn_gpio, 0); + } + + return 0; +} + +static int __maybe_unused rt9120_runtime_resume(struct device *dev) +{ + struct rt9120_data *data = dev_get_drvdata(dev); + + if (data->pwdnn_gpio) { + gpiod_set_value(data->pwdnn_gpio, 1); + msleep(RT9120_CHIPON_WAITMS); + regcache_cache_only(data->regmap, false); + regcache_sync(data->regmap); + } + + return 0; +} + +static const struct dev_pm_ops rt9120_pm_ops = { + SET_RUNTIME_PM_OPS(rt9120_runtime_suspend, rt9120_runtime_resume, NULL) +}; + static const struct of_device_id __maybe_unused rt9120_device_table[] = { { .compatible = "richtek,rt9120", }, { } @@ -522,8 +620,10 @@ static struct i2c_driver rt9120_driver = { .driver = { .name = "rt9120", .of_match_table = rt9120_device_table, + .pm = &rt9120_pm_ops, }, .probe_new = rt9120_probe, + .remove = rt9120_remove, }; module_i2c_driver(rt9120_driver); diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c index 7c612aaf31c7..288b55368d2a 100644 --- a/sound/soc/codecs/sdw-mockup.c +++ b/sound/soc/codecs/sdw-mockup.c @@ -38,6 +38,7 @@ static void sdw_mockup_component_remove(struct snd_soc_component *component) static const struct snd_soc_component_driver snd_soc_sdw_mockup_component = { .probe = sdw_mockup_component_probe, .remove = sdw_mockup_component_remove, + .endianness = 1, }; static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 8eebf27d0ea2..2aa48aef6a97 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1579,8 +1579,7 @@ static void sgtl5000_fill_defaults(struct i2c_client *client) } } -static int sgtl5000_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sgtl5000_i2c_probe(struct i2c_client *client) { struct sgtl5000_priv *sgtl5000; int ret, reg, rev; @@ -1821,7 +1820,7 @@ static struct i2c_driver sgtl5000_i2c_driver = { .name = "sgtl5000", .of_match_table = sgtl5000_dt_ids, }, - .probe = sgtl5000_i2c_probe, + .probe_new = sgtl5000_i2c_probe, .remove = sgtl5000_i2c_remove, .id_table = sgtl5000_id, }; diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index 09449c6c4024..83acbdbb8e0d 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -735,8 +735,7 @@ static const struct regmap_config ssm2518_regmap_config = { .num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults), }; -static int ssm2518_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ssm2518_i2c_probe(struct i2c_client *i2c) { struct ssm2518_platform_data *pdata = i2c->dev.platform_data; struct ssm2518 *ssm2518; @@ -815,7 +814,7 @@ static struct i2c_driver ssm2518_driver = { .name = "ssm2518", .of_match_table = of_match_ptr(ssm2518_dt_ids), }, - .probe = ssm2518_i2c_probe, + .probe_new = ssm2518_i2c_probe, .id_table = ssm2518_i2c_ids, }; module_i2c_driver(ssm2518_driver); diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c index afab81383d3a..3c85772901f5 100644 --- a/sound/soc/codecs/ssm2602-i2c.c +++ b/sound/soc/codecs/ssm2602-i2c.c @@ -13,15 +13,17 @@ #include "ssm2602.h" +static const struct i2c_device_id ssm2602_i2c_id[]; + /* * ssm2602 2 wire address is determined by GPIO5 * state during powerup. * low = 0x1a * high = 0x1b */ -static int ssm2602_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ssm2602_i2c_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_match_id(ssm2602_i2c_id, client); return ssm2602_probe(&client->dev, id->driver_data, devm_regmap_init_i2c(client, &ssm2602_regmap_config)); } @@ -47,7 +49,7 @@ static struct i2c_driver ssm2602_i2c_driver = { .name = "ssm2602", .of_match_table = ssm2602_of_match, }, - .probe = ssm2602_i2c_probe, + .probe_new = ssm2602_i2c_probe, .id_table = ssm2602_i2c_id, }; module_i2c_driver(ssm2602_i2c_driver); diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index 811b1a2c404a..08ced09ef001 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -444,8 +444,7 @@ static const struct regmap_config ssm4567_regmap_config = { .num_reg_defaults = ARRAY_SIZE(ssm4567_reg_defaults), }; -static int ssm4567_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ssm4567_i2c_probe(struct i2c_client *i2c) { struct ssm4567 *ssm4567; int ret; @@ -502,7 +501,7 @@ static struct i2c_driver ssm4567_driver = { .of_match_table = of_match_ptr(ssm4567_of_match), .acpi_match_table = ACPI_PTR(ssm4567_acpi_match), }, - .probe = ssm4567_i2c_probe, + .probe_new = ssm4567_i2c_probe, .id_table = ssm4567_i2c_ids, }; module_i2c_driver(ssm4567_driver); diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 86528b930de8..8585cbef4c9b 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -48,12 +48,9 @@ SNDRV_PCM_RATE_192000) #define STA32X_FORMATS \ - (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) /* Power-up register defaults */ static const struct reg_default sta32x_regs[] = { @@ -1094,8 +1091,7 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x) } #endif -static int sta32x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int sta32x_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct sta32x_priv *sta32x; @@ -1175,7 +1171,7 @@ static struct i2c_driver sta32x_i2c_driver = { .name = "sta32x", .of_match_table = of_match_ptr(st32x_dt_ids), }, - .probe = sta32x_i2c_probe, + .probe_new = sta32x_i2c_probe, .id_table = sta32x_i2c_id, }; diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index 75d3b0618ab5..9189fb3648f7 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -48,12 +48,9 @@ SNDRV_PCM_RATE_192000) #define STA350_FORMATS \ - (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) /* Power-up register defaults */ static const struct reg_default sta350_regs[] = { @@ -1187,8 +1184,7 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350) } #endif -static int sta350_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int sta350_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct sta350_priv *sta350; @@ -1263,7 +1259,7 @@ static struct i2c_driver sta350_i2c_driver = { .name = "sta350", .of_match_table = of_match_ptr(st350_dt_ids), }, - .probe = sta350_i2c_probe, + .probe_new = sta350_i2c_probe, .remove = sta350_i2c_remove, .id_table = sta350_i2c_id, }; diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 97b5f34027c0..d90e5512a731 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -337,8 +337,7 @@ static const struct regmap_config sta529_regmap = { .num_reg_defaults = ARRAY_SIZE(sta529_reg_defaults), }; -static int sta529_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int sta529_i2c_probe(struct i2c_client *i2c) { struct sta529 *sta529; int ret; @@ -381,7 +380,7 @@ static struct i2c_driver sta529_i2c_driver = { .name = "sta529", .of_match_table = sta529_of_match, }, - .probe = sta529_i2c_probe, + .probe_new = sta529_i2c_probe, .id_table = sta529_i2c_id, }; diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 700baa6314aa..b5c9c61ff5a8 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -681,8 +681,7 @@ static const struct regmap_config tas2552_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -static int tas2552_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tas2552_probe(struct i2c_client *client) { struct device *dev; struct tas2552_data *data; @@ -764,7 +763,7 @@ static struct i2c_driver tas2552_i2c_driver = { .of_match_table = of_match_ptr(tas2552_of_match), .pm = &tas2552_pm, }, - .probe = tas2552_probe, + .probe_new = tas2552_probe, .remove = tas2552_i2c_remove, .id_table = tas2552_id, }; diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index 10302552195e..e62a3da16aed 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -754,17 +754,27 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562) return ret; } -static int tas2562_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id tas2562_id[] = { + { "tas2562", TAS2562 }, + { "tas2563", TAS2563 }, + { "tas2564", TAS2564 }, + { "tas2110", TAS2110 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2562_id); + +static int tas2562_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct tas2562_data *data; int ret; + const struct i2c_device_id *id; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; + id = i2c_match_id(tas2562_id, client); data->client = client; data->dev = &client->dev; data->model_id = id->driver_data; @@ -792,15 +802,6 @@ static int tas2562_probe(struct i2c_client *client, } -static const struct i2c_device_id tas2562_id[] = { - { "tas2562", TAS2562 }, - { "tas2563", TAS2563 }, - { "tas2564", TAS2564 }, - { "tas2110", TAS2110 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tas2562_id); - #ifdef CONFIG_OF static const struct of_device_id tas2562_of_match[] = { { .compatible = "ti,tas2562", }, @@ -817,7 +818,7 @@ static struct i2c_driver tas2562_i2c_driver = { .name = "tas2562", .of_match_table = of_match_ptr(tas2562_of_match), }, - .probe = tas2562_probe, + .probe_new = tas2562_probe, .id_table = tas2562_id, }; diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 9265af41c235..d395feffb30b 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -621,8 +621,7 @@ static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764) return 0; } -static int tas2764_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tas2764_i2c_probe(struct i2c_client *client) { struct tas2764_priv *tas2764; int result; @@ -678,7 +677,7 @@ static struct i2c_driver tas2764_i2c_driver = { .name = "tas2764", .of_match_table = of_match_ptr(tas2764_of_match), }, - .probe = tas2764_i2c_probe, + .probe_new = tas2764_i2c_probe, .id_table = tas2764_i2c_id, }; module_i2c_driver(tas2764_i2c_driver); diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index c5ea3b115966..c1dbd978d550 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -672,8 +672,7 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) return 0; } -static int tas2770_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tas2770_i2c_probe(struct i2c_client *client) { struct tas2770_priv *tas2770; int result; @@ -739,7 +738,7 @@ static struct i2c_driver tas2770_i2c_driver = { .name = "tas2770", .of_match_table = of_match_ptr(tas2770_of_match), }, - .probe = tas2770_i2c_probe, + .probe_new = tas2770_i2c_probe, .id_table = tas2770_i2c_id, }; module_i2c_driver(tas2770_i2c_driver); diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 7831c96d0d83..5c0df3cd4832 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -911,8 +911,7 @@ static const struct regmap_config tas5086_regmap = { .reg_write = tas5086_reg_write, }; -static int tas5086_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int tas5086_i2c_probe(struct i2c_client *i2c) { struct tas5086_private *priv; struct device *dev = &i2c->dev; @@ -994,7 +993,7 @@ static struct i2c_driver tas5086_i2c_driver = { .of_match_table = of_match_ptr(tas5086_dt_ids), }, .id_table = tas5086_i2c_id, - .probe = tas5086_i2c_probe, + .probe_new = tas5086_i2c_probe, .remove = tas5086_i2c_remove, }; diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index a3e682376946..7b599664db20 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -774,9 +774,9 @@ static struct snd_soc_dai_driver tas571x_dai = { }; static const struct of_device_id tas571x_of_match[] __maybe_unused; +static const struct i2c_device_id tas571x_i2c_id[]; -static int tas571x_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tas571x_i2c_probe(struct i2c_client *client) { struct tas571x_private *priv; struct device *dev = &client->dev; @@ -791,8 +791,11 @@ static int tas571x_i2c_probe(struct i2c_client *client, of_id = of_match_device(tas571x_of_match, dev); if (of_id) priv->chip = of_id->data; - else + else { + const struct i2c_device_id *id = + i2c_match_id(tas571x_i2c_id, client); priv->chip = (void *) id->driver_data; + } priv->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { @@ -830,7 +833,8 @@ static int tas571x_i2c_probe(struct i2c_client *client, if (IS_ERR(priv->pdn_gpio)) { dev_err(dev, "error requesting pdn_gpio: %ld\n", PTR_ERR(priv->pdn_gpio)); - return PTR_ERR(priv->pdn_gpio); + ret = PTR_ERR(priv->pdn_gpio); + goto disable_regs; } priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", @@ -838,7 +842,8 @@ static int tas571x_i2c_probe(struct i2c_client *client, if (IS_ERR(priv->reset_gpio)) { dev_err(dev, "error requesting reset_gpio: %ld\n", PTR_ERR(priv->reset_gpio)); - return PTR_ERR(priv->reset_gpio); + ret = PTR_ERR(priv->reset_gpio); + goto disable_regs; } else if (priv->reset_gpio) { /* pulse the active low reset line for ~100us */ usleep_range(100, 200); @@ -914,7 +919,7 @@ static struct i2c_driver tas571x_i2c_driver = { .name = "tas571x", .of_match_table = of_match_ptr(tas571x_of_match), }, - .probe = tas571x_i2c_probe, + .probe_new = tas571x_i2c_probe, .remove = tas571x_i2c_remove, .id_table = tas571x_i2c_id, }; diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c index 9ff644ddb470..17034abef568 100644 --- a/sound/soc/codecs/tas5720.c +++ b/sound/soc/codecs/tas5720.c @@ -633,12 +633,19 @@ static struct snd_soc_dai_driver tas5720_dai[] = { }, }; -static int tas5720_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id tas5720_id[] = { + { "tas5720", TAS5720 }, + { "tas5722", TAS5722 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas5720_id); + +static int tas5720_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct tas5720_data *data; const struct regmap_config *regmap_config; + const struct i2c_device_id *id; int ret; int i; @@ -646,6 +653,7 @@ static int tas5720_probe(struct i2c_client *client, if (!data) return -ENOMEM; + id = i2c_match_id(tas5720_id, client); data->tas5720_client = client; data->devtype = id->driver_data; @@ -704,13 +712,6 @@ static int tas5720_probe(struct i2c_client *client, return 0; } -static const struct i2c_device_id tas5720_id[] = { - { "tas5720", TAS5720 }, - { "tas5722", TAS5722 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tas5720_id); - #if IS_ENABLED(CONFIG_OF) static const struct of_device_id tas5720_of_match[] = { { .compatible = "ti,tas5720", }, @@ -725,7 +726,7 @@ static struct i2c_driver tas5720_i2c_driver = { .name = "tas5720", .of_match_table = of_match_ptr(tas5720_of_match), }, - .probe = tas5720_probe, + .probe_new = tas5720_probe, .id_table = tas5720_id, }; diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c index 59543d392110..22b53856e691 100644 --- a/sound/soc/codecs/tas6424.c +++ b/sound/soc/codecs/tas6424.c @@ -682,8 +682,7 @@ static const struct of_device_id tas6424_of_ids[] = { MODULE_DEVICE_TABLE(of, tas6424_of_ids); #endif -static int tas6424_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tas6424_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct tas6424_data *tas6424; @@ -757,7 +756,7 @@ static int tas6424_i2c_probe(struct i2c_client *client, TAS6424_RESET, TAS6424_RESET); if (ret) { dev_err(dev, "unable to reset device: %d\n", ret); - return ret; + goto disable_regs; } INIT_DELAYED_WORK(&tas6424->fault_check_work, tas6424_fault_check_work); @@ -766,10 +765,14 @@ static int tas6424_i2c_probe(struct i2c_client *client, tas6424_dai, ARRAY_SIZE(tas6424_dai)); if (ret < 0) { dev_err(dev, "unable to register codec: %d\n", ret); - return ret; + goto disable_regs; } return 0; + +disable_regs: + regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), tas6424->supplies); + return ret; } static int tas6424_i2c_remove(struct i2c_client *client) @@ -786,10 +789,8 @@ static int tas6424_i2c_remove(struct i2c_client *client) ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), tas6424->supplies); - if (ret < 0) { + if (ret < 0) dev_err(dev, "unable to disable supplies: %d\n", ret); - return ret; - } return 0; } @@ -805,7 +806,7 @@ static struct i2c_driver tas6424_i2c_driver = { .name = "tas6424", .of_match_table = of_match_ptr(tas6424_of_ids), }, - .probe = tas6424_i2c_probe, + .probe_new = tas6424_i2c_probe, .remove = tas6424_i2c_remove, .id_table = tas6424_i2c_ids, }; diff --git a/sound/soc/codecs/tda7419.c b/sound/soc/codecs/tda7419.c index 83d220054c96..d964e5207569 100644 --- a/sound/soc/codecs/tda7419.c +++ b/sound/soc/codecs/tda7419.c @@ -571,8 +571,7 @@ static const struct snd_soc_component_driver tda7419_component_driver = { .num_dapm_routes = ARRAY_SIZE(tda7419_dapm_routes), }; -static int tda7419_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int tda7419_probe(struct i2c_client *i2c) { struct tda7419_data *tda7419; int i, ret; @@ -630,7 +629,7 @@ static struct i2c_driver tda7419_driver = { .name = "tda7419", .of_match_table = tda7419_of_match, }, - .probe = tda7419_probe, + .probe_new = tda7419_probe, .id_table = tda7419_i2c_id, }; diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index ae18982ac310..82532ad00c3c 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -1152,20 +1152,20 @@ static int adc3xxx_hw_params(struct snd_pcm_substream *substream, return i; /* select data word length */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (params_width(params)) { + case 16: iface_len = ADC3XXX_IFACE_16BITS; width = 16; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: iface_len = ADC3XXX_IFACE_20BITS; width = 20; break; - case SNDRV_PCM_FORMAT_S24_LE: + case 24: iface_len = ADC3XXX_IFACE_24BITS; width = 24; break; - case SNDRV_PCM_FORMAT_S32_LE: + case 32: iface_len = ADC3XXX_IFACE_32BITS; width = 32; break; @@ -1335,13 +1335,21 @@ static const struct snd_soc_component_driver soc_component_dev_adc3xxx = { .num_dapm_widgets = ARRAY_SIZE(adc3xxx_dapm_widgets), .dapm_routes = adc3xxx_intercon, .num_dapm_routes = ARRAY_SIZE(adc3xxx_intercon), + .endianness = 1, }; -static int adc3xxx_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static const struct i2c_device_id adc3xxx_i2c_id[] = { + { "tlv320adc3001", ADC3001 }, + { "tlv320adc3101", ADC3101 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, adc3xxx_i2c_id); + +static int adc3xxx_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct adc3xxx *adc3xxx = NULL; + const struct i2c_device_id *id; int ret; adc3xxx = devm_kzalloc(dev, sizeof(struct adc3xxx), GFP_KERNEL); @@ -1394,6 +1402,7 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, adc3xxx); + id = i2c_match_id(adc3xxx_i2c_id, i2c); adc3xxx->type = id->driver_data; /* Reset codec chip */ @@ -1436,19 +1445,12 @@ static const struct of_device_id tlv320adc3xxx_of_match[] = { }; MODULE_DEVICE_TABLE(of, tlv320adc3xxx_of_match); -static const struct i2c_device_id adc3xxx_i2c_id[] = { - { "tlv320adc3001", ADC3001 }, - { "tlv320adc3101", ADC3101 }, - {} -}; -MODULE_DEVICE_TABLE(i2c, adc3xxx_i2c_id); - static struct i2c_driver adc3xxx_i2c_driver = { .driver = { .name = "tlv320adc3xxx-codec", .of_match_table = tlv320adc3xxx_of_match, }, - .probe = adc3xxx_i2c_probe, + .probe_new = adc3xxx_i2c_probe, .remove = adc3xxx_i2c_remove, .id_table = adc3xxx_i2c_id, }; diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 32b120d624b2..b55f0b836932 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -1083,8 +1083,14 @@ static const struct of_device_id tlv320adcx140_of_match[] = { MODULE_DEVICE_TABLE(of, tlv320adcx140_of_match); #endif -static int adcx140_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static void adcx140_disable_regulator(void *arg) +{ + struct adcx140_priv *adcx140 = arg; + + regulator_disable(adcx140->supply_areg); +} + +static int adcx140_i2c_probe(struct i2c_client *i2c) { struct adcx140_priv *adcx140; int ret; @@ -1113,6 +1119,10 @@ static int adcx140_i2c_probe(struct i2c_client *i2c, dev_err(adcx140->dev, "Failed to enable areg\n"); return ret; } + + ret = devm_add_action_or_reset(&i2c->dev, adcx140_disable_regulator, adcx140); + if (ret) + return ret; } adcx140->regmap = devm_regmap_init_i2c(i2c, &adcx140_i2c_regmap); @@ -1143,7 +1153,7 @@ static struct i2c_driver adcx140_i2c_driver = { .name = "tlv320adcx140-codec", .of_match_table = of_match_ptr(tlv320adcx140_of_match), }, - .probe = adcx140_i2c_probe, + .probe_new = adcx140_i2c_probe, .id_table = adcx140_i2c_id, }; module_i2c_driver(adcx140_i2c_driver); diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c index dbb8f969274c..1f97673a1cc0 100644 --- a/sound/soc/codecs/tlv320aic23-i2c.c +++ b/sound/soc/codecs/tlv320aic23-i2c.c @@ -16,8 +16,7 @@ #include "tlv320aic23.h" -static int tlv320aic23_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *i2c_id) +static int tlv320aic23_i2c_probe(struct i2c_client *i2c) { struct regmap *regmap; @@ -48,7 +47,7 @@ static struct i2c_driver tlv320aic23_i2c_driver = { .name = "tlv320aic23-codec", .of_match_table = of_match_ptr(tlv320aic23_of_match), }, - .probe = tlv320aic23_i2c_probe, + .probe_new = tlv320aic23_i2c_probe, .id_table = tlv320aic23_id, }; diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 8331dc26bcd2..b2e59581c17a 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1628,11 +1628,24 @@ static void aic31xx_configure_ocmv(struct aic31xx_priv *priv) } } -static int aic31xx_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static const struct i2c_device_id aic31xx_i2c_id[] = { + { "tlv320aic310x", AIC3100 }, + { "tlv320aic311x", AIC3110 }, + { "tlv320aic3100", AIC3100 }, + { "tlv320aic3110", AIC3110 }, + { "tlv320aic3120", AIC3120 }, + { "tlv320aic3111", AIC3111 }, + { "tlv320dac3100", DAC3100 }, + { "tlv320dac3101", DAC3101 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id); + +static int aic31xx_i2c_probe(struct i2c_client *i2c) { struct aic31xx_priv *aic31xx; unsigned int micbias_value = MICBIAS_2_0V; + const struct i2c_device_id *id = i2c_match_id(aic31xx_i2c_id, i2c); int i, ret; dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__, @@ -1729,26 +1742,13 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, ARRAY_SIZE(aic31xx_dai_driver)); } -static const struct i2c_device_id aic31xx_i2c_id[] = { - { "tlv320aic310x", AIC3100 }, - { "tlv320aic311x", AIC3110 }, - { "tlv320aic3100", AIC3100 }, - { "tlv320aic3110", AIC3110 }, - { "tlv320aic3120", AIC3120 }, - { "tlv320aic3111", AIC3111 }, - { "tlv320dac3100", DAC3100 }, - { "tlv320dac3101", DAC3101 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id); - static struct i2c_driver aic31xx_i2c_driver = { .driver = { .name = "tlv320aic31xx-codec", .of_match_table = of_match_ptr(tlv320aic31xx_of_match), .acpi_match_table = ACPI_PTR(aic31xx_acpi_match), }, - .probe = aic31xx_i2c_probe, + .probe_new = aic31xx_i2c_probe, .id_table = aic31xx_i2c_id, }; module_i2c_driver(aic31xx_i2c_driver); diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c index ed70e3d9baf2..0645239901b1 100644 --- a/sound/soc/codecs/tlv320aic32x4-i2c.c +++ b/sound/soc/codecs/tlv320aic32x4-i2c.c @@ -17,9 +17,9 @@ #include "tlv320aic32x4.h" static const struct of_device_id aic32x4_of_id[]; +static const struct i2c_device_id aic32x4_i2c_id[]; -static int aic32x4_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int aic32x4_i2c_probe(struct i2c_client *i2c) { struct regmap *regmap; struct regmap_config config; @@ -35,7 +35,10 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c, oid = of_match_node(aic32x4_of_id, i2c->dev.of_node); dev_set_drvdata(&i2c->dev, (void *)oid->data); - } else if (id) { + } else { + const struct i2c_device_id *id; + + id = i2c_match_id(aic32x4_i2c_id, i2c); dev_set_drvdata(&i2c->dev, (void *)id->driver_data); } @@ -70,7 +73,7 @@ static struct i2c_driver aic32x4_i2c_driver = { .name = "tlv320aic32x4", .of_match_table = aic32x4_of_id, }, - .probe = aic32x4_i2c_probe, + .probe_new = aic32x4_i2c_probe, .remove = aic32x4_i2c_remove, .id_table = aic32x4_i2c_id, }; diff --git a/sound/soc/codecs/tlv320aic3x-i2c.c b/sound/soc/codecs/tlv320aic3x-i2c.c index 2f272bc3f5da..7bd9ce08bb7b 100644 --- a/sound/soc/codecs/tlv320aic3x-i2c.c +++ b/sound/soc/codecs/tlv320aic3x-i2c.c @@ -17,10 +17,21 @@ #include "tlv320aic3x.h" -static int aic3x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +static const struct i2c_device_id aic3x_i2c_id[] = { + { "tlv320aic3x", AIC3X_MODEL_3X }, + { "tlv320aic33", AIC3X_MODEL_33 }, + { "tlv320aic3007", AIC3X_MODEL_3007 }, + { "tlv320aic3104", AIC3X_MODEL_3104 }, + { "tlv320aic3106", AIC3X_MODEL_3106 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); + +static int aic3x_i2c_probe(struct i2c_client *i2c) { struct regmap *regmap; struct regmap_config config; + const struct i2c_device_id *id = i2c_match_id(aic3x_i2c_id, i2c); config = aic3x_regmap; config.reg_bits = 8; @@ -37,16 +48,6 @@ static int aic3x_i2c_remove(struct i2c_client *i2c) return 0; } -static const struct i2c_device_id aic3x_i2c_id[] = { - { "tlv320aic3x", AIC3X_MODEL_3X }, - { "tlv320aic33", AIC3X_MODEL_33 }, - { "tlv320aic3007", AIC3X_MODEL_3007 }, - { "tlv320aic3104", AIC3X_MODEL_3104 }, - { "tlv320aic3106", AIC3X_MODEL_3106 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); - static const struct of_device_id aic3x_of_id[] = { { .compatible = "ti,tlv320aic3x", }, { .compatible = "ti,tlv320aic33" }, @@ -62,7 +63,7 @@ static struct i2c_driver aic3x_i2c_driver = { .name = "tlv320aic3x", .of_match_table = aic3x_of_id, }, - .probe = aic3x_i2c_probe, + .probe_new = aic3x_i2c_probe, .remove = aic3x_i2c_remove, .id_table = aic3x_i2c_id, }; diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 48572d66cb3b..66f1d1cd6cf0 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1463,8 +1463,7 @@ static struct snd_soc_dai_driver dac33_dai = { .ops = &dac33_dai_ops, }; -static int dac33_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int dac33_i2c_probe(struct i2c_client *client) { struct tlv320dac33_platform_data *pdata; struct tlv320dac33_priv *dac33; @@ -1566,7 +1565,7 @@ static struct i2c_driver tlv320dac33_i2c_driver = { .driver = { .name = "tlv320dac33-codec", }, - .probe = dac33_i2c_probe, + .probe_new = dac33_i2c_probe, .remove = dac33_i2c_remove, .id_table = tlv320dac33_i2c_id, }; diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index e2d7ae615c52..8c00db32996b 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -209,13 +209,20 @@ static const struct regmap_config tpa6130a2_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -static int tpa6130a2_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const struct i2c_device_id tpa6130a2_id[] = { + { "tpa6130a2", TPA6130A2 }, + { "tpa6140a2", TPA6140A2 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tpa6130a2_id); + +static int tpa6130a2_probe(struct i2c_client *client) { struct device *dev; struct tpa6130a2_data *data; struct tpa6130a2_platform_data *pdata = client->dev.platform_data; struct device_node *np = client->dev.of_node; + const struct i2c_device_id *id; const char *regulator; unsigned int version; int ret; @@ -244,6 +251,7 @@ static int tpa6130a2_probe(struct i2c_client *client, i2c_set_clientdata(client, data); + id = i2c_match_id(tpa6130a2_id, client); data->id = id->driver_data; if (data->power_gpio >= 0) { @@ -297,13 +305,6 @@ static int tpa6130a2_probe(struct i2c_client *client, &tpa6130a2_component_driver, NULL, 0); } -static const struct i2c_device_id tpa6130a2_id[] = { - { "tpa6130a2", TPA6130A2 }, - { "tpa6140a2", TPA6140A2 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tpa6130a2_id); - #if IS_ENABLED(CONFIG_OF) static const struct of_device_id tpa6130a2_of_match[] = { { .compatible = "ti,tpa6130a2", }, @@ -318,7 +319,7 @@ static struct i2c_driver tpa6130a2_i2c_driver = { .name = "tpa6130a2", .of_match_table = of_match_ptr(tpa6130a2_of_match), }, - .probe = tpa6130a2_probe, + .probe_new = tpa6130a2_probe, .id_table = tpa6130a2_id, }; diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 962f5d48378a..d8ab0810fceb 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -282,8 +282,7 @@ static int ts3a227e_parse_device_property(struct ts3a227e *ts3a227e, return 0; } -static int ts3a227e_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int ts3a227e_i2c_probe(struct i2c_client *i2c) { struct ts3a227e *ts3a227e; struct device *dev = &i2c->dev; @@ -389,7 +388,7 @@ static struct i2c_driver ts3a227e_driver = { .of_match_table = of_match_ptr(ts3a227e_of_match), .acpi_match_table = ACPI_PTR(ts3a227e_acpi_match), }, - .probe = ts3a227e_i2c_probe, + .probe_new = ts3a227e_i2c_probe, .id_table = ts3a227e_i2c_ids, }; module_i2c_driver(ts3a227e_driver); diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index 5b63e017a43b..4fb0bb01bcdc 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -1409,8 +1409,7 @@ static const struct reg_sequence tscs42xx_patch[] = { static char const * const src_names[TSCS42XX_PLL_SRC_CNT] = { "xtal", "mclk1", "mclk2"}; -static int tscs42xx_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int tscs42xx_i2c_probe(struct i2c_client *i2c) { struct tscs42xx *tscs42xx; int src; @@ -1505,7 +1504,7 @@ static struct i2c_driver tscs42xx_i2c_driver = { .name = "tscs42xx", .of_match_table = tscs42xx_of_match, }, - .probe = tscs42xx_i2c_probe, + .probe_new = tscs42xx_i2c_probe, .id_table = tscs42xx_i2c_id, }; diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c index 7e1826d6f06f..38622bc247fc 100644 --- a/sound/soc/codecs/tscs454.c +++ b/sound/soc/codecs/tscs454.c @@ -3120,18 +3120,17 @@ static int set_aif_sample_format(struct snd_soc_component *component, unsigned int width; int ret; - switch (format) { - case SNDRV_PCM_FORMAT_S16_LE: + switch (snd_pcm_format_width(format)) { + case 16: width = FV_WL_16; break; - case SNDRV_PCM_FORMAT_S20_3LE: + case 20: width = FV_WL_20; break; - case SNDRV_PCM_FORMAT_S24_3LE: + case 24: width = FV_WL_24; break; - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S32_LE: + case 32: width = FV_WL_32; break; default: @@ -3326,6 +3325,7 @@ static const struct snd_soc_component_driver soc_component_dev_tscs454 = { .num_dapm_routes = ARRAY_SIZE(tscs454_intercon), .controls = tscs454_snd_controls, .num_controls = ARRAY_SIZE(tscs454_snd_controls), + .endianness = 1, }; #define TSCS454_RATES SNDRV_PCM_RATE_8000_96000 @@ -3400,8 +3400,7 @@ static struct snd_soc_dai_driver tscs454_dais[] = { static char const * const src_names[] = { "xtal", "mclk1", "mclk2", "bclk"}; -static int tscs454_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int tscs454_i2c_probe(struct i2c_client *i2c) { struct tscs454 *tscs454; int src; @@ -3474,7 +3473,7 @@ static struct i2c_driver tscs454_i2c_driver = { .name = "tscs454", .of_match_table = tscs454_of_match, }, - .probe = tscs454_i2c_probe, + .probe_new = tscs454_i2c_probe, .id_table = tscs454_i2c_id, }; diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 13060a9a2388..b5004842520b 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -739,8 +739,7 @@ static const struct snd_soc_component_driver soc_component_dev_uda1380 = { .non_legacy_dai_naming = 1, }; -static int uda1380_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int uda1380_i2c_probe(struct i2c_client *i2c) { struct uda1380_platform_data *pdata = i2c->dev.platform_data; struct uda1380_priv *uda1380; @@ -800,7 +799,7 @@ static struct i2c_driver uda1380_i2c_driver = { .name = "uda1380-codec", .of_match_table = uda1380_of_match, }, - .probe = uda1380_i2c_probe, + .probe_new = uda1380_i2c_probe, .id_table = uda1380_i2c_id, }; diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 1e60db4056ad..617a36a89dfe 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -4924,6 +4924,7 @@ static const struct snd_soc_component_driver wcd9335_component_drv = { .num_dapm_widgets = ARRAY_SIZE(wcd9335_dapm_widgets), .dapm_routes = wcd9335_audio_map, .num_dapm_routes = ARRAY_SIZE(wcd9335_audio_map), + .endianness = 1, }; static int wcd9335_probe(struct wcd9335_codec *wcd) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index 6298ebe96e94..f56907d0942d 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -5847,6 +5847,7 @@ static const struct snd_soc_component_driver wcd934x_component_drv = { .dapm_routes = wcd934x_audio_map, .num_dapm_routes = ARRAY_SIZE(wcd934x_audio_map), .set_jack = wcd934x_codec_set_jack, + .endianness = 1, }; static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd) diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index 898b2887fa63..c1b61b997f69 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -4168,6 +4168,7 @@ static const struct snd_soc_component_driver soc_codec_dev_wcd938x = { .dapm_routes = wcd938x_audio_map, .num_dapm_routes = ARRAY_SIZE(wcd938x_audio_map), .set_jack = wcd938x_codec_set_jack, + .endianness = 1, }; static void wcd938x_dt_parse_micbias_info(struct device *dev, struct wcd938x_priv *wcd) diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index d6ffe99550fe..b6366dea15a6 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c @@ -192,8 +192,7 @@ static void wm1250_ev1_free(struct i2c_client *i2c) gpio_free_array(wm1250->gpios, ARRAY_SIZE(wm1250->gpios)); } -static int wm1250_ev1_probe(struct i2c_client *i2c, - const struct i2c_device_id *i2c_id) +static int wm1250_ev1_probe(struct i2c_client *i2c) { int id, board, rev, ret; @@ -247,7 +246,7 @@ static struct i2c_driver wm1250_ev1_i2c_driver = { .driver = { .name = "wm1250-ev1", }, - .probe = wm1250_ev1_probe, + .probe_new = wm1250_ev1_probe, .remove = wm1250_ev1_remove, .id_table = wm1250_ev1_i2c_id, }; diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 72e165cc6443..ede5f2a982a6 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -536,7 +536,7 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000, { struct i2c_client *i2c = wm2000->i2c; int i, j; - int ret; + int ret = 0; if (wm2000->anc_mode == mode) return 0; @@ -566,13 +566,13 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000, ret = anc_transitions[i].step[j](i2c, anc_transitions[i].analogue); if (ret != 0) - return ret; + break; } if (anc_transitions[i].dest == ANC_OFF) clk_disable_unprepare(wm2000->mclk); - return 0; + return ret; } static int wm2000_anc_set_mode(struct wm2000_priv *wm2000) @@ -803,12 +803,10 @@ static const struct snd_soc_component_driver soc_component_dev_wm2000 = { .num_dapm_routes = ARRAY_SIZE(wm2000_audio_map), .idle_bias_on = 1, .use_pmdown_time = 1, - .endianness = 1, .non_legacy_dai_naming = 1, }; -static int wm2000_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *i2c_id) +static int wm2000_i2c_probe(struct i2c_client *i2c) { struct wm2000_priv *wm2000; struct wm2000_platform_data *pdata; @@ -941,7 +939,7 @@ static struct i2c_driver wm2000_i2c_driver = { .driver = { .name = "wm2000", }, - .probe = wm2000_i2c_probe, + .probe_new = wm2000_i2c_probe, .id_table = wm2000_i2c_id, }; diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 8863b533f9c4..1cd544580c83 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -2176,8 +2176,7 @@ static const unsigned int wm2200_mic_ctrl_reg[] = { WM2200_IN3L_CONTROL, }; -static int wm2200_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm2200_i2c_probe(struct i2c_client *i2c) { struct wm2200_pdata *pdata = dev_get_platdata(&i2c->dev); struct wm2200_priv *wm2200; @@ -2489,7 +2488,7 @@ static struct i2c_driver wm2200_i2c_driver = { .name = "wm2200", .pm = &wm2200_pm, }, - .probe = wm2200_i2c_probe, + .probe_new = wm2200_i2c_probe, .remove = wm2200_i2c_remove, .id_table = wm2200_i2c_id, }; diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 9cab01ee4ee9..a89870918174 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2411,8 +2411,7 @@ static const unsigned int wm5100_mic_ctrl_reg[] = { WM5100_IN4L_CONTROL, }; -static int wm5100_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm5100_i2c_probe(struct i2c_client *i2c) { struct wm5100_pdata *pdata = dev_get_platdata(&i2c->dev); struct wm5100_priv *wm5100; @@ -2713,7 +2712,7 @@ static struct i2c_driver wm5100_i2c_driver = { .name = "wm5100", .pm = &wm5100_pm, }, - .probe = wm5100_i2c_probe, + .probe_new = wm5100_i2c_probe, .remove = wm5100_i2c_remove, .id_table = wm5100_i2c_id, }; diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index a18e2290b8c8..c6439d25006b 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -646,8 +646,7 @@ static struct spi_driver wm8510_spi_driver = { #endif /* CONFIG_SPI_MASTER */ #if IS_ENABLED(CONFIG_I2C) -static int wm8510_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8510_i2c_probe(struct i2c_client *i2c) { struct wm8510_priv *wm8510; int ret; @@ -680,7 +679,7 @@ static struct i2c_driver wm8510_i2c_driver = { .name = "wm8510", .of_match_table = wm8510_of_match, }, - .probe = wm8510_i2c_probe, + .probe_new = wm8510_i2c_probe, .id_table = wm8510_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index c8b50aac6c18..ba35a0221dc8 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -443,8 +443,7 @@ static const struct regmap_config wm8523_regmap = { .volatile_reg = wm8523_volatile_register, }; -static int wm8523_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8523_i2c_probe(struct i2c_client *i2c) { struct wm8523_priv *wm8523; unsigned int val; @@ -529,7 +528,7 @@ static struct i2c_driver wm8523_i2c_driver = { .name = "wm8523", .of_match_table = wm8523_of_match, }, - .probe = wm8523_i2c_probe, + .probe_new = wm8523_i2c_probe, .id_table = wm8523_i2c_id, }; diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 85ad2f03cfd0..84020195314d 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -996,8 +996,7 @@ static const struct of_device_id wm8580_of_match[] = { }; MODULE_DEVICE_TABLE(of, wm8580_of_match); -static int wm8580_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8580_i2c_probe(struct i2c_client *i2c) { const struct of_device_id *of_id; struct wm8580_priv *wm8580; @@ -1051,7 +1050,7 @@ static struct i2c_driver wm8580_i2c_driver = { .name = "wm8580", .of_match_table = wm8580_of_match, }, - .probe = wm8580_i2c_probe, + .probe_new = wm8580_i2c_probe, .id_table = wm8580_i2c_id, }; diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index bc4d161c59e5..b68a1ebcd061 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -432,8 +432,7 @@ static struct spi_driver wm8711_spi_driver = { #endif /* CONFIG_SPI_MASTER */ #if IS_ENABLED(CONFIG_I2C) -static int wm8711_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int wm8711_i2c_probe(struct i2c_client *client) { struct wm8711_priv *wm8711; int ret; @@ -466,7 +465,7 @@ static struct i2c_driver wm8711_i2c_driver = { .name = "wm8711", .of_match_table = wm8711_of_match, }, - .probe = wm8711_i2c_probe, + .probe_new = wm8711_i2c_probe, .id_table = wm8711_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 2cd58d133899..119ff0a1bb35 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -273,8 +273,7 @@ static struct spi_driver wm8728_spi_driver = { #endif /* CONFIG_SPI_MASTER */ #if IS_ENABLED(CONFIG_I2C) -static int wm8728_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8728_i2c_probe(struct i2c_client *i2c) { struct wm8728_priv *wm8728; int ret; @@ -307,7 +306,7 @@ static struct i2c_driver wm8728_i2c_driver = { .name = "wm8728", .of_match_table = wm8728_of_match, }, - .probe = wm8728_i2c_probe, + .probe_new = wm8728_i2c_probe, .id_table = wm8728_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8731-i2c.c b/sound/soc/codecs/wm8731-i2c.c new file mode 100644 index 000000000000..fdf03bf91606 --- /dev/null +++ b/sound/soc/codecs/wm8731-i2c.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8731-i2c.c -- WM8731 ALSA SoC Audio driver I2C code + * + * Copyright 2005 Openedhand Ltd. + * Copyright 2006-12 Wolfson Microelectronics, plc + * + * Author: Richard Purdie <richard@openedhand.com> + * + * Based on wm8753.c by Liam Girdwood + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> + +#include "wm8731.h" + + +static const struct of_device_id wm8731_of_match[] = { + { .compatible = "wlf,wm8731", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8731_of_match); + +static int wm8731_i2c_probe(struct i2c_client *i2c) +{ + struct wm8731_priv *wm8731; + int ret; + + wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv), + GFP_KERNEL); + if (wm8731 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, wm8731); + + wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); + if (IS_ERR(wm8731->regmap)) { + ret = PTR_ERR(wm8731->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + return wm8731_init(&i2c->dev, wm8731); +} + +static const struct i2c_device_id wm8731_i2c_id[] = { + { "wm8731", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); + +static struct i2c_driver wm8731_i2c_driver = { + .driver = { + .name = "wm8731", + .of_match_table = wm8731_of_match, + }, + .probe_new = wm8731_i2c_probe, + .id_table = wm8731_i2c_id, +}; + +module_i2c_driver(wm8731_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8731 driver - I2C"); +MODULE_AUTHOR("Richard Purdie"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8731-spi.c b/sound/soc/codecs/wm8731-spi.c new file mode 100644 index 000000000000..542ed097d89a --- /dev/null +++ b/sound/soc/codecs/wm8731-spi.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8731.c -- WM8731 ALSA SoC Audio driver + * + * Copyright 2005 Openedhand Ltd. + * Copyright 2006-12 Wolfson Microelectronics, plc + * + * Author: Richard Purdie <richard@openedhand.com> + * + * Based on wm8753.c by Liam Girdwood + */ + +#include <linux/spi/spi.h> +#include <linux/module.h> +#include <linux/of_device.h> + +#include "wm8731.h" + +static const struct of_device_id wm8731_of_match[] = { + { .compatible = "wlf,wm8731", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8731_of_match); + +static int wm8731_spi_probe(struct spi_device *spi) +{ + struct wm8731_priv *wm8731; + int ret; + + wm8731 = devm_kzalloc(&spi->dev, sizeof(*wm8731), GFP_KERNEL); + if (wm8731 == NULL) + return -ENOMEM; + + spi_set_drvdata(spi, wm8731); + + wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); + if (IS_ERR(wm8731->regmap)) { + ret = PTR_ERR(wm8731->regmap); + dev_err(&spi->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + return wm8731_init(&spi->dev, wm8731); +} + +static struct spi_driver wm8731_spi_driver = { + .driver = { + .name = "wm8731", + .of_match_table = wm8731_of_match, + }, + .probe = wm8731_spi_probe, +}; + +module_spi_driver(wm8731_spi_driver); + +MODULE_DESCRIPTION("ASoC WM8731 driver - SPI"); +MODULE_AUTHOR("Richard Purdie"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index b14c6d104e6d..2408c4a591d5 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -15,13 +15,9 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/i2c.h> #include <linux/slab.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> -#include <linux/spi/spi.h> -#include <linux/of_device.h> -#include <linux/mutex.h> #include <linux/clk.h> #include <sound/core.h> #include <sound/pcm.h> @@ -32,7 +28,6 @@ #include "wm8731.h" -#define WM8731_NUM_SUPPLIES 4 static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { "AVDD", "HPVDD", @@ -40,21 +35,6 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { "DBVDD", }; -/* codec private data */ -struct wm8731_priv { - struct regmap *regmap; - struct clk *mclk; - struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; - const struct snd_pcm_hw_constraint_list *constraints; - unsigned int sysclk; - int sysclk_type; - int playback_fs; - bool deemph; - - struct mutex lock; -}; - - /* * wm8731 register cache */ @@ -429,12 +409,11 @@ static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai, struct snd_soc_component *component = codec_dai->component; u16 iface = 0; - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: iface |= 0x0040; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; @@ -570,59 +549,6 @@ static struct snd_soc_dai_driver wm8731_dai = { .symmetric_rate = 1, }; -static int wm8731_request_supplies(struct device *dev, - struct wm8731_priv *wm8731) -{ - int ret = 0, i; - - for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) - wm8731->supplies[i].supply = wm8731_supply_names[i]; - - ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8731->supplies), - wm8731->supplies); - if (ret != 0) { - dev_err(dev, "Failed to request supplies: %d\n", ret); - return ret; - } - - ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), - wm8731->supplies); - if (ret != 0) { - dev_err(dev, "Failed to enable supplies: %d\n", ret); - return ret; - } - - return 0; -} - -static int wm8731_hw_init(struct device *dev, struct wm8731_priv *wm8731) -{ - int ret = 0; - - ret = wm8731_reset(wm8731->regmap); - if (ret < 0) { - dev_err(dev, "Failed to issue reset: %d\n", ret); - goto err; - } - - /* Clear POWEROFF, keep everything else disabled */ - regmap_write(wm8731->regmap, WM8731_PWR, 0x7f); - - /* Latch the update bits */ - regmap_update_bits(wm8731->regmap, WM8731_LOUT1V, 0x100, 0); - regmap_update_bits(wm8731->regmap, WM8731_ROUT1V, 0x100, 0); - regmap_update_bits(wm8731->regmap, WM8731_LINVOL, 0x100, 0); - regmap_update_bits(wm8731->regmap, WM8731_RINVOL, 0x100, 0); - - /* Disable bypass path by default */ - regmap_update_bits(wm8731->regmap, WM8731_APANA, 0x8, 0); - - regcache_mark_dirty(wm8731->regmap); - -err: - return ret; -} - static const struct snd_soc_component_driver soc_component_dev_wm8731 = { .set_bias_level = wm8731_set_bias_level, .controls = wm8731_snd_controls, @@ -638,136 +564,65 @@ static const struct snd_soc_component_driver soc_component_dev_wm8731 = { .non_legacy_dai_naming = 1, }; -static const struct of_device_id wm8731_of_match[] = { - { .compatible = "wlf,wm8731", }, - { } -}; - -MODULE_DEVICE_TABLE(of, wm8731_of_match); - -static const struct regmap_config wm8731_regmap = { - .reg_bits = 7, - .val_bits = 9, - - .max_register = WM8731_RESET, - .volatile_reg = wm8731_volatile, - - .cache_type = REGCACHE_RBTREE, - .reg_defaults = wm8731_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults), -}; - -#if defined(CONFIG_SPI_MASTER) -static int wm8731_spi_probe(struct spi_device *spi) +int wm8731_init(struct device *dev, struct wm8731_priv *wm8731) { - struct wm8731_priv *wm8731; - int ret; - - wm8731 = devm_kzalloc(&spi->dev, sizeof(*wm8731), GFP_KERNEL); - if (wm8731 == NULL) - return -ENOMEM; + int ret = 0, i; - wm8731->mclk = devm_clk_get(&spi->dev, "mclk"); + wm8731->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(wm8731->mclk)) { ret = PTR_ERR(wm8731->mclk); if (ret == -ENOENT) { wm8731->mclk = NULL; - dev_warn(&spi->dev, "Assuming static MCLK\n"); + dev_warn(dev, "Assuming static MCLK\n"); } else { - dev_err(&spi->dev, "Failed to get MCLK: %d\n", - ret); + dev_err(dev, "Failed to get MCLK: %d\n", ret); return ret; } } mutex_init(&wm8731->lock); - spi_set_drvdata(spi, wm8731); - - ret = wm8731_request_supplies(&spi->dev, wm8731); - if (ret != 0) - return ret; + for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) + wm8731->supplies[i].supply = wm8731_supply_names[i]; - wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); - if (IS_ERR(wm8731->regmap)) { - ret = PTR_ERR(wm8731->regmap); - dev_err(&spi->dev, "Failed to allocate register map: %d\n", - ret); + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) { + dev_err(dev, "Failed to request supplies: %d\n", ret); return ret; } - ret = wm8731_hw_init(&spi->dev, wm8731); - if (ret != 0) - return ret; - - ret = devm_snd_soc_register_component(&spi->dev, - &soc_component_dev_wm8731, &wm8731_dai, 1); + ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); if (ret != 0) { - dev_err(&spi->dev, "Failed to register CODEC: %d\n", ret); + dev_err(dev, "Failed to enable supplies: %d\n", ret); return ret; } - return 0; -} - -static struct spi_driver wm8731_spi_driver = { - .driver = { - .name = "wm8731", - .of_match_table = wm8731_of_match, - }, - .probe = wm8731_spi_probe, -}; -#endif /* CONFIG_SPI_MASTER */ - -#if IS_ENABLED(CONFIG_I2C) -static int wm8731_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct wm8731_priv *wm8731; - int ret; - - wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv), - GFP_KERNEL); - if (wm8731 == NULL) - return -ENOMEM; - - wm8731->mclk = devm_clk_get(&i2c->dev, "mclk"); - if (IS_ERR(wm8731->mclk)) { - ret = PTR_ERR(wm8731->mclk); - if (ret == -ENOENT) { - wm8731->mclk = NULL; - dev_warn(&i2c->dev, "Assuming static MCLK\n"); - } else { - dev_err(&i2c->dev, "Failed to get MCLK: %d\n", - ret); - return ret; - } + ret = wm8731_reset(wm8731->regmap); + if (ret < 0) { + dev_err(dev, "Failed to issue reset: %d\n", ret); + goto err_regulator_enable; } - mutex_init(&wm8731->lock); - - i2c_set_clientdata(i2c, wm8731); + /* Clear POWEROFF, keep everything else disabled */ + regmap_write(wm8731->regmap, WM8731_PWR, 0x7f); - ret = wm8731_request_supplies(&i2c->dev, wm8731); - if (ret != 0) - return ret; + /* Latch the update bits */ + regmap_update_bits(wm8731->regmap, WM8731_LOUT1V, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_ROUT1V, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_LINVOL, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_RINVOL, 0x100, 0); - wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); - if (IS_ERR(wm8731->regmap)) { - ret = PTR_ERR(wm8731->regmap); - dev_err(&i2c->dev, "Failed to allocate register map: %d\n", - ret); - goto err_regulator_enable; - } + /* Disable bypass path by default */ + regmap_update_bits(wm8731->regmap, WM8731_APANA, 0x8, 0); - ret = wm8731_hw_init(&i2c->dev, wm8731); - if (ret != 0) - goto err_regulator_enable; + regcache_mark_dirty(wm8731->regmap); - ret = devm_snd_soc_register_component(&i2c->dev, + ret = devm_snd_soc_register_component(dev, &soc_component_dev_wm8731, &wm8731_dai, 1); if (ret != 0) { - dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + dev_err(dev, "Failed to register CODEC: %d\n", ret); goto err_regulator_enable; } @@ -779,54 +634,20 @@ err_regulator_enable: return ret; } +EXPORT_SYMBOL_GPL(wm8731_init); -static const struct i2c_device_id wm8731_i2c_id[] = { - { "wm8731", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); - -static struct i2c_driver wm8731_i2c_driver = { - .driver = { - .name = "wm8731", - .of_match_table = wm8731_of_match, - }, - .probe = wm8731_i2c_probe, - .id_table = wm8731_i2c_id, -}; -#endif +const struct regmap_config wm8731_regmap = { + .reg_bits = 7, + .val_bits = 9, -static int __init wm8731_modinit(void) -{ - int ret = 0; -#if IS_ENABLED(CONFIG_I2C) - ret = i2c_add_driver(&wm8731_i2c_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n", - ret); - } -#endif -#if defined(CONFIG_SPI_MASTER) - ret = spi_register_driver(&wm8731_spi_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n", - ret); - } -#endif - return ret; -} -module_init(wm8731_modinit); + .max_register = WM8731_RESET, + .volatile_reg = wm8731_volatile, -static void __exit wm8731_exit(void) -{ -#if IS_ENABLED(CONFIG_I2C) - i2c_del_driver(&wm8731_i2c_driver); -#endif -#if defined(CONFIG_SPI_MASTER) - spi_unregister_driver(&wm8731_spi_driver); -#endif -} -module_exit(wm8731_exit); + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wm8731_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults), +}; +EXPORT_SYMBOL_GPL(wm8731_regmap); MODULE_DESCRIPTION("ASoC WM8731 driver"); MODULE_AUTHOR("Richard Purdie"); diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h index 4fcf1226d7c2..8d5a7a9ca6b2 100644 --- a/sound/soc/codecs/wm8731.h +++ b/sound/soc/codecs/wm8731.h @@ -12,6 +12,13 @@ #ifndef _WM8731_H #define _WM8731_H +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +struct clk; +struct snd_pcm_hw_constraint_list; + /* WM8731 register space */ #define WM8731_LINVOL 0x00 @@ -33,4 +40,24 @@ #define WM8731_DAI 0 +#define WM8731_NUM_SUPPLIES 4 + +/* codec private data */ +struct wm8731_priv { + struct regmap *regmap; + struct clk *mclk; + struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; + const struct snd_pcm_hw_constraint_list *constraints; + unsigned int sysclk; + int sysclk_type; + int playback_fs; + bool deemph; + + struct mutex lock; +}; + +extern const struct regmap_config wm8731_regmap; + +int wm8731_init(struct device *dev, struct wm8731_priv *wm8731); + #endif diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index 7a3f9fbe8d53..5778091d1c09 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -606,8 +606,7 @@ static const struct regmap_config wm8737_regmap = { }; #if IS_ENABLED(CONFIG_I2C) -static int wm8737_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8737_i2c_probe(struct i2c_client *i2c) { struct wm8737_priv *wm8737; int ret, i; @@ -651,7 +650,7 @@ static struct i2c_driver wm8737_i2c_driver = { .name = "wm8737", .of_match_table = wm8737_of_match, }, - .probe = wm8737_i2c_probe, + .probe_new = wm8737_i2c_probe, .id_table = wm8737_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 0e3994326936..871e2c5421b8 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -565,8 +565,7 @@ static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741) } #if IS_ENABLED(CONFIG_I2C) -static int wm8741_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8741_i2c_probe(struct i2c_client *i2c) { struct wm8741_priv *wm8741; int ret, i; @@ -618,7 +617,7 @@ static struct i2c_driver wm8741_i2c_driver = { .name = "wm8741", .of_match_table = wm8741_of_match, }, - .probe = wm8741_i2c_probe, + .probe_new = wm8741_i2c_probe, .id_table = wm8741_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 9491817020d8..1426fc1f7c5a 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -780,8 +780,7 @@ static struct spi_driver wm8750_spi_driver = { #endif /* CONFIG_SPI_MASTER */ #if IS_ENABLED(CONFIG_I2C) -static int wm8750_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8750_i2c_probe(struct i2c_client *i2c) { struct wm8750_priv *wm8750; struct regmap *regmap; @@ -815,7 +814,7 @@ static struct i2c_driver wm8750_i2c_driver = { .name = "wm8750", .of_match_table = wm8750_of_match, }, - .probe = wm8750_i2c_probe, + .probe_new = wm8750_i2c_probe, .id_table = wm8750_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index deaa54be6268..931134d334ec 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1552,8 +1552,7 @@ static struct spi_driver wm8753_spi_driver = { #endif /* CONFIG_SPI_MASTER */ #if IS_ENABLED(CONFIG_I2C) -static int wm8753_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8753_i2c_probe(struct i2c_client *i2c) { struct wm8753_priv *wm8753; int ret; @@ -1592,7 +1591,7 @@ static struct i2c_driver wm8753_i2c_driver = { .name = "wm8753", .of_match_table = wm8753_of_match, }, - .probe = wm8753_i2c_probe, + .probe_new = wm8753_i2c_probe, .id_table = wm8753_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index 554acf56130c..f164cb6744c4 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -490,8 +490,7 @@ static struct spi_driver wm8776_spi_driver = { #endif /* CONFIG_SPI_MASTER */ #if IS_ENABLED(CONFIG_I2C) -static int wm8776_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8776_i2c_probe(struct i2c_client *i2c) { struct wm8776_priv *wm8776; int ret; @@ -525,7 +524,7 @@ static struct i2c_driver wm8776_i2c_driver = { .name = "wm8776", .of_match_table = wm8776_of_match, }, - .probe = wm8776_i2c_probe, + .probe_new = wm8776_i2c_probe, .id_table = wm8776_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c index f97a75e64166..04dc9fb5afb4 100644 --- a/sound/soc/codecs/wm8804-i2c.c +++ b/sound/soc/codecs/wm8804-i2c.c @@ -14,8 +14,7 @@ #include "wm8804.h" -static int wm8804_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8804_i2c_probe(struct i2c_client *i2c) { struct regmap *regmap; @@ -62,7 +61,7 @@ static struct i2c_driver wm8804_i2c_driver = { .of_match_table = of_match_ptr(wm8804_of_match), .acpi_match_table = ACPI_PTR(wm8804_acpi_match), }, - .probe = wm8804_i2c_probe, + .probe_new = wm8804_i2c_probe, .remove = wm8804_i2c_remove, .id_table = wm8804_i2c_id }; diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index bf3a4415a85f..84a3daf0c11e 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1261,8 +1261,7 @@ static struct spi_driver wm8900_spi_driver = { #endif /* CONFIG_SPI_MASTER */ #if IS_ENABLED(CONFIG_I2C) -static int wm8900_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8900_i2c_probe(struct i2c_client *i2c) { struct wm8900_priv *wm8900; int ret; @@ -1299,7 +1298,7 @@ static struct i2c_driver wm8900_i2c_driver = { .driver = { .name = "wm8900", }, - .probe = wm8900_i2c_probe, + .probe_new = wm8900_i2c_probe, .remove = wm8900_i2c_remove, .id_table = wm8900_i2c_id, }; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 75f30154c809..3c95c2aea515 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1981,8 +1981,7 @@ static int wm8903_set_pdata_from_of(struct i2c_client *i2c, return 0; } -static int wm8903_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8903_i2c_probe(struct i2c_client *i2c) { struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev); struct wm8903_priv *wm8903; @@ -2132,7 +2131,7 @@ static int wm8903_i2c_probe(struct i2c_client *i2c, if (ret != 0) { dev_err(wm8903->dev, "Failed to request IRQ: %d\n", ret); - return ret; + goto err; } /* Enable write sequencer interrupts */ @@ -2214,7 +2213,7 @@ static struct i2c_driver wm8903_i2c_driver = { .name = "wm8903", .of_match_table = wm8903_of_match, }, - .probe = wm8903_i2c_probe, + .probe_new = wm8903_i2c_probe, .remove = wm8903_i2c_remove, .id_table = wm8903_i2c_id, }; diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index a02a77fef360..04bb8e392497 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -2162,8 +2162,9 @@ static const struct of_device_id wm8904_of_match[] = { MODULE_DEVICE_TABLE(of, wm8904_of_match); #endif -static int wm8904_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static const struct i2c_device_id wm8904_i2c_id[]; + +static int wm8904_i2c_probe(struct i2c_client *i2c) { struct wm8904_priv *wm8904; unsigned int val; @@ -2197,6 +2198,8 @@ static int wm8904_i2c_probe(struct i2c_client *i2c, return -EINVAL; wm8904->devtype = (enum wm8904_type)match->data; } else { + const struct i2c_device_id *id = + i2c_match_id(wm8904_i2c_id, i2c); wm8904->devtype = id->driver_data; } @@ -2328,7 +2331,7 @@ static struct i2c_driver wm8904_i2c_driver = { .name = "wm8904", .of_match_table = of_match_ptr(wm8904_of_match), }, - .probe = wm8904_i2c_probe, + .probe_new = wm8904_i2c_probe, .id_table = wm8904_i2c_id, }; diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 440d048ef0c0..589394d420ce 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -750,8 +750,7 @@ static const struct regmap_config wm8940_regmap = { .volatile_reg = wm8940_volatile_register, }; -static int wm8940_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8940_i2c_probe(struct i2c_client *i2c) { struct wm8940_priv *wm8940; int ret; @@ -779,11 +778,18 @@ static const struct i2c_device_id wm8940_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id); +static const struct of_device_id wm8940_of_match[] = { + { .compatible = "wlf,wm8940", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8940_of_match); + static struct i2c_driver wm8940_i2c_driver = { .driver = { .name = "wm8940", + .of_match_table = wm8940_of_match, }, - .probe = wm8940_i2c_probe, + .probe_new = wm8940_i2c_probe, .id_table = wm8940_i2c_id, }; diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 513df47bd87d..80e3cbd704ee 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -968,8 +968,7 @@ static const struct regmap_config wm8955_regmap = { .num_reg_defaults = ARRAY_SIZE(wm8955_reg_defaults), }; -static int wm8955_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8955_i2c_probe(struct i2c_client *i2c) { struct wm8955_priv *wm8955; int ret; @@ -1005,7 +1004,7 @@ static struct i2c_driver wm8955_i2c_driver = { .driver = { .name = "wm8955", }, - .probe = wm8955_i2c_probe, + .probe_new = wm8955_i2c_probe, .id_table = wm8955_i2c_id, }; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index ca7660f4bb05..8c8f32b23083 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -14,6 +14,7 @@ #include <linux/pm.h> #include <linux/clk.h> #include <linux/i2c.h> +#include <linux/acpi.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -1410,8 +1411,7 @@ static void wm8960_set_pdata_from_of(struct i2c_client *i2c, ARRAY_SIZE(pdata->hp_cfg)); } -static int wm8960_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8960_i2c_probe(struct i2c_client *i2c) { struct wm8960_data *pdata = dev_get_platdata(&i2c->dev); struct wm8960_priv *wm8960; @@ -1498,18 +1498,30 @@ static const struct i2c_device_id wm8960_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id); +#if defined(CONFIG_OF) static const struct of_device_id wm8960_of_match[] = { { .compatible = "wlf,wm8960", }, { } }; MODULE_DEVICE_TABLE(of, wm8960_of_match); +#endif + +#if defined(CONFIG_ACPI) +static const struct acpi_device_id wm8960_acpi_match[] = { + { "1AEC8960", 0 }, /* Wolfson PCI ID + part ID */ + { "10138960", 0 }, /* Cirrus Logic PCI ID + part ID */ + { }, +}; +MODULE_DEVICE_TABLE(acpi, wm8960_acpi_match); +#endif static struct i2c_driver wm8960_i2c_driver = { .driver = { .name = "wm8960", - .of_match_table = wm8960_of_match, + .of_match_table = of_match_ptr(wm8960_of_match), + .acpi_match_table = ACPI_PTR(wm8960_acpi_match), }, - .probe = wm8960_i2c_probe, + .probe_new = wm8960_i2c_probe, .remove = wm8960_i2c_remove, .id_table = wm8960_i2c_id, }; diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index ef80d9fc1eec..69eb731dbf4b 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -911,8 +911,7 @@ static const struct regmap_config wm8961_regmap = { .readable_reg = wm8961_readable, }; -static int wm8961_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8961_i2c_probe(struct i2c_client *i2c) { struct wm8961_priv *wm8961; unsigned int val; @@ -977,7 +976,7 @@ static struct i2c_driver wm8961_i2c_driver = { .driver = { .name = "wm8961", }, - .probe = wm8961_i2c_probe, + .probe_new = wm8961_i2c_probe, .id_table = wm8961_i2c_id, }; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 2c41d31956aa..34cd5a2a997c 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2896,9 +2896,8 @@ static int wm8962_set_fll(struct snd_soc_component *component, int fll_id, int s reinit_completion(&wm8962->fll_lock); - ret = pm_runtime_get_sync(component->dev); + ret = pm_runtime_resume_and_get(component->dev); if (ret < 0) { - pm_runtime_put_noidle(component->dev); dev_err(component->dev, "Failed to resume device: %d\n", ret); return ret; } @@ -3030,9 +3029,8 @@ static irqreturn_t wm8962_irq(int irq, void *data) unsigned int active; int reg, ret; - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) { - pm_runtime_put_noidle(dev); dev_err(dev, "Failed to resume: %d\n", ret); return IRQ_NONE; } @@ -3555,8 +3553,7 @@ static int wm8962_set_pdata_from_of(struct i2c_client *i2c, return PTR_ERR_OR_ZERO(pdata->mclk); } -static int wm8962_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8962_i2c_probe(struct i2c_client *i2c) { struct wm8962_pdata *pdata = dev_get_platdata(&i2c->dev); struct wm8962_priv *wm8962; @@ -3892,7 +3889,7 @@ static struct i2c_driver wm8962_i2c_driver = { .of_match_table = wm8962_of_match, .pm = &wm8962_pm, }, - .probe = wm8962_i2c_probe, + .probe_new = wm8962_i2c_probe, .remove = wm8962_i2c_remove, .id_table = wm8962_i2c_id, }; diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index ddf0e2f5e66a..8a289b048e66 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -672,8 +672,7 @@ static const struct regmap_config wm8971_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int wm8971_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8971_i2c_probe(struct i2c_client *i2c) { struct wm8971_priv *wm8971; @@ -702,7 +701,7 @@ static struct i2c_driver wm8971_i2c_driver = { .driver = { .name = "wm8971", }, - .probe = wm8971_i2c_probe, + .probe_new = wm8971_i2c_probe, .id_table = wm8971_i2c_id, }; diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index fdc68ab49742..a8d7809f3f64 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -685,8 +685,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8974 = { .non_legacy_dai_naming = 1, }; -static int wm8974_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8974_i2c_probe(struct i2c_client *i2c) { struct wm8974_priv *priv; struct regmap *regmap; @@ -725,7 +724,7 @@ static struct i2c_driver wm8974_i2c_driver = { .name = "wm8974", .of_match_table = wm8974_of_match, }, - .probe = wm8974_i2c_probe, + .probe_new = wm8974_i2c_probe, .id_table = wm8974_i2c_id, }; diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index 7091e1a9d516..141f50bfec68 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -1020,8 +1020,7 @@ static const struct regmap_config wm8978_regmap_config = { .num_reg_defaults = ARRAY_SIZE(wm8978_reg_defaults), }; -static int wm8978_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8978_i2c_probe(struct i2c_client *i2c) { struct wm8978_priv *wm8978; int ret; @@ -1074,7 +1073,7 @@ static struct i2c_driver wm8978_i2c_driver = { .name = "wm8978", .of_match_table = wm8978_of_match, }, - .probe = wm8978_i2c_probe, + .probe_new = wm8978_i2c_probe, .id_table = wm8978_i2c_id, }; diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index d8ed22a9caac..ae89554d47bc 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -1035,8 +1035,7 @@ static struct spi_driver wm8983_spi_driver = { #endif #if IS_ENABLED(CONFIG_I2C) -static int wm8983_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8983_i2c_probe(struct i2c_client *i2c) { struct wm8983_priv *wm8983; int ret; @@ -1070,7 +1069,7 @@ static struct i2c_driver wm8983_i2c_driver = { .driver = { .name = "wm8983", }, - .probe = wm8983_i2c_probe, + .probe_new = wm8983_i2c_probe, .id_table = wm8983_i2c_id }; #endif diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index a7e01106fbc0..cf2c32eac773 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -1167,10 +1167,12 @@ static struct spi_driver wm8985_spi_driver = { #endif #if IS_ENABLED(CONFIG_I2C) -static int wm8985_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static const struct i2c_device_id wm8985_i2c_id[]; + +static int wm8985_i2c_probe(struct i2c_client *i2c) { struct wm8985_priv *wm8985; + const struct i2c_device_id *id = i2c_match_id(wm8985_i2c_id, i2c); int ret; wm8985 = devm_kzalloc(&i2c->dev, sizeof *wm8985, GFP_KERNEL); @@ -1205,7 +1207,7 @@ static struct i2c_driver wm8985_i2c_driver = { .driver = { .name = "wm8985", }, - .probe = wm8985_i2c_probe, + .probe_new = wm8985_i2c_probe, .id_table = wm8985_i2c_id }; #endif diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 1d2f881bbcae..27538d6598cf 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -872,8 +872,7 @@ static struct spi_driver wm8988_spi_driver = { #endif /* CONFIG_SPI_MASTER */ #if IS_ENABLED(CONFIG_I2C) -static int wm8988_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8988_i2c_probe(struct i2c_client *i2c) { struct wm8988_priv *wm8988; int ret; @@ -907,7 +906,7 @@ static struct i2c_driver wm8988_i2c_driver = { .driver = { .name = "wm8988", }, - .probe = wm8988_i2c_probe, + .probe_new = wm8988_i2c_probe, .id_table = wm8988_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 938940777e5d..c9448a59c872 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -1220,8 +1220,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8990 = { .non_legacy_dai_naming = 1, }; -static int wm8990_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8990_i2c_probe(struct i2c_client *i2c) { struct wm8990_priv *wm8990; int ret; @@ -1249,7 +1248,7 @@ static struct i2c_driver wm8990_i2c_driver = { .driver = { .name = "wm8990", }, - .probe = wm8990_i2c_probe, + .probe_new = wm8990_i2c_probe, .id_table = wm8990_i2c_id, }; diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index 16bc8609d0d2..998bc89bb7e1 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -1257,8 +1257,7 @@ static const struct regmap_config wm8991_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int wm8991_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8991_i2c_probe(struct i2c_client *i2c) { struct wm8991_priv *wm8991; unsigned int val; @@ -1325,7 +1324,7 @@ static struct i2c_driver wm8991_i2c_driver = { .driver = { .name = "wm8991", }, - .probe = wm8991_i2c_probe, + .probe_new = wm8991_i2c_probe, .id_table = wm8991_i2c_id, }; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index c4f41692b806..f4da77ec9d6c 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1624,8 +1624,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8993 = { .non_legacy_dai_naming = 1, }; -static int wm8993_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8993_i2c_probe(struct i2c_client *i2c) { struct wm8993_priv *wm8993; unsigned int reg; @@ -1745,7 +1744,7 @@ static struct i2c_driver wm8993_i2c_driver = { .driver = { .name = "wm8993", }, - .probe = wm8993_i2c_probe, + .probe_new = wm8993_i2c_probe, .remove = wm8993_i2c_remove, .id_table = wm8993_i2c_id, }; diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index b896d9c5bea0..ea9727446707 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -2231,8 +2231,7 @@ static struct spi_driver wm8995_spi_driver = { #endif #if IS_ENABLED(CONFIG_I2C) -static int wm8995_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8995_i2c_probe(struct i2c_client *i2c) { struct wm8995_priv *wm8995; int ret; @@ -2270,7 +2269,7 @@ static struct i2c_driver wm8995_i2c_driver = { .driver = { .name = "wm8995", }, - .probe = wm8995_i2c_probe, + .probe_new = wm8995_i2c_probe, .id_table = wm8995_i2c_id }; #endif diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 197ae7d84a49..f7bb27d1c76d 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -2755,8 +2755,7 @@ static struct snd_soc_dai_driver wm8996_dai[] = { }, }; -static int wm8996_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8996_i2c_probe(struct i2c_client *i2c) { struct wm8996_priv *wm8996; int ret, i; @@ -3091,7 +3090,7 @@ static struct i2c_driver wm8996_i2c_driver = { .driver = { .name = "wm8996", }, - .probe = wm8996_i2c_probe, + .probe_new = wm8996_i2c_probe, .remove = wm8996_i2c_remove, .id_table = wm8996_i2c_id, }; diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 4a667ee82fe2..87b58448cea7 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1299,8 +1299,7 @@ static const struct regmap_config wm9081_regmap = { .cache_type = REGCACHE_RBTREE, }; -static int wm9081_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm9081_i2c_probe(struct i2c_client *i2c) { struct wm9081_priv *wm9081; unsigned int reg; @@ -1373,7 +1372,7 @@ static struct i2c_driver wm9081_i2c_driver = { .driver = { .name = "wm9081", }, - .probe = wm9081_i2c_probe, + .probe_new = wm9081_i2c_probe, .remove = wm9081_i2c_remove, .id_table = wm9081_i2c_id, }; diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index e0231a54609c..f7d80f1e37a8 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -543,7 +543,6 @@ static const struct snd_soc_component_driver soc_component_dev_wm9090 = { .suspend_bias_off = 1, .idle_bias_on = 1, .use_pmdown_time = 1, - .endianness = 1, .non_legacy_dai_naming = 1, }; @@ -561,8 +560,7 @@ static const struct regmap_config wm9090_regmap = { }; -static int wm9090_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm9090_i2c_probe(struct i2c_client *i2c) { struct wm9090_priv *wm9090; unsigned int reg; @@ -619,7 +617,7 @@ static struct i2c_driver wm9090_i2c_driver = { .driver = { .name = "wm9090", }, - .probe = wm9090_i2c_probe, + .probe_new = wm9090_i2c_probe, .id_table = wm9090_id, }; diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 616b26c70c3b..f3a56f3ce487 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -1066,6 +1066,7 @@ static const struct snd_soc_component_driver wsa881x_component_drv = { .num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets), .dapm_routes = wsa881x_audio_map, .num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map), + .endianness = 1, }; static int wsa881x_update_status(struct sdw_slave *slave, diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index d7d1536a4f37..20a9f8e924b3 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -11,7 +11,7 @@ #include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/of_platform.h> -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> #include <linux/pm_runtime.h> #include <sound/dmaengine_pcm.h> #include <sound/pcm_params.h> @@ -1211,11 +1211,9 @@ static int fsl_asrc_probe(struct platform_device *pdev) goto err_pm_disable; } - ret = pm_runtime_get_sync(&pdev->dev); - if (ret < 0) { - pm_runtime_put_noidle(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) goto err_pm_get_sync; - } ret = fsl_asrc_init(asrc); if (ret) { diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index cd9b36ec0ecb..5038faf035cb 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -8,7 +8,7 @@ #include <linux/dma-mapping.h> #include <linux/module.h> -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> #include <sound/dmaengine_pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h index 30620d56252c..86d5c360d4f5 100644 --- a/sound/soc/fsl/fsl_easrc.h +++ b/sound/soc/fsl/fsl_easrc.h @@ -7,7 +7,7 @@ #define _FSL_EASRC_H #include <sound/asound.h> -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> #include "fsl_asrc_common.h" diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index ed444e8f1d6b..1a2bdf8e76f0 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -1050,11 +1050,9 @@ static int fsl_esai_probe(struct platform_device *pdev) goto err_pm_disable; } - ret = pm_runtime_get_sync(&pdev->dev); - if (ret < 0) { - pm_runtime_put_noidle(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) goto err_pm_get_sync; - } ret = fsl_esai_hw_init(esai_priv); if (ret) diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 9f90989ac59a..7b88d52f27de 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright 2018 NXP +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/interrupt.h> @@ -15,6 +16,7 @@ #include <linux/regmap.h> #include <linux/sysfs.h> #include <linux/types.h> +#include <linux/dma/imx-dma.h> #include <sound/dmaengine_pcm.h> #include <sound/pcm.h> #include <sound/soc.h> @@ -22,10 +24,17 @@ #include <sound/core.h> #include "fsl_micfil.h" -#include "imx-pcm.h" -#define FSL_MICFIL_RATES SNDRV_PCM_RATE_8000_48000 -#define FSL_MICFIL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) +#define MICFIL_OSR_DEFAULT 16 + +enum quality { + QUALITY_HIGH, + QUALITY_MEDIUM, + QUALITY_LOW, + QUALITY_VLOW0, + QUALITY_VLOW1, + QUALITY_VLOW2, +}; struct fsl_micfil { struct platform_device *pdev; @@ -34,13 +43,11 @@ struct fsl_micfil { struct clk *busclk; struct clk *mclk; struct snd_dmaengine_dai_dma_data dma_params_rx; + struct sdma_peripheral_config sdmacfg; unsigned int dataline; char name[32]; int irq[MICFIL_IRQ_LINES]; - unsigned int mclk_streams; - int quality; /*QUALITY 2-0 bits */ - bool slave_mode; - int channel_gain[8]; + enum quality quality; }; struct fsl_micfil_soc_data { @@ -48,6 +55,7 @@ struct fsl_micfil_soc_data { unsigned int fifo_depth; unsigned int dataline; bool imx; + u64 formats; }; static struct fsl_micfil_soc_data fsl_micfil_imx8mm = { @@ -55,37 +63,91 @@ static struct fsl_micfil_soc_data fsl_micfil_imx8mm = { .fifos = 8, .fifo_depth = 8, .dataline = 0xf, + .formats = SNDRV_PCM_FMTBIT_S16_LE, +}; + +static struct fsl_micfil_soc_data fsl_micfil_imx8mp = { + .imx = true, + .fifos = 8, + .fifo_depth = 32, + .dataline = 0xf, + .formats = SNDRV_PCM_FMTBIT_S32_LE, }; static const struct of_device_id fsl_micfil_dt_ids[] = { { .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm }, + { .compatible = "fsl,imx8mp-micfil", .data = &fsl_micfil_imx8mp }, {} }; MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids); -/* Table 5. Quality Modes - * Medium 0 0 0 - * High 0 0 1 - * Very Low 2 1 0 0 - * Very Low 1 1 0 1 - * Very Low 0 1 1 0 - * Low 1 1 1 - */ static const char * const micfil_quality_select_texts[] = { - "Medium", "High", - "N/A", "N/A", - "VLow2", "VLow1", - "VLow0", "Low", + [QUALITY_HIGH] = "High", + [QUALITY_MEDIUM] = "Medium", + [QUALITY_LOW] = "Low", + [QUALITY_VLOW0] = "VLow0", + [QUALITY_VLOW1] = "Vlow1", + [QUALITY_VLOW2] = "Vlow2", }; static const struct soc_enum fsl_micfil_quality_enum = - SOC_ENUM_SINGLE(REG_MICFIL_CTRL2, - MICFIL_CTRL2_QSEL_SHIFT, - ARRAY_SIZE(micfil_quality_select_texts), - micfil_quality_select_texts); + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(micfil_quality_select_texts), + micfil_quality_select_texts); static DECLARE_TLV_DB_SCALE(gain_tlv, 0, 100, 0); +static int micfil_set_quality(struct fsl_micfil *micfil) +{ + u32 qsel; + + switch (micfil->quality) { + case QUALITY_HIGH: + qsel = MICFIL_QSEL_HIGH_QUALITY; + break; + case QUALITY_MEDIUM: + qsel = MICFIL_QSEL_MEDIUM_QUALITY; + break; + case QUALITY_LOW: + qsel = MICFIL_QSEL_LOW_QUALITY; + break; + case QUALITY_VLOW0: + qsel = MICFIL_QSEL_VLOW0_QUALITY; + break; + case QUALITY_VLOW1: + qsel = MICFIL_QSEL_VLOW1_QUALITY; + break; + case QUALITY_VLOW2: + qsel = MICFIL_QSEL_VLOW2_QUALITY; + break; + } + + return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, + MICFIL_CTRL2_QSEL, + FIELD_PREP(MICFIL_CTRL2_QSEL, qsel)); +} + +static int micfil_quality_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); + + ucontrol->value.integer.value[0] = micfil->quality; + + return 0; +} + +static int micfil_quality_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct fsl_micfil *micfil = snd_soc_component_get_drvdata(cmpnt); + + micfil->quality = ucontrol->value.integer.value[0]; + + return micfil_set_quality(micfil); +} + static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { SOC_SINGLE_SX_TLV("CH0 Volume", REG_MICFIL_OUT_CTRL, MICFIL_OUTGAIN_CHX_SHIFT(0), 0xF, 0x7, gain_tlv), @@ -105,64 +167,9 @@ static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { MICFIL_OUTGAIN_CHX_SHIFT(7), 0xF, 0x7, gain_tlv), SOC_ENUM_EXT("MICFIL Quality Select", fsl_micfil_quality_enum, - snd_soc_get_enum_double, snd_soc_put_enum_double), + micfil_quality_get, micfil_quality_set), }; -static inline int get_pdm_clk(struct fsl_micfil *micfil, - unsigned int rate) -{ - u32 ctrl2_reg; - int qsel, osr; - int bclk; - - regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg); - osr = 16 - ((ctrl2_reg & MICFIL_CTRL2_CICOSR_MASK) - >> MICFIL_CTRL2_CICOSR_SHIFT); - - regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg); - qsel = ctrl2_reg & MICFIL_CTRL2_QSEL_MASK; - - switch (qsel) { - case MICFIL_HIGH_QUALITY: - bclk = rate * 8 * osr / 2; /* kfactor = 0.5 */ - break; - case MICFIL_MEDIUM_QUALITY: - case MICFIL_VLOW0_QUALITY: - bclk = rate * 4 * osr * 1; /* kfactor = 1 */ - break; - case MICFIL_LOW_QUALITY: - case MICFIL_VLOW1_QUALITY: - bclk = rate * 2 * osr * 2; /* kfactor = 2 */ - break; - case MICFIL_VLOW2_QUALITY: - bclk = rate * osr * 4; /* kfactor = 4 */ - break; - default: - dev_err(&micfil->pdev->dev, - "Please make sure you select a valid quality.\n"); - bclk = -1; - break; - } - - return bclk; -} - -static inline int get_clk_div(struct fsl_micfil *micfil, - unsigned int rate) -{ - u32 ctrl2_reg; - long mclk_rate; - int clk_div; - - regmap_read(micfil->regmap, REG_MICFIL_CTRL2, &ctrl2_reg); - - mclk_rate = clk_get_rate(micfil->mclk); - - clk_div = mclk_rate / (get_pdm_clk(micfil, rate) * 2); - - return clk_div; -} - /* The SRES is a self-negated bit which provides the CPU with the * capability to initialize the PDM Interface module through the * slave-bus interface. This bit always reads as zero, and this @@ -173,45 +180,19 @@ static int fsl_micfil_reset(struct device *dev) struct fsl_micfil *micfil = dev_get_drvdata(dev); int ret; - ret = regmap_update_bits(micfil->regmap, - REG_MICFIL_CTRL1, - MICFIL_CTRL1_MDIS_MASK, - 0); - if (ret) { - dev_err(dev, "failed to clear MDIS bit %d\n", ret); + ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_MDIS); + if (ret) return ret; - } - ret = regmap_update_bits(micfil->regmap, - REG_MICFIL_CTRL1, - MICFIL_CTRL1_SRES_MASK, - MICFIL_CTRL1_SRES); - if (ret) { - dev_err(dev, "failed to reset MICFIL: %d\n", ret); + ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_SRES); + if (ret) return ret; - } return 0; } -static int fsl_micfil_set_mclk_rate(struct fsl_micfil *micfil, - unsigned int freq) -{ - struct device *dev = &micfil->pdev->dev; - int ret; - - clk_disable_unprepare(micfil->mclk); - - ret = clk_set_rate(micfil->mclk, freq * 1024); - if (ret) - dev_warn(dev, "failed to set rate (%u): %d\n", - freq * 1024, ret); - - clk_prepare_enable(micfil->mclk); - - return ret; -} - static int fsl_micfil_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -249,42 +230,32 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, * 11 - reserved */ ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, - MICFIL_CTRL1_DISEL_MASK, - (1 << MICFIL_CTRL1_DISEL_SHIFT)); - if (ret) { - dev_err(dev, "failed to update DISEL bits\n"); + MICFIL_CTRL1_DISEL, + FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DMA)); + if (ret) return ret; - } /* Enable the module */ - ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, - MICFIL_CTRL1_PDMIEN_MASK, - MICFIL_CTRL1_PDMIEN); - if (ret) { - dev_err(dev, "failed to enable the module\n"); + ret = regmap_set_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_PDMIEN); + if (ret) return ret; - } break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* Disable the module */ - ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, - MICFIL_CTRL1_PDMIEN_MASK, - 0); - if (ret) { - dev_err(dev, "failed to enable the module\n"); + ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_PDMIEN); + if (ret) return ret; - } ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, - MICFIL_CTRL1_DISEL_MASK, - (0 << MICFIL_CTRL1_DISEL_SHIFT)); - if (ret) { - dev_err(dev, "failed to update DISEL bits\n"); + MICFIL_CTRL1_DISEL, + FIELD_PREP(MICFIL_CTRL1_DISEL, MICFIL_CTRL1_DISEL_DISABLE)); + if (ret) return ret; - } break; default: return -EINVAL; @@ -292,39 +263,6 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static int fsl_set_clock_params(struct device *dev, unsigned int rate) -{ - struct fsl_micfil *micfil = dev_get_drvdata(dev); - int clk_div; - int ret; - - ret = fsl_micfil_set_mclk_rate(micfil, rate); - if (ret < 0) - dev_err(dev, "failed to set mclk[%lu] to rate %u\n", - clk_get_rate(micfil->mclk), rate); - - /* set CICOSR */ - ret |= regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, - MICFIL_CTRL2_CICOSR_MASK, - MICFIL_CTRL2_OSR_DEFAULT); - if (ret) - dev_err(dev, "failed to set CICOSR in reg 0x%X\n", - REG_MICFIL_CTRL2); - - /* set CLK_DIV */ - clk_div = get_clk_div(micfil, rate); - if (clk_div < 0) - ret = -EINVAL; - - ret |= regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, - MICFIL_CTRL2_CLKDIV_MASK, clk_div); - if (ret) - dev_err(dev, "failed to set CLKDIV in reg 0x%X\n", - REG_MICFIL_CTRL2); - - return ret; -} - static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -332,97 +270,69 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); unsigned int channels = params_channels(params); unsigned int rate = params_rate(params); - struct device *dev = &micfil->pdev->dev; + int clk_div = 8; + int osr = MICFIL_OSR_DEFAULT; int ret; /* 1. Disable the module */ - ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, - MICFIL_CTRL1_PDMIEN_MASK, 0); - if (ret) { - dev_err(dev, "failed to disable the module\n"); + ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_PDMIEN); + if (ret) return ret; - } /* enable channels */ ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL1, 0xFF, ((1 << channels) - 1)); - if (ret) { - dev_err(dev, "failed to enable channels %d, reg 0x%X\n", ret, - REG_MICFIL_CTRL1); + if (ret) return ret; - } - ret = fsl_set_clock_params(dev, rate); - if (ret < 0) { - dev_err(dev, "Failed to set clock parameters [%d]\n", ret); + ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8); + if (ret) + return ret; + + ret = micfil_set_quality(micfil); + if (ret) return ret; - } + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, + MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR, + FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) | + FIELD_PREP(MICFIL_CTRL2_CICOSR, 16 - osr)); + + micfil->dma_params_rx.peripheral_config = &micfil->sdmacfg; + micfil->dma_params_rx.peripheral_size = sizeof(micfil->sdmacfg); + micfil->sdmacfg.n_fifos_src = channels; + micfil->sdmacfg.sw_done = true; micfil->dma_params_rx.maxburst = channels * MICFIL_DMA_MAXBURST_RX; return 0; } -static int fsl_micfil_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, - unsigned int freq, int dir) -{ - struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); - struct device *dev = &micfil->pdev->dev; - - int ret; - - if (!freq) - return 0; - - ret = fsl_micfil_set_mclk_rate(micfil, freq); - if (ret < 0) - dev_err(dev, "failed to set mclk[%lu] to rate %u\n", - clk_get_rate(micfil->mclk), freq); - - return ret; -} - static const struct snd_soc_dai_ops fsl_micfil_dai_ops = { .startup = fsl_micfil_startup, .trigger = fsl_micfil_trigger, .hw_params = fsl_micfil_hw_params, - .set_sysclk = fsl_micfil_set_dai_sysclk, }; static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev); - struct device *dev = cpu_dai->dev; - unsigned int val; int ret; - int i; - /* set qsel to medium */ - ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, - MICFIL_CTRL2_QSEL_MASK, MICFIL_MEDIUM_QUALITY); - if (ret) { - dev_err(dev, "failed to set quality mode bits, reg 0x%X\n", - REG_MICFIL_CTRL2); - return ret; - } + micfil->quality = QUALITY_MEDIUM; /* set default gain to max_gain */ regmap_write(micfil->regmap, REG_MICFIL_OUT_CTRL, 0x77777777); - for (i = 0; i < 8; i++) - micfil->channel_gain[i] = 0xF; snd_soc_dai_init_dma_data(cpu_dai, NULL, &micfil->dma_params_rx); /* FIFO Watermark Control - FIFOWMK*/ - val = MICFIL_FIFO_CTRL_FIFOWMK(micfil->soc->fifo_depth) - 1; ret = regmap_update_bits(micfil->regmap, REG_MICFIL_FIFO_CTRL, - MICFIL_FIFO_CTRL_FIFOWMK_MASK, - val); - if (ret) { - dev_err(dev, "failed to set FIFOWMK\n"); + MICFIL_FIFO_CTRL_FIFOWMK, + FIELD_PREP(MICFIL_FIFO_CTRL_FIFOWMK, micfil->soc->fifo_depth - 1)); + if (ret) return ret; - } return 0; } @@ -433,8 +343,8 @@ static struct snd_soc_dai_driver fsl_micfil_dai = { .stream_name = "CPU-Capture", .channels_min = 1, .channels_max = 8, - .rates = FSL_MICFIL_RATES, - .formats = FSL_MICFIL_FORMATS, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &fsl_micfil_dai_ops, }; @@ -578,11 +488,11 @@ static irqreturn_t micfil_isr(int irq, void *devid) regmap_read(micfil->regmap, REG_MICFIL_CTRL1, &ctrl1_reg); regmap_read(micfil->regmap, REG_MICFIL_FIFO_STAT, &fifo_stat_reg); - dma_enabled = MICFIL_DMA_ENABLED(ctrl1_reg); + dma_enabled = FIELD_GET(MICFIL_CTRL1_DISEL, ctrl1_reg) == MICFIL_CTRL1_DISEL_DMA; /* Channel 0-7 Output Data Flags */ for (i = 0; i < MICFIL_OUTPUT_CHANNELS; i++) { - if (stat_reg & MICFIL_STAT_CHXF_MASK(i)) + if (stat_reg & MICFIL_STAT_CHXF(i)) dev_dbg(&pdev->dev, "Data available in Data Channel %d\n", i); /* if DMA is not enabled, field must be written with 1 @@ -591,17 +501,17 @@ static irqreturn_t micfil_isr(int irq, void *devid) if (!dma_enabled) regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, - MICFIL_STAT_CHXF_MASK(i), + MICFIL_STAT_CHXF(i), 1); } for (i = 0; i < MICFIL_FIFO_NUM; i++) { - if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER_MASK(i)) + if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_OVER(i)) dev_dbg(&pdev->dev, "FIFO Overflow Exception flag for channel %d\n", i); - if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER_MASK(i)) + if (fifo_stat_reg & MICFIL_FIFO_STAT_FIFOX_UNDER(i)) dev_dbg(&pdev->dev, "FIFO Underflow Exception flag for channel %d\n", i); @@ -618,16 +528,16 @@ static irqreturn_t micfil_err_isr(int irq, void *devid) regmap_read(micfil->regmap, REG_MICFIL_STAT, &stat_reg); - if (stat_reg & MICFIL_STAT_BSY_FIL_MASK) + if (stat_reg & MICFIL_STAT_BSY_FIL) dev_dbg(&pdev->dev, "isr: Decimation Filter is running\n"); - if (stat_reg & MICFIL_STAT_FIR_RDY_MASK) + if (stat_reg & MICFIL_STAT_FIR_RDY) dev_dbg(&pdev->dev, "isr: FIR Filter Data ready\n"); - if (stat_reg & MICFIL_STAT_LOWFREQF_MASK) { + if (stat_reg & MICFIL_STAT_LOWFREQF) { dev_dbg(&pdev->dev, "isr: ipg_clk_app is too low\n"); regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, - MICFIL_STAT_LOWFREQF_MASK, 1); + MICFIL_STAT_LOWFREQF, 1); } return IRQ_HANDLED; @@ -640,7 +550,6 @@ static int fsl_micfil_probe(struct platform_device *pdev) struct resource *res; void __iomem *regs; int ret, i; - unsigned long irqflag = 0; micfil = devm_kzalloc(&pdev->dev, sizeof(*micfil), GFP_KERNEL); if (!micfil) @@ -699,17 +608,13 @@ static int fsl_micfil_probe(struct platform_device *pdev) /* get IRQs */ for (i = 0; i < MICFIL_IRQ_LINES; i++) { micfil->irq[i] = platform_get_irq(pdev, i); - dev_err(&pdev->dev, "GET IRQ: %d\n", micfil->irq[i]); if (micfil->irq[i] < 0) return micfil->irq[i]; } - if (of_property_read_bool(np, "fsl,shared-interrupt")) - irqflag = IRQF_SHARED; - /* Digital Microphone interface interrupt */ ret = devm_request_irq(&pdev->dev, micfil->irq[0], - micfil_isr, irqflag, + micfil_isr, IRQF_SHARED, micfil->name, micfil); if (ret) { dev_err(&pdev->dev, "failed to claim mic interface irq %u\n", @@ -719,7 +624,7 @@ static int fsl_micfil_probe(struct platform_device *pdev) /* Digital Microphone interface error interrupt */ ret = devm_request_irq(&pdev->dev, micfil->irq[1], - micfil_err_isr, irqflag, + micfil_err_isr, IRQF_SHARED, micfil->name, micfil); if (ret) { dev_err(&pdev->dev, "failed to claim mic interface error irq %u\n", @@ -731,7 +636,6 @@ static int fsl_micfil_probe(struct platform_device *pdev) micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0; micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX; - platform_set_drvdata(pdev, micfil); pm_runtime_enable(&pdev->dev); @@ -747,6 +651,8 @@ static int fsl_micfil_probe(struct platform_device *pdev) return ret; } + fsl_micfil_dai.capture.formats = micfil->soc->formats; + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_micfil_component, &fsl_micfil_dai, 1); if (ret) { diff --git a/sound/soc/fsl/fsl_micfil.h b/sound/soc/fsl/fsl_micfil.h index bac825c3135a..053caba3caf3 100644 --- a/sound/soc/fsl/fsl_micfil.h +++ b/sound/soc/fsl/fsl_micfil.h @@ -33,240 +33,94 @@ #define REG_MICFIL_VAD0_ZCD 0xA8 /* MICFIL Control Register 1 -- REG_MICFILL_CTRL1 0x00 */ -#define MICFIL_CTRL1_MDIS_SHIFT 31 -#define MICFIL_CTRL1_MDIS_MASK BIT(MICFIL_CTRL1_MDIS_SHIFT) -#define MICFIL_CTRL1_MDIS BIT(MICFIL_CTRL1_MDIS_SHIFT) -#define MICFIL_CTRL1_DOZEN_SHIFT 30 -#define MICFIL_CTRL1_DOZEN_MASK BIT(MICFIL_CTRL1_DOZEN_SHIFT) -#define MICFIL_CTRL1_DOZEN BIT(MICFIL_CTRL1_DOZEN_SHIFT) -#define MICFIL_CTRL1_PDMIEN_SHIFT 29 -#define MICFIL_CTRL1_PDMIEN_MASK BIT(MICFIL_CTRL1_PDMIEN_SHIFT) -#define MICFIL_CTRL1_PDMIEN BIT(MICFIL_CTRL1_PDMIEN_SHIFT) -#define MICFIL_CTRL1_DBG_SHIFT 28 -#define MICFIL_CTRL1_DBG_MASK BIT(MICFIL_CTRL1_DBG_SHIFT) -#define MICFIL_CTRL1_DBG BIT(MICFIL_CTRL1_DBG_SHIFT) -#define MICFIL_CTRL1_SRES_SHIFT 27 -#define MICFIL_CTRL1_SRES_MASK BIT(MICFIL_CTRL1_SRES_SHIFT) -#define MICFIL_CTRL1_SRES BIT(MICFIL_CTRL1_SRES_SHIFT) -#define MICFIL_CTRL1_DBGE_SHIFT 26 -#define MICFIL_CTRL1_DBGE_MASK BIT(MICFIL_CTRL1_DBGE_SHIFT) -#define MICFIL_CTRL1_DBGE BIT(MICFIL_CTRL1_DBGE_SHIFT) -#define MICFIL_CTRL1_DISEL_SHIFT 24 -#define MICFIL_CTRL1_DISEL_WIDTH 2 -#define MICFIL_CTRL1_DISEL_MASK ((BIT(MICFIL_CTRL1_DISEL_WIDTH) - 1) \ - << MICFIL_CTRL1_DISEL_SHIFT) -#define MICFIL_CTRL1_DISEL(v) (((v) << MICFIL_CTRL1_DISEL_SHIFT) \ - & MICFIL_CTRL1_DISEL_MASK) -#define MICFIL_CTRL1_ERREN_SHIFT 23 -#define MICFIL_CTRL1_ERREN_MASK BIT(MICFIL_CTRL1_ERREN_SHIFT) -#define MICFIL_CTRL1_ERREN BIT(MICFIL_CTRL1_ERREN_SHIFT) -#define MICFIL_CTRL1_CHEN_SHIFT 0 -#define MICFIL_CTRL1_CHEN_WIDTH 8 -#define MICFIL_CTRL1_CHEN_MASK(x) (BIT(x) << MICFIL_CTRL1_CHEN_SHIFT) -#define MICFIL_CTRL1_CHEN(x) (MICFIL_CTRL1_CHEN_MASK(x)) +#define MICFIL_CTRL1_MDIS BIT(31) +#define MICFIL_CTRL1_DOZEN BIT(30) +#define MICFIL_CTRL1_PDMIEN BIT(29) +#define MICFIL_CTRL1_DBG BIT(28) +#define MICFIL_CTRL1_SRES BIT(27) +#define MICFIL_CTRL1_DBGE BIT(26) + +#define MICFIL_CTRL1_DISEL_DISABLE 0 +#define MICFIL_CTRL1_DISEL_DMA 1 +#define MICFIL_CTRL1_DISEL_IRQ 2 +#define MICFIL_CTRL1_DISEL GENMASK(25, 24) +#define MICFIL_CTRL1_ERREN BIT(23) +#define MICFIL_CTRL1_CHEN(ch) BIT(ch) /* MICFIL Control Register 2 -- REG_MICFILL_CTRL2 0x04 */ #define MICFIL_CTRL2_QSEL_SHIFT 25 -#define MICFIL_CTRL2_QSEL_WIDTH 3 -#define MICFIL_CTRL2_QSEL_MASK ((BIT(MICFIL_CTRL2_QSEL_WIDTH) - 1) \ - << MICFIL_CTRL2_QSEL_SHIFT) -#define MICFIL_HIGH_QUALITY BIT(MICFIL_CTRL2_QSEL_SHIFT) -#define MICFIL_MEDIUM_QUALITY (0 << MICFIL_CTRL2_QSEL_SHIFT) -#define MICFIL_LOW_QUALITY (7 << MICFIL_CTRL2_QSEL_SHIFT) -#define MICFIL_VLOW0_QUALITY (6 << MICFIL_CTRL2_QSEL_SHIFT) -#define MICFIL_VLOW1_QUALITY (5 << MICFIL_CTRL2_QSEL_SHIFT) -#define MICFIL_VLOW2_QUALITY (4 << MICFIL_CTRL2_QSEL_SHIFT) +#define MICFIL_CTRL2_QSEL GENMASK(27, 25) +#define MICFIL_QSEL_MEDIUM_QUALITY 0 +#define MICFIL_QSEL_HIGH_QUALITY 1 +#define MICFIL_QSEL_LOW_QUALITY 7 +#define MICFIL_QSEL_VLOW0_QUALITY 6 +#define MICFIL_QSEL_VLOW1_QUALITY 5 +#define MICFIL_QSEL_VLOW2_QUALITY 4 -#define MICFIL_CTRL2_CICOSR_SHIFT 16 -#define MICFIL_CTRL2_CICOSR_WIDTH 4 -#define MICFIL_CTRL2_CICOSR_MASK ((BIT(MICFIL_CTRL2_CICOSR_WIDTH) - 1) \ - << MICFIL_CTRL2_CICOSR_SHIFT) -#define MICFIL_CTRL2_CICOSR(v) (((v) << MICFIL_CTRL2_CICOSR_SHIFT) \ - & MICFIL_CTRL2_CICOSR_MASK) -#define MICFIL_CTRL2_CLKDIV_SHIFT 0 -#define MICFIL_CTRL2_CLKDIV_WIDTH 8 -#define MICFIL_CTRL2_CLKDIV_MASK ((BIT(MICFIL_CTRL2_CLKDIV_WIDTH) - 1) \ - << MICFIL_CTRL2_CLKDIV_SHIFT) -#define MICFIL_CTRL2_CLKDIV(v) (((v) << MICFIL_CTRL2_CLKDIV_SHIFT) \ - & MICFIL_CTRL2_CLKDIV_MASK) +#define MICFIL_CTRL2_CICOSR GENMASK(19, 16) +#define MICFIL_CTRL2_CLKDIV GENMASK(7, 0) /* MICFIL Status Register -- REG_MICFIL_STAT 0x08 */ -#define MICFIL_STAT_BSY_FIL_SHIFT 31 -#define MICFIL_STAT_BSY_FIL_MASK BIT(MICFIL_STAT_BSY_FIL_SHIFT) -#define MICFIL_STAT_BSY_FIL BIT(MICFIL_STAT_BSY_FIL_SHIFT) -#define MICFIL_STAT_FIR_RDY_SHIFT 30 -#define MICFIL_STAT_FIR_RDY_MASK BIT(MICFIL_STAT_FIR_RDY_SHIFT) -#define MICFIL_STAT_FIR_RDY BIT(MICFIL_STAT_FIR_RDY_SHIFT) -#define MICFIL_STAT_LOWFREQF_SHIFT 29 -#define MICFIL_STAT_LOWFREQF_MASK BIT(MICFIL_STAT_LOWFREQF_SHIFT) -#define MICFIL_STAT_LOWFREQF BIT(MICFIL_STAT_LOWFREQF_SHIFT) -#define MICFIL_STAT_CHXF_SHIFT(v) (v) -#define MICFIL_STAT_CHXF_MASK(v) BIT(MICFIL_STAT_CHXF_SHIFT(v)) -#define MICFIL_STAT_CHXF(v) BIT(MICFIL_STAT_CHXF_SHIFT(v)) +#define MICFIL_STAT_BSY_FIL BIT(31) +#define MICFIL_STAT_FIR_RDY BIT(30) +#define MICFIL_STAT_LOWFREQF BIT(29) +#define MICFIL_STAT_CHXF(ch) BIT(ch) /* MICFIL FIFO Control Register -- REG_MICFIL_FIFO_CTRL 0x10 */ -#define MICFIL_FIFO_CTRL_FIFOWMK_SHIFT 0 -#define MICFIL_FIFO_CTRL_FIFOWMK_WIDTH 3 -#define MICFIL_FIFO_CTRL_FIFOWMK_MASK ((BIT(MICFIL_FIFO_CTRL_FIFOWMK_WIDTH) - 1) \ - << MICFIL_FIFO_CTRL_FIFOWMK_SHIFT) -#define MICFIL_FIFO_CTRL_FIFOWMK(v) (((v) << MICFIL_FIFO_CTRL_FIFOWMK_SHIFT) \ - & MICFIL_FIFO_CTRL_FIFOWMK_MASK) +#define MICFIL_FIFO_CTRL_FIFOWMK GENMASK(2, 0) /* MICFIL FIFO Status Register -- REG_MICFIL_FIFO_STAT 0x14 */ -#define MICFIL_FIFO_STAT_FIFOX_OVER_SHIFT(v) (v) -#define MICFIL_FIFO_STAT_FIFOX_OVER_MASK(v) BIT(MICFIL_FIFO_STAT_FIFOX_OVER_SHIFT(v)) -#define MICFIL_FIFO_STAT_FIFOX_UNDER_SHIFT(v) ((v) + 8) -#define MICFIL_FIFO_STAT_FIFOX_UNDER_MASK(v) BIT(MICFIL_FIFO_STAT_FIFOX_UNDER_SHIFT(v)) +#define MICFIL_FIFO_STAT_FIFOX_OVER(ch) BIT(ch) +#define MICFIL_FIFO_STAT_FIFOX_UNDER(ch) BIT((ch) + 8) /* MICFIL HWVAD0 Control 1 Register -- REG_MICFIL_VAD0_CTRL1*/ -#define MICFIL_VAD0_CTRL1_CHSEL_SHIFT 24 -#define MICFIL_VAD0_CTRL1_CHSEL_WIDTH 3 -#define MICFIL_VAD0_CTRL1_CHSEL_MASK ((BIT(MICFIL_VAD0_CTRL1_CHSEL_WIDTH) - 1) \ - << MICFIL_VAD0_CTRL1_CHSEL_SHIFT) -#define MICFIL_VAD0_CTRL1_CHSEL(v) (((v) << MICFIL_VAD0_CTRL1_CHSEL_SHIFT) \ - & MICFIL_VAD0_CTRL1_CHSEL_MASK) -#define MICFIL_VAD0_CTRL1_CICOSR_SHIFT 16 -#define MICFIL_VAD0_CTRL1_CICOSR_WIDTH 4 -#define MICFIL_VAD0_CTRL1_CICOSR_MASK ((BIT(MICFIL_VAD0_CTRL1_CICOSR_WIDTH) - 1) \ - << MICFIL_VAD0_CTRL1_CICOSR_SHIFT) -#define MICFIL_VAD0_CTRL1_CICOSR(v) (((v) << MICFIL_VAD0_CTRL1_CICOSR_SHIFT) \ - & MICFIL_VAD0_CTRL1_CICOSR_MASK) -#define MICFIL_VAD0_CTRL1_INITT_SHIFT 8 -#define MICFIL_VAD0_CTRL1_INITT_WIDTH 5 -#define MICFIL_VAD0_CTRL1_INITT_MASK ((BIT(MICFIL_VAD0_CTRL1_INITT_WIDTH) - 1) \ - << MICFIL_VAD0_CTRL1_INITT_SHIFT) -#define MICFIL_VAD0_CTRL1_INITT(v) (((v) << MICFIL_VAD0_CTRL1_INITT_SHIFT) \ - & MICFIL_VAD0_CTRL1_INITT_MASK) -#define MICFIL_VAD0_CTRL1_ST10_SHIFT 4 -#define MICFIL_VAD0_CTRL1_ST10_MASK BIT(MICFIL_VAD0_CTRL1_ST10_SHIFT) -#define MICFIL_VAD0_CTRL1_ST10 BIT(MICFIL_VAD0_CTRL1_ST10_SHIFT) -#define MICFIL_VAD0_CTRL1_ERIE_SHIFT 3 -#define MICFIL_VAD0_CTRL1_ERIE_MASK BIT(MICFIL_VAD0_CTRL1_ERIE_SHIFT) -#define MICFIL_VAD0_CTRL1_ERIE BIT(MICFIL_VAD0_CTRL1_ERIE_SHIFT) -#define MICFIL_VAD0_CTRL1_IE_SHIFT 2 -#define MICFIL_VAD0_CTRL1_IE_MASK BIT(MICFIL_VAD0_CTRL1_IE_SHIFT) -#define MICFIL_VAD0_CTRL1_IE BIT(MICFIL_VAD0_CTRL1_IE_SHIFT) -#define MICFIL_VAD0_CTRL1_RST_SHIFT 1 -#define MICFIL_VAD0_CTRL1_RST_MASK BIT(MICFIL_VAD0_CTRL1_RST_SHIFT) -#define MICFIL_VAD0_CTRL1_RST BIT(MICFIL_VAD0_CTRL1_RST_SHIFT) -#define MICFIL_VAD0_CTRL1_EN_SHIFT 0 -#define MICFIL_VAD0_CTRL1_EN_MASK BIT(MICFIL_VAD0_CTRL1_EN_SHIFT) -#define MICFIL_VAD0_CTRL1_EN BIT(MICFIL_VAD0_CTRL1_EN_SHIFT) +#define MICFIL_VAD0_CTRL1_CHSEL GENMASK(26, 24) +#define MICFIL_VAD0_CTRL1_CICOSR GENMASK(19, 16) +#define MICFIL_VAD0_CTRL1_INITT GENMASK(12, 8) +#define MICFIL_VAD0_CTRL1_ST10 BIT(4) +#define MICFIL_VAD0_CTRL1_ERIE BIT(3) +#define MICFIL_VAD0_CTRL1_IE BIT(2) +#define MICFIL_VAD0_CTRL1_RST BIT(1) +#define MICFIL_VAD0_CTRL1_EN BIT(0) /* MICFIL HWVAD0 Control 2 Register -- REG_MICFIL_VAD0_CTRL2*/ -#define MICFIL_VAD0_CTRL2_FRENDIS_SHIFT 31 -#define MICFIL_VAD0_CTRL2_FRENDIS_MASK BIT(MICFIL_VAD0_CTRL2_FRENDIS_SHIFT) -#define MICFIL_VAD0_CTRL2_FRENDIS BIT(MICFIL_VAD0_CTRL2_FRENDIS_SHIFT) -#define MICFIL_VAD0_CTRL2_PREFEN_SHIFT 30 -#define MICFIL_VAD0_CTRL2_PREFEN_MASK BIT(MICFIL_VAD0_CTRL2_PREFEN_SHIFT) -#define MICFIL_VAD0_CTRL2_PREFEN BIT(MICFIL_VAD0_CTRL2_PREFEN_SHIFT) -#define MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT 28 -#define MICFIL_VAD0_CTRL2_FOUTDIS_MASK BIT(MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT) -#define MICFIL_VAD0_CTRL2_FOUTDIS BIT(MICFIL_VAD0_CTRL2_FOUTDIS_SHIFT) -#define MICFIL_VAD0_CTRL2_FRAMET_SHIFT 16 -#define MICFIL_VAD0_CTRL2_FRAMET_WIDTH 6 -#define MICFIL_VAD0_CTRL2_FRAMET_MASK ((BIT(MICFIL_VAD0_CTRL2_FRAMET_WIDTH) - 1) \ - << MICFIL_VAD0_CTRL2_FRAMET_SHIFT) -#define MICFIL_VAD0_CTRL2_FRAMET(v) (((v) << MICFIL_VAD0_CTRL2_FRAMET_SHIFT) \ - & MICFIL_VAD0_CTRL2_FRAMET_MASK) -#define MICFIL_VAD0_CTRL2_INPGAIN_SHIFT 8 -#define MICFIL_VAD0_CTRL2_INPGAIN_WIDTH 4 -#define MICFIL_VAD0_CTRL2_INPGAIN_MASK ((BIT(MICFIL_VAD0_CTRL2_INPGAIN_WIDTH) - 1) \ - << MICFIL_VAD0_CTRL2_INPGAIN_SHIFT) -#define MICFIL_VAD0_CTRL2_INPGAIN(v) (((v) << MICFIL_VAD0_CTRL2_INPGAIN_SHIFT) \ - & MICFIL_VAD0_CTRL2_INPGAIN_MASK) -#define MICFIL_VAD0_CTRL2_HPF_SHIFT 0 -#define MICFIL_VAD0_CTRL2_HPF_WIDTH 2 -#define MICFIL_VAD0_CTRL2_HPF_MASK ((BIT(MICFIL_VAD0_CTRL2_HPF_WIDTH) - 1) \ - << MICFIL_VAD0_CTRL2_HPF_SHIFT) -#define MICFIL_VAD0_CTRL2_HPF(v) (((v) << MICFIL_VAD0_CTRL2_HPF_SHIFT) \ - & MICFIL_VAD0_CTRL2_HPF_MASK) +#define MICFIL_VAD0_CTRL2_FRENDIS BIT(31) +#define MICFIL_VAD0_CTRL2_PREFEN BIT(30) +#define MICFIL_VAD0_CTRL2_FOUTDIS BIT(28) +#define MICFIL_VAD0_CTRL2_FRAMET GENMASK(21, 16) +#define MICFIL_VAD0_CTRL2_INPGAIN GENMASK(11, 8) +#define MICFIL_VAD0_CTRL2_HPF GENMASK(1, 0) /* MICFIL HWVAD0 Signal CONFIG Register -- REG_MICFIL_VAD0_SCONFIG */ -#define MICFIL_VAD0_SCONFIG_SFILEN_SHIFT 31 -#define MICFIL_VAD0_SCONFIG_SFILEN_MASK BIT(MICFIL_VAD0_SCONFIG_SFILEN_SHIFT) -#define MICFIL_VAD0_SCONFIG_SFILEN BIT(MICFIL_VAD0_SCONFIG_SFILEN_SHIFT) -#define MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT 30 -#define MICFIL_VAD0_SCONFIG_SMAXEN_MASK BIT(MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT) -#define MICFIL_VAD0_SCONFIG_SMAXEN BIT(MICFIL_VAD0_SCONFIG_SMAXEN_SHIFT) -#define MICFIL_VAD0_SCONFIG_SGAIN_SHIFT 0 -#define MICFIL_VAD0_SCONFIG_SGAIN_WIDTH 4 -#define MICFIL_VAD0_SCONFIG_SGAIN_MASK ((BIT(MICFIL_VAD0_SCONFIG_SGAIN_WIDTH) - 1) \ - << MICFIL_VAD0_SCONFIG_SGAIN_SHIFT) -#define MICFIL_VAD0_SCONFIG_SGAIN(v) (((v) << MICFIL_VAD0_SCONFIG_SGAIN_SHIFT) \ - & MICFIL_VAD0_SCONFIG_SGAIN_MASK) +#define MICFIL_VAD0_SCONFIG_SFILEN BIT(31) +#define MICFIL_VAD0_SCONFIG_SMAXEN BIT(30) +#define MICFIL_VAD0_SCONFIG_SGAIN GENMASK(3, 0) /* MICFIL HWVAD0 Noise CONFIG Register -- REG_MICFIL_VAD0_NCONFIG */ -#define MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT 31 -#define MICFIL_VAD0_NCONFIG_NFILAUT_MASK BIT(MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT) -#define MICFIL_VAD0_NCONFIG_NFILAUT BIT(MICFIL_VAD0_NCONFIG_NFILAUT_SHIFT) -#define MICFIL_VAD0_NCONFIG_NMINEN_SHIFT 30 -#define MICFIL_VAD0_NCONFIG_NMINEN_MASK BIT(MICFIL_VAD0_NCONFIG_NMINEN_SHIFT) -#define MICFIL_VAD0_NCONFIG_NMINEN BIT(MICFIL_VAD0_NCONFIG_NMINEN_SHIFT) -#define MICFIL_VAD0_NCONFIG_NDECEN_SHIFT 29 -#define MICFIL_VAD0_NCONFIG_NDECEN_MASK BIT(MICFIL_VAD0_NCONFIG_NDECEN_SHIFT) -#define MICFIL_VAD0_NCONFIG_NDECEN BIT(MICFIL_VAD0_NCONFIG_NDECEN_SHIFT) -#define MICFIL_VAD0_NCONFIG_NOREN_SHIFT 28 -#define MICFIL_VAD0_NCONFIG_NOREN BIT(MICFIL_VAD0_NCONFIG_NOREN_SHIFT) -#define MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT 8 -#define MICFIL_VAD0_NCONFIG_NFILADJ_WIDTH 5 -#define MICFIL_VAD0_NCONFIG_NFILADJ_MASK ((BIT(MICFIL_VAD0_NCONFIG_NFILADJ_WIDTH) - 1) \ - << MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT) -#define MICFIL_VAD0_NCONFIG_NFILADJ(v) (((v) << MICFIL_VAD0_NCONFIG_NFILADJ_SHIFT) \ - & MICFIL_VAD0_NCONFIG_NFILADJ_MASK) -#define MICFIL_VAD0_NCONFIG_NGAIN_SHIFT 0 -#define MICFIL_VAD0_NCONFIG_NGAIN_WIDTH 4 -#define MICFIL_VAD0_NCONFIG_NGAIN_MASK ((BIT(MICFIL_VAD0_NCONFIG_NGAIN_WIDTH) - 1) \ - << MICFIL_VAD0_NCONFIG_NGAIN_SHIFT) -#define MICFIL_VAD0_NCONFIG_NGAIN(v) (((v) << MICFIL_VAD0_NCONFIG_NGAIN_SHIFT) \ - & MICFIL_VAD0_NCONFIG_NGAIN_MASK) +#define MICFIL_VAD0_NCONFIG_NFILAUT BIT(31) +#define MICFIL_VAD0_NCONFIG_NMINEN BIT(30) +#define MICFIL_VAD0_NCONFIG_NDECEN BIT(29) +#define MICFIL_VAD0_NCONFIG_NOREN BIT(28) +#define MICFIL_VAD0_NCONFIG_NFILADJ GENMASK(12, 8) +#define MICFIL_VAD0_NCONFIG_NGAIN GENMASK(3, 0) /* MICFIL HWVAD0 Zero-Crossing Detector - REG_MICFIL_VAD0_ZCD */ -#define MICFIL_VAD0_ZCD_ZCDTH_SHIFT 16 -#define MICFIL_VAD0_ZCD_ZCDTH_WIDTH 10 -#define MICFIL_VAD0_ZCD_ZCDTH_MASK ((BIT(MICFIL_VAD0_ZCD_ZCDTH_WIDTH) - 1) \ - << MICFIL_VAD0_ZCD_ZCDTH_SHIFT) -#define MICFIL_VAD0_ZCD_ZCDTH(v) (((v) << MICFIL_VAD0_ZCD_ZCDTH_SHIFT)\ - & MICFIL_VAD0_ZCD_ZCDTH_MASK) -#define MICFIL_VAD0_ZCD_ZCDADJ_SHIFT 8 -#define MICFIL_VAD0_ZCD_ZCDADJ_WIDTH 4 -#define MICFIL_VAD0_ZCD_ZCDADJ_MASK ((BIT(MICFIL_VAD0_ZCD_ZCDADJ_WIDTH) - 1)\ - << MICFIL_VAD0_ZCD_ZCDADJ_SHIFT) -#define MICFIL_VAD0_ZCD_ZCDADJ(v) (((v) << MICFIL_VAD0_ZCD_ZCDADJ_SHIFT)\ - & MICFIL_VAD0_ZCD_ZCDADJ_MASK) -#define MICFIL_VAD0_ZCD_ZCDAND_SHIFT 4 -#define MICFIL_VAD0_ZCD_ZCDAND_MASK BIT(MICFIL_VAD0_ZCD_ZCDAND_SHIFT) -#define MICFIL_VAD0_ZCD_ZCDAND BIT(MICFIL_VAD0_ZCD_ZCDAND_SHIFT) -#define MICFIL_VAD0_ZCD_ZCDAUT_SHIFT 2 -#define MICFIL_VAD0_ZCD_ZCDAUT_MASK BIT(MICFIL_VAD0_ZCD_ZCDAUT_SHIFT) -#define MICFIL_VAD0_ZCD_ZCDAUT BIT(MICFIL_VAD0_ZCD_ZCDAUT_SHIFT) -#define MICFIL_VAD0_ZCD_ZCDEN_SHIFT 0 -#define MICFIL_VAD0_ZCD_ZCDEN_MASK BIT(MICFIL_VAD0_ZCD_ZCDEN_SHIFT) -#define MICFIL_VAD0_ZCD_ZCDEN BIT(MICFIL_VAD0_ZCD_ZCDEN_SHIFT) +#define MICFIL_VAD0_ZCD_ZCDTH GENMASK(25, 16) +#define MICFIL_VAD0_ZCD_ZCDADJ GENMASK(11, 8) +#define MICFIL_VAD0_ZCD_ZCDAND BIT(4) +#define MICFIL_VAD0_ZCD_ZCDAUT BIT(2) +#define MICFIL_VAD0_ZCD_ZCDEN BIT(0) /* MICFIL HWVAD0 Status Register - REG_MICFIL_VAD0_STAT */ -#define MICFIL_VAD0_STAT_INITF_SHIFT 31 -#define MICFIL_VAD0_STAT_INITF_MASK BIT(MICFIL_VAD0_STAT_INITF_SHIFT) -#define MICFIL_VAD0_STAT_INITF BIT(MICFIL_VAD0_STAT_INITF_SHIFT) -#define MICFIL_VAD0_STAT_INSATF_SHIFT 16 -#define MICFIL_VAD0_STAT_INSATF_MASK BIT(MICFIL_VAD0_STAT_INSATF_SHIFT) -#define MICFIL_VAD0_STAT_INSATF BIT(MICFIL_VAD0_STAT_INSATF_SHIFT) -#define MICFIL_VAD0_STAT_EF_SHIFT 15 -#define MICFIL_VAD0_STAT_EF_MASK BIT(MICFIL_VAD0_STAT_EF_SHIFT) -#define MICFIL_VAD0_STAT_EF BIT(MICFIL_VAD0_STAT_EF_SHIFT) -#define MICFIL_VAD0_STAT_IF_SHIFT 0 -#define MICFIL_VAD0_STAT_IF_MASK BIT(MICFIL_VAD0_STAT_IF_SHIFT) -#define MICFIL_VAD0_STAT_IF BIT(MICFIL_VAD0_STAT_IF_SHIFT) +#define MICFIL_VAD0_STAT_INITF BIT(31) +#define MICFIL_VAD0_STAT_INSATF BIT(16) +#define MICFIL_VAD0_STAT_EF BIT(15) +#define MICFIL_VAD0_STAT_IF BIT(0) /* MICFIL Output Control Register */ #define MICFIL_OUTGAIN_CHX_SHIFT(v) (4 * (v)) /* Constants */ -#define MICFIL_DMA_IRQ_DISABLED(v) ((v) & MICFIL_CTRL1_DISEL_MASK) -#define MICFIL_DMA_ENABLED(v) ((0x1 << MICFIL_CTRL1_DISEL_SHIFT) \ - == ((v) & MICFIL_CTRL1_DISEL_MASK)) -#define MICFIL_IRQ_ENABLED(v) ((0x2 << MICFIL_CTRL1_DISEL_SHIFT) \ - == ((v) & MICFIL_CTRL1_DISEL_MASK)) #define MICFIL_OUTPUT_CHANNELS 8 #define MICFIL_FIFO_NUM 8 @@ -278,6 +132,5 @@ #define MICFIL_SLEEP_MIN 90000 /* in us */ #define MICFIL_SLEEP_MAX 100000 /* in us */ #define MICFIL_DMA_MAXBURST_RX 6 -#define MICFIL_CTRL2_OSR_DEFAULT (0 << MICFIL_CTRL2_CICOSR_SHIFT) #endif /* _FSL_MICFIL_H */ diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index ffc24afb5a7a..fa950dde5310 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -1147,7 +1147,7 @@ static int fsl_sai_probe(struct platform_device *pdev) /* Select MCLK direction */ if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) && - sai->verid.version >= 0x0301) { + sai->soc_data->max_register >= FSL_SAI_MCTL) { regmap_update_bits(sai->regmap, FSL_SAI_MCTL, FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN); } @@ -1203,6 +1203,7 @@ static const struct fsl_sai_soc_data fsl_sai_vf610_data = { .reg_offset = 0, .mclk0_is_mclk1 = false, .flags = 0, + .max_register = FSL_SAI_RMR, }; static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = { @@ -1213,6 +1214,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = { .reg_offset = 0, .mclk0_is_mclk1 = true, .flags = 0, + .max_register = FSL_SAI_RMR, }; static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = { @@ -1223,6 +1225,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = { .reg_offset = 8, .mclk0_is_mclk1 = false, .flags = PMQOS_CPU_LATENCY, + .max_register = FSL_SAI_RMR, }; static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = { @@ -1233,6 +1236,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = { .reg_offset = 8, .mclk0_is_mclk1 = false, .flags = 0, + .max_register = FSL_SAI_RMR, }; static const struct fsl_sai_soc_data fsl_sai_imx8qm_data = { @@ -1243,6 +1247,40 @@ static const struct fsl_sai_soc_data fsl_sai_imx8qm_data = { .reg_offset = 0, .mclk0_is_mclk1 = false, .flags = 0, + .max_register = FSL_SAI_RMR, +}; + +static const struct fsl_sai_soc_data fsl_sai_imx8mm_data = { + .use_imx_pcm = true, + .use_edma = false, + .fifo_depth = 128, + .reg_offset = 8, + .mclk0_is_mclk1 = false, + .pins = 8, + .flags = 0, + .max_register = FSL_SAI_MCTL, +}; + +static const struct fsl_sai_soc_data fsl_sai_imx8mp_data = { + .use_imx_pcm = true, + .use_edma = false, + .fifo_depth = 128, + .reg_offset = 8, + .mclk0_is_mclk1 = false, + .pins = 8, + .flags = 0, + .max_register = FSL_SAI_MDIV, +}; + +static const struct fsl_sai_soc_data fsl_sai_imx8ulp_data = { + .use_imx_pcm = true, + .use_edma = true, + .fifo_depth = 16, + .reg_offset = 8, + .mclk0_is_mclk1 = false, + .pins = 4, + .flags = PMQOS_CPU_LATENCY, + .max_register = FSL_SAI_RTCAP, }; static const struct of_device_id fsl_sai_ids[] = { @@ -1252,6 +1290,9 @@ static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,imx7ulp-sai", .data = &fsl_sai_imx7ulp_data }, { .compatible = "fsl,imx8mq-sai", .data = &fsl_sai_imx8mq_data }, { .compatible = "fsl,imx8qm-sai", .data = &fsl_sai_imx8qm_data }, + { .compatible = "fsl,imx8mm-sai", .data = &fsl_sai_imx8mm_data }, + { .compatible = "fsl,imx8mp-sai", .data = &fsl_sai_imx8mp_data }, + { .compatible = "fsl,imx8ulp-sai", .data = &fsl_sai_imx8ulp_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_sai_ids); diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 7310fd02cc3c..e4965efe6102 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -223,6 +223,7 @@ struct fsl_sai_soc_data { unsigned int pins; unsigned int reg_offset; unsigned int flags; + unsigned int max_register; }; /** diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index ca30a4ede076..84cb36d9dfea 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -40,6 +40,7 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> +#include <linux/dma/imx-dma.h> #include <sound/core.h> #include <sound/pcm.h> @@ -214,6 +215,7 @@ struct fsl_ssi_soc_data { * @synchronous: Use synchronous mode - both of TX and RX use STCK and SFCK * @use_dma: DMA is used or FIQ with stream filter * @use_dual_fifo: DMA with support for dual FIFO mode + * @use_dyna_fifo: DMA with support for multi FIFO script * @has_ipg_clk_name: If "ipg" is in the clock name list of device tree * @fifo_depth: Depth of the SSI FIFOs * @slot_width: Width of each DAI slot @@ -243,6 +245,7 @@ struct fsl_ssi_soc_data { * @dma_maxburst: Max number of words to transfer in one go. So far, * this is always the same as fifo_watermark. * @ac97_reg_lock: Mutex lock to serialize AC97 register access operations + * @audio_config: configure for dma multi fifo script */ struct fsl_ssi { struct regmap *regs; @@ -255,6 +258,7 @@ struct fsl_ssi { bool synchronous; bool use_dma; bool use_dual_fifo; + bool use_dyna_fifo; bool has_ipg_clk_name; unsigned int fifo_depth; unsigned int slot_width; @@ -287,6 +291,7 @@ struct fsl_ssi { u32 dma_maxburst; struct mutex ac97_reg_lock; + struct sdma_peripheral_config audio_config[2]; }; /* @@ -643,7 +648,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, * task from fifo0, fifo1 would be neglected at the end of each * period. But SSI would still access fifo1 with an invalid data. */ - if (ssi->use_dual_fifo) + if (ssi->use_dual_fifo || ssi->use_dyna_fifo) snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); @@ -802,6 +807,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, { bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); + struct fsl_ssi_regvals *vals = ssi->regvals; struct regmap *regs = ssi->regs; unsigned int channels = params_channels(hw_params); unsigned int sample_size = params_width(hw_params); @@ -856,6 +862,28 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, tx2 = tx || ssi->synchronous; regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl); + if (ssi->use_dyna_fifo) { + if (channels == 1) { + ssi->audio_config[0].n_fifos_dst = 1; + ssi->audio_config[1].n_fifos_src = 1; + vals[RX].srcr &= ~SSI_SRCR_RFEN1; + vals[TX].stcr &= ~SSI_STCR_TFEN1; + vals[RX].scr &= ~SSI_SCR_TCH_EN; + vals[TX].scr &= ~SSI_SCR_TCH_EN; + } else { + ssi->audio_config[0].n_fifos_dst = 2; + ssi->audio_config[1].n_fifos_src = 2; + vals[RX].srcr |= SSI_SRCR_RFEN1; + vals[TX].stcr |= SSI_STCR_TFEN1; + vals[RX].scr |= SSI_SCR_TCH_EN; + vals[TX].scr |= SSI_SCR_TCH_EN; + } + ssi->dma_params_tx.peripheral_config = &ssi->audio_config[0]; + ssi->dma_params_tx.peripheral_size = sizeof(ssi->audio_config[0]); + ssi->dma_params_rx.peripheral_config = &ssi->audio_config[1]; + ssi->dma_params_rx.peripheral_size = sizeof(ssi->audio_config[1]); + } + return 0; } @@ -1353,7 +1381,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev, ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0; /* Use even numbers to avoid channel swap due to SDMA script design */ - if (ssi->use_dual_fifo) { + if (ssi->use_dual_fifo || ssi->use_dyna_fifo) { ssi->dma_params_tx.maxburst &= ~0x1; ssi->dma_params_rx.maxburst &= ~0x1; } @@ -1446,6 +1474,8 @@ static int fsl_ssi_probe_from_dt(struct fsl_ssi *ssi) if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) ssi->use_dual_fifo = true; + if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI) + ssi->use_dyna_fifo = true; /* * Backward compatible for older bindings by manually triggering the * machine driver's probe(). Use /compatible property, including the diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c index 168973035e35..b80c57362fb8 100644 --- a/sound/soc/fsl/imx-es8328.c +++ b/sound/soc/fsl/imx-es8328.c @@ -48,7 +48,7 @@ static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd) if (gpio_is_valid(data->jack_gpio)) { ret = snd_soc_card_jack_new(rtd->card, "Headphone", SND_JACK_HEADPHONE | SND_JACK_BTN_0, - &headset_jack, NULL, 0); + &headset_jack); if (ret) return ret; diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c index 929f69b758af..a780cf5a65ff 100644 --- a/sound/soc/fsl/imx-hdmi.c +++ b/sound/soc/fsl/imx-hdmi.c @@ -78,8 +78,9 @@ static int imx_hdmi_init(struct snd_soc_pcm_runtime *rtd) data->hdmi_jack_pin.pin = "HDMI Jack"; data->hdmi_jack_pin.mask = SND_JACK_LINEOUT; /* enable jack detection */ - ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT, - &data->hdmi_jack, &data->hdmi_jack_pin, 1); + ret = snd_soc_card_jack_new_pins(card, "HDMI Jack", SND_JACK_LINEOUT, + &data->hdmi_jack, + &data->hdmi_jack_pin, 1); if (ret) { dev_err(card->dev, "Can't new HDMI Jack %d\n", ret); return ret; @@ -126,6 +127,7 @@ static int imx_hdmi_probe(struct platform_device *pdev) data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) { ret = -ENOMEM; + put_device(&cpu_pdev->dev); goto fail; } @@ -205,8 +207,7 @@ static int imx_hdmi_probe(struct platform_device *pdev) } fail: - if (cpu_np) - of_node_put(cpu_np); + of_node_put(cpu_np); return ret; } diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index 5c6cf1ca8c8a..06b25f4b26b6 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -9,7 +9,7 @@ #ifndef _IMX_PCM_H #define _IMX_PCM_H -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> /* * Do not change this as the FIQ handler depends on this size diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 8daced42d55e..580a0d963f0e 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -120,19 +120,19 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) { ret = -ENOMEM; - goto fail; + goto put_device; } comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL); if (!comp) { ret = -ENOMEM; - goto fail; + goto put_device; } data->codec_clk = clk_get(&codec_dev->dev, NULL); if (IS_ERR(data->codec_clk)) { ret = PTR_ERR(data->codec_clk); - goto fail; + goto put_device; } data->clk_frequency = clk_get_rate(data->codec_clk); @@ -158,10 +158,10 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) data->card.dev = &pdev->dev; ret = snd_soc_of_parse_card_name(&data->card, "model"); if (ret) - goto fail; + goto put_device; ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); if (ret) - goto fail; + goto put_device; data->card.num_links = 1; data->card.owner = THIS_MODULE; data->card.dai_link = &data->dai; @@ -174,7 +174,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, &data->card); if (ret) { dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); - goto fail; + goto put_device; } of_node_put(ssi_np); @@ -182,6 +182,8 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) return 0; +put_device: + put_device(&codec_dev->dev); fail: if (data && !IS_ERR(data->codec_clk)) clk_put(data->codec_clk); diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h index 19cd0937e740..2d30d822451a 100644 --- a/sound/soc/fsl/imx-ssi.h +++ b/sound/soc/fsl/imx-ssi.h @@ -182,7 +182,7 @@ #define DRV_NAME "imx-ssi" #include <linux/dmaengine.h> -#include <linux/platform_data/dma-imx.h> +#include <linux/dma/imx-dma.h> #include <sound/dmaengine_pcm.h> #include "imx-pcm.h" diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index c0f3907a01fd..77ac4051b827 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -711,7 +711,7 @@ static void graph_link_init(struct asoc_simple_priv *priv, */ daiclk = snd_soc_daifmt_clock_provider_from_bitmap(bit_frame); if (is_cpu_node) - daiclk = snd_soc_daifmt_clock_provider_fliped(daiclk); + daiclk = snd_soc_daifmt_clock_provider_flipped(daiclk); dai_link->dai_fmt = daifmt | daiclk; dai_link->init = asoc_simple_dai_init; @@ -1178,8 +1178,6 @@ int audio_graph2_parse_of(struct asoc_simple_priv *priv, struct device *dev, struct link_info *li; int ret; - dev_warn(dev, "Audio Graph Card2 is still under Experimental stage\n"); - li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); if (!li) return -ENOMEM; @@ -1245,6 +1243,9 @@ err: if (ret < 0) dev_err_probe(dev, ret, "parse error\n"); + if (ret == 0) + dev_warn(dev, "Audio Graph Card2 is still under Experimental stage\n"); + return ret; } EXPORT_SYMBOL_GPL(audio_graph2_parse_of); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index da0c27828ce6..539d7f081bd7 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -721,9 +721,8 @@ int asoc_simple_init_jack(struct snd_soc_card *card, sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW); sjack->gpio.debounce_time = 150; - snd_soc_card_jack_new(card, pin_name, mask, - &sjack->jack, - &sjack->pin, 1); + snd_soc_card_jack_new_pins(card, pin_name, mask, &sjack->jack, + &sjack->pin, 1); snd_soc_jack_add_gpios(&sjack->jack, 1, &sjack->gpio); diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c index f1f36f15a503..09d23b11621c 100644 --- a/sound/soc/img/img-i2s-in.c +++ b/sound/soc/img/img-i2s-in.c @@ -342,11 +342,9 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK; - ret = pm_runtime_get_sync(i2s->dev); - if (ret < 0) { - pm_runtime_put_noidle(i2s->dev); + ret = pm_runtime_resume_and_get(i2s->dev); + if (ret < 0) return ret; - } for (i = 0; i < i2s->active_channels; i++) img_i2s_in_ch_disable(i2s, i); diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c index 800f247283cd..cd6a6a825741 100644 --- a/sound/soc/img/img-parallel-out.c +++ b/sound/soc/img/img-parallel-out.c @@ -162,11 +162,9 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - ret = pm_runtime_get_sync(prl->dev); - if (ret < 0) { - pm_runtime_put_noidle(prl->dev); + ret = pm_runtime_resume_and_get(prl->dev); + if (ret < 0) return ret; - } reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL); reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set; diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c index 95914d0612fe..a79d1ccaeec0 100644 --- a/sound/soc/img/img-spdif-in.c +++ b/sound/soc/img/img-spdif-in.c @@ -749,11 +749,9 @@ static int img_spdif_in_probe(struct platform_device *pdev) if (ret) goto err_pm_disable; } - ret = pm_runtime_get_sync(&pdev->dev); - if (ret < 0) { - pm_runtime_put_noidle(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) goto err_suspend; - } rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); if (IS_ERR(rst)) { diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c index c3189d9ff72f..f7062eba2611 100644 --- a/sound/soc/img/img-spdif-out.c +++ b/sound/soc/img/img-spdif-out.c @@ -362,11 +362,9 @@ static int img_spdif_out_probe(struct platform_device *pdev) if (ret) goto err_pm_disable; } - ret = pm_runtime_get_sync(&pdev->dev); - if (ret < 0) { - pm_runtime_put_noidle(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) goto err_suspend; - } img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK, IMG_SPDIF_OUT_CTL); diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index d025ca0c77fa..7c85d1bb9c12 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -211,11 +211,16 @@ config SND_SOC_INTEL_KEEMBAY config SND_SOC_INTEL_AVS tristate "Intel AVS driver" - depends on PCI && ACPI + depends on X86 || COMPILE_TEST + depends on PCI depends on COMMON_CLK - select SND_SOC_ACPI + select SND_SOC_ACPI if ACPI + select SND_SOC_TOPOLOGY + select SND_HDA select SND_HDA_EXT_CORE select SND_HDA_DSP_LOADER + select SND_INTEL_DSP_CONFIG + select WANT_DEV_COREDUMP help Enable support for Intel(R) cAVS 1.5 platforms with DSP capabilities. This includes Skylake, Kabylake, Amberlake and diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index e21e11dac000..3a42d68c0247 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -360,7 +360,6 @@ void sst_context_cleanup(struct intel_sst_drv *ctx) sst_unregister(ctx->dev); sst_set_fw_state_locked(ctx, SST_SHUTDOWN); sysfs_remove_group(&ctx->dev->kobj, &sst_fw_version_attr_group); - flush_scheduled_work(); destroy_workqueue(ctx->post_msg_wq); cpu_latency_qos_remove_request(ctx->qos); kfree(ctx->fw_sg_list.src); diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c index 0af618dd8073..dc31c2c8f54c 100644 --- a/sound/soc/intel/atom/sst/sst_drv_interface.c +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c @@ -136,11 +136,10 @@ static int sst_power_control(struct device *dev, bool state) int usage_count = 0; if (state) { - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); usage_count = GET_USAGE_COUNT(dev); dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count); if (ret < 0) { - pm_runtime_put_sync(dev); dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret); return ret; } @@ -193,11 +192,9 @@ static int sst_cdev_open(struct device *dev, struct stream_info *stream; struct intel_sst_drv *ctx = dev_get_drvdata(dev); - retval = pm_runtime_get_sync(ctx->dev); - if (retval < 0) { - pm_runtime_put_sync(ctx->dev); + retval = pm_runtime_resume_and_get(ctx->dev); + if (retval < 0) return retval; - } str_id = sst_get_stream(ctx, str_params); if (str_id > 0) { @@ -645,11 +642,9 @@ static int sst_send_byte_stream(struct device *dev, if (NULL == bytes) return -EINVAL; - ret_val = pm_runtime_get_sync(ctx->dev); - if (ret_val < 0) { - pm_runtime_put_sync(ctx->dev); + ret_val = pm_runtime_resume_and_get(ctx->dev); + if (ret_val < 0) return ret_val; - } ret_val = sst_send_byte_stream_mrfld(ctx, bytes); sst_pm_runtime_put(ctx); diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index f842bfc5e97e..b6b93ae80304 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -1,6 +1,12 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o +snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \ + topology.o path.o pcm.o board_selection.o snd-soc-avs-objs += cldma.o +snd-soc-avs-objs += skl.o apl.o + +snd-soc-avs-objs += trace.o +# tell define_trace.h where to find the trace header +CFLAGS_trace.o := -I$(src) obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c new file mode 100644 index 000000000000..b8e2b23c9f64 --- /dev/null +++ b/sound/soc/intel/avs/apl.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/devcoredump.h> +#include <linux/slab.h> +#include "avs.h" +#include "messages.h" +#include "path.h" +#include "topology.h" + +static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +{ + struct apl_log_state_info *info; + u32 size, num_cores = adev->hw_cfg.dsp_cores; + int ret, i; + + if (fls_long(resource_mask) > num_cores) + return -EINVAL; + size = struct_size(info, logs_core, num_cores); + info = kzalloc(size, GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->aging_timer_period = aging_period; + info->fifo_full_timer_period = fifo_full_period; + info->core_mask = resource_mask; + if (enable) + for_each_set_bit(i, &resource_mask, num_cores) { + info->logs_core[i].enable = enable; + info->logs_core[i].min_priority = *priorities++; + } + else + for_each_set_bit(i, &resource_mask, num_cores) + info->logs_core[i].enable = enable; + + ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size); + kfree(info); + if (ret) + return AVS_IPC_RET(ret); + + return 0; +} + +static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) +{ + struct apl_log_buffer_layout layout; + unsigned long flags; + void __iomem *addr, *buf; + + addr = avs_log_buffer_addr(adev, msg->log.core); + if (!addr) + return -ENXIO; + + memcpy_fromio(&layout, addr, sizeof(layout)); + + spin_lock_irqsave(&adev->dbg.trace_lock, flags); + if (!kfifo_initialized(&adev->dbg.trace_fifo)) + /* consume the logs regardless of consumer presence */ + goto update_read_ptr; + + buf = apl_log_payload_addr(addr); + + if (layout.read_ptr > layout.write_ptr) { + __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr, + apl_log_payload_size(adev) - layout.read_ptr, + &adev->dbg.fifo_lock); + layout.read_ptr = 0; + } + __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr, + layout.write_ptr - layout.read_ptr, &adev->dbg.fifo_lock); + + wake_up(&adev->dbg.trace_waitq); + +update_read_ptr: + spin_unlock_irqrestore(&adev->dbg.trace_lock, flags); + writel(layout.write_ptr, addr); + return 0; +} + +static int apl_wait_log_entry(struct avs_dev *adev, u32 core, struct apl_log_buffer_layout *layout) +{ + unsigned long timeout; + void __iomem *addr; + + addr = avs_log_buffer_addr(adev, core); + if (!addr) + return -ENXIO; + + timeout = jiffies + msecs_to_jiffies(10); + + do { + memcpy_fromio(layout, addr, sizeof(*layout)); + if (layout->read_ptr != layout->write_ptr) + return 0; + usleep_range(500, 1000); + } while (!time_after(jiffies, timeout)); + + return -ETIMEDOUT; +} + +/* reads log header and tests its type */ +#define apl_is_entry_stackdump(addr) ((readl(addr) >> 30) & 0x1) + +static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) +{ + struct apl_log_buffer_layout layout; + void __iomem *addr, *buf; + size_t dump_size; + u16 offset = 0; + u8 *dump, *pos; + + dump_size = AVS_FW_REGS_SIZE + msg->ext.coredump.stack_dump_size; + dump = vzalloc(dump_size); + if (!dump) + return -ENOMEM; + + memcpy_fromio(dump, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE); + + if (!msg->ext.coredump.stack_dump_size) + goto exit; + + /* Dump the registers even if an external error prevents gathering the stack. */ + addr = avs_log_buffer_addr(adev, msg->ext.coredump.core_id); + if (!addr) + goto exit; + + buf = apl_log_payload_addr(addr); + memcpy_fromio(&layout, addr, sizeof(layout)); + if (!apl_is_entry_stackdump(buf + layout.read_ptr)) { + /* + * DSP awaits the remaining logs to be + * gathered before dumping stack + */ + msg->log.core = msg->ext.coredump.core_id; + avs_dsp_op(adev, log_buffer_status, msg); + } + + pos = dump + AVS_FW_REGS_SIZE; + /* gather the stack */ + do { + u32 count; + + if (apl_wait_log_entry(adev, msg->ext.coredump.core_id, &layout)) + break; + + if (layout.read_ptr > layout.write_ptr) { + count = apl_log_payload_size(adev) - layout.read_ptr; + memcpy_fromio(pos + offset, buf + layout.read_ptr, count); + layout.read_ptr = 0; + offset += count; + } + count = layout.write_ptr - layout.read_ptr; + memcpy_fromio(pos + offset, buf + layout.read_ptr, count); + offset += count; + + /* update read pointer */ + writel(layout.write_ptr, addr); + } while (offset < msg->ext.coredump.stack_dump_size); + +exit: + dev_coredumpv(adev->dev, dump, dump_size, GFP_KERNEL); + + return 0; +} + +static bool apl_lp_streaming(struct avs_dev *adev) +{ + struct avs_path *path; + + /* Any gateway without buffer allocated in LP area disqualifies D0IX. */ + list_for_each_entry(path, &adev->path_list, node) { + struct avs_path_pipeline *ppl; + + list_for_each_entry(ppl, &path->ppl_list, node) { + struct avs_path_module *mod; + + list_for_each_entry(mod, &ppl->mod_list, node) { + struct avs_tplg_modcfg_ext *cfg; + + cfg = mod->template->cfg_ext; + + /* only copiers have gateway attributes */ + if (!guid_equal(&cfg->type, &AVS_COPIER_MOD_UUID)) + continue; + /* non-gateway copiers do not prevent PG */ + if (cfg->copier.dma_type == INVALID_OBJECT_ID) + continue; + + if (!mod->gtw_attrs.lp_buffer_alloc) + return false; + } + } + } + + return true; +} + +static bool apl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) +{ + /* wake in all cases */ + if (wake) + return true; + + /* + * If no pipelines are running, allow for d0ix schedule. + * If all gateways have lp=1, allow for d0ix schedule. + * If any gateway with lp=0 is allocated, abort scheduling d0ix. + * + * Note: for cAVS 1.5+ and 1.8, D0IX is LP-firmware transition, + * not the power-gating mechanism known from cAVS 2.0. + */ + return apl_lp_streaming(adev); +} + +static int apl_set_d0ix(struct avs_dev *adev, bool enable) +{ + bool streaming = false; + int ret; + + if (enable) + /* Either idle or all gateways with lp=1. */ + streaming = !list_empty(&adev->path_list); + + ret = avs_ipc_set_d0ix(adev, enable, streaming); + return AVS_IPC_RET(ret); +} + +const struct avs_dsp_ops apl_dsp_ops = { + .power = avs_dsp_core_power, + .reset = avs_dsp_core_reset, + .stall = avs_dsp_core_stall, + .irq_handler = avs_dsp_irq_handler, + .irq_thread = avs_dsp_irq_thread, + .int_control = avs_dsp_interrupt_control, + .load_basefw = avs_hda_load_basefw, + .load_lib = avs_hda_load_library, + .transfer_mods = avs_hda_transfer_modules, + .enable_logs = apl_enable_logs, + .log_buffer_offset = skl_log_buffer_offset, + .log_buffer_status = apl_log_buffer_status, + .coredump = apl_coredump, + .d0ix_toggle = apl_d0ix_toggle, + .set_d0ix = apl_set_d0ix, +}; diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index b48a342fd184..92e37722d280 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -11,12 +11,18 @@ #include <linux/device.h> #include <linux/firmware.h> +#include <linux/kfifo.h> #include <sound/hda_codec.h> #include <sound/hda_register.h> +#include <sound/soc-component.h> #include "messages.h" #include "registers.h" struct avs_dev; +struct avs_tplg; +struct avs_tplg_library; +struct avs_soc_component; +struct avs_ipc_msg; /* * struct avs_dsp_ops - Platform-specific DSP operations @@ -38,11 +44,21 @@ struct avs_dsp_ops { int (* const load_basefw)(struct avs_dev *, struct firmware *); int (* const load_lib)(struct avs_dev *, struct firmware *, u32); int (* const transfer_mods)(struct avs_dev *, bool, struct avs_module_entry *, u32); + int (* const enable_logs)(struct avs_dev *, enum avs_log_enable, u32, u32, unsigned long, + u32 *); + int (* const log_buffer_offset)(struct avs_dev *, u32); + int (* const log_buffer_status)(struct avs_dev *, union avs_notify_msg *); + int (* const coredump)(struct avs_dev *, union avs_notify_msg *); + bool (* const d0ix_toggle)(struct avs_dev *, struct avs_ipc_msg *, bool); + int (* const set_d0ix)(struct avs_dev *, bool); }; #define avs_dsp_op(adev, op, ...) \ ((adev)->spec->dsp_ops->op(adev, ## __VA_ARGS__)) +extern const struct avs_dsp_ops skl_dsp_ops; +extern const struct avs_dsp_ops apl_dsp_ops; + #define AVS_PLATATTR_CLDMA BIT_ULL(0) #define AVS_PLATATTR_IMR BIT_ULL(1) @@ -70,6 +86,16 @@ struct avs_fw_entry { struct list_head node; }; +struct avs_debug { + struct kfifo trace_fifo; + spinlock_t fifo_lock; /* serialize I/O for trace_fifo */ + spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */ + wait_queue_head_t trace_waitq; + u32 aging_timer_period; + u32 fifo_full_timer_period; + u32 logged_resources; /* context dependent: core or library */ +}; + /* * struct avs_dev - Intel HD-Audio driver data * @@ -103,6 +129,16 @@ struct avs_dev { char **lib_names; struct completion fw_ready; + struct work_struct probe_work; + + struct nhlt_acpi_table *nhlt; + struct list_head comp_list; + struct mutex comp_list_mutex; + struct list_head path_list; + spinlock_t path_list_lock; + struct mutex path_mutex; + + struct avs_debug dbg; }; /* from hda_bus to avs_dev */ @@ -153,12 +189,18 @@ struct avs_ipc { struct avs_ipc_msg rx; u32 default_timeout_ms; bool ready; + atomic_t recovering; bool rx_completed; spinlock_t rx_lock; struct mutex msg_mutex; struct completion done_completion; struct completion busy_completion; + + struct work_struct recovery_work; + struct delayed_work d0ix_work; + atomic_t d0ix_disable_depth; + bool in_d0ix; }; #define AVS_EIPC EREMOTEIO @@ -195,6 +237,11 @@ int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *reply, int timeout); int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request, struct avs_ipc_msg *reply); +/* Two variants below are for messages that control DSP power states. */ +int avs_dsp_send_pm_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, + struct avs_ipc_msg *reply, int timeout, bool wake_d0i0); +int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request, + struct avs_ipc_msg *reply, bool wake_d0i0); int avs_dsp_send_rom_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout); int avs_dsp_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request); @@ -202,6 +249,11 @@ void avs_dsp_interrupt_control(struct avs_dev *adev, bool enable); int avs_ipc_init(struct avs_ipc *ipc, struct device *dev); void avs_ipc_block(struct avs_ipc *ipc); +int avs_dsp_disable_d0ix(struct avs_dev *adev); +int avs_dsp_enable_d0ix(struct avs_dev *adev); + +int skl_log_buffer_offset(struct avs_dev *adev, u32 core); + /* Firmware resources management */ int avs_get_module_entry(struct avs_dev *adev, const guid_t *uuid, struct avs_module_entry *entry); @@ -232,6 +284,7 @@ void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable); void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable); void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable); +int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs); int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge); int avs_dsp_first_boot_firmware(struct avs_dev *adev); @@ -244,4 +297,53 @@ int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id); int avs_hda_transfer_modules(struct avs_dev *adev, bool load, struct avs_module_entry *mods, u32 num_mods); +/* Soc component members */ + +struct avs_soc_component { + struct snd_soc_component base; + struct avs_tplg *tplg; + + struct list_head node; +}; + +#define to_avs_soc_component(comp) \ + container_of(comp, struct avs_soc_component, base) + +extern const struct snd_soc_dai_ops avs_dai_fe_ops; + +int avs_dmic_platform_register(struct avs_dev *adev, const char *name); +int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask, + unsigned long *tdms); +int avs_hda_platform_register(struct avs_dev *adev, const char *name); + +int avs_register_all_boards(struct avs_dev *adev); +void avs_unregister_all_boards(struct avs_dev *adev); + +/* Firmware tracing helpers */ + +unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len, + spinlock_t *lock); + +#define avs_log_buffer_size(adev) \ + ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores) + +#define avs_log_buffer_addr(adev, core) \ +({ \ + s32 __offset = avs_dsp_op(adev, log_buffer_offset, core); \ + (__offset < 0) ? NULL : \ + (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \ +}) + +struct apl_log_buffer_layout { + u32 read_ptr; + u32 write_ptr; + u8 buffer[]; +} __packed; + +#define apl_log_payload_size(adev) \ + (avs_log_buffer_size(adev) - sizeof(struct apl_log_buffer_layout)) + +#define apl_log_payload_addr(addr) \ + (addr + sizeof(struct apl_log_buffer_layout)) + #endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c new file mode 100644 index 000000000000..80cb0164678a --- /dev/null +++ b/sound/soc/intel/avs/board_selection.c @@ -0,0 +1,501 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/acpi.h> +#include <linux/module.h> +#include <linux/dmi.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <sound/hda_codec.h> +#include <sound/hda_register.h> +#include <sound/intel-nhlt.h> +#include <sound/soc-acpi.h> +#include <sound/soc-component.h> +#include "avs.h" + +static bool i2s_test; +module_param(i2s_test, bool, 0444); +MODULE_PARM_DESC(i2s_test, "Probe I2S test-board and skip all other I2S boards"); + +static const struct dmi_system_id kbl_dmi_table[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Skylake Y LPDDR3 RVP3"), + }, + }, + {} +}; + +static const struct dmi_system_id kblr_dmi_table[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Kabylake R DDR4 RVP"), + }, + }, + {} +}; + +static struct snd_soc_acpi_mach *dmi_match_quirk(void *arg) +{ + struct snd_soc_acpi_mach *mach = arg; + const struct dmi_system_id *dmi_id; + struct dmi_system_id *dmi_table; + + if (mach->quirk_data == NULL) + return mach; + + dmi_table = (struct dmi_system_id *)mach->quirk_data; + + dmi_id = dmi_first_match(dmi_table); + if (!dmi_id) + return NULL; + + return mach; +} + +#define AVS_SSP(x) (BIT(x)) +#define AVS_SSP_RANGE(a, b) (GENMASK(b, a)) + +/* supported I2S board codec configurations */ +static struct snd_soc_acpi_mach avs_skl_i2s_machines[] = { + { + .id = "INT343A", + .drv_name = "avs_rt286", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "rt286-tplg.bin", + }, + { + .id = "10508825", + .drv_name = "avs_nau8825", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "nau8825-tplg.bin", + }, + { + .id = "INT343B", + .drv_name = "avs_ssm4567", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "ssm4567-tplg.bin", + }, + { + .id = "MX98357A", + .drv_name = "avs_max98357a", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "max98357a-tplg.bin", + }, + {}, +}; + +static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = { + { + .id = "INT343A", + .drv_name = "avs_rt286", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .quirk_data = &kbl_dmi_table, + .machine_quirk = dmi_match_quirk, + .tplg_filename = "rt286-tplg.bin", + }, + { + .id = "INT343A", + .drv_name = "avs_rt298", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .quirk_data = &kblr_dmi_table, + .machine_quirk = dmi_match_quirk, + .tplg_filename = "rt298-tplg.bin", + }, + { + .id = "MX98373", + .drv_name = "avs_max98373", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "max98373-tplg.bin", + }, + { + .id = "DLGS7219", + .drv_name = "avs_da7219", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "da7219-tplg.bin", + }, + {}, +}; + +static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = { + { + .id = "INT343A", + .drv_name = "avs_rt298", + .mach_params = { + .i2s_link_mask = AVS_SSP(5), + }, + .tplg_filename = "rt298-tplg.bin", + }, + { + .id = "INT34C3", + .drv_name = "avs_tdf8532", + .mach_params = { + .i2s_link_mask = AVS_SSP_RANGE(0, 5), + }, + .pdata = (unsigned long[]){ 0, 0, 0x14, 0, 0, 0 }, /* SSP2 TDMs */ + .tplg_filename = "tdf8532-tplg.bin", + }, + { + .id = "MX98357A", + .drv_name = "avs_max98357a", + .mach_params = { + .i2s_link_mask = AVS_SSP(5), + }, + .tplg_filename = "max98357a-tplg.bin", + }, + { + .id = "DLGS7219", + .drv_name = "avs_da7219", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "da7219-tplg.bin", + }, + {}, +}; + +static struct snd_soc_acpi_mach avs_gml_i2s_machines[] = { + { + .id = "INT343A", + .drv_name = "avs_rt298", + .mach_params = { + .i2s_link_mask = AVS_SSP(2), + }, + .tplg_filename = "rt298-tplg.bin", + }, + {}, +}; + +static struct snd_soc_acpi_mach avs_test_i2s_machines[] = { + { + .drv_name = "avs_i2s_test", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "i2s-test-tplg.bin", + }, + { + .drv_name = "avs_i2s_test", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "i2s-test-tplg.bin", + }, + { + .drv_name = "avs_i2s_test", + .mach_params = { + .i2s_link_mask = AVS_SSP(2), + }, + .tplg_filename = "i2s-test-tplg.bin", + }, + { + .drv_name = "avs_i2s_test", + .mach_params = { + .i2s_link_mask = AVS_SSP(3), + }, + .tplg_filename = "i2s-test-tplg.bin", + }, + { + .drv_name = "avs_i2s_test", + .mach_params = { + .i2s_link_mask = AVS_SSP(4), + }, + .tplg_filename = "i2s-test-tplg.bin", + }, + { + .drv_name = "avs_i2s_test", + .mach_params = { + .i2s_link_mask = AVS_SSP(5), + }, + .tplg_filename = "i2s-test-tplg.bin", + }, + /* no NULL terminator, as we depend on ARRAY SIZE due to .id == NULL */ +}; + +struct avs_acpi_boards { + int id; + struct snd_soc_acpi_mach *machs; +}; + +#define AVS_MACH_ENTRY(_id, _mach) \ + { .id = (_id), .machs = (_mach), } + +/* supported I2S boards per platform */ +static const struct avs_acpi_boards i2s_boards[] = { + AVS_MACH_ENTRY(0x9d70, avs_skl_i2s_machines), /* SKL */ + AVS_MACH_ENTRY(0x9d71, avs_kbl_i2s_machines), /* KBL */ + AVS_MACH_ENTRY(0x5a98, avs_apl_i2s_machines), /* APL */ + AVS_MACH_ENTRY(0x3198, avs_gml_i2s_machines), /* GML */ + {}, +}; + +static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev) +{ + int id, i; + + id = adev->base.pci->device; + for (i = 0; i < ARRAY_SIZE(i2s_boards); i++) + if (i2s_boards[i].id == id) + return &i2s_boards[i]; + return NULL; +} + +/* platform devices owned by AVS audio are removed with this hook */ +static void board_pdev_unregister(void *data) +{ + platform_device_unregister(data); +} + +static int avs_register_dmic_board(struct avs_dev *adev) +{ + struct platform_device *codec, *board; + struct snd_soc_acpi_mach mach = {{0}}; + int ret; + + if (!adev->nhlt || + !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_DMIC)) { + dev_dbg(adev->dev, "no DMIC endpoints present\n"); + return 0; + } + + codec = platform_device_register_simple("dmic-codec", PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(codec)) { + dev_err(adev->dev, "dmic codec register failed\n"); + return PTR_ERR(codec); + } + + ret = devm_add_action(adev->dev, board_pdev_unregister, codec); + if (ret < 0) { + platform_device_unregister(codec); + return ret; + } + + ret = avs_dmic_platform_register(adev, "dmic-platform"); + if (ret < 0) + return ret; + + mach.tplg_filename = "dmic-tplg.bin"; + mach.mach_params.platform = "dmic-platform"; + + board = platform_device_register_data(NULL, "avs_dmic", PLATFORM_DEVID_NONE, + (const void *)&mach, sizeof(mach)); + if (IS_ERR(board)) { + dev_err(adev->dev, "dmic board register failed\n"); + return PTR_ERR(board); + } + + ret = devm_add_action(adev->dev, board_pdev_unregister, board); + if (ret < 0) { + platform_device_unregister(board); + return ret; + } + + return 0; +} + +static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach) +{ + struct platform_device *board; + int num_ssps; + char *name; + int ret; + + num_ssps = adev->hw_cfg.i2s_caps.ctrl_count; + if (fls(mach->mach_params.i2s_link_mask) > num_ssps) { + dev_err(adev->dev, "Platform supports %d SSPs but board %s requires SSP%ld\n", + num_ssps, mach->drv_name, __fls(mach->mach_params.i2s_link_mask)); + return -ENODEV; + } + + name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name, + mach->mach_params.i2s_link_mask); + if (!name) + return -ENOMEM; + + ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, mach->pdata); + if (ret < 0) + return ret; + + mach->mach_params.platform = name; + + board = platform_device_register_data(NULL, mach->drv_name, mach->mach_params.i2s_link_mask, + (const void *)mach, sizeof(*mach)); + if (IS_ERR(board)) { + dev_err(adev->dev, "ssp board register failed\n"); + return PTR_ERR(board); + } + + ret = devm_add_action(adev->dev, board_pdev_unregister, board); + if (ret < 0) { + platform_device_unregister(board); + return ret; + } + + return 0; +} + +static int avs_register_i2s_boards(struct avs_dev *adev) +{ + const struct avs_acpi_boards *boards; + struct snd_soc_acpi_mach *mach; + int ret; + + if (!adev->nhlt || !intel_nhlt_has_endpoint_type(adev->nhlt, NHLT_LINK_SSP)) { + dev_dbg(adev->dev, "no I2S endpoints present\n"); + return 0; + } + + if (i2s_test) { + int i, num_ssps; + + num_ssps = adev->hw_cfg.i2s_caps.ctrl_count; + /* constrain just in case FW says there can be more SSPs than possible */ + num_ssps = min_t(int, ARRAY_SIZE(avs_test_i2s_machines), num_ssps); + + mach = avs_test_i2s_machines; + + for (i = 0; i < num_ssps; i++) { + ret = avs_register_i2s_board(adev, &mach[i]); + if (ret < 0) + dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, + ret); + } + return 0; + } + + boards = avs_get_i2s_boards(adev); + if (!boards) { + dev_dbg(adev->dev, "no I2S endpoints supported\n"); + return 0; + } + + for (mach = boards->machs; mach->id[0]; mach++) { + if (!acpi_dev_present(mach->id, NULL, -1)) + continue; + + if (mach->machine_quirk) + if (!mach->machine_quirk(mach)) + continue; + + ret = avs_register_i2s_board(adev, mach); + if (ret < 0) + dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret); + } + + return 0; +} + +static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec) +{ + struct snd_soc_acpi_mach mach = {{0}}; + struct platform_device *board; + struct hdac_device *hdev = &codec->core; + char *pname; + int ret, id; + + pname = devm_kasprintf(adev->dev, GFP_KERNEL, "%s-platform", dev_name(&hdev->dev)); + if (!pname) + return -ENOMEM; + + ret = avs_hda_platform_register(adev, pname); + if (ret < 0) + return ret; + + mach.pdata = codec; + mach.mach_params.platform = pname; + mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin", + hdev->vendor_id); + if (!mach.tplg_filename) + return -ENOMEM; + + id = adev->base.core.idx * HDA_MAX_CODECS + hdev->addr; + board = platform_device_register_data(NULL, "avs_hdaudio", id, (const void *)&mach, + sizeof(mach)); + if (IS_ERR(board)) { + dev_err(adev->dev, "hda board register failed\n"); + return PTR_ERR(board); + } + + ret = devm_add_action(adev->dev, board_pdev_unregister, board); + if (ret < 0) { + platform_device_unregister(board); + return ret; + } + + return 0; +} + +static int avs_register_hda_boards(struct avs_dev *adev) +{ + struct hdac_bus *bus = &adev->base.core; + struct hdac_device *hdev; + int ret; + + if (!bus->num_codecs) { + dev_dbg(adev->dev, "no HDA endpoints present\n"); + return 0; + } + + list_for_each_entry(hdev, &bus->codec_list, list) { + struct hda_codec *codec; + + codec = dev_to_hda_codec(&hdev->dev); + + ret = avs_register_hda_board(adev, codec); + if (ret < 0) + dev_warn(adev->dev, "register hda-%08x failed: %d\n", + codec->core.vendor_id, ret); + } + + return 0; +} + +int avs_register_all_boards(struct avs_dev *adev) +{ + int ret; + + ret = avs_register_dmic_board(adev); + if (ret < 0) + dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n", + ret); + + ret = avs_register_i2s_boards(adev); + if (ret < 0) + dev_warn(adev->dev, "enumerate I2S endpoints failed: %d\n", + ret); + + ret = avs_register_hda_boards(adev); + if (ret < 0) + dev_warn(adev->dev, "enumerate HDA endpoints failed: %d\n", + ret); + + return 0; +} + +void avs_unregister_all_boards(struct avs_dev *adev) +{ + snd_soc_unregister_component(adev->dev); +} diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index a4d063d12fec..3a0997c3af2b 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -14,9 +14,17 @@ // foundation of this driver // +#include <linux/module.h> #include <linux/pci.h> +#include <sound/hda_codec.h> +#include <sound/hda_i915.h> +#include <sound/hda_register.h> #include <sound/hdaudio.h> +#include <sound/hdaudio_ext.h> +#include <sound/intel-dsp-config.h> +#include <sound/intel-nhlt.h> #include "avs.h" +#include "cldma.h" static void avs_hda_update_config_dword(struct hdac_bus *bus, u32 reg, u32 mask, u32 value) @@ -59,3 +67,626 @@ void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable) value = enable ? AZX_VS_EM2_L1SEN : 0; snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN, value); } + +static int avs_hdac_bus_init_streams(struct hdac_bus *bus) +{ + unsigned int cp_streams, pb_streams; + unsigned int gcap; + + gcap = snd_hdac_chip_readw(bus, GCAP); + cp_streams = (gcap >> 8) & 0x0F; + pb_streams = (gcap >> 12) & 0x0F; + bus->num_streams = cp_streams + pb_streams; + + snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); + snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK); + + return snd_hdac_bus_alloc_stream_pages(bus); +} + +static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) +{ + struct hdac_ext_link *hlink; + bool ret; + + avs_hdac_clock_gating_enable(bus, false); + ret = snd_hdac_bus_init_chip(bus, full_reset); + + /* Reset stream-to-link mapping */ + list_for_each_entry(hlink, &bus->hlink_list, list) + writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); + + avs_hdac_clock_gating_enable(bus, true); + + /* Set DUM bit to address incorrect position reporting for capture + * streams. In order to do so, CTRL needs to be out of reset state + */ + snd_hdac_chip_updatel(bus, VS_EM2, AZX_VS_EM2_DUM, AZX_VS_EM2_DUM); + + return ret; +} + +static int probe_codec(struct hdac_bus *bus, int addr) +{ + struct hda_codec *codec; + unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | + (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; + unsigned int res = -1; + int ret; + + mutex_lock(&bus->cmd_mutex); + snd_hdac_bus_send_cmd(bus, cmd); + snd_hdac_bus_get_response(bus, addr, &res); + mutex_unlock(&bus->cmd_mutex); + if (res == -1) + return -EIO; + + dev_dbg(bus->dev, "codec #%d probed OK: 0x%x\n", addr, res); + + codec = snd_hda_codec_device_init(to_hda_bus(bus), addr, "hdaudioB%dD%d", bus->idx, addr); + if (IS_ERR(codec)) { + dev_err(bus->dev, "init codec failed: %ld\n", PTR_ERR(codec)); + return PTR_ERR(codec); + } + /* + * Allow avs_core suspend by forcing suspended state on all + * of its codec child devices. Component interested in + * dealing with hda codecs directly takes pm responsibilities + */ + pm_runtime_set_suspended(hda_codec_dev(codec)); + + /* configure effectively creates new ASoC component */ + ret = snd_hda_codec_configure(codec); + if (ret < 0) { + dev_err(bus->dev, "failed to config codec %d\n", ret); + return ret; + } + + return 0; +} + +static void avs_hdac_bus_probe_codecs(struct hdac_bus *bus) +{ + int c; + + /* First try to probe all given codec slots */ + for (c = 0; c < HDA_MAX_CODECS; c++) { + if (!(bus->codec_mask & BIT(c))) + continue; + + if (!probe_codec(bus, c)) + /* success, continue probing */ + continue; + + /* + * Some BIOSen give you wrong codec addresses + * that don't exist + */ + dev_warn(bus->dev, "Codec #%d probe error; disabling it...\n", c); + bus->codec_mask &= ~BIT(c); + /* + * More badly, accessing to a non-existing + * codec often screws up the controller bus, + * and disturbs the further communications. + * Thus if an error occurs during probing, + * better to reset the controller bus to get + * back to the sanity state. + */ + snd_hdac_bus_stop_chip(bus); + avs_hdac_bus_init_chip(bus, true); + } +} + +static void avs_hda_probe_work(struct work_struct *work) +{ + struct avs_dev *adev = container_of(work, struct avs_dev, probe_work); + struct hdac_bus *bus = &adev->base.core; + struct hdac_ext_link *hlink; + int ret; + + pm_runtime_set_active(bus->dev); /* clear runtime_error flag */ + + ret = snd_hdac_i915_init(bus); + if (ret < 0) + dev_info(bus->dev, "i915 init unsuccessful: %d\n", ret); + + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true); + avs_hdac_bus_init_chip(bus, true); + avs_hdac_bus_probe_codecs(bus); + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); + + /* with all codecs probed, links can be powered down */ + list_for_each_entry(hlink, &bus->hlink_list, list) + snd_hdac_ext_bus_link_put(bus, hlink); + + snd_hdac_ext_bus_ppcap_enable(bus, true); + snd_hdac_ext_bus_ppcap_int_enable(bus, true); + + ret = avs_dsp_first_boot_firmware(adev); + if (ret < 0) + return; + + adev->nhlt = intel_nhlt_init(adev->dev); + if (!adev->nhlt) + dev_info(bus->dev, "platform has no NHLT\n"); + + avs_register_all_boards(adev); + + /* configure PM */ + pm_runtime_set_autosuspend_delay(bus->dev, 2000); + pm_runtime_use_autosuspend(bus->dev); + pm_runtime_mark_last_busy(bus->dev); + pm_runtime_put_autosuspend(bus->dev); + pm_runtime_allow(bus->dev); +} + +static void hdac_stream_update_pos(struct hdac_stream *stream, u64 buffer_size) +{ + u64 prev_pos, pos, num_bytes; + + div64_u64_rem(stream->curr_pos, buffer_size, &prev_pos); + pos = snd_hdac_stream_get_pos_posbuf(stream); + + if (pos < prev_pos) + num_bytes = (buffer_size - prev_pos) + pos; + else + num_bytes = pos - prev_pos; + + stream->curr_pos += num_bytes; +} + +/* called from IRQ */ +static void hdac_update_stream(struct hdac_bus *bus, struct hdac_stream *stream) +{ + if (stream->substream) { + snd_pcm_period_elapsed(stream->substream); + } else if (stream->cstream) { + u64 buffer_size = stream->cstream->runtime->buffer_size; + + hdac_stream_update_pos(stream, buffer_size); + snd_compr_fragment_elapsed(stream->cstream); + } +} + +static irqreturn_t hdac_bus_irq_handler(int irq, void *context) +{ + struct hdac_bus *bus = context; + u32 mask, int_enable; + u32 status; + int ret = IRQ_NONE; + + if (!pm_runtime_active(bus->dev)) + return ret; + + spin_lock(&bus->reg_lock); + + status = snd_hdac_chip_readl(bus, INTSTS); + if (status == 0 || status == UINT_MAX) { + spin_unlock(&bus->reg_lock); + return ret; + } + + /* clear rirb int */ + status = snd_hdac_chip_readb(bus, RIRBSTS); + if (status & RIRB_INT_MASK) { + if (status & RIRB_INT_RESPONSE) + snd_hdac_bus_update_rirb(bus); + snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); + } + + mask = (0x1 << bus->num_streams) - 1; + + status = snd_hdac_chip_readl(bus, INTSTS); + status &= mask; + if (status) { + /* Disable stream interrupts; Re-enable in bottom half */ + int_enable = snd_hdac_chip_readl(bus, INTCTL); + snd_hdac_chip_writel(bus, INTCTL, (int_enable & (~mask))); + ret = IRQ_WAKE_THREAD; + } else { + ret = IRQ_HANDLED; + } + + spin_unlock(&bus->reg_lock); + return ret; +} + +static irqreturn_t hdac_bus_irq_thread(int irq, void *context) +{ + struct hdac_bus *bus = context; + u32 status; + u32 int_enable; + u32 mask; + unsigned long flags; + + status = snd_hdac_chip_readl(bus, INTSTS); + + snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream); + + /* Re-enable stream interrupts */ + mask = (0x1 << bus->num_streams) - 1; + spin_lock_irqsave(&bus->reg_lock, flags); + int_enable = snd_hdac_chip_readl(bus, INTCTL); + snd_hdac_chip_writel(bus, INTCTL, (int_enable | mask)); + spin_unlock_irqrestore(&bus->reg_lock, flags); + + return IRQ_HANDLED; +} + +static int avs_hdac_acquire_irq(struct avs_dev *adev) +{ + struct hdac_bus *bus = &adev->base.core; + struct pci_dev *pci = to_pci_dev(bus->dev); + int ret; + + /* request one and check that we only got one interrupt */ + ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_LEGACY); + if (ret != 1) { + dev_err(adev->dev, "Failed to allocate IRQ vector: %d\n", ret); + return ret; + } + + ret = pci_request_irq(pci, 0, hdac_bus_irq_handler, hdac_bus_irq_thread, bus, + KBUILD_MODNAME); + if (ret < 0) { + dev_err(adev->dev, "Failed to request stream IRQ handler: %d\n", ret); + goto free_vector; + } + + ret = pci_request_irq(pci, 0, avs_dsp_irq_handler, avs_dsp_irq_thread, adev, + KBUILD_MODNAME); + if (ret < 0) { + dev_err(adev->dev, "Failed to request IPC IRQ handler: %d\n", ret); + goto free_stream_irq; + } + + return 0; + +free_stream_irq: + pci_free_irq(pci, 0, bus); +free_vector: + pci_free_irq_vectors(pci); + return ret; +} + +static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct pci_device_id *id) +{ + struct hda_bus *bus = &adev->base; + struct avs_ipc *ipc; + struct device *dev = &pci->dev; + int ret; + + ret = snd_hdac_ext_bus_init(&bus->core, dev, NULL, NULL); + if (ret < 0) + return ret; + + bus->core.use_posbuf = 1; + bus->core.bdl_pos_adj = 0; + bus->core.sync_write = 1; + bus->pci = pci; + bus->mixer_assigned = -1; + mutex_init(&bus->prepare_mutex); + + ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL); + if (!ipc) + return -ENOMEM; + ret = avs_ipc_init(ipc, dev); + if (ret < 0) + return ret; + + adev->dev = dev; + adev->spec = (const struct avs_spec *)id->driver_data; + adev->ipc = ipc; + adev->hw_cfg.dsp_cores = hweight_long(AVS_MAIN_CORE_MASK); + INIT_WORK(&adev->probe_work, avs_hda_probe_work); + INIT_LIST_HEAD(&adev->comp_list); + INIT_LIST_HEAD(&adev->path_list); + INIT_LIST_HEAD(&adev->fw_list); + init_completion(&adev->fw_ready); + spin_lock_init(&adev->path_list_lock); + mutex_init(&adev->modres_mutex); + mutex_init(&adev->comp_list_mutex); + mutex_init(&adev->path_mutex); + + return 0; +} + +static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) +{ + struct hdac_bus *bus; + struct avs_dev *adev; + struct device *dev = &pci->dev; + int ret; + + ret = snd_intel_dsp_driver_probe(pci); + if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_AVS) + return -ENODEV; + + ret = pcim_enable_device(pci); + if (ret < 0) + return ret; + + adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL); + if (!adev) + return -ENOMEM; + ret = avs_bus_init(adev, pci, id); + if (ret < 0) { + dev_err(dev, "failed to init avs bus: %d\n", ret); + return ret; + } + + ret = pci_request_regions(pci, "AVS HDAudio"); + if (ret < 0) + return ret; + + bus = &adev->base.core; + bus->addr = pci_resource_start(pci, 0); + bus->remap_addr = pci_ioremap_bar(pci, 0); + if (!bus->remap_addr) { + dev_err(bus->dev, "ioremap error\n"); + ret = -ENXIO; + goto err_remap_bar0; + } + + adev->dsp_ba = pci_ioremap_bar(pci, 4); + if (!adev->dsp_ba) { + dev_err(bus->dev, "ioremap error\n"); + ret = -ENXIO; + goto err_remap_bar4; + } + + snd_hdac_bus_parse_capabilities(bus); + if (bus->mlcap) + snd_hdac_ext_bus_get_ml_capabilities(bus); + + if (!dma_set_mask(dev, DMA_BIT_MASK(64))) { + dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); + } else { + dma_set_mask(dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); + } + + ret = avs_hdac_bus_init_streams(bus); + if (ret < 0) { + dev_err(dev, "failed to init streams: %d\n", ret); + goto err_init_streams; + } + + ret = avs_hdac_acquire_irq(adev); + if (ret < 0) { + dev_err(bus->dev, "failed to acquire irq: %d\n", ret); + goto err_acquire_irq; + } + + pci_set_master(pci); + pci_set_drvdata(pci, bus); + device_disable_async_suspend(dev); + + schedule_work(&adev->probe_work); + + return 0; + +err_acquire_irq: + snd_hdac_bus_free_stream_pages(bus); + snd_hdac_stream_free_all(bus); +err_init_streams: + iounmap(adev->dsp_ba); +err_remap_bar4: + iounmap(bus->remap_addr); +err_remap_bar0: + pci_release_regions(pci); + return ret; +} + +static void avs_pci_remove(struct pci_dev *pci) +{ + struct hdac_device *hdev, *save; + struct hdac_bus *bus = pci_get_drvdata(pci); + struct avs_dev *adev = hdac_to_avs(bus); + + cancel_work_sync(&adev->probe_work); + avs_ipc_block(adev->ipc); + + avs_unregister_all_boards(adev); + + if (adev->nhlt) + intel_nhlt_free(adev->nhlt); + + if (avs_platattr_test(adev, CLDMA)) + hda_cldma_free(&code_loader); + + snd_hdac_stop_streams_and_chip(bus); + avs_dsp_op(adev, int_control, false); + snd_hdac_ext_bus_ppcap_int_enable(bus, false); + + /* it is safe to remove all codecs from the system now */ + list_for_each_entry_safe(hdev, save, &bus->codec_list, list) + snd_hda_codec_unregister(hdac_to_hda_codec(hdev)); + + snd_hdac_bus_free_stream_pages(bus); + snd_hdac_stream_free_all(bus); + /* reverse ml_capabilities */ + snd_hdac_link_free_all(bus); + snd_hdac_ext_bus_exit(bus); + + avs_dsp_core_disable(adev, GENMASK(adev->hw_cfg.dsp_cores - 1, 0)); + snd_hdac_ext_bus_ppcap_enable(bus, false); + + /* snd_hdac_stop_streams_and_chip does that already? */ + snd_hdac_bus_stop_chip(bus); + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); + if (bus->audio_component) + snd_hdac_i915_exit(bus); + + avs_module_info_free(adev); + pci_free_irq(pci, 0, adev); + pci_free_irq(pci, 0, bus); + pci_free_irq_vectors(pci); + iounmap(bus->remap_addr); + iounmap(adev->dsp_ba); + pci_release_regions(pci); + + /* Firmware is not needed anymore */ + avs_release_firmwares(adev); + + /* pm_runtime_forbid() can rpm_resume() which we do not want */ + pm_runtime_disable(&pci->dev); + pm_runtime_forbid(&pci->dev); + pm_runtime_enable(&pci->dev); + pm_runtime_get_noresume(&pci->dev); +} + +static int __maybe_unused avs_suspend_common(struct avs_dev *adev) +{ + struct hdac_bus *bus = &adev->base.core; + int ret; + + flush_work(&adev->probe_work); + + snd_hdac_ext_bus_link_power_down_all(bus); + + ret = avs_ipc_set_dx(adev, AVS_MAIN_CORE_MASK, false); + /* + * pm_runtime is blocked on DSP failure but system-wide suspend is not. + * Do not block entire system from suspending if that's the case. + */ + if (ret && ret != -EPERM) { + dev_err(adev->dev, "set dx failed: %d\n", ret); + return AVS_IPC_RET(ret); + } + + avs_dsp_op(adev, int_control, false); + snd_hdac_ext_bus_ppcap_int_enable(bus, false); + + ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); + if (ret < 0) { + dev_err(adev->dev, "core_mask %ld disable failed: %d\n", AVS_MAIN_CORE_MASK, ret); + return ret; + } + + snd_hdac_ext_bus_ppcap_enable(bus, false); + /* disable LP SRAM retention */ + avs_hda_power_gating_enable(adev, false); + snd_hdac_bus_stop_chip(bus); + /* disable CG when putting controller to reset */ + avs_hdac_clock_gating_enable(bus, false); + snd_hdac_bus_enter_link_reset(bus); + avs_hdac_clock_gating_enable(bus, true); + + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, false); + + return 0; +} + +static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool purge) +{ + struct hdac_bus *bus = &adev->base.core; + struct hdac_ext_link *hlink; + int ret; + + snd_hdac_display_power(bus, HDA_CODEC_IDX_CONTROLLER, true); + avs_hdac_bus_init_chip(bus, true); + + snd_hdac_ext_bus_ppcap_enable(bus, true); + snd_hdac_ext_bus_ppcap_int_enable(bus, true); + + ret = avs_dsp_boot_firmware(adev, purge); + if (ret < 0) { + dev_err(adev->dev, "firmware boot failed: %d\n", ret); + return ret; + } + + /* turn off the links that were off before suspend */ + list_for_each_entry(hlink, &bus->hlink_list, list) { + if (!hlink->ref_count) + snd_hdac_ext_bus_link_power_down(hlink); + } + + /* check dma status and clean up CORB/RIRB buffers */ + if (!bus->cmd_dma_state) + snd_hdac_bus_stop_cmd_io(bus); + + return 0; +} + +static int __maybe_unused avs_suspend(struct device *dev) +{ + return avs_suspend_common(to_avs_dev(dev)); +} + +static int __maybe_unused avs_resume(struct device *dev) +{ + return avs_resume_common(to_avs_dev(dev), true); +} + +static int __maybe_unused avs_runtime_suspend(struct device *dev) +{ + return avs_suspend_common(to_avs_dev(dev)); +} + +static int __maybe_unused avs_runtime_resume(struct device *dev) +{ + return avs_resume_common(to_avs_dev(dev), true); +} + +static const struct dev_pm_ops avs_dev_pm = { + SET_SYSTEM_SLEEP_PM_OPS(avs_suspend, avs_resume) + SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL) +}; + +static const struct avs_spec skl_desc = { + .name = "skl", + .min_fw_version = { + .major = 9, + .minor = 21, + .hotfix = 0, + .build = 4732, + }, + .dsp_ops = &skl_dsp_ops, + .core_init_mask = 1, + .attributes = AVS_PLATATTR_CLDMA, + .sram_base_offset = SKL_ADSP_SRAM_BASE_OFFSET, + .sram_window_size = SKL_ADSP_SRAM_WINDOW_SIZE, + .rom_status = SKL_ADSP_SRAM_BASE_OFFSET, +}; + +static const struct avs_spec apl_desc = { + .name = "apl", + .min_fw_version = { + .major = 9, + .minor = 22, + .hotfix = 1, + .build = 4323, + }, + .dsp_ops = &apl_dsp_ops, + .core_init_mask = 3, + .attributes = AVS_PLATATTR_IMR, + .sram_base_offset = APL_ADSP_SRAM_BASE_OFFSET, + .sram_window_size = APL_ADSP_SRAM_WINDOW_SIZE, + .rom_status = APL_ADSP_SRAM_BASE_OFFSET, +}; + +static const struct pci_device_id avs_ids[] = { + { PCI_VDEVICE(INTEL, 0x9d70), (unsigned long)&skl_desc }, /* SKL */ + { PCI_VDEVICE(INTEL, 0x9d71), (unsigned long)&skl_desc }, /* KBL */ + { PCI_VDEVICE(INTEL, 0x5a98), (unsigned long)&apl_desc }, /* APL */ + { PCI_VDEVICE(INTEL, 0x3198), (unsigned long)&apl_desc }, /* GML */ + { 0 } +}; +MODULE_DEVICE_TABLE(pci, avs_ids); + +static struct pci_driver avs_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = avs_ids, + .probe = avs_pci_probe, + .remove = avs_pci_remove, + .driver = { + .pm = &avs_dev_pm, + }, +}; +module_pci_driver(avs_pci_driver); + +MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>"); +MODULE_AUTHOR("Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>"); +MODULE_DESCRIPTION("Intel cAVS sound driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c index 3ff17bd22a5a..06d2f7af520f 100644 --- a/sound/soc/intel/avs/dsp.c +++ b/sound/soc/intel/avs/dsp.c @@ -6,10 +6,10 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // -#include <linux/module.h> #include <sound/hdaudio_ext.h> #include "avs.h" #include "registers.h" +#include "trace.h" #define AVS_ADSPCS_INTERVAL_US 500 #define AVS_ADSPCS_TIMEOUT_US 50000 @@ -19,6 +19,9 @@ int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power) u32 value, mask, reg; int ret; + value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS); + trace_avs_dsp_core_op(value, core_mask, "power", power); + mask = AVS_ADSPCS_SPA_MASK(core_mask); value = power ? mask : 0; @@ -43,6 +46,9 @@ int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset) u32 value, mask, reg; int ret; + value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS); + trace_avs_dsp_core_op(value, core_mask, "reset", reset); + mask = AVS_ADSPCS_CRST_MASK(core_mask); value = reset ? mask : 0; @@ -64,6 +70,9 @@ int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall) u32 value, mask, reg; int ret; + value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS); + trace_avs_dsp_core_op(value, core_mask, "stall", stall); + mask = AVS_ADSPCS_CSTALL_MASK(core_mask); value = stall ? mask : 0; @@ -152,6 +161,15 @@ static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id) adev->core_refs[core_id]++; if (adev->core_refs[core_id] == 1) { + /* + * No cores other than main-core can be running for DSP + * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted, + * simply d0ix power state will no longer be attempted. + */ + ret = avs_dsp_disable_d0ix(adev); + if (ret && ret != -AVS_EIPC) + goto err_disable_d0ix; + ret = avs_dsp_enable(adev, mask); if (ret) goto err_enable_dsp; @@ -160,6 +178,8 @@ static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id) return 0; err_enable_dsp: + avs_dsp_enable_d0ix(adev); +err_disable_d0ix: adev->core_refs[core_id]--; err: dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret); @@ -185,6 +205,9 @@ static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id) ret = avs_dsp_disable(adev, mask); if (ret) goto err; + + /* Match disable_d0ix in avs_dsp_get_core(). */ + avs_dsp_enable_d0ix(adev); } return 0; @@ -298,5 +321,3 @@ int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id) ida_free(&adev->ppl_ida, instance_id); return ret; } - -MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index 68aaf01edbf2..d755ba8b8518 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -6,18 +6,185 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/slab.h> #include <sound/hdaudio_ext.h> #include "avs.h" #include "messages.h" #include "registers.h" +#include "trace.h" #define AVS_IPC_TIMEOUT_MS 300 +#define AVS_D0IX_DELAY_MS 300 + +static int +avs_dsp_set_d0ix(struct avs_dev *adev, bool enable) +{ + struct avs_ipc *ipc = adev->ipc; + int ret; + + /* Is transition required? */ + if (ipc->in_d0ix == enable) + return 0; + + ret = avs_dsp_op(adev, set_d0ix, enable); + if (ret) { + /* Prevent further d0ix attempts on conscious IPC failure. */ + if (ret == -AVS_EIPC) + atomic_inc(&ipc->d0ix_disable_depth); + + ipc->in_d0ix = false; + return ret; + } + + ipc->in_d0ix = enable; + return 0; +} + +static void avs_dsp_schedule_d0ix(struct avs_dev *adev, struct avs_ipc_msg *tx) +{ + if (atomic_read(&adev->ipc->d0ix_disable_depth)) + return; + + mod_delayed_work(system_power_efficient_wq, &adev->ipc->d0ix_work, + msecs_to_jiffies(AVS_D0IX_DELAY_MS)); +} + +static void avs_dsp_d0ix_work(struct work_struct *work) +{ + struct avs_ipc *ipc = container_of(work, struct avs_ipc, d0ix_work.work); + + avs_dsp_set_d0ix(to_avs_dev(ipc->dev), true); +} + +static int avs_dsp_wake_d0i0(struct avs_dev *adev, struct avs_ipc_msg *tx) +{ + struct avs_ipc *ipc = adev->ipc; + + if (!atomic_read(&ipc->d0ix_disable_depth)) { + cancel_delayed_work_sync(&ipc->d0ix_work); + return avs_dsp_set_d0ix(adev, false); + } + + return 0; +} + +int avs_dsp_disable_d0ix(struct avs_dev *adev) +{ + struct avs_ipc *ipc = adev->ipc; + + /* Prevent PG only on the first disable. */ + if (atomic_add_return(1, &ipc->d0ix_disable_depth) == 1) { + cancel_delayed_work_sync(&ipc->d0ix_work); + return avs_dsp_set_d0ix(adev, false); + } + + return 0; +} + +int avs_dsp_enable_d0ix(struct avs_dev *adev) +{ + struct avs_ipc *ipc = adev->ipc; + + if (atomic_dec_and_test(&ipc->d0ix_disable_depth)) + queue_delayed_work(system_power_efficient_wq, &ipc->d0ix_work, + msecs_to_jiffies(AVS_D0IX_DELAY_MS)); + return 0; +} + +static void avs_dsp_recovery(struct avs_dev *adev) +{ + struct avs_soc_component *acomp; + unsigned int core_mask; + int ret; + + mutex_lock(&adev->comp_list_mutex); + /* disconnect all running streams */ + list_for_each_entry(acomp, &adev->comp_list, node) { + struct snd_soc_pcm_runtime *rtd; + struct snd_soc_card *card; + + card = acomp->base.card; + if (!card) + continue; + + for_each_card_rtds(card, rtd) { + struct snd_pcm *pcm; + int dir; + + pcm = rtd->pcm; + if (!pcm || rtd->dai_link->no_pcm) + continue; + + for_each_pcm_streams(dir) { + struct snd_pcm_substream *substream; + + substream = pcm->streams[dir].substream; + if (!substream || !substream->runtime) + continue; + + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + } + } + } + mutex_unlock(&adev->comp_list_mutex); + + /* forcibly shutdown all cores */ + core_mask = GENMASK(adev->hw_cfg.dsp_cores - 1, 0); + avs_dsp_core_disable(adev, core_mask); + + /* attempt dsp reboot */ + ret = avs_dsp_boot_firmware(adev, true); + if (ret < 0) + dev_err(adev->dev, "dsp reboot failed: %d\n", ret); + + pm_runtime_mark_last_busy(adev->dev); + pm_runtime_enable(adev->dev); + pm_request_autosuspend(adev->dev); + + atomic_set(&adev->ipc->recovering, 0); +} + +static void avs_dsp_recovery_work(struct work_struct *work) +{ + struct avs_ipc *ipc = container_of(work, struct avs_ipc, recovery_work); + + avs_dsp_recovery(to_avs_dev(ipc->dev)); +} + +static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg *msg) +{ + struct avs_ipc *ipc = adev->ipc; + + /* Account for the double-exception case. */ + ipc->ready = false; + + if (!atomic_add_unless(&ipc->recovering, 1, 1)) { + dev_err(adev->dev, "dsp recovery is already in progress\n"); + return; + } + + dev_crit(adev->dev, "communication severed, rebooting dsp..\n"); + + cancel_delayed_work_sync(&ipc->d0ix_work); + ipc->in_d0ix = false; + /* Re-enabled on recovery completion. */ + pm_runtime_disable(adev->dev); + + /* Process received notification. */ + avs_dsp_op(adev, coredump, msg); + + schedule_work(&ipc->recovery_work); +} static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header) { struct avs_ipc *ipc = adev->ipc; union avs_reply_msg msg = AVS_MSG(header); + u64 reg; + + reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW)); + trace_avs_ipc_reply_msg(header, reg); ipc->rx.header = header; /* Abort copying payload if request processing was unsuccessful. */ @@ -28,6 +195,7 @@ static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header) ipc->rx.size = msg.ext.large_config.data_off_size; memcpy_fromio(ipc->rx.data, avs_uplink_addr(adev), ipc->rx.size); + trace_avs_msg_payload(ipc->rx.data, ipc->rx.size); } } @@ -37,6 +205,10 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header) union avs_notify_msg msg = AVS_MSG(header); size_t data_size = 0; void *data = NULL; + u64 reg; + + reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW)); + trace_avs_ipc_notify_msg(header, reg); /* Ignore spurious notifications until handshake is established. */ if (!adev->ipc->ready && msg.notify_msg_type != AVS_NOTIFY_FW_READY) { @@ -57,6 +229,10 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header) data_size = sizeof(struct avs_notify_res_data); break; + case AVS_NOTIFY_LOG_BUFFER_STATUS: + case AVS_NOTIFY_EXCEPTION_CAUGHT: + break; + case AVS_NOTIFY_MODULE_EVENT: /* To know the total payload size, header needs to be read first. */ memcpy_fromio(&mod_data, avs_uplink_addr(adev), sizeof(mod_data)); @@ -74,6 +250,7 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header) return; memcpy_fromio(data, avs_uplink_addr(adev), data_size); + trace_avs_msg_payload(data, data_size); } /* Perform notification-specific operations. */ @@ -84,6 +261,14 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header) complete(&adev->fw_ready); break; + case AVS_NOTIFY_LOG_BUFFER_STATUS: + avs_dsp_op(adev, log_buffer_status, &msg); + break; + + case AVS_NOTIFY_EXCEPTION_CAUGHT: + avs_dsp_exception_caught(adev, &msg); + break; + default: break; } @@ -249,9 +434,15 @@ static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply) reinit_completion(&ipc->busy_completion); } -static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx) +static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool read_fwregs) { + u64 reg = ULONG_MAX; + tx->header |= SKL_ADSP_HIPCI_BUSY; + if (read_fwregs) + reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW)); + + trace_avs_request(tx, reg); if (tx->size) memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size); @@ -272,15 +463,16 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request spin_lock(&ipc->rx_lock); avs_ipc_msg_init(ipc, reply); - avs_dsp_send_tx(adev, request); + avs_dsp_send_tx(adev, request, true); spin_unlock(&ipc->rx_lock); ret = avs_ipc_wait_busy_completion(ipc, timeout); if (ret) { if (ret == -ETIMEDOUT) { - dev_crit(adev->dev, "communication severed: %d, rebooting dsp..\n", ret); + union avs_notify_msg msg = AVS_NOTIFICATION(EXCEPTION_CAUGHT); - avs_ipc_block(ipc); + /* Same treatment as on exception, just stack_dump=0. */ + avs_dsp_exception_caught(adev, &msg); } goto exit; } @@ -297,10 +489,37 @@ exit: return ret; } +static int avs_dsp_send_msg_sequence(struct avs_dev *adev, struct avs_ipc_msg *request, + struct avs_ipc_msg *reply, int timeout, bool wake_d0i0, + bool schedule_d0ix) +{ + int ret; + + trace_avs_d0ix("wake", wake_d0i0, request->header); + if (wake_d0i0) { + ret = avs_dsp_wake_d0i0(adev, request); + if (ret) + return ret; + } + + ret = avs_dsp_do_send_msg(adev, request, reply, timeout); + if (ret) + return ret; + + trace_avs_d0ix("schedule", schedule_d0ix, request->header); + if (schedule_d0ix) + avs_dsp_schedule_d0ix(adev, request); + + return 0; +} + int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, struct avs_ipc_msg *reply, int timeout) { - return avs_dsp_do_send_msg(adev, request, reply, timeout); + bool wake_d0i0 = avs_dsp_op(adev, d0ix_toggle, request, true); + bool schedule_d0ix = avs_dsp_op(adev, d0ix_toggle, request, false); + + return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, schedule_d0ix); } int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request, @@ -309,6 +528,19 @@ int avs_dsp_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request, return avs_dsp_send_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms); } +int avs_dsp_send_pm_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request, + struct avs_ipc_msg *reply, int timeout, bool wake_d0i0) +{ + return avs_dsp_send_msg_sequence(adev, request, reply, timeout, wake_d0i0, false); +} + +int avs_dsp_send_pm_msg(struct avs_dev *adev, struct avs_ipc_msg *request, + struct avs_ipc_msg *reply, bool wake_d0i0) +{ + return avs_dsp_send_pm_msg_timeout(adev, request, reply, adev->ipc->default_timeout_ms, + wake_d0i0); +} + static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *request, int timeout) { struct avs_ipc *ipc = adev->ipc; @@ -318,7 +550,11 @@ static int avs_dsp_do_send_rom_msg(struct avs_dev *adev, struct avs_ipc_msg *req spin_lock(&ipc->rx_lock); avs_ipc_msg_init(ipc, NULL); - avs_dsp_send_tx(adev, request); + /* + * with hw still stalled, memory windows may not be + * configured properly so avoid accessing SRAM + */ + avs_dsp_send_tx(adev, request, false); spin_unlock(&ipc->rx_lock); /* ROM messages must be sent before main core is unstalled */ @@ -368,6 +604,8 @@ int avs_ipc_init(struct avs_ipc *ipc, struct device *dev) ipc->dev = dev; ipc->ready = false; ipc->default_timeout_ms = AVS_IPC_TIMEOUT_MS; + INIT_WORK(&ipc->recovery_work, avs_dsp_recovery_work); + INIT_DELAYED_WORK(&ipc->d0ix_work, avs_dsp_d0ix_work); init_completion(&ipc->done_completion); init_completion(&ipc->busy_completion); spin_lock_init(&ipc->rx_lock); @@ -379,4 +617,7 @@ int avs_ipc_init(struct avs_ipc *ipc, struct device *dev) void avs_ipc_block(struct avs_ipc *ipc) { ipc->ready = false; + cancel_work_sync(&ipc->recovery_work); + cancel_delayed_work_sync(&ipc->d0ix_work); + ipc->in_d0ix = false; } diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index c47f85161d95..542fd44aa501 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -15,6 +15,7 @@ #include "cldma.h" #include "messages.h" #include "registers.h" +#include "topology.h" #define AVS_ROM_STS_MASK 0xFF #define AVS_ROM_INIT_DONE 0x1 @@ -36,6 +37,8 @@ #define AVS_EXT_MANIFEST_MAGIC 0x31454124 #define SKL_MANIFEST_MAGIC 0x00000006 #define SKL_ADSPFW_OFFSET 0x284 +#define APL_MANIFEST_MAGIC 0x44504324 +#define APL_ADSPFW_OFFSET 0x2000 /* Occasionally, engineering (release candidate) firmware is provided for testing. */ static bool debug_ignore_fw_version; @@ -86,6 +89,8 @@ static int avs_fw_manifest_offset(struct firmware *fw) switch (magic) { case SKL_MANIFEST_MAGIC: return SKL_ADSPFW_OFFSET; + case APL_MANIFEST_MAGIC: + return APL_ADSPFW_OFFSET; default: return -EINVAL; } @@ -466,6 +471,71 @@ int avs_hda_transfer_modules(struct avs_dev *adev, bool load, return 0; } +int avs_dsp_load_libraries(struct avs_dev *adev, struct avs_tplg_library *libs, u32 num_libs) +{ + int start, id, i = 0; + int ret; + + /* Calculate the id to assign for the next lib. */ + for (id = 0; id < adev->fw_cfg.max_libs_count; id++) + if (adev->lib_names[id][0] == '\0') + break; + if (id + num_libs >= adev->fw_cfg.max_libs_count) + return -EINVAL; + + start = id; + while (i < num_libs) { + struct avs_fw_manifest *man; + const struct firmware *fw; + struct firmware stripped_fw; + char *filename; + int j; + + filename = kasprintf(GFP_KERNEL, "%s/%s/%s", AVS_ROOT_DIR, adev->spec->name, + libs[i].name); + if (!filename) + return -ENOMEM; + + /* + * If any call after this one fails, requested firmware is not released with + * avs_release_last_firmware() as failing to load code results in need for reload + * of entire driver module. And then avs_release_firmwares() is in place already. + */ + ret = avs_request_firmware(adev, &fw, filename); + kfree(filename); + if (ret < 0) + return ret; + + stripped_fw = *fw; + ret = avs_fw_manifest_strip_verify(adev, &stripped_fw, NULL); + if (ret) { + dev_err(adev->dev, "invalid library data: %d\n", ret); + return ret; + } + + ret = avs_fw_manifest_offset(&stripped_fw); + if (ret < 0) + return ret; + man = (struct avs_fw_manifest *)(stripped_fw.data + ret); + + /* Don't load anything that's already in DSP memory. */ + for (j = 0; j < id; j++) + if (!strncmp(adev->lib_names[j], man->name, AVS_LIB_NAME_SIZE)) + goto next_lib; + + ret = avs_dsp_op(adev, load_lib, &stripped_fw, id); + if (ret) + return ret; + + strncpy(adev->lib_names[id], man->name, AVS_LIB_NAME_SIZE); + id++; +next_lib: + i++; + } + + return start == id ? 1 : 0; +} + static int avs_dsp_load_basefw(struct avs_dev *adev) { const struct avs_fw_version *min_req; @@ -519,6 +589,7 @@ release_fw: int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) { + struct avs_soc_component *acomp; int ret, i; /* Forgo full boot if flash from IMR succeeds. */ @@ -538,7 +609,20 @@ int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) avs_hda_l1sen_enable(adev, false); ret = avs_dsp_load_basefw(adev); + if (ret) + goto reenable_gating; + + mutex_lock(&adev->comp_list_mutex); + list_for_each_entry(acomp, &adev->comp_list, node) { + struct avs_tplg *tplg = acomp->tplg; + + ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); + if (ret < 0) + break; + } + mutex_unlock(&adev->comp_list_mutex); +reenable_gating: avs_hda_l1sen_enable(adev, true); avs_hda_clock_gating_enable(adev, true); diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index 004da166a943..6404fce8cde4 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -432,7 +432,7 @@ int avs_ipc_set_dx(struct avs_dev *adev, u32 core_mask, bool powerup) request.data = &dx; request.size = sizeof(dx); - ret = avs_dsp_send_msg(adev, &request, NULL); + ret = avs_dsp_send_pm_msg(adev, &request, NULL, true); if (ret) avs_ipc_err(adev, &request, "set dx", ret); @@ -456,7 +456,7 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming) request.header = msg.val; - ret = avs_dsp_send_msg(adev, &request, NULL); + ret = avs_dsp_send_pm_msg(adev, &request, NULL, false); if (ret) avs_ipc_err(adev, &request, "set d0ix", ret); @@ -677,6 +677,37 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info) return 0; } +int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size) +{ + int ret; + + ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, + AVS_BASEFW_ENABLE_LOGS, log_info, size); + if (ret) + dev_err(adev->dev, "enable logs failed: %d\n", ret); + + return ret; +} + +int avs_ipc_set_system_time(struct avs_dev *adev) +{ + struct avs_sys_time sys_time; + int ret; + u64 us; + + /* firmware expects UTC time in micro seconds */ + us = ktime_to_us(ktime_get()); + sys_time.val_l = us & UINT_MAX; + sys_time.val_u = us >> 32; + + ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, + AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time)); + if (ret) + dev_err(adev->dev, "set system time failed: %d\n", ret); + + return ret; +} + int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, u8 instance_id, u32 sink_id, const struct avs_audio_format *src_fmt, diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index 0395dd7150eb..c0f90dba9af8 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -186,7 +186,9 @@ union avs_reply_msg { enum avs_notify_msg_type { AVS_NOTIFY_PHRASE_DETECTED = 4, AVS_NOTIFY_RESOURCE_EVENT = 5, + AVS_NOTIFY_LOG_BUFFER_STATUS = 6, AVS_NOTIFY_FW_READY = 8, + AVS_NOTIFY_EXCEPTION_CAUGHT = 10, AVS_NOTIFY_MODULE_EVENT = 12, }; @@ -202,9 +204,17 @@ union avs_notify_msg { u32 msg_direction:1; u32 msg_target:1; }; + struct { + u16 rsvd:12; + u16 core:4; + } log; }; union { u32 val; + struct { + u32 core_id:2; + u32 stack_dump_size:16; + } coredump; } ext; }; } __packed; @@ -324,12 +334,46 @@ int avs_ipc_set_d0ix(struct avs_dev *adev, bool enable_pg, bool streaming); #define AVS_BASEFW_INST_ID 0 enum avs_basefw_runtime_param { + AVS_BASEFW_ENABLE_LOGS = 6, AVS_BASEFW_FIRMWARE_CONFIG = 7, AVS_BASEFW_HARDWARE_CONFIG = 8, AVS_BASEFW_MODULES_INFO = 9, AVS_BASEFW_LIBRARIES_INFO = 16, + AVS_BASEFW_SYSTEM_TIME = 20, +}; + +enum avs_log_enable { + AVS_LOG_DISABLE = 0, + AVS_LOG_ENABLE = 1 }; +enum avs_skl_log_priority { + AVS_SKL_LOG_CRITICAL = 1, + AVS_SKL_LOG_HIGH, + AVS_SKL_LOG_MEDIUM, + AVS_SKL_LOG_LOW, + AVS_SKL_LOG_VERBOSE, +}; + +struct skl_log_state { + u32 enable; + u32 min_priority; +} __packed; + +struct skl_log_state_info { + u32 core_mask; + struct skl_log_state logs_core[]; +} __packed; + +struct apl_log_state_info { + u32 aging_timer_period; + u32 fifo_full_timer_period; + u32 core_mask; + struct skl_log_state logs_core[]; +} __packed; + +int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size); + struct avs_fw_version { u16 major; u16 minor; @@ -497,6 +541,13 @@ static inline bool avs_module_entry_is_loaded(struct avs_module_entry *mentry) int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info); +struct avs_sys_time { + u32 val_l; + u32 val_u; +} __packed; + +int avs_ipc_set_system_time(struct avs_dev *adev); + /* Module configuration */ #define AVS_MIXIN_MOD_UUID \ diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c new file mode 100644 index 000000000000..3d46dd5e5bc4 --- /dev/null +++ b/sound/soc/intel/avs/path.c @@ -0,0 +1,1005 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <sound/intel-nhlt.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "avs.h" +#include "path.h" +#include "topology.h" + +/* Must be called with adev->comp_list_mutex held. */ +static struct avs_tplg * +avs_path_find_tplg(struct avs_dev *adev, const char *name) +{ + struct avs_soc_component *acomp; + + list_for_each_entry(acomp, &adev->comp_list, node) + if (!strcmp(acomp->tplg->name, name)) + return acomp->tplg; + return NULL; +} + +static struct avs_path_module * +avs_path_find_module(struct avs_path_pipeline *ppl, u32 template_id) +{ + struct avs_path_module *mod; + + list_for_each_entry(mod, &ppl->mod_list, node) + if (mod->template->id == template_id) + return mod; + return NULL; +} + +static struct avs_path_pipeline * +avs_path_find_pipeline(struct avs_path *path, u32 template_id) +{ + struct avs_path_pipeline *ppl; + + list_for_each_entry(ppl, &path->ppl_list, node) + if (ppl->template->id == template_id) + return ppl; + return NULL; +} + +static struct avs_path * +avs_path_find_path(struct avs_dev *adev, const char *name, u32 template_id) +{ + struct avs_tplg_path_template *pos, *template = NULL; + struct avs_tplg *tplg; + struct avs_path *path; + + tplg = avs_path_find_tplg(adev, name); + if (!tplg) + return NULL; + + list_for_each_entry(pos, &tplg->path_tmpl_list, node) { + if (pos->id == template_id) { + template = pos; + break; + } + } + if (!template) + return NULL; + + spin_lock(&adev->path_list_lock); + /* Only one variant of given path template may be instantiated at a time. */ + list_for_each_entry(path, &adev->path_list, node) { + if (path->template->owner == template) { + spin_unlock(&adev->path_list_lock); + return path; + } + } + + spin_unlock(&adev->path_list_lock); + return NULL; +} + +static bool avs_test_hw_params(struct snd_pcm_hw_params *params, + struct avs_audio_format *fmt) +{ + return (params_rate(params) == fmt->sampling_freq && + params_channels(params) == fmt->num_channels && + params_physical_width(params) == fmt->bit_depth && + params_width(params) == fmt->valid_bit_depth); +} + +static struct avs_tplg_path * +avs_path_find_variant(struct avs_dev *adev, + struct avs_tplg_path_template *template, + struct snd_pcm_hw_params *fe_params, + struct snd_pcm_hw_params *be_params) +{ + struct avs_tplg_path *variant; + + list_for_each_entry(variant, &template->path_list, node) { + dev_dbg(adev->dev, "check FE rate %d chn %d vbd %d bd %d\n", + variant->fe_fmt->sampling_freq, variant->fe_fmt->num_channels, + variant->fe_fmt->valid_bit_depth, variant->fe_fmt->bit_depth); + dev_dbg(adev->dev, "check BE rate %d chn %d vbd %d bd %d\n", + variant->be_fmt->sampling_freq, variant->be_fmt->num_channels, + variant->be_fmt->valid_bit_depth, variant->be_fmt->bit_depth); + + if (variant->fe_fmt && avs_test_hw_params(fe_params, variant->fe_fmt) && + variant->be_fmt && avs_test_hw_params(be_params, variant->be_fmt)) + return variant; + } + + return NULL; +} + +__maybe_unused +static bool avs_dma_type_is_host(u32 dma_type) +{ + return dma_type == AVS_DMA_HDA_HOST_OUTPUT || + dma_type == AVS_DMA_HDA_HOST_INPUT; +} + +__maybe_unused +static bool avs_dma_type_is_link(u32 dma_type) +{ + return !avs_dma_type_is_host(dma_type); +} + +__maybe_unused +static bool avs_dma_type_is_output(u32 dma_type) +{ + return dma_type == AVS_DMA_HDA_HOST_OUTPUT || + dma_type == AVS_DMA_HDA_LINK_OUTPUT || + dma_type == AVS_DMA_I2S_LINK_OUTPUT; +} + +__maybe_unused +static bool avs_dma_type_is_input(u32 dma_type) +{ + return !avs_dma_type_is_output(dma_type); +} + +static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct nhlt_acpi_table *nhlt = adev->nhlt; + struct avs_tplg_module *t = mod->template; + struct avs_copier_cfg *cfg; + struct nhlt_specific_cfg *ep_blob; + union avs_connector_node_id node_id = {0}; + size_t cfg_size, data_size = 0; + void *data = NULL; + u32 dma_type; + int ret; + + dma_type = t->cfg_ext->copier.dma_type; + node_id.dma_type = dma_type; + + switch (dma_type) { + struct avs_audio_format *fmt; + int direction; + + case AVS_DMA_I2S_LINK_OUTPUT: + case AVS_DMA_I2S_LINK_INPUT: + if (avs_dma_type_is_input(dma_type)) + direction = SNDRV_PCM_STREAM_CAPTURE; + else + direction = SNDRV_PCM_STREAM_PLAYBACK; + + if (t->cfg_ext->copier.blob_fmt) + fmt = t->cfg_ext->copier.blob_fmt; + else if (direction == SNDRV_PCM_STREAM_CAPTURE) + fmt = t->in_fmt; + else + fmt = t->cfg_ext->copier.out_fmt; + + ep_blob = intel_nhlt_get_endpoint_blob(adev->dev, + nhlt, t->cfg_ext->copier.vindex.i2s.instance, + NHLT_LINK_SSP, fmt->valid_bit_depth, fmt->bit_depth, + fmt->num_channels, fmt->sampling_freq, direction, + NHLT_DEVICE_I2S); + if (!ep_blob) { + dev_err(adev->dev, "no I2S ep_blob found\n"); + return -ENOENT; + } + + data = ep_blob->caps; + data_size = ep_blob->size; + /* I2S gateway's vindex is statically assigned in topology */ + node_id.vindex = t->cfg_ext->copier.vindex.val; + + break; + + case AVS_DMA_DMIC_LINK_INPUT: + direction = SNDRV_PCM_STREAM_CAPTURE; + + if (t->cfg_ext->copier.blob_fmt) + fmt = t->cfg_ext->copier.blob_fmt; + else + fmt = t->in_fmt; + + ep_blob = intel_nhlt_get_endpoint_blob(adev->dev, nhlt, 0, + NHLT_LINK_DMIC, fmt->valid_bit_depth, + fmt->bit_depth, fmt->num_channels, + fmt->sampling_freq, direction, NHLT_DEVICE_DMIC); + if (!ep_blob) { + dev_err(adev->dev, "no DMIC ep_blob found\n"); + return -ENOENT; + } + + data = ep_blob->caps; + data_size = ep_blob->size; + /* DMIC gateway's vindex is statically assigned in topology */ + node_id.vindex = t->cfg_ext->copier.vindex.val; + + break; + + case AVS_DMA_HDA_HOST_OUTPUT: + case AVS_DMA_HDA_HOST_INPUT: + /* HOST gateway's vindex is dynamically assigned with DMA id */ + node_id.vindex = mod->owner->owner->dma_id; + break; + + case AVS_DMA_HDA_LINK_OUTPUT: + case AVS_DMA_HDA_LINK_INPUT: + node_id.vindex = t->cfg_ext->copier.vindex.val | + mod->owner->owner->dma_id; + break; + + case INVALID_OBJECT_ID: + default: + node_id = INVALID_NODE_ID; + break; + } + + cfg_size = sizeof(*cfg) + data_size; + /* Every config-BLOB contains gateway attributes. */ + if (data_size) + cfg_size -= sizeof(cfg->gtw_cfg.config.attrs); + + cfg = kzalloc(cfg_size, GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + cfg->base.cpc = t->cfg_base->cpc; + cfg->base.ibs = t->cfg_base->ibs; + cfg->base.obs = t->cfg_base->obs; + cfg->base.is_pages = t->cfg_base->is_pages; + cfg->base.audio_fmt = *t->in_fmt; + cfg->out_fmt = *t->cfg_ext->copier.out_fmt; + cfg->feature_mask = t->cfg_ext->copier.feature_mask; + cfg->gtw_cfg.node_id = node_id; + cfg->gtw_cfg.dma_buffer_size = t->cfg_ext->copier.dma_buffer_size; + /* config_length in DWORDs */ + cfg->gtw_cfg.config_length = DIV_ROUND_UP(data_size, 4); + if (data) + memcpy(&cfg->gtw_cfg.config, data, data_size); + + mod->gtw_attrs = cfg->gtw_cfg.config.attrs; + + ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, + t->core_id, t->domain, cfg, cfg_size, + &mod->instance_id); + kfree(cfg); + return ret; +} + +static int avs_updown_mix_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_updown_mixer_cfg cfg; + int i; + + cfg.base.cpc = t->cfg_base->cpc; + cfg.base.ibs = t->cfg_base->ibs; + cfg.base.obs = t->cfg_base->obs; + cfg.base.is_pages = t->cfg_base->is_pages; + cfg.base.audio_fmt = *t->in_fmt; + cfg.out_channel_config = t->cfg_ext->updown_mix.out_channel_config; + cfg.coefficients_select = t->cfg_ext->updown_mix.coefficients_select; + for (i = 0; i < AVS_CHANNELS_MAX; i++) + cfg.coefficients[i] = t->cfg_ext->updown_mix.coefficients[i]; + cfg.channel_map = t->cfg_ext->updown_mix.channel_map; + + return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, + t->core_id, t->domain, &cfg, sizeof(cfg), + &mod->instance_id); +} + +static int avs_src_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_src_cfg cfg; + + cfg.base.cpc = t->cfg_base->cpc; + cfg.base.ibs = t->cfg_base->ibs; + cfg.base.obs = t->cfg_base->obs; + cfg.base.is_pages = t->cfg_base->is_pages; + cfg.base.audio_fmt = *t->in_fmt; + cfg.out_freq = t->cfg_ext->src.out_freq; + + return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, + t->core_id, t->domain, &cfg, sizeof(cfg), + &mod->instance_id); +} + +static int avs_asrc_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_asrc_cfg cfg; + + cfg.base.cpc = t->cfg_base->cpc; + cfg.base.ibs = t->cfg_base->ibs; + cfg.base.obs = t->cfg_base->obs; + cfg.base.is_pages = t->cfg_base->is_pages; + cfg.base.audio_fmt = *t->in_fmt; + cfg.out_freq = t->cfg_ext->asrc.out_freq; + cfg.mode = t->cfg_ext->asrc.mode; + cfg.disable_jitter_buffer = t->cfg_ext->asrc.disable_jitter_buffer; + + return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, + t->core_id, t->domain, &cfg, sizeof(cfg), + &mod->instance_id); +} + +static int avs_aec_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_aec_cfg cfg; + + cfg.base.cpc = t->cfg_base->cpc; + cfg.base.ibs = t->cfg_base->ibs; + cfg.base.obs = t->cfg_base->obs; + cfg.base.is_pages = t->cfg_base->is_pages; + cfg.base.audio_fmt = *t->in_fmt; + cfg.ref_fmt = *t->cfg_ext->aec.ref_fmt; + cfg.out_fmt = *t->cfg_ext->aec.out_fmt; + cfg.cpc_lp_mode = t->cfg_ext->aec.cpc_lp_mode; + + return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, + t->core_id, t->domain, &cfg, sizeof(cfg), + &mod->instance_id); +} + +static int avs_mux_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_mux_cfg cfg; + + cfg.base.cpc = t->cfg_base->cpc; + cfg.base.ibs = t->cfg_base->ibs; + cfg.base.obs = t->cfg_base->obs; + cfg.base.is_pages = t->cfg_base->is_pages; + cfg.base.audio_fmt = *t->in_fmt; + cfg.ref_fmt = *t->cfg_ext->mux.ref_fmt; + cfg.out_fmt = *t->cfg_ext->mux.out_fmt; + + return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, + t->core_id, t->domain, &cfg, sizeof(cfg), + &mod->instance_id); +} + +static int avs_wov_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_wov_cfg cfg; + + cfg.base.cpc = t->cfg_base->cpc; + cfg.base.ibs = t->cfg_base->ibs; + cfg.base.obs = t->cfg_base->obs; + cfg.base.is_pages = t->cfg_base->is_pages; + cfg.base.audio_fmt = *t->in_fmt; + cfg.cpc_lp_mode = t->cfg_ext->wov.cpc_lp_mode; + + return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, + t->core_id, t->domain, &cfg, sizeof(cfg), + &mod->instance_id); +} + +static int avs_micsel_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_micsel_cfg cfg; + + cfg.base.cpc = t->cfg_base->cpc; + cfg.base.ibs = t->cfg_base->ibs; + cfg.base.obs = t->cfg_base->obs; + cfg.base.is_pages = t->cfg_base->is_pages; + cfg.base.audio_fmt = *t->in_fmt; + cfg.out_fmt = *t->cfg_ext->micsel.out_fmt; + + return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, + t->core_id, t->domain, &cfg, sizeof(cfg), + &mod->instance_id); +} + +static int avs_modbase_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_modcfg_base cfg; + + cfg.cpc = t->cfg_base->cpc; + cfg.ibs = t->cfg_base->ibs; + cfg.obs = t->cfg_base->obs; + cfg.is_pages = t->cfg_base->is_pages; + cfg.audio_fmt = *t->in_fmt; + + return avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, + t->core_id, t->domain, &cfg, sizeof(cfg), + &mod->instance_id); +} + +static int avs_modext_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_tplg_modcfg_ext *tcfg = t->cfg_ext; + struct avs_modcfg_ext *cfg; + size_t cfg_size, num_pins; + int ret, i; + + num_pins = tcfg->generic.num_input_pins + tcfg->generic.num_output_pins; + cfg_size = sizeof(*cfg) + sizeof(*cfg->pin_fmts) * num_pins; + + cfg = kzalloc(cfg_size, GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + cfg->base.cpc = t->cfg_base->cpc; + cfg->base.ibs = t->cfg_base->ibs; + cfg->base.obs = t->cfg_base->obs; + cfg->base.is_pages = t->cfg_base->is_pages; + cfg->base.audio_fmt = *t->in_fmt; + cfg->num_input_pins = tcfg->generic.num_input_pins; + cfg->num_output_pins = tcfg->generic.num_output_pins; + + /* configure pin formats */ + for (i = 0; i < num_pins; i++) { + struct avs_tplg_pin_format *tpin = &tcfg->generic.pin_fmts[i]; + struct avs_pin_format *pin = &cfg->pin_fmts[i]; + + pin->pin_index = tpin->pin_index; + pin->iobs = tpin->iobs; + pin->audio_fmt = *tpin->fmt; + } + + ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, + t->core_id, t->domain, cfg, cfg_size, + &mod->instance_id); + kfree(cfg); + return ret; +} + +static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + const guid_t *type = &mod->template->cfg_ext->type; + + if (guid_equal(type, &AVS_MIXIN_MOD_UUID) || + guid_equal(type, &AVS_MIXOUT_MOD_UUID) || + guid_equal(type, &AVS_KPBUFF_MOD_UUID)) + return avs_modbase_create(adev, mod); + if (guid_equal(type, &AVS_COPIER_MOD_UUID)) + return avs_copier_create(adev, mod); + if (guid_equal(type, &AVS_MICSEL_MOD_UUID)) + return avs_micsel_create(adev, mod); + if (guid_equal(type, &AVS_MUX_MOD_UUID)) + return avs_mux_create(adev, mod); + if (guid_equal(type, &AVS_UPDWMIX_MOD_UUID)) + return avs_updown_mix_create(adev, mod); + if (guid_equal(type, &AVS_SRCINTC_MOD_UUID)) + return avs_src_create(adev, mod); + if (guid_equal(type, &AVS_AEC_MOD_UUID)) + return avs_aec_create(adev, mod); + if (guid_equal(type, &AVS_ASRC_MOD_UUID)) + return avs_asrc_create(adev, mod); + if (guid_equal(type, &AVS_INTELWOV_MOD_UUID)) + return avs_wov_create(adev, mod); + + if (guid_equal(type, &AVS_PROBE_MOD_UUID)) { + dev_err(adev->dev, "Probe module can't be instantiated by topology"); + return -EINVAL; + } + + return avs_modext_create(adev, mod); +} + +static void avs_path_module_free(struct avs_dev *adev, struct avs_path_module *mod) +{ + kfree(mod); +} + +static struct avs_path_module * +avs_path_module_create(struct avs_dev *adev, + struct avs_path_pipeline *owner, + struct avs_tplg_module *template) +{ + struct avs_path_module *mod; + int module_id, ret; + + module_id = avs_get_module_id(adev, &template->cfg_ext->type); + if (module_id < 0) + return ERR_PTR(module_id); + + mod = kzalloc(sizeof(*mod), GFP_KERNEL); + if (!mod) + return ERR_PTR(-ENOMEM); + + mod->template = template; + mod->module_id = module_id; + mod->owner = owner; + INIT_LIST_HEAD(&mod->node); + + ret = avs_path_module_type_create(adev, mod); + if (ret) { + dev_err(adev->dev, "module-type create failed: %d\n", ret); + kfree(mod); + return ERR_PTR(ret); + } + + return mod; +} + +static int avs_path_binding_arm(struct avs_dev *adev, struct avs_path_binding *binding) +{ + struct avs_path_module *this_mod, *target_mod; + struct avs_path_pipeline *target_ppl; + struct avs_path *target_path; + struct avs_tplg_binding *t; + + t = binding->template; + this_mod = avs_path_find_module(binding->owner, + t->mod_id); + if (!this_mod) { + dev_err(adev->dev, "path mod %d not found\n", t->mod_id); + return -EINVAL; + } + + /* update with target_tplg_name too */ + target_path = avs_path_find_path(adev, t->target_tplg_name, + t->target_path_tmpl_id); + if (!target_path) { + dev_err(adev->dev, "target path %s:%d not found\n", + t->target_tplg_name, t->target_path_tmpl_id); + return -EINVAL; + } + + target_ppl = avs_path_find_pipeline(target_path, + t->target_ppl_id); + if (!target_ppl) { + dev_err(adev->dev, "target ppl %d not found\n", t->target_ppl_id); + return -EINVAL; + } + + target_mod = avs_path_find_module(target_ppl, t->target_mod_id); + if (!target_mod) { + dev_err(adev->dev, "target mod %d not found\n", t->target_mod_id); + return -EINVAL; + } + + if (t->is_sink) { + binding->sink = this_mod; + binding->sink_pin = t->mod_pin; + binding->source = target_mod; + binding->source_pin = t->target_mod_pin; + } else { + binding->sink = target_mod; + binding->sink_pin = t->target_mod_pin; + binding->source = this_mod; + binding->source_pin = t->mod_pin; + } + + return 0; +} + +static void avs_path_binding_free(struct avs_dev *adev, struct avs_path_binding *binding) +{ + kfree(binding); +} + +static struct avs_path_binding *avs_path_binding_create(struct avs_dev *adev, + struct avs_path_pipeline *owner, + struct avs_tplg_binding *t) +{ + struct avs_path_binding *binding; + + binding = kzalloc(sizeof(*binding), GFP_KERNEL); + if (!binding) + return ERR_PTR(-ENOMEM); + + binding->template = t; + binding->owner = owner; + INIT_LIST_HEAD(&binding->node); + + return binding; +} + +static int avs_path_pipeline_arm(struct avs_dev *adev, + struct avs_path_pipeline *ppl) +{ + struct avs_path_module *mod; + + list_for_each_entry(mod, &ppl->mod_list, node) { + struct avs_path_module *source, *sink; + int ret; + + /* + * Only one module (so it's implicitly last) or it is the last + * one, either way we don't have next module to bind it to. + */ + if (mod == list_last_entry(&ppl->mod_list, + struct avs_path_module, node)) + break; + + /* bind current module to next module on list */ + source = mod; + sink = list_next_entry(mod, node); + if (!source || !sink) + return -EINVAL; + + ret = avs_ipc_bind(adev, source->module_id, source->instance_id, + sink->module_id, sink->instance_id, 0, 0); + if (ret) + return AVS_IPC_RET(ret); + } + + return 0; +} + +static void avs_path_pipeline_free(struct avs_dev *adev, + struct avs_path_pipeline *ppl) +{ + struct avs_path_binding *binding, *bsave; + struct avs_path_module *mod, *save; + + list_for_each_entry_safe(binding, bsave, &ppl->binding_list, node) { + list_del(&binding->node); + avs_path_binding_free(adev, binding); + } + + avs_dsp_delete_pipeline(adev, ppl->instance_id); + + /* Unload resources occupied by owned modules */ + list_for_each_entry_safe(mod, save, &ppl->mod_list, node) { + avs_dsp_delete_module(adev, mod->module_id, mod->instance_id, + mod->owner->instance_id, + mod->template->core_id); + avs_path_module_free(adev, mod); + } + + list_del(&ppl->node); + kfree(ppl); +} + +static struct avs_path_pipeline * +avs_path_pipeline_create(struct avs_dev *adev, struct avs_path *owner, + struct avs_tplg_pipeline *template) +{ + struct avs_path_pipeline *ppl; + struct avs_tplg_pplcfg *cfg = template->cfg; + struct avs_tplg_module *tmod; + int ret, i; + + ppl = kzalloc(sizeof(*ppl), GFP_KERNEL); + if (!ppl) + return ERR_PTR(-ENOMEM); + + ppl->template = template; + ppl->owner = owner; + INIT_LIST_HEAD(&ppl->binding_list); + INIT_LIST_HEAD(&ppl->mod_list); + INIT_LIST_HEAD(&ppl->node); + + ret = avs_dsp_create_pipeline(adev, cfg->req_size, cfg->priority, + cfg->lp, cfg->attributes, + &ppl->instance_id); + if (ret) { + dev_err(adev->dev, "error creating pipeline %d\n", ret); + kfree(ppl); + return ERR_PTR(ret); + } + + list_for_each_entry(tmod, &template->mod_list, node) { + struct avs_path_module *mod; + + mod = avs_path_module_create(adev, ppl, tmod); + if (IS_ERR(mod)) { + ret = PTR_ERR(mod); + dev_err(adev->dev, "error creating module %d\n", ret); + goto init_err; + } + + list_add_tail(&mod->node, &ppl->mod_list); + } + + for (i = 0; i < template->num_bindings; i++) { + struct avs_path_binding *binding; + + binding = avs_path_binding_create(adev, ppl, template->bindings[i]); + if (IS_ERR(binding)) { + ret = PTR_ERR(binding); + dev_err(adev->dev, "error creating binding %d\n", ret); + goto init_err; + } + + list_add_tail(&binding->node, &ppl->binding_list); + } + + return ppl; + +init_err: + avs_path_pipeline_free(adev, ppl); + return ERR_PTR(ret); +} + +static int avs_path_init(struct avs_dev *adev, struct avs_path *path, + struct avs_tplg_path *template, u32 dma_id) +{ + struct avs_tplg_pipeline *tppl; + + path->owner = adev; + path->template = template; + path->dma_id = dma_id; + INIT_LIST_HEAD(&path->ppl_list); + INIT_LIST_HEAD(&path->node); + + /* create all the pipelines */ + list_for_each_entry(tppl, &template->ppl_list, node) { + struct avs_path_pipeline *ppl; + + ppl = avs_path_pipeline_create(adev, path, tppl); + if (IS_ERR(ppl)) + return PTR_ERR(ppl); + + list_add_tail(&ppl->node, &path->ppl_list); + } + + spin_lock(&adev->path_list_lock); + list_add_tail(&path->node, &adev->path_list); + spin_unlock(&adev->path_list_lock); + + return 0; +} + +static int avs_path_arm(struct avs_dev *adev, struct avs_path *path) +{ + struct avs_path_pipeline *ppl; + struct avs_path_binding *binding; + int ret; + + list_for_each_entry(ppl, &path->ppl_list, node) { + /* + * Arm all ppl bindings before binding internal modules + * as it costs no IPCs which isn't true for the latter. + */ + list_for_each_entry(binding, &ppl->binding_list, node) { + ret = avs_path_binding_arm(adev, binding); + if (ret < 0) + return ret; + } + + ret = avs_path_pipeline_arm(adev, ppl); + if (ret < 0) + return ret; + } + + return 0; +} + +static void avs_path_free_unlocked(struct avs_path *path) +{ + struct avs_path_pipeline *ppl, *save; + + spin_lock(&path->owner->path_list_lock); + list_del(&path->node); + spin_unlock(&path->owner->path_list_lock); + + list_for_each_entry_safe(ppl, save, &path->ppl_list, node) + avs_path_pipeline_free(path->owner, ppl); + + kfree(path); +} + +static struct avs_path *avs_path_create_unlocked(struct avs_dev *adev, u32 dma_id, + struct avs_tplg_path *template) +{ + struct avs_path *path; + int ret; + + path = kzalloc(sizeof(*path), GFP_KERNEL); + if (!path) + return ERR_PTR(-ENOMEM); + + ret = avs_path_init(adev, path, template, dma_id); + if (ret < 0) + goto err; + + ret = avs_path_arm(adev, path); + if (ret < 0) + goto err; + + path->state = AVS_PPL_STATE_INVALID; + return path; +err: + avs_path_free_unlocked(path); + return ERR_PTR(ret); +} + +void avs_path_free(struct avs_path *path) +{ + struct avs_dev *adev = path->owner; + + mutex_lock(&adev->path_mutex); + avs_path_free_unlocked(path); + mutex_unlock(&adev->path_mutex); +} + +struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, + struct avs_tplg_path_template *template, + struct snd_pcm_hw_params *fe_params, + struct snd_pcm_hw_params *be_params) +{ + struct avs_tplg_path *variant; + struct avs_path *path; + + variant = avs_path_find_variant(adev, template, fe_params, be_params); + if (!variant) { + dev_err(adev->dev, "no matching variant found\n"); + return ERR_PTR(-ENOENT); + } + + /* Serialize path and its components creation. */ + mutex_lock(&adev->path_mutex); + /* Satisfy needs of avs_path_find_tplg(). */ + mutex_lock(&adev->comp_list_mutex); + + path = avs_path_create_unlocked(adev, dma_id, variant); + + mutex_unlock(&adev->comp_list_mutex); + mutex_unlock(&adev->path_mutex); + + return path; +} + +static int avs_path_bind_prepare(struct avs_dev *adev, + struct avs_path_binding *binding) +{ + const struct avs_audio_format *src_fmt, *sink_fmt; + struct avs_tplg_module *tsource = binding->source->template; + struct avs_path_module *source = binding->source; + int ret; + + /* + * only copier modules about to be bound + * to output pin other than 0 need preparation + */ + if (!binding->source_pin) + return 0; + if (!guid_equal(&tsource->cfg_ext->type, &AVS_COPIER_MOD_UUID)) + return 0; + + src_fmt = tsource->in_fmt; + sink_fmt = binding->sink->template->in_fmt; + + ret = avs_ipc_copier_set_sink_format(adev, source->module_id, + source->instance_id, binding->source_pin, + src_fmt, sink_fmt); + if (ret) { + dev_err(adev->dev, "config copier failed: %d\n", ret); + return AVS_IPC_RET(ret); + } + + return 0; +} + +int avs_path_bind(struct avs_path *path) +{ + struct avs_path_pipeline *ppl; + struct avs_dev *adev = path->owner; + int ret; + + list_for_each_entry(ppl, &path->ppl_list, node) { + struct avs_path_binding *binding; + + list_for_each_entry(binding, &ppl->binding_list, node) { + struct avs_path_module *source, *sink; + + source = binding->source; + sink = binding->sink; + + ret = avs_path_bind_prepare(adev, binding); + if (ret < 0) + return ret; + + ret = avs_ipc_bind(adev, source->module_id, + source->instance_id, sink->module_id, + sink->instance_id, binding->sink_pin, + binding->source_pin); + if (ret) { + dev_err(adev->dev, "bind path failed: %d\n", ret); + return AVS_IPC_RET(ret); + } + } + } + + return 0; +} + +int avs_path_unbind(struct avs_path *path) +{ + struct avs_path_pipeline *ppl; + struct avs_dev *adev = path->owner; + int ret; + + list_for_each_entry(ppl, &path->ppl_list, node) { + struct avs_path_binding *binding; + + list_for_each_entry(binding, &ppl->binding_list, node) { + struct avs_path_module *source, *sink; + + source = binding->source; + sink = binding->sink; + + ret = avs_ipc_unbind(adev, source->module_id, + source->instance_id, sink->module_id, + sink->instance_id, binding->sink_pin, + binding->source_pin); + if (ret) { + dev_err(adev->dev, "unbind path failed: %d\n", ret); + return AVS_IPC_RET(ret); + } + } + } + + return 0; +} + +int avs_path_reset(struct avs_path *path) +{ + struct avs_path_pipeline *ppl; + struct avs_dev *adev = path->owner; + int ret; + + if (path->state == AVS_PPL_STATE_RESET) + return 0; + + list_for_each_entry(ppl, &path->ppl_list, node) { + ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, + AVS_PPL_STATE_RESET); + if (ret) { + dev_err(adev->dev, "reset path failed: %d\n", ret); + path->state = AVS_PPL_STATE_INVALID; + return AVS_IPC_RET(ret); + } + } + + path->state = AVS_PPL_STATE_RESET; + return 0; +} + +int avs_path_pause(struct avs_path *path) +{ + struct avs_path_pipeline *ppl; + struct avs_dev *adev = path->owner; + int ret; + + if (path->state == AVS_PPL_STATE_PAUSED) + return 0; + + list_for_each_entry_reverse(ppl, &path->ppl_list, node) { + ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, + AVS_PPL_STATE_PAUSED); + if (ret) { + dev_err(adev->dev, "pause path failed: %d\n", ret); + path->state = AVS_PPL_STATE_INVALID; + return AVS_IPC_RET(ret); + } + } + + path->state = AVS_PPL_STATE_PAUSED; + return 0; +} + +int avs_path_run(struct avs_path *path, int trigger) +{ + struct avs_path_pipeline *ppl; + struct avs_dev *adev = path->owner; + int ret; + + if (path->state == AVS_PPL_STATE_RUNNING && trigger == AVS_TPLG_TRIGGER_AUTO) + return 0; + + list_for_each_entry(ppl, &path->ppl_list, node) { + if (ppl->template->cfg->trigger != trigger) + continue; + + ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id, + AVS_PPL_STATE_RUNNING); + if (ret) { + dev_err(adev->dev, "run path failed: %d\n", ret); + path->state = AVS_PPL_STATE_INVALID; + return AVS_IPC_RET(ret); + } + } + + path->state = AVS_PPL_STATE_RUNNING; + return 0; +} diff --git a/sound/soc/intel/avs/path.h b/sound/soc/intel/avs/path.h new file mode 100644 index 000000000000..197222c5e008 --- /dev/null +++ b/sound/soc/intel/avs/path.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2021 Intel Corporation. All rights reserved. + * + * Authors: Cezary Rojewski <cezary.rojewski@intel.com> + * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> + */ + +#ifndef __SOUND_SOC_INTEL_AVS_PATH_H +#define __SOUND_SOC_INTEL_AVS_PATH_H + +#include <linux/list.h> +#include "avs.h" +#include "topology.h" + +struct avs_path { + u32 dma_id; + struct list_head ppl_list; + u32 state; + + struct avs_tplg_path *template; + struct avs_dev *owner; + /* device path management */ + struct list_head node; +}; + +struct avs_path_pipeline { + u8 instance_id; + struct list_head mod_list; + struct list_head binding_list; + + struct avs_tplg_pipeline *template; + struct avs_path *owner; + /* path pipelines management */ + struct list_head node; +}; + +struct avs_path_module { + u16 module_id; + u16 instance_id; + union avs_gtw_attributes gtw_attrs; + + struct avs_tplg_module *template; + struct avs_path_pipeline *owner; + /* pipeline modules management */ + struct list_head node; +}; + +struct avs_path_binding { + struct avs_path_module *source; + u8 source_pin; + struct avs_path_module *sink; + u8 sink_pin; + + struct avs_tplg_binding *template; + struct avs_path_pipeline *owner; + /* pipeline bindings management */ + struct list_head node; +}; + +void avs_path_free(struct avs_path *path); +struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id, + struct avs_tplg_path_template *template, + struct snd_pcm_hw_params *fe_params, + struct snd_pcm_hw_params *be_params); +int avs_path_bind(struct avs_path *path); +int avs_path_unbind(struct avs_path *path); +int avs_path_reset(struct avs_path *path); +int avs_path_pause(struct avs_path *path); +int avs_path_run(struct avs_path *path, int trigger); + +#endif diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c new file mode 100644 index 000000000000..668f533578a6 --- /dev/null +++ b/sound/soc/intel/avs/pcm.c @@ -0,0 +1,1182 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/debugfs.h> +#include <linux/device.h> +#include <sound/hda_register.h> +#include <sound/hdaudio_ext.h> +#include <sound/pcm_params.h> +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include <sound/soc-component.h> +#include "avs.h" +#include "path.h" +#include "topology.h" + +struct avs_dma_data { + struct avs_tplg_path_template *template; + struct avs_path *path; + /* + * link stream is stored within substream's runtime + * private_data to fulfill the needs of codec BE path + * + * host stream assigned + */ + struct hdac_ext_stream *host_stream; +}; + +static struct avs_tplg_path_template * +avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction) +{ + struct snd_soc_dapm_widget *dw; + struct snd_soc_dapm_path *dp; + enum snd_soc_dapm_direction dir; + + if (direction == SNDRV_PCM_STREAM_CAPTURE) { + dw = dai->capture_widget; + dir = is_fe ? SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN; + } else { + dw = dai->playback_widget; + dir = is_fe ? SND_SOC_DAPM_DIR_IN : SND_SOC_DAPM_DIR_OUT; + } + + dp = list_first_entry_or_null(&dw->edges[dir], typeof(*dp), list_node[dir]); + if (!dp) + return NULL; + + /* Get the other widget, with actual path template data */ + dw = (dp->source == dw) ? dp->sink : dp->source; + + return dw->priv; +} + +static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe) +{ + struct avs_tplg_path_template *template; + struct avs_dma_data *data; + + template = avs_dai_find_path_template(dai, is_fe, substream->stream); + if (!template) { + dev_err(dai->dev, "no %s path for dai %s, invalid tplg?\n", + snd_pcm_stream_str(substream), dai->name); + return -EINVAL; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->template = template; + snd_soc_dai_set_dma_data(dai, substream, data); + + return 0; +} + +static int avs_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *fe_hw_params, + struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai, + int dma_id) +{ + struct avs_dma_data *data; + struct avs_path *path; + struct avs_dev *adev = to_avs_dev(dai->dev); + int ret; + + data = snd_soc_dai_get_dma_data(dai, substream); + + dev_dbg(dai->dev, "%s FE hw_params str %p rtd %p", + __func__, substream, substream->runtime); + dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n", + params_rate(fe_hw_params), params_channels(fe_hw_params), + params_width(fe_hw_params), params_physical_width(fe_hw_params)); + + dev_dbg(dai->dev, "%s BE hw_params str %p rtd %p", + __func__, substream, substream->runtime); + dev_dbg(dai->dev, "rate %d chn %d vbd %d bd %d\n", + params_rate(be_hw_params), params_channels(be_hw_params), + params_width(be_hw_params), params_physical_width(be_hw_params)); + + path = avs_path_create(adev, dma_id, data->template, fe_hw_params, be_hw_params); + if (IS_ERR(path)) { + ret = PTR_ERR(path); + dev_err(dai->dev, "create path failed: %d\n", ret); + return ret; + } + + data->path = path; + return 0; +} + +static int avs_dai_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *be_hw_params, struct snd_soc_dai *dai, + int dma_id) +{ + struct snd_pcm_hw_params *fe_hw_params = NULL; + struct snd_soc_pcm_runtime *fe, *be; + struct snd_soc_dpcm *dpcm; + + be = asoc_substream_to_rtd(substream); + for_each_dpcm_fe(be, substream->stream, dpcm) { + fe = dpcm->fe; + fe_hw_params = &fe->dpcm[substream->stream].hw_params; + } + + return avs_dai_hw_params(substream, fe_hw_params, be_hw_params, dai, dma_id); +} + +static int avs_dai_prepare(struct avs_dev *adev, struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct avs_dma_data *data; + int ret; + + data = snd_soc_dai_get_dma_data(dai, substream); + if (!data->path) + return 0; + + ret = avs_path_reset(data->path); + if (ret < 0) { + dev_err(dai->dev, "reset path failed: %d\n", ret); + return ret; + } + + ret = avs_path_pause(data->path); + if (ret < 0) + dev_err(dai->dev, "pause path failed: %d\n", ret); + return ret; +} + +static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + return avs_dai_startup(substream, dai, false); +} + +static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct avs_dma_data *data; + + data = snd_soc_dai_get_dma_data(dai, substream); + + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(data); +} + +static int avs_dai_nonhda_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) +{ + struct avs_dma_data *data; + + data = snd_soc_dai_get_dma_data(dai, substream); + if (data->path) + return 0; + + /* Actual port-id comes from topology. */ + return avs_dai_be_hw_params(substream, hw_params, dai, 0); +} + +static int avs_dai_nonhda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct avs_dma_data *data; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + data = snd_soc_dai_get_dma_data(dai, substream); + if (data->path) { + avs_path_free(data->path); + data->path = NULL; + } + + return 0; +} + +static int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + return avs_dai_prepare(to_avs_dev(dai->dev), substream, dai); +} + +static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct avs_dma_data *data; + int ret = 0; + + data = snd_soc_dai_get_dma_data(dai, substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); + if (ret < 0) + dev_err(dai->dev, "run BE path failed: %d\n", ret); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + ret = avs_path_pause(data->path); + if (ret < 0) + dev_err(dai->dev, "pause BE path failed: %d\n", ret); + + if (cmd == SNDRV_PCM_TRIGGER_STOP) { + ret = avs_path_reset(data->path); + if (ret < 0) + dev_err(dai->dev, "reset BE path failed: %d\n", ret); + } + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = { + .startup = avs_dai_nonhda_be_startup, + .shutdown = avs_dai_nonhda_be_shutdown, + .hw_params = avs_dai_nonhda_be_hw_params, + .hw_free = avs_dai_nonhda_be_hw_free, + .prepare = avs_dai_nonhda_be_prepare, + .trigger = avs_dai_nonhda_be_trigger, +}; + +static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + return avs_dai_startup(substream, dai, false); +} + +static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + return avs_dai_nonhda_be_shutdown(substream, dai); +} + +static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) +{ + struct avs_dma_data *data; + struct hdac_ext_stream *link_stream; + + data = snd_soc_dai_get_dma_data(dai, substream); + if (data->path) + return 0; + + link_stream = substream->runtime->private_data; + + return avs_dai_be_hw_params(substream, hw_params, dai, + hdac_stream(link_stream)->stream_tag - 1); +} + +static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct avs_dma_data *data; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct hdac_ext_stream *link_stream; + struct hdac_ext_link *link; + struct hda_codec *codec; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + data = snd_soc_dai_get_dma_data(dai, substream); + if (!data->path) + return 0; + + link_stream = substream->runtime->private_data; + link_stream->link_prepared = false; + avs_path_free(data->path); + data->path = NULL; + + /* clear link <-> stream mapping */ + codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); + link = snd_hdac_ext_bus_link_at(&codec->bus->core, codec->core.addr); + if (!link) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_hdac_ext_link_clear_stream_id(link, hdac_stream(link_stream)->stream_tag); + + return 0; +} + +static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct hdac_ext_stream *link_stream = runtime->private_data; + struct hdac_ext_link *link; + struct hda_codec *codec; + struct hdac_bus *bus; + unsigned int format_val; + int ret; + + if (link_stream->link_prepared) + return 0; + + codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); + bus = &codec->bus->core; + format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, + runtime->sample_bits, 0); + + snd_hdac_ext_stream_decouple(bus, link_stream, true); + snd_hdac_ext_link_stream_reset(link_stream); + snd_hdac_ext_link_stream_setup(link_stream, format_val); + + link = snd_hdac_ext_bus_link_at(bus, codec->core.addr); + if (!link) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_stream)->stream_tag); + + ret = avs_dai_prepare(to_avs_dev(dai->dev), substream, dai); + if (ret) + return ret; + + link_stream->link_prepared = true; + return 0; +} + +static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *link_stream; + struct avs_dma_data *data; + int ret = 0; + + dev_dbg(dai->dev, "entry %s cmd=%d\n", __func__, cmd); + + data = snd_soc_dai_get_dma_data(dai, substream); + link_stream = substream->runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_hdac_ext_link_stream_start(link_stream); + + ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); + if (ret < 0) + dev_err(dai->dev, "run BE path failed: %d\n", ret); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + ret = avs_path_pause(data->path); + if (ret < 0) + dev_err(dai->dev, "pause BE path failed: %d\n", ret); + + snd_hdac_ext_link_stream_clear(link_stream); + + if (cmd == SNDRV_PCM_TRIGGER_STOP) { + ret = avs_path_reset(data->path); + if (ret < 0) + dev_err(dai->dev, "reset BE path failed: %d\n", ret); + } + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct snd_soc_dai_ops avs_dai_hda_be_ops = { + .startup = avs_dai_hda_be_startup, + .shutdown = avs_dai_hda_be_shutdown, + .hw_params = avs_dai_hda_be_hw_params, + .hw_free = avs_dai_hda_be_hw_free, + .prepare = avs_dai_hda_be_prepare, + .trigger = avs_dai_hda_be_trigger, +}; + +static const unsigned int rates[] = { + 8000, 11025, 12000, 16000, + 22050, 24000, 32000, 44100, + 48000, 64000, 88200, 96000, + 128000, 176400, 192000, +}; + +static const struct snd_pcm_hw_constraint_list hw_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct avs_dma_data *data; + struct avs_dev *adev = to_avs_dev(dai->dev); + struct hdac_bus *bus = &adev->base.core; + struct hdac_ext_stream *host_stream; + int ret; + + ret = avs_dai_startup(substream, dai, true); + if (ret) + return ret; + + data = snd_soc_dai_get_dma_data(dai, substream); + + host_stream = snd_hdac_ext_stream_assign(bus, substream, HDAC_EXT_STREAM_TYPE_HOST); + if (!host_stream) { + kfree(data); + return -EBUSY; + } + + data->host_stream = host_stream; + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + /* avoid wrap-around with wall-clock */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME, 20, 178000000); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_rates); + snd_pcm_set_sync(substream); + + dev_dbg(dai->dev, "%s fe STARTUP tag %d str %p", + __func__, hdac_stream(host_stream)->stream_tag, substream); + + return 0; +} + +static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct avs_dma_data *data; + + data = snd_soc_dai_get_dma_data(dai, substream); + + snd_soc_dai_set_dma_data(dai, substream, NULL); + snd_hdac_ext_stream_release(data->host_stream, HDAC_EXT_STREAM_TYPE_HOST); + kfree(data); +} + +static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) +{ + struct snd_pcm_hw_params *be_hw_params = NULL; + struct snd_soc_pcm_runtime *fe, *be; + struct snd_soc_dpcm *dpcm; + struct avs_dma_data *data; + struct hdac_ext_stream *host_stream; + int ret; + + data = snd_soc_dai_get_dma_data(dai, substream); + if (data->path) + return 0; + + host_stream = data->host_stream; + + hdac_stream(host_stream)->bufsize = 0; + hdac_stream(host_stream)->period_bytes = 0; + hdac_stream(host_stream)->format_val = 0; + + fe = asoc_substream_to_rtd(substream); + for_each_dpcm_be(fe, substream->stream, dpcm) { + be = dpcm->be; + be_hw_params = &be->dpcm[substream->stream].hw_params; + } + + ret = avs_dai_hw_params(substream, hw_params, be_hw_params, dai, + hdac_stream(host_stream)->stream_tag - 1); + if (ret) + goto create_err; + + ret = avs_path_bind(data->path); + if (ret < 0) { + dev_err(dai->dev, "bind FE <-> BE failed: %d\n", ret); + goto bind_err; + } + + return 0; + +bind_err: + avs_path_free(data->path); + data->path = NULL; +create_err: + snd_pcm_lib_free_pages(substream); + return ret; +} + +static int avs_dai_fe_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct avs_dma_data *data; + struct hdac_ext_stream *host_stream; + int ret; + + dev_dbg(dai->dev, "%s fe HW_FREE str %p rtd %p", + __func__, substream, substream->runtime); + + data = snd_soc_dai_get_dma_data(dai, substream); + if (!data->path) + return 0; + + host_stream = data->host_stream; + + ret = avs_path_unbind(data->path); + if (ret < 0) + dev_err(dai->dev, "unbind FE <-> BE failed: %d\n", ret); + + avs_path_free(data->path); + data->path = NULL; + snd_hdac_stream_cleanup(hdac_stream(host_stream)); + hdac_stream(host_stream)->prepared = false; + + ret = snd_pcm_lib_free_pages(substream); + if (ret < 0) + dev_dbg(dai->dev, "Failed to free pages!\n"); + + return ret; +} + +static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct avs_dma_data *data; + struct avs_dev *adev = to_avs_dev(dai->dev); + struct hdac_ext_stream *host_stream; + struct hdac_bus *bus; + unsigned int format_val; + int ret; + + data = snd_soc_dai_get_dma_data(dai, substream); + host_stream = data->host_stream; + + if (hdac_stream(host_stream)->prepared) + return 0; + + bus = hdac_stream(host_stream)->bus; + snd_hdac_ext_stream_decouple(bus, data->host_stream, true); + snd_hdac_stream_reset(hdac_stream(host_stream)); + + format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, + runtime->sample_bits, 0); + + ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val); + if (ret < 0) + return ret; + + ret = snd_hdac_stream_setup(hdac_stream(host_stream)); + if (ret < 0) + return ret; + + ret = avs_dai_prepare(adev, substream, dai); + if (ret) + return ret; + + hdac_stream(host_stream)->prepared = true; + return 0; +} + +static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) +{ + struct avs_dma_data *data; + struct hdac_ext_stream *host_stream; + struct hdac_bus *bus; + unsigned long flags; + int ret = 0; + + data = snd_soc_dai_get_dma_data(dai, substream); + host_stream = data->host_stream; + bus = hdac_stream(host_stream)->bus; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + spin_lock_irqsave(&bus->reg_lock, flags); + snd_hdac_stream_start(hdac_stream(host_stream), true); + spin_unlock_irqrestore(&bus->reg_lock, flags); + + ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO); + if (ret < 0) + dev_err(dai->dev, "run FE path failed: %d\n", ret); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + ret = avs_path_pause(data->path); + if (ret < 0) + dev_err(dai->dev, "pause FE path failed: %d\n", ret); + + spin_lock_irqsave(&bus->reg_lock, flags); + snd_hdac_stream_stop(hdac_stream(host_stream)); + spin_unlock_irqrestore(&bus->reg_lock, flags); + + if (cmd == SNDRV_PCM_TRIGGER_STOP) { + ret = avs_path_reset(data->path); + if (ret < 0) + dev_err(dai->dev, "reset FE path failed: %d\n", ret); + } + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +const struct snd_soc_dai_ops avs_dai_fe_ops = { + .startup = avs_dai_fe_startup, + .shutdown = avs_dai_fe_shutdown, + .hw_params = avs_dai_fe_hw_params, + .hw_free = avs_dai_fe_hw_free, + .prepare = avs_dai_fe_prepare, + .trigger = avs_dai_fe_trigger, +}; + +static ssize_t topology_name_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct snd_soc_component *component = file->private_data; + struct snd_soc_card *card = component->card; + struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); + char buf[64]; + size_t len; + + len = snprintf(buf, sizeof(buf), "%s/%s\n", component->driver->topology_name_prefix, + mach->tplg_filename); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations topology_name_fops = { + .open = simple_open, + .read = topology_name_read, + .llseek = default_llseek, +}; + +static int avs_component_load_libraries(struct avs_soc_component *acomp) +{ + struct avs_tplg *tplg = acomp->tplg; + struct avs_dev *adev = to_avs_dev(acomp->base.dev); + int ret; + + if (!tplg->num_libs) + return 0; + + /* Parent device may be asleep and library loading involves IPCs. */ + ret = pm_runtime_resume_and_get(adev->dev); + if (ret < 0) + return ret; + + avs_hda_clock_gating_enable(adev, false); + avs_hda_l1sen_enable(adev, false); + + ret = avs_dsp_load_libraries(adev, tplg->libs, tplg->num_libs); + + avs_hda_l1sen_enable(adev, true); + avs_hda_clock_gating_enable(adev, true); + + if (!ret) + ret = avs_module_info_init(adev, false); + + pm_runtime_mark_last_busy(adev->dev); + pm_runtime_put_autosuspend(adev->dev); + + return ret; +} + +static int avs_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_card *card = component->card; + struct snd_soc_acpi_mach *mach; + struct avs_soc_component *acomp; + struct avs_dev *adev; + char *filename; + int ret; + + dev_dbg(card->dev, "probing %s card %s\n", component->name, card->name); + mach = dev_get_platdata(card->dev); + acomp = to_avs_soc_component(component); + adev = to_avs_dev(component->dev); + + acomp->tplg = avs_tplg_new(component); + if (!acomp->tplg) + return -ENOMEM; + + if (!mach->tplg_filename) + goto finalize; + + /* Load specified topology and create debugfs for it. */ + filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix, + mach->tplg_filename); + if (!filename) + return -ENOMEM; + + ret = avs_load_topology(component, filename); + kfree(filename); + if (ret < 0) + return ret; + + ret = avs_component_load_libraries(acomp); + if (ret < 0) { + dev_err(card->dev, "libraries loading failed: %d\n", ret); + goto err_load_libs; + } + +finalize: + debugfs_create_file("topology_name", 0444, component->debugfs_root, component, + &topology_name_fops); + + mutex_lock(&adev->comp_list_mutex); + list_add_tail(&acomp->node, &adev->comp_list); + mutex_unlock(&adev->comp_list_mutex); + + return 0; + +err_load_libs: + avs_remove_topology(component); + return ret; +} + +static void avs_component_remove(struct snd_soc_component *component) +{ + struct avs_soc_component *acomp = to_avs_soc_component(component); + struct snd_soc_acpi_mach *mach; + struct avs_dev *adev = to_avs_dev(component->dev); + int ret; + + mach = dev_get_platdata(component->card->dev); + + mutex_lock(&adev->comp_list_mutex); + list_del(&acomp->node); + mutex_unlock(&adev->comp_list_mutex); + + if (mach->tplg_filename) { + ret = avs_remove_topology(component); + if (ret < 0) + dev_err(component->dev, "unload topology failed: %d\n", ret); + } +} + +static int avs_component_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_pcm_hardware hwparams; + + /* only FE DAI links are handled here */ + if (rtd->dai_link->no_pcm) + return 0; + + hwparams.info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP; + + hwparams.formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE; + hwparams.period_bytes_min = 128; + hwparams.period_bytes_max = AZX_MAX_BUF_SIZE / 2; + hwparams.periods_min = 2; + hwparams.periods_max = AZX_MAX_FRAG; + hwparams.buffer_bytes_max = AZX_MAX_BUF_SIZE; + hwparams.fifo_size = 0; + + return snd_soc_set_runtime_hwparams(substream, &hwparams); +} + +static unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream) +{ + return readl(hdac_stream(stream)->bus->remap_addr + AZX_REG_VS_SDXDPIB_XBASE + + (AZX_REG_VS_SDXDPIB_XINTERVAL * hdac_stream(stream)->index)); +} + +static snd_pcm_uframes_t +avs_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct avs_dma_data *data; + struct hdac_ext_stream *host_stream; + unsigned int pos; + + data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); + if (!data->host_stream) + return 0; + + host_stream = data->host_stream; + pos = avs_hda_stream_dpib_read(host_stream); + + if (pos >= hdac_stream(host_stream)->bufsize) + pos = 0; + + return bytes_to_frames(substream->runtime, pos); +} + +static int avs_component_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + return snd_pcm_lib_default_mmap(substream, vma); +} + +#define MAX_PREALLOC_SIZE (32 * 1024 * 1024) + +static int avs_component_construct(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_pcm *pcm = rtd->pcm; + + if (dai->driver->playback.channels_min) + snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, + SNDRV_DMA_TYPE_DEV_SG, component->dev, 0, + MAX_PREALLOC_SIZE); + + if (dai->driver->capture.channels_min) + snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_DEV_SG, component->dev, 0, + MAX_PREALLOC_SIZE); + + return 0; +} + +static const struct snd_soc_component_driver avs_component_driver = { + .name = "avs-pcm", + .probe = avs_component_probe, + .remove = avs_component_remove, + .open = avs_component_open, + .pointer = avs_component_pointer, + .mmap = avs_component_mmap, + .pcm_construct = avs_component_construct, + .module_get_upon_open = 1, /* increment refcount when a pcm is opened */ + .topology_name_prefix = "intel/avs", + .non_legacy_dai_naming = true, +}; + +static int avs_soc_component_register(struct device *dev, const char *name, + const struct snd_soc_component_driver *drv, + struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) +{ + struct avs_soc_component *acomp; + int ret; + + acomp = devm_kzalloc(dev, sizeof(*acomp), GFP_KERNEL); + if (!acomp) + return -ENOMEM; + + ret = snd_soc_component_initialize(&acomp->base, drv, dev); + if (ret < 0) + return ret; + + /* force name change after ASoC is done with its init */ + acomp->base.name = name; + INIT_LIST_HEAD(&acomp->node); + + return snd_soc_add_component(&acomp->base, cpu_dais, num_cpu_dais); +} + +static struct snd_soc_dai_driver dmic_cpu_dais[] = { +{ + .name = "DMIC Pin", + .ops = &avs_dai_nonhda_be_ops, + .capture = { + .stream_name = "DMIC Rx", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "DMIC WoV Pin", + .ops = &avs_dai_nonhda_be_ops, + .capture = { + .stream_name = "DMIC WoV Rx", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +}; + +int avs_dmic_platform_register(struct avs_dev *adev, const char *name) +{ + return avs_soc_component_register(adev->dev, name, &avs_component_driver, dmic_cpu_dais, + ARRAY_SIZE(dmic_cpu_dais)); +} + +static const struct snd_soc_dai_driver i2s_dai_template = { + .ops = &avs_dai_nonhda_be_ops, + .playback = { + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000 | + SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000 | + SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}; + +int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask, + unsigned long *tdms) +{ + struct snd_soc_dai_driver *cpus, *dai; + size_t ssp_count, cpu_count; + int i, j; + + ssp_count = adev->hw_cfg.i2s_caps.ctrl_count; + cpu_count = hweight_long(port_mask); + if (tdms) + for_each_set_bit(i, &port_mask, ssp_count) + cpu_count += hweight_long(tdms[i]); + + cpus = devm_kzalloc(adev->dev, sizeof(*cpus) * cpu_count, GFP_KERNEL); + if (!cpus) + return -ENOMEM; + + dai = cpus; + for_each_set_bit(i, &port_mask, ssp_count) { + memcpy(dai, &i2s_dai_template, sizeof(*dai)); + + dai->name = + devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d Pin", i); + dai->playback.stream_name = + devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Tx", i); + dai->capture.stream_name = + devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d Rx", i); + + if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name) + return -ENOMEM; + dai++; + } + + if (!tdms) + goto plat_register; + + for_each_set_bit(i, &port_mask, ssp_count) { + for_each_set_bit(j, &tdms[i], ssp_count) { + memcpy(dai, &i2s_dai_template, sizeof(*dai)); + + dai->name = + devm_kasprintf(adev->dev, GFP_KERNEL, "SSP%d:%d Pin", i, j); + dai->playback.stream_name = + devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Tx", i, j); + dai->capture.stream_name = + devm_kasprintf(adev->dev, GFP_KERNEL, "ssp%d:%d Rx", i, j); + + if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name) + return -ENOMEM; + dai++; + } + } + +plat_register: + return avs_soc_component_register(adev->dev, name, &avs_component_driver, cpus, cpu_count); +} + +/* HD-Audio CPU DAI template */ +static const struct snd_soc_dai_driver hda_cpu_dai = { + .ops = &avs_dai_hda_be_ops, + .playback = { + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}; + +static void avs_component_hda_unregister_dais(struct snd_soc_component *component) +{ + struct snd_soc_acpi_mach *mach; + struct snd_soc_dai *dai, *save; + struct hda_codec *codec; + char name[32]; + + mach = dev_get_platdata(component->card->dev); + codec = mach->pdata; + sprintf(name, "%s-cpu", dev_name(&codec->core.dev)); + + for_each_component_dais_safe(component, dai, save) { + if (!strstr(dai->driver->name, name)) + continue; + + if (dai->playback_widget) + snd_soc_dapm_free_widget(dai->playback_widget); + if (dai->capture_widget) + snd_soc_dapm_free_widget(dai->capture_widget); + snd_soc_unregister_dai(dai); + } +} + +static int avs_component_hda_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_dai_driver *dais; + struct snd_soc_acpi_mach *mach; + struct hda_codec *codec; + struct hda_pcm *pcm; + const char *cname; + int pcm_count = 0, ret, i; + + mach = dev_get_platdata(component->card->dev); + if (!mach) + return -EINVAL; + + codec = mach->pdata; + if (list_empty(&codec->pcm_list_head)) + return -EINVAL; + list_for_each_entry(pcm, &codec->pcm_list_head, list) + pcm_count++; + + dais = devm_kcalloc(component->dev, pcm_count, sizeof(*dais), + GFP_KERNEL); + if (!dais) + return -ENOMEM; + + cname = dev_name(&codec->core.dev); + dapm = snd_soc_component_get_dapm(component); + pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list); + + for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) { + struct snd_soc_dai *dai; + + memcpy(&dais[i], &hda_cpu_dai, sizeof(*dais)); + dais[i].id = i; + dais[i].name = devm_kasprintf(component->dev, GFP_KERNEL, + "%s-cpu%d", cname, i); + if (!dais[i].name) { + ret = -ENOMEM; + goto exit; + } + + if (pcm->stream[0].substreams) { + dais[i].playback.stream_name = + devm_kasprintf(component->dev, GFP_KERNEL, + "%s-cpu%d Tx", cname, i); + if (!dais[i].playback.stream_name) { + ret = -ENOMEM; + goto exit; + } + } + + if (pcm->stream[1].substreams) { + dais[i].capture.stream_name = + devm_kasprintf(component->dev, GFP_KERNEL, + "%s-cpu%d Rx", cname, i); + if (!dais[i].capture.stream_name) { + ret = -ENOMEM; + goto exit; + } + } + + dai = snd_soc_register_dai(component, &dais[i], false); + if (!dai) { + dev_err(component->dev, "register dai for %s failed\n", + pcm->name); + ret = -EINVAL; + goto exit; + } + + ret = snd_soc_dapm_new_dai_widgets(dapm, dai); + if (ret < 0) { + dev_err(component->dev, "create widgets failed: %d\n", + ret); + goto exit; + } + } + + ret = avs_component_probe(component); +exit: + if (ret) + avs_component_hda_unregister_dais(component); + + return ret; +} + +static void avs_component_hda_remove(struct snd_soc_component *component) +{ + avs_component_hda_unregister_dais(component); + avs_component_remove(component); +} + +static int avs_component_hda_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct hdac_ext_stream *link_stream; + struct hda_codec *codec; + + /* only BE DAI links are handled here */ + if (!rtd->dai_link->no_pcm) + return avs_component_open(component, substream); + + codec = dev_to_hda_codec(asoc_rtd_to_codec(rtd, 0)->dev); + link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream, + HDAC_EXT_STREAM_TYPE_LINK); + if (!link_stream) + return -EBUSY; + + substream->runtime->private_data = link_stream; + return 0; +} + +static int avs_component_hda_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct hdac_ext_stream *link_stream; + + /* only BE DAI links are handled here */ + if (!rtd->dai_link->no_pcm) + return 0; + + link_stream = substream->runtime->private_data; + snd_hdac_ext_stream_release(link_stream, HDAC_EXT_STREAM_TYPE_LINK); + substream->runtime->private_data = NULL; + + return 0; +} + +static const struct snd_soc_component_driver avs_hda_component_driver = { + .name = "avs-hda-pcm", + .probe = avs_component_hda_probe, + .remove = avs_component_hda_remove, + .open = avs_component_hda_open, + .close = avs_component_hda_close, + .pointer = avs_component_pointer, + .mmap = avs_component_mmap, + .pcm_construct = avs_component_construct, + /* + * hda platform component's probe() is dependent on + * codec->pcm_list_head, it needs to be initialized after codec + * component. remove_order is here for completeness sake + */ + .probe_order = SND_SOC_COMP_ORDER_LATE, + .remove_order = SND_SOC_COMP_ORDER_EARLY, + .module_get_upon_open = 1, + .topology_name_prefix = "intel/avs", + .non_legacy_dai_naming = true, +}; + +int avs_hda_platform_register(struct avs_dev *adev, const char *name) +{ + return avs_soc_component_register(adev->dev, name, + &avs_hda_component_driver, NULL, 0); +} diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h index 3fd02389ed2b..95be86148cf3 100644 --- a/sound/soc/intel/avs/registers.h +++ b/sound/soc/intel/avs/registers.h @@ -14,6 +14,7 @@ #define AZX_PGCTL_LSRMD_MASK BIT(4) #define AZX_CGCTL_MISCBDCGE_MASK BIT(6) #define AZX_VS_EM2_L1SEN BIT(13) +#define AZX_VS_EM2_DUM BIT(23) /* Intel HD Audio General DSP Registers */ #define AVS_ADSP_GEN_BASE 0x0 @@ -47,6 +48,12 @@ #define SKL_ADSP_HIPCIE_DONE BIT(30) #define SKL_ADSP_HIPCT_BUSY BIT(31) +/* Intel HD Audio SRAM windows base addresses */ +#define SKL_ADSP_SRAM_BASE_OFFSET 0x8000 +#define SKL_ADSP_SRAM_WINDOW_SIZE 0x2000 +#define APL_ADSP_SRAM_BASE_OFFSET 0x80000 +#define APL_ADSP_SRAM_WINDOW_SIZE 0x20000 + /* Constants used when accessing SRAM, space shared with firmware */ #define AVS_FW_REG_BASE(adev) ((adev)->spec->sram_base_offset) #define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0) @@ -58,6 +65,7 @@ #define AVS_UPLINK_WINDOW AVS_FW_REGS_WINDOW /* HOST -> DSP communication window */ #define AVS_DOWNLINK_WINDOW 1 +#define AVS_DEBUG_WINDOW 2 /* registry I/O helpers */ #define avs_sram_offset(adev, window_idx) \ diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c new file mode 100644 index 000000000000..bda5ec7510fe --- /dev/null +++ b/sound/soc/intel/avs/skl.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/devcoredump.h> +#include <linux/slab.h> +#include <sound/hdaudio_ext.h> +#include "avs.h" +#include "messages.h" + +static int skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +{ + struct skl_log_state_info *info; + u32 size, num_cores = adev->hw_cfg.dsp_cores; + int ret, i; + + if (fls_long(resource_mask) > num_cores) + return -EINVAL; + size = struct_size(info, logs_core, num_cores); + info = kzalloc(size, GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->core_mask = resource_mask; + if (enable) + for_each_set_bit(i, &resource_mask, GENMASK(num_cores, 0)) { + info->logs_core[i].enable = enable; + info->logs_core[i].min_priority = *priorities++; + } + else + for_each_set_bit(i, &resource_mask, GENMASK(num_cores, 0)) + info->logs_core[i].enable = enable; + + ret = avs_ipc_set_enable_logs(adev, (u8 *)info, size); + kfree(info); + if (ret) + return AVS_IPC_RET(ret); + + return 0; +} + +int skl_log_buffer_offset(struct avs_dev *adev, u32 core) +{ + return core * avs_log_buffer_size(adev); +} + +/* fw DbgLogWp registers */ +#define FW_REGS_DBG_LOG_WP(core) (0x30 + 0x4 * core) + +static int +skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) +{ + unsigned long flags; + void __iomem *buf; + u16 size, write, offset; + + spin_lock_irqsave(&adev->dbg.trace_lock, flags); + if (!kfifo_initialized(&adev->dbg.trace_fifo)) { + spin_unlock_irqrestore(&adev->dbg.trace_lock, flags); + return 0; + } + + size = avs_log_buffer_size(adev) / 2; + write = readl(avs_sram_addr(adev, AVS_FW_REGS_WINDOW) + FW_REGS_DBG_LOG_WP(msg->log.core)); + /* determine buffer half */ + offset = (write < size) ? size : 0; + + /* Address is guaranteed to exist in SRAM2. */ + buf = avs_log_buffer_addr(adev, msg->log.core) + offset; + __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf, size, &adev->dbg.fifo_lock); + wake_up(&adev->dbg.trace_waitq); + spin_unlock_irqrestore(&adev->dbg.trace_lock, flags); + + return 0; +} + +static int skl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) +{ + u8 *dump; + + dump = vzalloc(AVS_FW_REGS_SIZE); + if (!dump) + return -ENOMEM; + + memcpy_fromio(dump, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE); + dev_coredumpv(adev->dev, dump, AVS_FW_REGS_SIZE, GFP_KERNEL); + + return 0; +} + +static bool +skl_d0ix_toggle(struct avs_dev *adev, struct avs_ipc_msg *tx, bool wake) +{ + /* unsupported on cAVS 1.5 hw */ + return false; +} + +static int skl_set_d0ix(struct avs_dev *adev, bool enable) +{ + /* unsupported on cAVS 1.5 hw */ + return 0; +} + +const struct avs_dsp_ops skl_dsp_ops = { + .power = avs_dsp_core_power, + .reset = avs_dsp_core_reset, + .stall = avs_dsp_core_stall, + .irq_handler = avs_dsp_irq_handler, + .irq_thread = avs_dsp_irq_thread, + .int_control = avs_dsp_interrupt_control, + .load_basefw = avs_cldma_load_basefw, + .load_lib = avs_cldma_load_library, + .transfer_mods = avs_cldma_transfer_modules, + .enable_logs = skl_enable_logs, + .log_buffer_offset = skl_log_buffer_offset, + .log_buffer_status = skl_log_buffer_status, + .coredump = skl_coredump, + .d0ix_toggle = skl_d0ix_toggle, + .set_d0ix = skl_set_d0ix, +}; diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c new file mode 100644 index 000000000000..0d11cc8aab0b --- /dev/null +++ b/sound/soc/intel/avs/topology.c @@ -0,0 +1,1598 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/firmware.h> +#include <linux/uuid.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include <sound/soc-topology.h> +#include <uapi/sound/intel/avs/tokens.h> +#include "avs.h" +#include "topology.h" + +/* Get pointer to vendor array at the specified offset. */ +#define avs_tplg_vendor_array_at(array, offset) \ + ((struct snd_soc_tplg_vendor_array *)((u8 *)array + offset)) + +/* Get pointer to vendor array that is next in line. */ +#define avs_tplg_vendor_array_next(array) \ + (avs_tplg_vendor_array_at(array, le32_to_cpu((array)->size))) + +/* + * Scan provided block of tuples for the specified token. If found, + * @offset is updated with position at which first matching token is + * located. + * + * Returns 0 on success, -ENOENT if not found and error code otherwise. + */ +static int +avs_tplg_vendor_array_lookup(struct snd_soc_tplg_vendor_array *tuples, + u32 block_size, u32 token, u32 *offset) +{ + u32 pos = 0; + + while (block_size > 0) { + struct snd_soc_tplg_vendor_value_elem *tuple; + u32 tuples_size = le32_to_cpu(tuples->size); + + if (tuples_size > block_size) + return -EINVAL; + + tuple = tuples->value; + if (le32_to_cpu(tuple->token) == token) { + *offset = pos; + return 0; + } + + block_size -= tuples_size; + pos += tuples_size; + tuples = avs_tplg_vendor_array_next(tuples); + } + + return -ENOENT; +} + +/* + * See avs_tplg_vendor_array_lookup() for description. + * + * Behaves exactly like avs_tplg_vendor_lookup() but starts from the + * next vendor array in line. Useful when searching for the finish line + * of an arbitrary entry in a list of entries where each is composed of + * several vendor tuples and a specific token marks the beginning of + * a new entry block. + */ +static int +avs_tplg_vendor_array_lookup_next(struct snd_soc_tplg_vendor_array *tuples, + u32 block_size, u32 token, u32 *offset) +{ + u32 tuples_size = le32_to_cpu(tuples->size); + int ret; + + if (tuples_size > block_size) + return -EINVAL; + + tuples = avs_tplg_vendor_array_next(tuples); + block_size -= tuples_size; + + ret = avs_tplg_vendor_array_lookup(tuples, block_size, token, offset); + if (!ret) + *offset += tuples_size; + return ret; +} + +/* + * Scan provided block of tuples for the specified token which marks + * the border of an entry block. Behavior is similar to + * avs_tplg_vendor_array_lookup() except 0 is also returned if no + * matching token has been found. In such case, returned @size is + * assigned to @block_size as the entire block belongs to the current + * entry. + * + * Returns 0 on success, error code otherwise. + */ +static int +avs_tplg_vendor_entry_size(struct snd_soc_tplg_vendor_array *tuples, + u32 block_size, u32 entry_id_token, u32 *size) +{ + int ret; + + ret = avs_tplg_vendor_array_lookup_next(tuples, block_size, entry_id_token, size); + if (ret == -ENOENT) { + *size = block_size; + ret = 0; + } + + return ret; +} + +/* + * Vendor tuple parsing descriptor. + * + * @token: vendor specific token that identifies tuple + * @type: tuple type, one of SND_SOC_TPLG_TUPLE_TYPE_XXX + * @offset: offset of a struct's field to initialize + * @parse: parsing function, extracts and assigns value to object's field + */ +struct avs_tplg_token_parser { + enum avs_tplg_token token; + u32 type; + u32 offset; + int (*parse)(struct snd_soc_component *comp, void *elem, void *object, u32 offset); +}; + +static int +avs_parse_uuid_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset) +{ + struct snd_soc_tplg_vendor_value_elem *tuple = elem; + guid_t *val = (guid_t *)((u8 *)object + offset); + + guid_copy((guid_t *)val, (const guid_t *)&tuple->value); + + return 0; +} + +static int +avs_parse_bool_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset) +{ + struct snd_soc_tplg_vendor_value_elem *tuple = elem; + bool *val = (bool *)((u8 *)object + offset); + + *val = le32_to_cpu(tuple->value); + + return 0; +} + +static int +avs_parse_byte_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset) +{ + struct snd_soc_tplg_vendor_value_elem *tuple = elem; + u8 *val = ((u8 *)object + offset); + + *val = le32_to_cpu(tuple->value); + + return 0; +} + +static int +avs_parse_short_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset) +{ + struct snd_soc_tplg_vendor_value_elem *tuple = elem; + u16 *val = (u16 *)((u8 *)object + offset); + + *val = le32_to_cpu(tuple->value); + + return 0; +} + +static int +avs_parse_word_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset) +{ + struct snd_soc_tplg_vendor_value_elem *tuple = elem; + u32 *val = (u32 *)((u8 *)object + offset); + + *val = le32_to_cpu(tuple->value); + + return 0; +} + +static int +avs_parse_string_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset) +{ + struct snd_soc_tplg_vendor_string_elem *tuple = elem; + char *val = (char *)((u8 *)object + offset); + + snprintf(val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", tuple->string); + + return 0; +} + +static int avs_parse_uuid_tokens(struct snd_soc_component *comp, void *object, + const struct avs_tplg_token_parser *parsers, int count, + struct snd_soc_tplg_vendor_array *tuples) +{ + struct snd_soc_tplg_vendor_uuid_elem *tuple; + int ret, i, j; + + /* Parse element by element. */ + for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) { + tuple = &tuples->uuid[i]; + + for (j = 0; j < count; j++) { + /* Ignore non-UUID tokens. */ + if (parsers[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID || + parsers[j].token != le32_to_cpu(tuple->token)) + continue; + + ret = parsers[j].parse(comp, tuple, object, parsers[j].offset); + if (ret) + return ret; + } + } + + return 0; +} + +static int avs_parse_string_tokens(struct snd_soc_component *comp, void *object, + const struct avs_tplg_token_parser *parsers, int count, + struct snd_soc_tplg_vendor_array *tuples) +{ + struct snd_soc_tplg_vendor_string_elem *tuple; + int ret, i, j; + + /* Parse element by element. */ + for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) { + tuple = &tuples->string[i]; + + for (j = 0; j < count; j++) { + /* Ignore non-string tokens. */ + if (parsers[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING || + parsers[j].token != le32_to_cpu(tuple->token)) + continue; + + ret = parsers[j].parse(comp, tuple, object, parsers[j].offset); + if (ret) + return ret; + } + } + + return 0; +} + +static int avs_parse_word_tokens(struct snd_soc_component *comp, void *object, + const struct avs_tplg_token_parser *parsers, int count, + struct snd_soc_tplg_vendor_array *tuples) +{ + struct snd_soc_tplg_vendor_value_elem *tuple; + int ret, i, j; + + /* Parse element by element. */ + for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) { + tuple = &tuples->value[i]; + + for (j = 0; j < count; j++) { + /* Ignore non-integer tokens. */ + if (!(parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD || + parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT || + parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE || + parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL)) + continue; + + if (parsers[j].token != le32_to_cpu(tuple->token)) + continue; + + ret = parsers[j].parse(comp, tuple, object, parsers[j].offset); + if (ret) + return ret; + } + } + + return 0; +} + +static int avs_parse_tokens(struct snd_soc_component *comp, void *object, + const struct avs_tplg_token_parser *parsers, size_t count, + struct snd_soc_tplg_vendor_array *tuples, int priv_size) +{ + int array_size, ret; + + while (priv_size > 0) { + array_size = le32_to_cpu(tuples->size); + + if (array_size <= 0) { + dev_err(comp->dev, "invalid array size 0x%x\n", array_size); + return -EINVAL; + } + + /* Make sure there is enough data before parsing. */ + priv_size -= array_size; + if (priv_size < 0) { + dev_err(comp->dev, "invalid array size 0x%x\n", array_size); + return -EINVAL; + } + + switch (le32_to_cpu(tuples->type)) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + ret = avs_parse_uuid_tokens(comp, object, parsers, count, tuples); + break; + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + ret = avs_parse_string_tokens(comp, object, parsers, count, tuples); + break; + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: + case SND_SOC_TPLG_TUPLE_TYPE_BYTE: + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + case SND_SOC_TPLG_TUPLE_TYPE_WORD: + ret = avs_parse_word_tokens(comp, object, parsers, count, tuples); + break; + default: + dev_err(comp->dev, "unknown token type %d\n", tuples->type); + ret = -EINVAL; + } + + if (ret) { + dev_err(comp->dev, "parsing %zu tokens of %d type failed: %d\n", + count, tuples->type, ret); + return ret; + } + + tuples = avs_tplg_vendor_array_next(tuples); + } + + return 0; +} + +#define AVS_DEFINE_PTR_PARSER(name, type, member) \ +static int \ +avs_parse_##name##_ptr(struct snd_soc_component *comp, void *elem, void *object, u32 offset) \ +{ \ + struct snd_soc_tplg_vendor_value_elem *tuple = elem; \ + struct avs_soc_component *acomp = to_avs_soc_component(comp); \ + type **val = (type **)(object + offset); \ + u32 idx; \ + \ + idx = le32_to_cpu(tuple->value); \ + if (idx >= acomp->tplg->num_##member) \ + return -EINVAL; \ + \ + *val = &acomp->tplg->member[idx]; \ + \ + return 0; \ +} + +AVS_DEFINE_PTR_PARSER(audio_format, struct avs_audio_format, fmts); +AVS_DEFINE_PTR_PARSER(modcfg_base, struct avs_tplg_modcfg_base, modcfgs_base); +AVS_DEFINE_PTR_PARSER(modcfg_ext, struct avs_tplg_modcfg_ext, modcfgs_ext); +AVS_DEFINE_PTR_PARSER(pplcfg, struct avs_tplg_pplcfg, pplcfgs); +AVS_DEFINE_PTR_PARSER(binding, struct avs_tplg_binding, bindings); + +static int +parse_audio_format_bitfield(struct snd_soc_component *comp, void *elem, void *object, u32 offset) +{ + struct snd_soc_tplg_vendor_value_elem *velem = elem; + struct avs_audio_format *audio_format = object; + + switch (offset) { + case AVS_TKN_AFMT_NUM_CHANNELS_U32: + audio_format->num_channels = le32_to_cpu(velem->value); + break; + case AVS_TKN_AFMT_VALID_BIT_DEPTH_U32: + audio_format->valid_bit_depth = le32_to_cpu(velem->value); + break; + case AVS_TKN_AFMT_SAMPLE_TYPE_U32: + audio_format->sample_type = le32_to_cpu(velem->value); + break; + } + + return 0; +} + +static int parse_link_formatted_string(struct snd_soc_component *comp, void *elem, + void *object, u32 offset) +{ + struct snd_soc_tplg_vendor_string_elem *tuple = elem; + struct snd_soc_acpi_mach *mach = dev_get_platdata(comp->card->dev); + char *val = (char *)((u8 *)object + offset); + + /* + * Dynamic naming - string formats, e.g.: ssp%d - supported only for + * topologies describing single device e.g.: an I2S codec on SSP0. + */ + if (hweight_long(mach->mach_params.i2s_link_mask) != 1) + return avs_parse_string_token(comp, elem, object, offset); + + snprintf(val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, tuple->string, + __ffs(mach->mach_params.i2s_link_mask)); + + return 0; +} + +static int +parse_dictionary_header(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, + void **dict, u32 *num_entries, size_t entry_size, + u32 num_entries_token) +{ + struct snd_soc_tplg_vendor_value_elem *tuple; + + /* Dictionary header consists of single tuple - entry count. */ + tuple = tuples->value; + if (le32_to_cpu(tuple->token) != num_entries_token) { + dev_err(comp->dev, "invalid dictionary header, expected: %d\n", + num_entries_token); + return -EINVAL; + } + + *num_entries = le32_to_cpu(tuple->value); + *dict = devm_kcalloc(comp->card->dev, *num_entries, entry_size, GFP_KERNEL); + if (!*dict) + return -ENOMEM; + + return 0; +} + +static int +parse_dictionary_entries(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, u32 block_size, + void *dict, u32 num_entries, size_t entry_size, + u32 entry_id_token, + const struct avs_tplg_token_parser *parsers, size_t num_parsers) +{ + void *pos = dict; + int i; + + for (i = 0; i < num_entries; i++) { + u32 esize; + int ret; + + ret = avs_tplg_vendor_entry_size(tuples, block_size, + entry_id_token, &esize); + if (ret) + return ret; + + ret = avs_parse_tokens(comp, pos, parsers, num_parsers, tuples, esize); + if (ret < 0) { + dev_err(comp->dev, "parse entry: %d of type: %d failed: %d\n", + i, entry_id_token, ret); + return ret; + } + + pos += entry_size; + block_size -= esize; + tuples = avs_tplg_vendor_array_at(tuples, esize); + } + + return 0; +} + +static int parse_dictionary(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, u32 block_size, + void **dict, u32 *num_entries, size_t entry_size, + u32 num_entries_token, u32 entry_id_token, + const struct avs_tplg_token_parser *parsers, size_t num_parsers) +{ + int ret; + + ret = parse_dictionary_header(comp, tuples, dict, num_entries, + entry_size, num_entries_token); + if (ret) + return ret; + + block_size -= le32_to_cpu(tuples->size); + /* With header parsed, move on to parsing entries. */ + tuples = avs_tplg_vendor_array_next(tuples); + + return parse_dictionary_entries(comp, tuples, block_size, *dict, + *num_entries, entry_size, + entry_id_token, parsers, num_parsers); +} + +static const struct avs_tplg_token_parser library_parsers[] = { + { + .token = AVS_TKN_LIBRARY_NAME_STRING, + .type = SND_SOC_TPLG_TUPLE_TYPE_STRING, + .offset = offsetof(struct avs_tplg_library, name), + .parse = avs_parse_string_token, + }, +}; + +static int avs_tplg_parse_libraries(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, u32 block_size) +{ + struct avs_soc_component *acomp = to_avs_soc_component(comp); + struct avs_tplg *tplg = acomp->tplg; + + return parse_dictionary(comp, tuples, block_size, (void **)&tplg->libs, + &tplg->num_libs, sizeof(*tplg->libs), + AVS_TKN_MANIFEST_NUM_LIBRARIES_U32, + AVS_TKN_LIBRARY_ID_U32, + library_parsers, ARRAY_SIZE(library_parsers)); +} + +static const struct avs_tplg_token_parser audio_format_parsers[] = { + { + .token = AVS_TKN_AFMT_SAMPLE_RATE_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_audio_format, sampling_freq), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_AFMT_BIT_DEPTH_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_audio_format, bit_depth), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_AFMT_CHANNEL_MAP_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_audio_format, channel_map), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_AFMT_CHANNEL_CFG_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_audio_format, channel_config), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_AFMT_INTERLEAVING_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_audio_format, interleaving), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_AFMT_NUM_CHANNELS_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = AVS_TKN_AFMT_NUM_CHANNELS_U32, + .parse = parse_audio_format_bitfield, + }, + { + .token = AVS_TKN_AFMT_VALID_BIT_DEPTH_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = AVS_TKN_AFMT_VALID_BIT_DEPTH_U32, + .parse = parse_audio_format_bitfield, + }, + { + .token = AVS_TKN_AFMT_SAMPLE_TYPE_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = AVS_TKN_AFMT_SAMPLE_TYPE_U32, + .parse = parse_audio_format_bitfield, + }, +}; + +static int avs_tplg_parse_audio_formats(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, + u32 block_size) +{ + struct avs_soc_component *acomp = to_avs_soc_component(comp); + struct avs_tplg *tplg = acomp->tplg; + + return parse_dictionary(comp, tuples, block_size, (void **)&tplg->fmts, + &tplg->num_fmts, sizeof(*tplg->fmts), + AVS_TKN_MANIFEST_NUM_AFMTS_U32, + AVS_TKN_AFMT_ID_U32, + audio_format_parsers, ARRAY_SIZE(audio_format_parsers)); +} + +static const struct avs_tplg_token_parser modcfg_base_parsers[] = { + { + .token = AVS_TKN_MODCFG_BASE_CPC_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_base, cpc), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_BASE_IBS_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_base, ibs), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_BASE_OBS_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_base, obs), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_BASE_PAGES_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_base, is_pages), + .parse = avs_parse_word_token, + }, +}; + +static int avs_tplg_parse_modcfgs_base(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, + u32 block_size) +{ + struct avs_soc_component *acomp = to_avs_soc_component(comp); + struct avs_tplg *tplg = acomp->tplg; + + return parse_dictionary(comp, tuples, block_size, (void **)&tplg->modcfgs_base, + &tplg->num_modcfgs_base, sizeof(*tplg->modcfgs_base), + AVS_TKN_MANIFEST_NUM_MODCFGS_BASE_U32, + AVS_TKN_MODCFG_BASE_ID_U32, + modcfg_base_parsers, ARRAY_SIZE(modcfg_base_parsers)); +} + +static const struct avs_tplg_token_parser modcfg_ext_parsers[] = { + { + .token = AVS_TKN_MODCFG_EXT_TYPE_UUID, + .type = SND_SOC_TPLG_TUPLE_TYPE_UUID, + .offset = offsetof(struct avs_tplg_modcfg_ext, type), + .parse = avs_parse_uuid_token, + }, + { + .token = AVS_TKN_MODCFG_CPR_OUT_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, copier.out_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_CPR_FEATURE_MASK_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, copier.feature_mask), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_CPR_VINDEX_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_modcfg_ext, copier.vindex), + .parse = avs_parse_byte_token, + }, + { + .token = AVS_TKN_MODCFG_CPR_DMA_TYPE_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, copier.dma_type), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_CPR_DMABUFF_SIZE_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, copier.dma_buffer_size), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_CPR_BLOB_FMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, copier.blob_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_MICSEL_OUT_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, micsel.out_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_INTELWOV_CPC_LP_MODE_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, wov.cpc_lp_mode), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_SRC_OUT_FREQ_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, src.out_freq), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_MUX_REF_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, mux.ref_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_MUX_OUT_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, mux.out_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_AEC_REF_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, aec.ref_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_AEC_OUT_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, aec.out_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_AEC_CPC_LP_MODE_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, aec.cpc_lp_mode), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_ASRC_OUT_FREQ_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.out_freq), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_ASRC_MODE_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.mode), + .parse = avs_parse_byte_token, + }, + { + .token = AVS_TKN_MODCFG_ASRC_DISABLE_JITTER_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_modcfg_ext, asrc.disable_jitter_buffer), + .parse = avs_parse_byte_token, + }, + { + .token = AVS_TKN_MODCFG_UPDOWN_MIX_OUT_CHAN_CFG_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.out_channel_config), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_SELECT_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients_select), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_0_S32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[0]), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_1_S32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[1]), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_2_S32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[2]), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_3_S32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[3]), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_4_S32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[4]), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_5_S32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[5]), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_6_S32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[6]), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_UPDOWN_MIX_COEFF_7_S32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.coefficients[7]), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_UPDOWN_MIX_CHAN_MAP_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, updown_mix.channel_map), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_EXT_NUM_INPUT_PINS_U16, + .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT, + .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_input_pins), + .parse = avs_parse_short_token, + }, + { + .token = AVS_TKN_MODCFG_EXT_NUM_OUTPUT_PINS_U16, + .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT, + .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_output_pins), + .parse = avs_parse_short_token, + }, +}; + +static const struct avs_tplg_token_parser pin_format_parsers[] = { + { + .token = AVS_TKN_PIN_FMT_INDEX_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_pin_format, pin_index), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_PIN_FMT_IOBS_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_pin_format, iobs), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_PIN_FMT_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_pin_format, fmt), + .parse = avs_parse_audio_format_ptr, + }, +}; + +static int avs_tplg_parse_modcfg_ext(struct snd_soc_component *comp, + struct avs_tplg_modcfg_ext *cfg, + struct snd_soc_tplg_vendor_array *tuples, + u32 block_size) +{ + u32 esize; + int ret; + + /* See where pin block starts. */ + ret = avs_tplg_vendor_entry_size(tuples, block_size, + AVS_TKN_PIN_FMT_INDEX_U32, &esize); + if (ret) + return ret; + + ret = avs_parse_tokens(comp, cfg, modcfg_ext_parsers, + ARRAY_SIZE(modcfg_ext_parsers), tuples, esize); + if (ret) + return ret; + + block_size -= esize; + /* Parse trailing in/out pin formats if any. */ + if (block_size) { + struct avs_tplg_pin_format *pins; + u32 num_pins; + + num_pins = cfg->generic.num_input_pins + cfg->generic.num_output_pins; + if (!num_pins) + return -EINVAL; + + pins = devm_kcalloc(comp->card->dev, num_pins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + tuples = avs_tplg_vendor_array_at(tuples, esize); + ret = parse_dictionary_entries(comp, tuples, block_size, + pins, num_pins, sizeof(*pins), + AVS_TKN_PIN_FMT_INDEX_U32, + pin_format_parsers, + ARRAY_SIZE(pin_format_parsers)); + if (ret) + return ret; + cfg->generic.pin_fmts = pins; + } + + return 0; +} + +static int avs_tplg_parse_modcfgs_ext(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, + u32 block_size) +{ + struct avs_soc_component *acomp = to_avs_soc_component(comp); + struct avs_tplg *tplg = acomp->tplg; + int ret, i; + + ret = parse_dictionary_header(comp, tuples, (void **)&tplg->modcfgs_ext, + &tplg->num_modcfgs_ext, + sizeof(*tplg->modcfgs_ext), + AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32); + if (ret) + return ret; + + block_size -= le32_to_cpu(tuples->size); + /* With header parsed, move on to parsing entries. */ + tuples = avs_tplg_vendor_array_next(tuples); + + for (i = 0; i < tplg->num_modcfgs_ext; i++) { + struct avs_tplg_modcfg_ext *cfg = &tplg->modcfgs_ext[i]; + u32 esize; + + ret = avs_tplg_vendor_entry_size(tuples, block_size, + AVS_TKN_MODCFG_EXT_ID_U32, &esize); + if (ret) + return ret; + + ret = avs_tplg_parse_modcfg_ext(comp, cfg, tuples, esize); + if (ret) + return ret; + + block_size -= esize; + tuples = avs_tplg_vendor_array_at(tuples, esize); + } + + return 0; +} + +static const struct avs_tplg_token_parser pplcfg_parsers[] = { + { + .token = AVS_TKN_PPLCFG_REQ_SIZE_U16, + .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT, + .offset = offsetof(struct avs_tplg_pplcfg, req_size), + .parse = avs_parse_short_token, + }, + { + .token = AVS_TKN_PPLCFG_PRIORITY_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_pplcfg, priority), + .parse = avs_parse_byte_token, + }, + { + .token = AVS_TKN_PPLCFG_LOW_POWER_BOOL, + .type = SND_SOC_TPLG_TUPLE_TYPE_BOOL, + .offset = offsetof(struct avs_tplg_pplcfg, lp), + .parse = avs_parse_bool_token, + }, + { + .token = AVS_TKN_PPLCFG_ATTRIBUTES_U16, + .type = SND_SOC_TPLG_TUPLE_TYPE_SHORT, + .offset = offsetof(struct avs_tplg_pplcfg, attributes), + .parse = avs_parse_short_token, + }, + { + .token = AVS_TKN_PPLCFG_TRIGGER_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_pplcfg, trigger), + .parse = avs_parse_word_token, + }, +}; + +static int avs_tplg_parse_pplcfgs(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, + u32 block_size) +{ + struct avs_soc_component *acomp = to_avs_soc_component(comp); + struct avs_tplg *tplg = acomp->tplg; + + return parse_dictionary(comp, tuples, block_size, (void **)&tplg->pplcfgs, + &tplg->num_pplcfgs, sizeof(*tplg->pplcfgs), + AVS_TKN_MANIFEST_NUM_PPLCFGS_U32, + AVS_TKN_PPLCFG_ID_U32, + pplcfg_parsers, ARRAY_SIZE(pplcfg_parsers)); +} + +static const struct avs_tplg_token_parser binding_parsers[] = { + { + .token = AVS_TKN_BINDING_TARGET_TPLG_NAME_STRING, + .type = SND_SOC_TPLG_TUPLE_TYPE_STRING, + .offset = offsetof(struct avs_tplg_binding, target_tplg_name), + .parse = parse_link_formatted_string, + }, + { + .token = AVS_TKN_BINDING_TARGET_PATH_TMPL_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_binding, target_path_tmpl_id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_BINDING_TARGET_PPL_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_binding, target_ppl_id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_BINDING_TARGET_MOD_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_binding, target_mod_id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_BINDING_TARGET_MOD_PIN_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_binding, target_mod_pin), + .parse = avs_parse_byte_token, + }, + { + .token = AVS_TKN_BINDING_MOD_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_binding, mod_id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_BINDING_MOD_PIN_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_binding, mod_pin), + .parse = avs_parse_byte_token, + }, + { + .token = AVS_TKN_BINDING_IS_SINK_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_binding, is_sink), + .parse = avs_parse_byte_token, + }, +}; + +static int avs_tplg_parse_bindings(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, + u32 block_size) +{ + struct avs_soc_component *acomp = to_avs_soc_component(comp); + struct avs_tplg *tplg = acomp->tplg; + + return parse_dictionary(comp, tuples, block_size, (void **)&tplg->bindings, + &tplg->num_bindings, sizeof(*tplg->bindings), + AVS_TKN_MANIFEST_NUM_BINDINGS_U32, + AVS_TKN_BINDING_ID_U32, + binding_parsers, ARRAY_SIZE(binding_parsers)); +} + +static const struct avs_tplg_token_parser module_parsers[] = { + { + .token = AVS_TKN_MOD_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_module, id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MOD_MODCFG_BASE_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_module, cfg_base), + .parse = avs_parse_modcfg_base_ptr, + }, + { + .token = AVS_TKN_MOD_IN_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_module, in_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MOD_CORE_ID_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_module, core_id), + .parse = avs_parse_byte_token, + }, + { + .token = AVS_TKN_MOD_PROC_DOMAIN_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_module, domain), + .parse = avs_parse_byte_token, + }, + { + .token = AVS_TKN_MOD_MODCFG_EXT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_module, cfg_ext), + .parse = avs_parse_modcfg_ext_ptr, + }, +}; + +static struct avs_tplg_module * +avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline *owner, + struct snd_soc_tplg_vendor_array *tuples, u32 block_size) +{ + struct avs_tplg_module *module; + int ret; + + module = devm_kzalloc(comp->card->dev, sizeof(*module), GFP_KERNEL); + if (!module) + return ERR_PTR(-ENOMEM); + + ret = avs_parse_tokens(comp, module, module_parsers, + ARRAY_SIZE(module_parsers), tuples, block_size); + if (ret < 0) + return ERR_PTR(ret); + + module->owner = owner; + INIT_LIST_HEAD(&module->node); + + return module; +} + +static const struct avs_tplg_token_parser pipeline_parsers[] = { + { + .token = AVS_TKN_PPL_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_pipeline, id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_PPL_PPLCFG_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_pipeline, cfg), + .parse = avs_parse_pplcfg_ptr, + }, + { + .token = AVS_TKN_PPL_NUM_BINDING_IDS_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_pipeline, num_bindings), + .parse = avs_parse_word_token, + }, +}; + +static const struct avs_tplg_token_parser bindings_parsers[] = { + { + .token = AVS_TKN_PPL_BINDING_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = 0, /* to treat pipeline->bindings as dictionary */ + .parse = avs_parse_binding_ptr, + }, +}; + +static struct avs_tplg_pipeline * +avs_tplg_pipeline_create(struct snd_soc_component *comp, struct avs_tplg_path *owner, + struct snd_soc_tplg_vendor_array *tuples, u32 block_size) +{ + struct avs_tplg_pipeline *pipeline; + u32 modblk_size, offset; + int ret; + + pipeline = devm_kzalloc(comp->card->dev, sizeof(*pipeline), GFP_KERNEL); + if (!pipeline) + return ERR_PTR(-ENOMEM); + + pipeline->owner = owner; + INIT_LIST_HEAD(&pipeline->mod_list); + + /* Pipeline header MUST be followed by at least one module. */ + ret = avs_tplg_vendor_array_lookup(tuples, block_size, + AVS_TKN_MOD_ID_U32, &offset); + if (!ret && !offset) + ret = -EINVAL; + if (ret) + return ERR_PTR(ret); + + /* Process header which precedes module sections. */ + ret = avs_parse_tokens(comp, pipeline, pipeline_parsers, + ARRAY_SIZE(pipeline_parsers), tuples, offset); + if (ret < 0) + return ERR_PTR(ret); + + block_size -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + + /* Optionally, binding sections follow module ones. */ + ret = avs_tplg_vendor_array_lookup_next(tuples, block_size, + AVS_TKN_PPL_BINDING_ID_U32, &offset); + if (ret) { + if (ret != -ENOENT) + return ERR_PTR(ret); + + /* Does header information match actual block layout? */ + if (pipeline->num_bindings) + return ERR_PTR(-EINVAL); + + modblk_size = block_size; + } else { + pipeline->bindings = devm_kcalloc(comp->card->dev, pipeline->num_bindings, + sizeof(*pipeline->bindings), GFP_KERNEL); + if (!pipeline->bindings) + return ERR_PTR(-ENOMEM); + + modblk_size = offset; + } + + block_size -= modblk_size; + do { + struct avs_tplg_module *module; + u32 esize; + + ret = avs_tplg_vendor_entry_size(tuples, modblk_size, + AVS_TKN_MOD_ID_U32, &esize); + if (ret) + return ERR_PTR(ret); + + module = avs_tplg_module_create(comp, pipeline, tuples, esize); + if (IS_ERR(module)) { + dev_err(comp->dev, "parse module failed: %ld\n", + PTR_ERR(module)); + return ERR_CAST(module); + } + + list_add_tail(&module->node, &pipeline->mod_list); + modblk_size -= esize; + tuples = avs_tplg_vendor_array_at(tuples, esize); + } while (modblk_size > 0); + + /* What's left is optional range of bindings. */ + ret = parse_dictionary_entries(comp, tuples, block_size, pipeline->bindings, + pipeline->num_bindings, sizeof(*pipeline->bindings), + AVS_TKN_PPL_BINDING_ID_U32, + bindings_parsers, ARRAY_SIZE(bindings_parsers)); + if (ret) + return ERR_PTR(ret); + + return pipeline; +} + +static const struct avs_tplg_token_parser path_parsers[] = { + { + .token = AVS_TKN_PATH_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_path, id), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_PATH_FE_FMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_path, fe_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_PATH_BE_FMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_path, be_fmt), + .parse = avs_parse_audio_format_ptr, + }, +}; + +static struct avs_tplg_path * +avs_tplg_path_create(struct snd_soc_component *comp, struct avs_tplg_path_template *owner, + struct snd_soc_tplg_vendor_array *tuples, u32 block_size, + const struct avs_tplg_token_parser *parsers, u32 num_parsers) +{ + struct avs_tplg_pipeline *pipeline; + struct avs_tplg_path *path; + u32 offset; + int ret; + + path = devm_kzalloc(comp->card->dev, sizeof(*path), GFP_KERNEL); + if (!path) + return ERR_PTR(-ENOMEM); + + path->owner = owner; + INIT_LIST_HEAD(&path->ppl_list); + INIT_LIST_HEAD(&path->node); + + /* Path header MAY be followed by one or more pipelines. */ + ret = avs_tplg_vendor_array_lookup(tuples, block_size, + AVS_TKN_PPL_ID_U32, &offset); + if (ret == -ENOENT) + offset = block_size; + else if (ret) + return ERR_PTR(ret); + else if (!offset) + return ERR_PTR(-EINVAL); + + /* Process header which precedes pipeline sections. */ + ret = avs_parse_tokens(comp, path, parsers, num_parsers, tuples, offset); + if (ret < 0) + return ERR_PTR(ret); + + block_size -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + while (block_size > 0) { + u32 esize; + + ret = avs_tplg_vendor_entry_size(tuples, block_size, + AVS_TKN_PPL_ID_U32, &esize); + if (ret) + return ERR_PTR(ret); + + pipeline = avs_tplg_pipeline_create(comp, path, tuples, esize); + if (IS_ERR(pipeline)) { + dev_err(comp->dev, "parse pipeline failed: %ld\n", + PTR_ERR(pipeline)); + return ERR_CAST(pipeline); + } + + list_add_tail(&pipeline->node, &path->ppl_list); + block_size -= esize; + tuples = avs_tplg_vendor_array_at(tuples, esize); + } + + return path; +} + +static const struct avs_tplg_token_parser path_tmpl_parsers[] = { + { + .token = AVS_TKN_PATH_TMPL_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_path_template, id), + .parse = avs_parse_word_token, + }, +}; + +static int parse_path_template(struct snd_soc_component *comp, + struct snd_soc_tplg_vendor_array *tuples, u32 block_size, + struct avs_tplg_path_template *template, + const struct avs_tplg_token_parser *tmpl_tokens, u32 num_tmpl_tokens, + const struct avs_tplg_token_parser *path_tokens, u32 num_path_tokens) +{ + struct avs_tplg_path *path; + u32 offset; + int ret; + + /* Path template header MUST be followed by at least one path variant. */ + ret = avs_tplg_vendor_array_lookup(tuples, block_size, + AVS_TKN_PATH_ID_U32, &offset); + if (ret) + return ret; + + /* Process header which precedes path variants sections. */ + ret = avs_parse_tokens(comp, template, tmpl_tokens, num_tmpl_tokens, tuples, offset); + if (ret < 0) + return ret; + + block_size -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + do { + u32 esize; + + ret = avs_tplg_vendor_entry_size(tuples, block_size, + AVS_TKN_PATH_ID_U32, &esize); + if (ret) + return ret; + + path = avs_tplg_path_create(comp, template, tuples, esize, path_tokens, + num_path_tokens); + if (IS_ERR(path)) { + dev_err(comp->dev, "parse path failed: %ld\n", PTR_ERR(path)); + return PTR_ERR(path); + } + + list_add_tail(&path->node, &template->path_list); + block_size -= esize; + tuples = avs_tplg_vendor_array_at(tuples, esize); + } while (block_size > 0); + + return 0; +} + +static struct avs_tplg_path_template * +avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *owner, + struct snd_soc_tplg_vendor_array *tuples, u32 block_size) +{ + struct avs_tplg_path_template *template; + int ret; + + template = devm_kzalloc(comp->card->dev, sizeof(*template), GFP_KERNEL); + if (!template) + return ERR_PTR(-ENOMEM); + + template->owner = owner; /* Used to access component tplg is assigned to. */ + INIT_LIST_HEAD(&template->path_list); + INIT_LIST_HEAD(&template->node); + + ret = parse_path_template(comp, tuples, block_size, template, path_tmpl_parsers, + ARRAY_SIZE(path_tmpl_parsers), path_parsers, + ARRAY_SIZE(path_parsers)); + if (ret) + return ERR_PTR(ret); + + return template; +} + +static int avs_route_load(struct snd_soc_component *comp, int index, + struct snd_soc_dapm_route *route) +{ + 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]; + u32 port; + + /* See parse_link_formatted_string() for dynamic naming when(s). */ + if (hweight_long(mach->mach_params.i2s_link_mask) == 1) { + port = __ffs(mach->mach_params.i2s_link_mask); + + snprintf(buf, len, route->source, port); + strncpy((char *)route->source, buf, len); + snprintf(buf, len, route->sink, port); + strncpy((char *)route->sink, buf, len); + if (route->control) { + snprintf(buf, len, route->control, port); + strncpy((char *)route->control, buf, len); + } + } + + return 0; +} + +static int avs_widget_load(struct snd_soc_component *comp, int index, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *dw) +{ + struct snd_soc_acpi_mach *mach; + struct avs_tplg_path_template *template; + struct avs_soc_component *acomp = to_avs_soc_component(comp); + struct avs_tplg *tplg; + + if (!le32_to_cpu(dw->priv.size)) + return 0; + + tplg = acomp->tplg; + mach = dev_get_platdata(comp->card->dev); + + /* See parse_link_formatted_string() for dynamic naming when(s). */ + if (hweight_long(mach->mach_params.i2s_link_mask) == 1) { + kfree(w->name); + /* w->name is freed later by soc_tplg_dapm_widget_create() */ + w->name = kasprintf(GFP_KERNEL, dw->name, __ffs(mach->mach_params.i2s_link_mask)); + if (!w->name) + return -ENOMEM; + } + + template = avs_tplg_path_template_create(comp, tplg, dw->priv.array, + le32_to_cpu(dw->priv.size)); + if (IS_ERR(template)) { + dev_err(comp->dev, "widget %s load failed: %ld\n", dw->name, + PTR_ERR(template)); + return PTR_ERR(template); + } + + w->priv = template; /* link path information to widget */ + list_add_tail(&template->node, &tplg->path_tmpl_list); + return 0; +} + +static int avs_dai_load(struct snd_soc_component *comp, int index, + struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm, + struct snd_soc_dai *dai) +{ + if (pcm) + dai_drv->ops = &avs_dai_fe_ops; + return 0; +} + +static int avs_link_load(struct snd_soc_component *comp, int index, struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg) +{ + if (!link->no_pcm) { + /* Stream control handled by IPCs. */ + link->nonatomic = true; + + /* Open LINK (BE) pipes last and close them first to prevent xruns. */ + link->trigger[0] = SND_SOC_DPCM_TRIGGER_PRE; + link->trigger[1] = SND_SOC_DPCM_TRIGGER_PRE; + } + + return 0; +} + +static const struct avs_tplg_token_parser manifest_parsers[] = { + { + .token = AVS_TKN_MANIFEST_NAME_STRING, + .type = SND_SOC_TPLG_TUPLE_TYPE_STRING, + .offset = offsetof(struct avs_tplg, name), + .parse = parse_link_formatted_string, + }, + { + .token = AVS_TKN_MANIFEST_VERSION_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg, version), + .parse = avs_parse_word_token, + }, +}; + +static int avs_manifest(struct snd_soc_component *comp, int index, + struct snd_soc_tplg_manifest *manifest) +{ + struct snd_soc_tplg_vendor_array *tuples = manifest->priv.array; + struct avs_soc_component *acomp = to_avs_soc_component(comp); + size_t remaining = le32_to_cpu(manifest->priv.size); + u32 offset; + int ret; + + ret = avs_tplg_vendor_array_lookup(tuples, remaining, + AVS_TKN_MANIFEST_NUM_LIBRARIES_U32, &offset); + /* Manifest MUST begin with a header. */ + if (!ret && !offset) + ret = -EINVAL; + if (ret) { + dev_err(comp->dev, "incorrect manifest format: %d\n", ret); + return ret; + } + + /* Process header which precedes any of the dictionaries. */ + ret = avs_parse_tokens(comp, acomp->tplg, manifest_parsers, + ARRAY_SIZE(manifest_parsers), tuples, offset); + if (ret < 0) + return ret; + + remaining -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + + ret = avs_tplg_vendor_array_lookup(tuples, remaining, + AVS_TKN_MANIFEST_NUM_AFMTS_U32, &offset); + if (ret) { + dev_err(comp->dev, "audio formats lookup failed: %d\n", ret); + return ret; + } + + /* Libraries dictionary. */ + ret = avs_tplg_parse_libraries(comp, tuples, offset); + if (ret < 0) + return ret; + + remaining -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + + ret = avs_tplg_vendor_array_lookup(tuples, remaining, + AVS_TKN_MANIFEST_NUM_MODCFGS_BASE_U32, &offset); + if (ret) { + dev_err(comp->dev, "modcfgs_base lookup failed: %d\n", ret); + return ret; + } + + /* Audio formats dictionary. */ + ret = avs_tplg_parse_audio_formats(comp, tuples, offset); + if (ret < 0) + return ret; + + remaining -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + + ret = avs_tplg_vendor_array_lookup(tuples, remaining, + AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32, &offset); + if (ret) { + dev_err(comp->dev, "modcfgs_ext lookup failed: %d\n", ret); + return ret; + } + + /* Module configs-base dictionary. */ + ret = avs_tplg_parse_modcfgs_base(comp, tuples, offset); + if (ret < 0) + return ret; + + remaining -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + + ret = avs_tplg_vendor_array_lookup(tuples, remaining, + AVS_TKN_MANIFEST_NUM_PPLCFGS_U32, &offset); + if (ret) { + dev_err(comp->dev, "pplcfgs lookup failed: %d\n", ret); + return ret; + } + + /* Module configs-ext dictionary. */ + ret = avs_tplg_parse_modcfgs_ext(comp, tuples, offset); + if (ret < 0) + return ret; + + remaining -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + + ret = avs_tplg_vendor_array_lookup(tuples, remaining, + AVS_TKN_MANIFEST_NUM_BINDINGS_U32, &offset); + if (ret) { + dev_err(comp->dev, "bindings lookup failed: %d\n", ret); + return ret; + } + + /* Pipeline configs dictionary. */ + ret = avs_tplg_parse_pplcfgs(comp, tuples, offset); + if (ret < 0) + return ret; + + remaining -= offset; + tuples = avs_tplg_vendor_array_at(tuples, offset); + + /* Bindings dictionary. */ + return avs_tplg_parse_bindings(comp, tuples, remaining); +} + +static struct snd_soc_tplg_ops avs_tplg_ops = { + .dapm_route_load = avs_route_load, + .widget_load = avs_widget_load, + .dai_load = avs_dai_load, + .link_load = avs_link_load, + .manifest = avs_manifest, +}; + +struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp) +{ + struct avs_tplg *tplg; + + tplg = devm_kzalloc(comp->card->dev, sizeof(*tplg), GFP_KERNEL); + if (!tplg) + return NULL; + + tplg->comp = comp; + INIT_LIST_HEAD(&tplg->path_tmpl_list); + + return tplg; +} + +int avs_load_topology(struct snd_soc_component *comp, const char *filename) +{ + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, filename, comp->dev); + if (ret < 0) { + dev_err(comp->dev, "request topology \"%s\" failed: %d\n", filename, ret); + return ret; + } + + ret = snd_soc_tplg_component_load(comp, &avs_tplg_ops, fw); + if (ret < 0) + dev_err(comp->dev, "load topology \"%s\" failed: %d\n", filename, ret); + + release_firmware(fw); + return ret; +} + +int avs_remove_topology(struct snd_soc_component *comp) +{ + snd_soc_tplg_component_remove(comp); + + return 0; +} diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h new file mode 100644 index 000000000000..68e5f6312353 --- /dev/null +++ b/sound/soc/intel/avs/topology.h @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2021 Intel Corporation. All rights reserved. + * + * Authors: Cezary Rojewski <cezary.rojewski@intel.com> + * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> + */ + +#ifndef __SOUND_SOC_INTEL_AVS_TPLG_H +#define __SOUND_SOC_INTEL_AVS_TPLG_H + +#include <linux/list.h> +#include "messages.h" + +#define INVALID_OBJECT_ID UINT_MAX + +struct snd_soc_component; + +struct avs_tplg { + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + u32 version; + struct snd_soc_component *comp; + + struct avs_tplg_library *libs; + u32 num_libs; + struct avs_audio_format *fmts; + u32 num_fmts; + struct avs_tplg_modcfg_base *modcfgs_base; + u32 num_modcfgs_base; + struct avs_tplg_modcfg_ext *modcfgs_ext; + u32 num_modcfgs_ext; + struct avs_tplg_pplcfg *pplcfgs; + u32 num_pplcfgs; + struct avs_tplg_binding *bindings; + u32 num_bindings; + + struct list_head path_tmpl_list; +}; + +struct avs_tplg_library { + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; +}; + +/* Matches header of struct avs_mod_cfg_base. */ +struct avs_tplg_modcfg_base { + u32 cpc; + u32 ibs; + u32 obs; + u32 is_pages; +}; + +struct avs_tplg_pin_format { + u32 pin_index; + u32 iobs; + struct avs_audio_format *fmt; +}; + +struct avs_tplg_modcfg_ext { + guid_t type; + + union { + struct { + u16 num_input_pins; + u16 num_output_pins; + struct avs_tplg_pin_format *pin_fmts; + } generic; + struct { + struct avs_audio_format *out_fmt; + struct avs_audio_format *blob_fmt; /* optional override */ + u32 feature_mask; + union avs_virtual_index vindex; + u32 dma_type; + u32 dma_buffer_size; + u32 config_length; + /* config_data part of priv data */ + } copier; + struct { + u32 out_channel_config; + u32 coefficients_select; + s32 coefficients[AVS_CHANNELS_MAX]; + u32 channel_map; + } updown_mix; + struct { + u32 out_freq; + } src; + struct { + u32 out_freq; + u8 mode; + u8 disable_jitter_buffer; + } asrc; + struct { + u32 cpc_lp_mode; + } wov; + struct { + struct avs_audio_format *ref_fmt; + struct avs_audio_format *out_fmt; + u32 cpc_lp_mode; + } aec; + struct { + struct avs_audio_format *ref_fmt; + struct avs_audio_format *out_fmt; + } mux; + struct { + struct avs_audio_format *out_fmt; + } micsel; + }; +}; + +/* Specifies path behaviour during PCM ->trigger(START) command. */ +enum avs_tplg_trigger { + AVS_TPLG_TRIGGER_AUTO = 0, +}; + +struct avs_tplg_pplcfg { + u16 req_size; + u8 priority; + bool lp; + u16 attributes; + enum avs_tplg_trigger trigger; +}; + +struct avs_tplg_binding { + char target_tplg_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + u32 target_path_tmpl_id; + u32 target_ppl_id; + u32 target_mod_id; + u8 target_mod_pin; + u32 mod_id; + u8 mod_pin; + u8 is_sink; +}; + +struct avs_tplg_path_template_id { + u32 id; + char tplg_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; +}; + +struct avs_tplg_path_template { + u32 id; + + struct list_head path_list; + + struct avs_tplg *owner; + /* Driver path templates management. */ + struct list_head node; +}; + +struct avs_tplg_path { + u32 id; + + /* Path format requirements. */ + struct avs_audio_format *fe_fmt; + struct avs_audio_format *be_fmt; + + struct list_head ppl_list; + + struct avs_tplg_path_template *owner; + /* Path template path-variants management. */ + struct list_head node; +}; + +struct avs_tplg_pipeline { + u32 id; + + struct avs_tplg_pplcfg *cfg; + struct avs_tplg_binding **bindings; + u32 num_bindings; + struct list_head mod_list; + + struct avs_tplg_path *owner; + /* Path pipelines management. */ + struct list_head node; +}; + +struct avs_tplg_module { + u32 id; + + struct avs_tplg_modcfg_base *cfg_base; + struct avs_audio_format *in_fmt; + u8 core_id; + u8 domain; + struct avs_tplg_modcfg_ext *cfg_ext; + + struct avs_tplg_pipeline *owner; + /* Pipeline modules management. */ + struct list_head node; +}; + +struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp); + +int avs_load_topology(struct snd_soc_component *comp, const char *filename); +int avs_remove_topology(struct snd_soc_component *comp); + +#endif diff --git a/sound/soc/intel/avs/trace.c b/sound/soc/intel/avs/trace.c new file mode 100644 index 000000000000..fcb7cfc823d6 --- /dev/null +++ b/sound/soc/intel/avs/trace.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/types.h> + +#define CREATE_TRACE_POINTS +#include "trace.h" + +#define BYTES_PER_LINE 16 +#define MAX_CHUNK_SIZE ((PAGE_SIZE - 150) /* Place for trace header */ \ + / (2 * BYTES_PER_LINE + 4) /* chars per line */ \ + * BYTES_PER_LINE) + +void trace_avs_msg_payload(const void *data, size_t size) +{ + size_t remaining = size; + size_t offset = 0; + + while (remaining > 0) { + u32 chunk; + + chunk = min(remaining, (size_t)MAX_CHUNK_SIZE); + trace_avs_ipc_msg_payload(data, chunk, offset, size); + + remaining -= chunk; + offset += chunk; + } +} diff --git a/sound/soc/intel/avs/trace.h b/sound/soc/intel/avs/trace.h new file mode 100644 index 000000000000..855b06bb14b0 --- /dev/null +++ b/sound/soc/intel/avs/trace.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM intel_avs + +#if !defined(_TRACE_INTEL_AVS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_INTEL_AVS_H + +#include <linux/types.h> +#include <linux/tracepoint.h> + +TRACE_EVENT(avs_dsp_core_op, + + TP_PROTO(unsigned int reg, unsigned int mask, const char *op, bool flag), + + TP_ARGS(reg, mask, op, flag), + + TP_STRUCT__entry( + __field(unsigned int, reg ) + __field(unsigned int, mask ) + __string(op, op ) + __field(bool, flag ) + ), + + TP_fast_assign( + __entry->reg = reg; + __entry->mask = mask; + __assign_str(op, op); + __entry->flag = flag; + ), + + TP_printk("%s: %d, core mask: 0x%X, prev state: 0x%08X", + __get_str(op), __entry->flag, __entry->mask, __entry->reg) +); + +#ifndef __TRACE_INTEL_AVS_TRACE_HELPER +#define __TRACE_INTEL_AVS_TRACE_HELPER + +void trace_avs_msg_payload(const void *data, size_t size); + +#define trace_avs_request(msg, fwregs) \ +({ \ + trace_avs_ipc_request_msg((msg)->header, fwregs); \ + trace_avs_msg_payload((msg)->data, (msg)->size); \ +}) + +#define trace_avs_reply(msg, fwregs) \ +({ \ + trace_avs_ipc_reply_msg((msg)->header, fwregs); \ + trace_avs_msg_payload((msg)->data, (msg)->size); \ +}) + +#define trace_avs_notify(msg, fwregs) \ +({ \ + trace_avs_ipc_notify_msg((msg)->header, fwregs); \ + trace_avs_msg_payload((msg)->data, (msg)->size); \ +}) +#endif + +DECLARE_EVENT_CLASS(avs_ipc_msg_hdr, + + TP_PROTO(u64 header, u64 fwregs), + + TP_ARGS(header, fwregs), + + TP_STRUCT__entry( + __field(u64, header) + __field(u64, fwregs) + ), + + TP_fast_assign( + __entry->header = header; + __entry->fwregs = fwregs; + ), + + TP_printk("primary: 0x%08X, extension: 0x%08X,\n" + "fwstatus: 0x%08X, fwerror: 0x%08X", + lower_32_bits(__entry->header), upper_32_bits(__entry->header), + lower_32_bits(__entry->fwregs), upper_32_bits(__entry->fwregs)) +); + +DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_request_msg, + TP_PROTO(u64 header, u64 fwregs), + TP_ARGS(header, fwregs) +); + +DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_reply_msg, + TP_PROTO(u64 header, u64 fwregs), + TP_ARGS(header, fwregs) +); + +DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_notify_msg, + TP_PROTO(u64 header, u64 fwregs), + TP_ARGS(header, fwregs) +); + +TRACE_EVENT_CONDITION(avs_ipc_msg_payload, + + TP_PROTO(const u8 *data, size_t size, size_t offset, size_t total), + + TP_ARGS(data, size, offset, total), + + TP_CONDITION(data && size), + + TP_STRUCT__entry( + __dynamic_array(u8, buf, size ) + __field(size_t, offset ) + __field(size_t, pos ) + __field(size_t, total ) + ), + + TP_fast_assign( + memcpy(__get_dynamic_array(buf), data + offset, size); + __entry->offset = offset; + __entry->pos = offset + size; + __entry->total = total; + ), + + TP_printk("range %zu-%zu out of %zu bytes%s", + __entry->offset, __entry->pos, __entry->total, + __print_hex_dump("", DUMP_PREFIX_NONE, 16, 4, + __get_dynamic_array(buf), + __get_dynamic_array_len(buf), false)) +); + +TRACE_EVENT(avs_d0ix, + + TP_PROTO(const char *op, bool proceed, u64 header), + + TP_ARGS(op, proceed, header), + + TP_STRUCT__entry( + __string(op, op ) + __field(bool, proceed ) + __field(u64, header ) + ), + + TP_fast_assign( + __assign_str(op, op); + __entry->proceed = proceed; + __entry->header = header; + ), + + TP_printk("%s%s for request: 0x%08X 0x%08X", + __entry->proceed ? "" : "ignore ", __get_str(op), + lower_32_bits(__entry->header), upper_32_bits(__entry->header)) +); + +#endif /* _TRACE_INTEL_AVS_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE trace +#include <trace/define_trace.h> diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c index 6473e3ae4c6e..13611dee9787 100644 --- a/sound/soc/intel/avs/utils.c +++ b/sound/soc/intel/avs/utils.c @@ -7,6 +7,7 @@ // #include <linux/firmware.h> +#include <linux/kfifo.h> #include <linux/slab.h> #include "avs.h" #include "messages.h" @@ -299,3 +300,25 @@ void avs_release_firmwares(struct avs_dev *adev) kfree(entry); } } + +unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len, + spinlock_t *lock) +{ + struct __kfifo *__fifo = &fifo->kfifo; + unsigned long flags; + unsigned int l, off; + + spin_lock_irqsave(lock, flags); + len = min(len, kfifo_avail(fifo)); + off = __fifo->in & __fifo->mask; + l = min(len, kfifo_size(fifo) - off); + + memcpy_fromio(__fifo->data + off, src, l); + memcpy_fromio(__fifo->data, src + l, len - l); + /* Make sure data copied from SRAM is visible to all CPUs. */ + smp_mb(); + __fifo->in += len; + spin_unlock_irqrestore(lock, flags); + + return len; +} diff --git a/sound/soc/intel/boards/bdw-rt5650.c b/sound/soc/intel/boards/bdw-rt5650.c index bc0eab1c304a..aae857fdcdb8 100644 --- a/sound/soc/intel/boards/bdw-rt5650.c +++ b/sound/soc/intel/boards/bdw-rt5650.c @@ -192,15 +192,15 @@ static int bdw_rt5650_init(struct snd_soc_pcm_runtime *rtd) } /* Create and initialize headphone jack */ - if (snd_soc_card_jack_new(rtd->card, "Headphone Jack", + if (snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, &headphone_jack, &headphone_jack_pin, 1)) { dev_err(component->dev, "Can't create headphone jack\n"); } /* Create and initialize mic jack */ - if (snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE, - &mic_jack, &mic_jack_pin, 1)) { + if (snd_soc_card_jack_new_pins(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, &mic_jack, &mic_jack_pin, 1)) { dev_err(component->dev, "Can't create mic jack\n"); } diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index 071557fada29..d0ecbba2febe 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -256,7 +256,7 @@ static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd) } /* Create and initialize headphone jack */ - if (!snd_soc_card_jack_new(rtd->card, "Headphone Jack", + if (!snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, &headphone_jack, &headphone_jack_pin, 1)) { headphone_jack_gpio.gpiod_dev = component->dev; @@ -268,7 +268,7 @@ static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd) } /* Create and initialize mic jack */ - if (!snd_soc_card_jack_new(rtd->card, "Mic Jack", + if (!snd_soc_card_jack_new_pins(rtd->card, "Mic Jack", SND_JACK_MICROPHONE, &mic_jack, &mic_jack_pin, 1)) { mic_jack_gpio.gpiod_dev = component->dev; diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index d37c74fd1a3c..c30a9dca6801 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -69,7 +69,7 @@ static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret = 0; - ret = snd_soc_card_jack_new(rtd->card, "Headset", + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset, broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins)); if (ret) diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 9bc7b88e346b..d98376da425a 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -234,7 +234,7 @@ static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT, - &broxton_headset, NULL, 0); + &broxton_headset); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); return ret; @@ -720,8 +720,7 @@ static int bxt_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &broxton_hdmi[i], - NULL, 0); + SND_JACK_AVOUT, &broxton_hdmi[i]); if (err) return err; diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index 05e833076499..75995d17597d 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -168,7 +168,7 @@ static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret = 0; - ret = snd_soc_card_jack_new(rtd->card, "Headset", + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0, &broxton_headset, broxton_headset_pins, ARRAY_SIZE(broxton_headset_pins)); @@ -544,8 +544,7 @@ static int bxt_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &broxton_hdmi[i], - NULL, 0); + SND_JACK_AVOUT, &broxton_hdmi[i]); if (err) return err; diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c index 96d3201efbbd..0eed68a11f7e 100644 --- a/sound/soc/intel/boards/bytcht_cx2072x.c +++ b/sound/soc/intel/boards/bytcht_cx2072x.c @@ -87,11 +87,11 @@ static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_card_jack_new(card, "Headset", - SND_JACK_HEADSET | SND_JACK_BTN_0, - &byt_cht_cx2072x_headset, - byt_cht_cx2072x_headset_pins, - ARRAY_SIZE(byt_cht_cx2072x_headset_pins)); + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &byt_cht_cx2072x_headset, + byt_cht_cx2072x_headset_pins, + ARRAY_SIZE(byt_cht_cx2072x_headset_pins)); if (ret) return ret; diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index e18371b5a771..a08507783e44 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -219,10 +219,10 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) return ret; } - ret = snd_soc_card_jack_new(card, "Headset", - SND_JACK_HEADSET | SND_JACK_BTN_0, - &priv->jack, byt_cht_es8316_jack_pins, - ARRAY_SIZE(byt_cht_es8316_jack_pins)); + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &priv->jack, byt_cht_es8316_jack_pins, + ARRAY_SIZE(byt_cht_es8316_jack_pins)); if (ret) { dev_err(card->dev, "jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index d76a505052fb..7b948a219177 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -773,6 +773,18 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_OVCD_SF_0P75 | BYT_RT5640_MCLK_EN), }, + { /* HP Pro Tablet 408 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pro Tablet 408"), + }, + .driver_data = (void *)(BYT_RT5640_DMIC1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_1500UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, { /* HP Stream 7 */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), @@ -1300,10 +1312,10 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) } if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) { - ret = snd_soc_card_jack_new(card, "Headset", - SND_JACK_HEADSET | SND_JACK_BTN_0, - &priv->jack, rt5640_pins, - ARRAY_SIZE(rt5640_pins)); + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &priv->jack, rt5640_pins, + ARRAY_SIZE(rt5640_pins)); if (ret) { dev_err(card->dev, "Jack creation failed %d\n", ret); return ret; @@ -1321,17 +1333,17 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) } if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2) { - ret = snd_soc_card_jack_new(card, "Headset", - SND_JACK_HEADSET, - &priv->jack, rt5640_pins, - ARRAY_SIZE(rt5640_pins)); + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET, + &priv->jack, rt5640_pins, + ARRAY_SIZE(rt5640_pins)); if (ret) return ret; - ret = snd_soc_card_jack_new(card, "Headset 2", - SND_JACK_HEADSET, - &priv->jack2, rt5640_pins2, - ARRAY_SIZE(rt5640_pins2)); + ret = snd_soc_card_jack_new_pins(card, "Headset 2", + SND_JACK_HEADSET, + &priv->jack2, rt5640_pins2, + ARRAY_SIZE(rt5640_pins2)); if (ret) return ret; diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 39348d2b242f..d467fcaa48ea 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -652,9 +652,10 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) report = SND_JACK_HEADSET; if (report) { - ret = snd_soc_card_jack_new(runtime->card, "Headset", - report, &priv->jack, bytcr_jack_pins, - ARRAY_SIZE(bytcr_jack_pins)); + ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", + report, &priv->jack, + bytcr_jack_pins, + ARRAY_SIZE(bytcr_jack_pins)); if (ret) { dev_err(runtime->dev, "jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c index 8d8e96e3cd2d..00384c6fbcaa 100644 --- a/sound/soc/intel/boards/bytcr_wm5102.c +++ b/sound/soc/intel/boards/bytcr_wm5102.c @@ -226,9 +226,9 @@ static int byt_wm5102_init(struct snd_soc_pcm_runtime *runtime) jack_type = ARIZONA_JACK_MASK | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3; - ret = snd_soc_card_jack_new(card, "Headset", jack_type, - &priv->jack, byt_wm5102_pins, - ARRAY_SIZE(byt_wm5102_pins)); + ret = snd_soc_card_jack_new_pins(card, "Headset", jack_type, + &priv->jack, byt_wm5102_pins, + ARRAY_SIZE(byt_wm5102_pins)); if (ret) { dev_err(card->dev, "Error creating jack: %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index b3d7a0725ef2..a5160f27adea 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -201,9 +201,10 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE; - ret = snd_soc_card_jack_new(runtime->card, "Headset Jack", - jack_type, jack, - hs_jack_pins, ARRAY_SIZE(hs_jack_pins)); + ret = snd_soc_card_jack_new_pins(runtime->card, "Headset Jack", + jack_type, jack, + hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (ret) { dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret); return ret; @@ -306,8 +307,7 @@ static int cht_max98090_headset_init(struct snd_soc_component *component) SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3; - ret = snd_soc_card_jack_new(card, "Headset Jack", jack_type, - jack, NULL, 0); + ret = snd_soc_card_jack_new(card, "Headset Jack", jack_type, jack); if (ret) { dev_err(card->dev, "Headset Jack creation failed %d\n", ret); return ret; @@ -538,7 +538,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) const char *platform_name; bool sof_parent; - drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); if (!drv) return -ENOMEM; @@ -559,8 +559,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev) } /* override platform name, if required */ - snd_soc_card_cht.dev = &pdev->dev; - mach = pdev->dev.platform_data; + snd_soc_card_cht.dev = dev; + mach = dev->platform_data; platform_name = mach->mach_params.platform; ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht, @@ -576,9 +576,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev) else mclk_name = "pmc_plt_clk_3"; - drv->mclk = devm_clk_get(&pdev->dev, mclk_name); + drv->mclk = devm_clk_get(dev, mclk_name); if (IS_ERR(drv->mclk)) { - dev_err(&pdev->dev, + dev_err(dev, "Failed to get MCLK from %s: %ld\n", mclk_name, PTR_ERR(drv->mclk)); return PTR_ERR(drv->mclk); @@ -594,12 +594,12 @@ static int snd_cht_mc_probe(struct platform_device *pdev) if (drv->quirks & QUIRK_PMC_PLT_CLK_0) { ret_val = clk_prepare_enable(drv->mclk); if (ret_val < 0) { - dev_err(&pdev->dev, "MCLK enable error: %d\n", ret_val); + dev_err(dev, "MCLK enable error: %d\n", ret_val); return ret_val; } } - sof_parent = snd_soc_acpi_sof_parent(&pdev->dev); + sof_parent = snd_soc_acpi_sof_parent(dev); /* set card and driver name */ if (sof_parent) { @@ -614,9 +614,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev) if (sof_parent) dev->driver->pm = &snd_soc_pm_ops; - ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); + ret_val = devm_snd_soc_register_card(dev, &snd_soc_card_cht); if (ret_val) { - dev_err(&pdev->dev, + dev_err(dev, "snd_soc_register_card failed %d\n", ret_val); return ret_val; } diff --git a/sound/soc/intel/boards/cht_bsw_nau8824.c b/sound/soc/intel/boards/cht_bsw_nau8824.c index da6c659de266..4c1d83b317c7 100644 --- a/sound/soc/intel/boards/cht_bsw_nau8824.c +++ b/sound/soc/intel/boards/cht_bsw_nau8824.c @@ -108,8 +108,8 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) */ jack_type = SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3; - ret = snd_soc_card_jack_new(runtime->card, "Headset", jack_type, jack, - cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins)); + ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", jack_type, + jack, cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins)); if (ret) { dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret); diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index c21561c6a464..45c301ea5e00 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -302,9 +302,9 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) else jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE; - ret = snd_soc_card_jack_new(runtime->card, "Headset", - jack_type, &ctx->jack, - cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins)); + ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", jack_type, + &ctx->jack, cht_bsw_jack_pins, + ARRAY_SIZE(cht_bsw_jack_pins)); if (ret) { dev_err(runtime->dev, "Headset jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 9882aeb24d33..c80324f34b1b 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -221,12 +221,12 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; - ret = snd_soc_card_jack_new(runtime->card, "Headset", - SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1 | SND_JACK_BTN_2, - &ctx->headset, - cht_bsw_headset_pins, - ARRAY_SIZE(cht_bsw_headset_pins)); + ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2, + &ctx->headset, + cht_bsw_headset_pins, + ARRAY_SIZE(cht_bsw_headset_pins)); if (ret) return ret; diff --git a/sound/soc/intel/boards/cml_rt1011_rt5682.c b/sound/soc/intel/boards/cml_rt1011_rt5682.c index 27615acddacd..a99f74a15b5f 100644 --- a/sound/soc/intel/boards/cml_rt1011_rt5682.c +++ b/sound/soc/intel/boards/cml_rt1011_rt5682.c @@ -141,7 +141,7 @@ static int cml_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &ctx->headset, NULL, 0); + &ctx->headset); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); return ret; @@ -338,8 +338,7 @@ static int sof_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); ret = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &hdmi_jack[i], - NULL, 0); + SND_JACK_AVOUT, &hdmi_jack[i]); if (ret) return ret; diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c index e4bfb0fe5f12..170164baae7d 100644 --- a/sound/soc/intel/boards/glk_rt5682_max98357a.c +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -176,7 +176,7 @@ static int geminilake_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT, - &ctx->geminilake_headset, NULL, 0); + &ctx->geminilake_headset); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); return ret; @@ -571,8 +571,7 @@ static int glk_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &geminilake_hdmi[i], - NULL, 0); + SND_JACK_AVOUT, &geminilake_hdmi[i]); if (err) return err; diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c index a4bdf634e9b9..ceabed85e9da 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98357a.c +++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c @@ -182,7 +182,7 @@ static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) ret = snd_soc_card_jack_new(kabylake_audio_card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT, - &ctx->kabylake_headset, NULL, 0); + &ctx->kabylake_headset); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); return ret; @@ -587,8 +587,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &skylake_hdmi[i], - NULL, 0); + SND_JACK_AVOUT, &skylake_hdmi[i]); if (err) return err; diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index 620a9fbcb08f..703ccff634b0 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -357,7 +357,7 @@ static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) ret = snd_soc_card_jack_new(kabylake_audio_card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT, - &ctx->kabylake_headset, NULL, 0); + &ctx->kabylake_headset); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); return ret; @@ -965,8 +965,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &kabylake_hdmi[i], - NULL, 0); + SND_JACK_AVOUT, &kabylake_hdmi[i]); if (err) return err; diff --git a/sound/soc/intel/boards/kbl_rt5660.c b/sound/soc/intel/boards/kbl_rt5660.c index 1cb56ec363e8..2c7a547f63c9 100644 --- a/sound/soc/intel/boards/kbl_rt5660.c +++ b/sound/soc/intel/boards/kbl_rt5660.c @@ -173,9 +173,9 @@ static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd) } /* Create and initialize headphone jack, this jack is not mandatory, don't return if fails */ - ret = snd_soc_card_jack_new(rtd->card, "Lineout Jack", - SND_JACK_LINEOUT, &lineout_jack, - &lineout_jack_pin, 1); + ret = snd_soc_card_jack_new_pins(rtd->card, "Lineout Jack", + SND_JACK_LINEOUT, &lineout_jack, + &lineout_jack_pin, 1); if (ret) dev_warn(component->dev, "Can't create Lineout jack\n"); else { @@ -187,9 +187,9 @@ static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd) } /* Create and initialize mic jack, this jack is not mandatory, don't return if fails */ - ret = snd_soc_card_jack_new(rtd->card, "Mic Jack", - SND_JACK_MICROPHONE, &mic_jack, - &mic_jack_pin, 1); + ret = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, &mic_jack, + &mic_jack_pin, 1); if (ret) dev_warn(component->dev, "Can't create mic jack\n"); else { @@ -485,8 +485,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &skylake_hdmi[i], - NULL, 0); + SND_JACK_AVOUT, &skylake_hdmi[i]); if (err) return err; diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index f24e0ce5d49f..8d37b2676a81 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -273,8 +273,8 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) */ ret = snd_soc_card_jack_new(kabylake_audio_card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, &ctx->kabylake_headset, - NULL, 0); + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &ctx->kabylake_headset); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); return ret; @@ -919,8 +919,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &skylake_hdmi[i], - NULL, 0); + SND_JACK_AVOUT, &skylake_hdmi[i]); if (err) return err; diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index 6874e981c8df..564c70a0fbc8 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -230,8 +230,8 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) */ ret = snd_soc_card_jack_new(&kabylake_audio_card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, &ctx->kabylake_headset, - NULL, 0); + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &ctx->kabylake_headset); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); return ret; @@ -743,8 +743,7 @@ static int kabylake_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP,pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &ctx->kabylake_hdmi[i], - NULL, 0); + SND_JACK_AVOUT, &ctx->kabylake_hdmi[i]); if (err) return err; diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.c b/sound/soc/intel/boards/skl_hda_dsp_common.c index 07bfb2e64b3b..e9cefa4ae56d 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_common.c +++ b/sound/soc/intel/boards/skl_hda_dsp_common.c @@ -150,17 +150,11 @@ int skl_hda_hdmi_jack_init(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &pcm->hdmi_jack, - NULL, 0); + SND_JACK_AVOUT, &pcm->hdmi_jack); if (err) return err; - err = snd_jack_add_new_kctl(pcm->hdmi_jack.jack, - jack_name, SND_JACK_AVOUT); - if (err) - dev_warn(component->dev, "failed creating Jack kctl\n"); - err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, &pcm->hdmi_jack); if (err < 0) diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 7297eb05613c..8e2d03e36079 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -165,8 +165,7 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) */ ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset, - NULL, 0); + SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); return ret; @@ -610,8 +609,7 @@ static int skylake_card_late_probe(struct snd_soc_card *card) "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, SND_JACK_AVOUT, - &skylake_hdmi[i], - NULL, 0); + &skylake_hdmi[i]); if (err) return err; diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 68efde1633b3..501f0bbfc404 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -184,8 +184,7 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) */ ret = snd_soc_card_jack_new(&skylake_audio_card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset, - NULL, 0); + SND_JACK_BTN_2 | SND_JACK_BTN_3, &skylake_headset); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); return ret; @@ -651,8 +650,7 @@ static int skylake_card_late_probe(struct snd_soc_card *card) "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, SND_JACK_AVOUT, - &skylake_hdmi[i], - NULL, 0); + &skylake_hdmi[i]); if (err) return err; diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index eca4a78668af..e9f9520dcea4 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -125,7 +125,7 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; int ret; - ret = snd_soc_card_jack_new(rtd->card, "Headset", + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0, &skylake_headset, skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins)); @@ -491,8 +491,7 @@ static int skylake_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &skylake_hdmi[i], - NULL, 0); + SND_JACK_AVOUT, &skylake_hdmi[i]); if (err) return err; diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c index ce78c1879887..6a979c333bc5 100644 --- a/sound/soc/intel/boards/sof_cs42l42.c +++ b/sound/soc/intel/boards/sof_cs42l42.c @@ -102,7 +102,7 @@ static int sof_cs42l42_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - jack, NULL, 0); + jack); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); return ret; @@ -186,8 +186,7 @@ static int sof_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &pcm->hdmi_jack, - NULL, 0); + SND_JACK_AVOUT, &pcm->hdmi_jack); if (err) return err; diff --git a/sound/soc/intel/boards/sof_da7219_max98373.c b/sound/soc/intel/boards/sof_da7219_max98373.c index b7b3b0bf994a..a83f30b687cf 100644 --- a/sound/soc/intel/boards/sof_da7219_max98373.c +++ b/sound/soc/intel/boards/sof_da7219_max98373.c @@ -160,7 +160,7 @@ static int da7219_codec_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT, - &headset, NULL, 0); + &headset); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index 9d617831dd20..23d03e0f7759 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -245,10 +245,10 @@ static int sof_es8316_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; - ret = snd_soc_card_jack_new(card, "Headset", - SND_JACK_HEADSET | SND_JACK_BTN_0, - &priv->jack, sof_es8316_jack_pins, - ARRAY_SIZE(sof_es8316_jack_pins)); + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &priv->jack, sof_es8316_jack_pins, + ARRAY_SIZE(sof_es8316_jack_pins)); if (ret) { dev_err(card->dev, "jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c index 33de043b66c6..97dcd204a246 100644 --- a/sound/soc/intel/boards/sof_nau8825.c +++ b/sound/soc/intel/boards/sof_nau8825.c @@ -97,7 +97,7 @@ static int sof_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &ctx->sof_headset, NULL, 0); + &ctx->sof_headset); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); return ret; diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c index a2bcbeee0216..2ab568c1d40b 100644 --- a/sound/soc/intel/boards/sof_realtek_common.c +++ b/sound/soc/intel/boards/sof_realtek_common.c @@ -459,5 +459,44 @@ void sof_rt1308_dai_link(struct snd_soc_dai_link *link) } EXPORT_SYMBOL_NS(sof_rt1308_dai_link, SND_SOC_INTEL_SOF_REALTEK_COMMON); +/* + * 2-amp Configuration for RT1019 + */ + +static const struct snd_soc_dapm_route rt1019_dapm_routes[] = { + /* speaker */ + { "Left Spk", NULL, "Speaker" }, + { "Right Spk", NULL, "Speaker" }, +}; + +static struct snd_soc_dai_link_component rt1019_components[] = { + { + .name = RT1019_DEV0_NAME, + .dai_name = RT1019_CODEC_DAI, + }, +}; + +static int rt1019_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_add_routes(&card->dapm, rt1019_dapm_routes, + ARRAY_SIZE(rt1019_dapm_routes)); + if (ret) { + dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret); + return ret; + } + return ret; +} + +void sof_rt1019_dai_link(struct snd_soc_dai_link *link) +{ + link->codecs = rt1019_components; + link->num_codecs = ARRAY_SIZE(rt1019_components); + link->init = rt1019_init; +} +EXPORT_SYMBOL_NS(sof_rt1019_dai_link, SND_SOC_INTEL_SOF_REALTEK_COMMON); + MODULE_DESCRIPTION("ASoC Intel SOF Realtek helpers"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/boards/sof_realtek_common.h b/sound/soc/intel/boards/sof_realtek_common.h index e0a5518e8dd2..ec3eea633e04 100644 --- a/sound/soc/intel/boards/sof_realtek_common.h +++ b/sound/soc/intel/boards/sof_realtek_common.h @@ -39,4 +39,9 @@ void sof_rt1015_codec_conf(struct snd_soc_card *card); #define RT1308_DEV0_NAME "i2c-10EC1308:00" void sof_rt1308_dai_link(struct snd_soc_dai_link *link); +#define RT1019_CODEC_DAI "HiFi" +#define RT1019_DEV0_NAME "RTL1019:00" + +void sof_rt1019_dai_link(struct snd_soc_dai_link *link); + #endif /* __SOF_REALTEK_COMMON_H */ diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 7126fcb63d90..5d67a2c87a1d 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -60,6 +60,7 @@ #define SOF_RT5682S_HEADPHONE_CODEC_PRESENT BIT(23) #define SOF_MAX98390_SPEAKER_AMP_PRESENT BIT(24) #define SOF_MAX98390_TWEETER_SPEAKER_PRESENT BIT(25) +#define SOF_RT1019_SPEAKER_AMP_PRESENT BIT(26) /* Default: MCLK on, MCLK 19.2M, SSP0 */ @@ -298,7 +299,7 @@ static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &ctx->sof_headset, NULL, 0); + &ctx->sof_headset); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); return ret; @@ -454,8 +455,7 @@ static int sof_card_late_probe(struct snd_soc_card *card) snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &sof_hdmi[i], - NULL, 0); + SND_JACK_AVOUT, &sof_hdmi[i]); if (err) return err; @@ -734,6 +734,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, SOF_RT1015_SPEAKER_AMP_100FS) ? 100 : 64); } else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT) { sof_rt1015p_dai_link(&links[id]); + } else if (sof_rt5682_quirk & SOF_RT1019_SPEAKER_AMP_PRESENT) { + sof_rt1019_dai_link(&links[id]); } else if (sof_rt5682_quirk & SOF_MAX98373_SPEAKER_AMP_PRESENT) { links[id].codecs = max_98373_components; @@ -1071,6 +1073,16 @@ static const struct platform_device_id board_ids[] = { SOF_BT_OFFLOAD_SSP(2) | SOF_SSP_BT_OFFLOAD_PRESENT), }, + { + .name = "adl_rt1019_rt5682s", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_RT5682S_HEADPHONE_CODEC_PRESENT | + SOF_SPEAKER_AMP_PRESENT | + SOF_RT1019_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4)), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/boards/sof_sdw_rt5682.c b/sound/soc/intel/boards/sof_sdw_rt5682.c index ea55479609a8..3a9be8211586 100644 --- a/sound/soc/intel/boards/sof_sdw_rt5682.c +++ b/sound/soc/intel/boards/sof_sdw_rt5682.c @@ -82,13 +82,13 @@ static int rt5682_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3, - &ctx->sdw_headset, - rt5682_jack_pins, - ARRAY_SIZE(rt5682_jack_pins)); + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + rt5682_jack_pins, + ARRAY_SIZE(rt5682_jack_pins)); if (ret) { dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", ret); diff --git a/sound/soc/intel/boards/sof_sdw_rt700.c b/sound/soc/intel/boards/sof_sdw_rt700.c index bb9584c8f866..c93b1f5b9440 100644 --- a/sound/soc/intel/boards/sof_sdw_rt700.c +++ b/sound/soc/intel/boards/sof_sdw_rt700.c @@ -82,13 +82,13 @@ static int rt700_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3, - &ctx->sdw_headset, - rt700_jack_pins, - ARRAY_SIZE(rt700_jack_pins)); + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + rt700_jack_pins, + ARRAY_SIZE(rt700_jack_pins)); if (ret) { dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", ret); diff --git a/sound/soc/intel/boards/sof_sdw_rt711.c b/sound/soc/intel/boards/sof_sdw_rt711.c index c38b70c9fac3..49ff0871e9e7 100644 --- a/sound/soc/intel/boards/sof_sdw_rt711.c +++ b/sound/soc/intel/boards/sof_sdw_rt711.c @@ -106,13 +106,13 @@ static int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3, - &ctx->sdw_headset, - rt711_jack_pins, - ARRAY_SIZE(rt711_jack_pins)); + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + rt711_jack_pins, + ARRAY_SIZE(rt711_jack_pins)); if (ret) { dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", ret); diff --git a/sound/soc/intel/boards/sof_sdw_rt711_sdca.c b/sound/soc/intel/boards/sof_sdw_rt711_sdca.c index 4215ddc36419..b3fc32bacfa8 100644 --- a/sound/soc/intel/boards/sof_sdw_rt711_sdca.c +++ b/sound/soc/intel/boards/sof_sdw_rt711_sdca.c @@ -107,13 +107,13 @@ static int rt711_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3, - &ctx->sdw_headset, - rt711_sdca_jack_pins, - ARRAY_SIZE(rt711_sdca_jack_pins)); + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + rt711_sdca_jack_pins, + ARRAY_SIZE(rt711_sdca_jack_pins)); if (ret) { dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", ret); diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c index 88530e9de543..4a762e002ac7 100644 --- a/sound/soc/intel/boards/sof_ssp_amp.c +++ b/sound/soc/intel/boards/sof_ssp_amp.c @@ -9,6 +9,7 @@ #include <linux/acpi.h> #include <linux/delay.h> +#include <linux/dmi.h> #include <linux/module.h> #include <linux/platform_device.h> #include <sound/core.h> @@ -78,6 +79,16 @@ struct sof_card_private { bool idisp_codec; }; +static const struct dmi_system_id chromebook_platforms[] = { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {}, +}; + static const struct snd_soc_dapm_widget sof_ssp_amp_dapm_widgets[] = { SND_SOC_DAPM_MIC("SoC DMIC", NULL), }; @@ -94,7 +105,7 @@ static int sof_card_late_probe(struct snd_soc_card *card) char jack_name[NAME_SIZE]; struct sof_hdmi_pcm *pcm; int err; - int i = 0; + int i; if (!(sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT)) return 0; @@ -113,13 +124,13 @@ static int sof_card_late_probe(struct snd_soc_card *card) return hda_dsp_hdmi_build_controls(card, component); } + i = 0; list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { component = pcm->codec_dai->component; snprintf(jack_name, sizeof(jack_name), "HDMI/DP, pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_AVOUT, &pcm->sof_hdmi, - NULL, 0); + SND_JACK_AVOUT, &pcm->sof_hdmi); if (err) return err; @@ -247,6 +258,9 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, sof_rt1308_dai_link(&links[id]); } else if (sof_ssp_amp_quirk & SOF_CS35L41_SPEAKER_AMP_PRESENT) { cs35l41_set_dai_link(&links[id]); + + /* feedback from amplifier */ + links[id].dpcm_capture = 1; } links[id].platforms = platform_component; links[id].num_platforms = ARRAY_SIZE(platform_component); @@ -371,7 +385,7 @@ static int sof_ssp_amp_probe(struct platform_device *pdev) struct snd_soc_dai_link *dai_links; struct snd_soc_acpi_mach *mach; struct sof_card_private *ctx; - int dmic_be_num, hdmi_num = 0; + int dmic_be_num = 0, hdmi_num = 0; int ret, ssp_codec; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); @@ -383,7 +397,8 @@ static int sof_ssp_amp_probe(struct platform_device *pdev) mach = pdev->dev.platform_data; - dmic_be_num = mach->mach_params.dmic_num; + if (dmi_check_system(chromebook_platforms) || mach->mach_params.dmic_num > 0) + dmic_be_num = 2; ssp_codec = sof_ssp_amp_quirk & SOF_AMPLIFIER_SSP_MASK; diff --git a/sound/soc/intel/catpt/messages.h b/sound/soc/intel/catpt/messages.h index 978a20b3f471..c17e948d9f52 100644 --- a/sound/soc/intel/catpt/messages.h +++ b/sound/soc/intel/catpt/messages.h @@ -219,11 +219,9 @@ int catpt_ipc_free_stream(struct catpt_dev *cdev, u8 stream_hw_id); enum catpt_ssp_iface { CATPT_SSP_IFACE_0 = 0, CATPT_SSP_IFACE_1 = 1, - CATPT_SSP_IFACE_LAST = CATPT_SSP_IFACE_1, + CATPT_SSP_COUNT, }; -#define CATPT_SSP_COUNT (CATPT_SSP_IFACE_LAST + 1) - enum catpt_mclk_frequency { CATPT_MCLK_OFF = 0, CATPT_MCLK_FREQ_6_MHZ = 1, diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index 8bfe7070b84a..e6e52c7b6803 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -456,6 +456,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .sof_tplg_filename = "sof-adl-mx98360a-nau8825.tplg", }, { + .id = "RTL5682", + .drv_name = "adl_rt1019_rt5682s", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_rt1019p_amp, + .sof_tplg_filename = "sof-adl-rt1019-rt5682.tplg", + }, + { .id = "10508825", .drv_name = "sof_nau8825", .sof_tplg_filename = "sof-adl-nau8825.tplg", diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 0d154350f180..9e5ce1a82639 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -179,6 +179,7 @@ config SND_SOC_MT8192_MT6359_RT1015_RT5682 select SND_SOC_RT1015 select SND_SOC_RT1015P select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S select SND_SOC_DMIC help This adds ASoC driver for Mediatek MT8192 boards @@ -198,34 +199,20 @@ config SND_SOC_MT8195 Select Y if you have such device. If unsure select "N". -config SND_SOC_MT8195_MT6359_RT1019_RT5682 - tristate "ASoC Audio driver for MT8195 with MT6359 RT1019 RT5682 codec" - depends on I2C && GPIOLIB - depends on SND_SOC_MT8195 && MTK_PMIC_WRAP - select SND_SOC_MT6359 - select SND_SOC_RT1015P - select SND_SOC_RT5682_I2C - select SND_SOC_RT5682S - select SND_SOC_DMIC - select SND_SOC_HDMI_CODEC - help - This adds ASoC driver for Mediatek MT8195 boards - with the MT6359 RT1019 RT5682 audio codec. - Select Y if you have such device. - If unsure select "N". - -config SND_SOC_MT8195_MT6359_RT1011_RT5682 - tristate "ASoC Audio driver for MT8195 with MT6359 RT1011 RT5682 codec" +config SND_SOC_MT8195_MT6359 + tristate "ASoC Audio driver for MT8195 with MT6359 and I2S codecs" depends on I2C && GPIOLIB depends on SND_SOC_MT8195 && MTK_PMIC_WRAP select SND_SOC_MT6359 select SND_SOC_RT1011 + select SND_SOC_RT1015P select SND_SOC_RT5682_I2C select SND_SOC_RT5682S + select SND_SOC_MAX98390 select SND_SOC_DMIC select SND_SOC_HDMI_CODEC help - This adds ASoC driver for Mediatek MT8195 boards - with the MT6359 RT1011 RT5682 audio codec. + This adds support for ASoC machine driver for Mediatek MT8195 + boards with the MT6359 and other I2S audio codecs. Select Y if you have such device. If unsure select "N". diff --git a/sound/soc/mediatek/mt2701/mt2701-wm8960.c b/sound/soc/mediatek/mt2701/mt2701-wm8960.c index f56de1b918bf..0cdf2ae36243 100644 --- a/sound/soc/mediatek/mt2701/mt2701-wm8960.c +++ b/sound/soc/mediatek/mt2701/mt2701-wm8960.c @@ -129,7 +129,8 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev) if (!codec_node) { dev_err(&pdev->dev, "Property 'audio-codec' missing or invalid\n"); - return -EINVAL; + ret = -EINVAL; + goto put_platform_node; } for_each_card_prelinks(card, i, dai_link) { if (dai_link->codecs->name) @@ -140,7 +141,7 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev) ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); if (ret) { dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); - return ret; + goto put_codec_node; } ret = devm_snd_soc_register_card(&pdev->dev, card); @@ -148,6 +149,10 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev) dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", __func__, ret); +put_codec_node: + of_node_put(codec_node); +put_platform_node: + of_node_put(platform_node); return ret; } diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c index 4cb90da89262..c2b0619b6158 100644 --- a/sound/soc/mediatek/mt8173/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -70,10 +70,10 @@ static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; /* enable jack detection */ - ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE, - &mt8173_max98090_jack, - mt8173_max98090_jack_pins, - ARRAY_SIZE(mt8173_max98090_jack_pins)); + ret = snd_soc_card_jack_new_pins(card, "Headphone", SND_JACK_HEADPHONE, + &mt8173_max98090_jack, + mt8173_max98090_jack_pins, + ARRAY_SIZE(mt8173_max98090_jack_pins)); if (ret) { dev_err(card->dev, "Can't create a new Jack %d\n", ret); return ret; @@ -167,7 +167,8 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev) if (!codec_node) { dev_err(&pdev->dev, "Property 'audio-codec' missing or invalid\n"); - return -EINVAL; + ret = -EINVAL; + goto put_platform_node; } for_each_card_prelinks(card, i, dai_link) { if (dai_link->codecs->name) @@ -179,6 +180,8 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); of_node_put(codec_node); + +put_platform_node: of_node_put(platform_node); return ret; } diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index b55122b99f07..12f40c81b101 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -86,7 +86,7 @@ static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime) SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &mt8173_rt5650_rt5514_jack, NULL, 0); + &mt8173_rt5650_rt5514_jack); if (ret) { dev_err(card->dev, "Can't new Headset Jack %d\n", ret); return ret; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index 5716d9299066..70bf312e855f 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -99,7 +99,7 @@ static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime) SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &mt8173_rt5650_rt5676_jack, NULL, 0); + &mt8173_rt5650_rt5676_jack); if (ret) { dev_err(card->dev, "Can't new Headset Jack %d\n", ret); return ret; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index fc164f4f95f8..d1c94acb4516 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -132,7 +132,7 @@ static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime) SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &mt8173_rt5650_jack, NULL, 0); + &mt8173_rt5650_jack); if (ret) { dev_err(card->dev, "Can't new Headset Jack %d\n", ret); return ret; @@ -149,7 +149,7 @@ static int mt8173_rt5650_hdmi_init(struct snd_soc_pcm_runtime *rtd) int ret; ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, - &mt8173_rt5650_hdmi_jack, NULL, 0); + &mt8173_rt5650_hdmi_jack); if (ret) return ret; diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index f090dee0c7a4..b33cc9a73ed1 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -364,7 +364,7 @@ static int mt8183_da7219_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd) int ret; ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, - &priv->hdmi_jack, NULL, 0); + &priv->hdmi_jack); if (ret) return ret; @@ -546,8 +546,7 @@ mt8183_da7219_max98357_headset_init(struct snd_soc_component *component) SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT, - &priv->headset_jack, - NULL, 0); + &priv->headset_jack); if (ret) return ret; diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index 889f9e4a96aa..ab157db78335 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -383,7 +383,7 @@ mt8183_mt6358_ts3a227_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd) int ret; ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, - &priv->hdmi_jack, NULL, 0); + &priv->hdmi_jack); if (ret) return ret; @@ -613,8 +613,7 @@ mt8183_mt6358_ts3a227_max98357_headset_init(struct snd_soc_component *component) SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &priv->headset_jack, - NULL, 0); + &priv->headset_jack); if (ret) return ret; diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index ee91569c0911..d0f9d66627b1 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -28,8 +28,13 @@ #define RT1015_DEV0_NAME "rt1015.1-0028" #define RT1015_DEV1_NAME "rt1015.1-0029" -#define RT5682_CODEC_DAI "rt5682-aif1" -#define RT5682_DEV0_NAME "rt5682.1-001a" +#define RT1015_RT5682_CARD_NAME "mt8192_mt6359_rt1015_rt5682" +#define RT1015P_RT5682_CARD_NAME "mt8192_mt6359_rt1015p_rt5682" +#define RT1015P_RT5682S_CARD_NAME "mt8192_mt6359_rt1015p_rt5682s" + +#define RT1015_RT5682_OF_NAME "mediatek,mt8192_mt6359_rt1015_rt5682" +#define RT1015P_RT5682_OF_NAME "mediatek,mt8192_mt6359_rt1015p_rt5682" +#define RT1015P_RT5682S_OF_NAME "mediatek,mt8192_mt6359_rt1015p_rt5682s" struct mt8192_mt6359_priv { struct snd_soc_jack headset_jack; @@ -71,8 +76,8 @@ static int mt8192_rt1015_i2s_hw_params(struct snd_pcm_substream *substream, return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs, SND_SOC_CLOCK_OUT); } -static int mt8192_rt5682_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int mt8192_rt5682x_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_card *card = rtd->card; @@ -121,8 +126,8 @@ static const struct snd_soc_ops mt8192_rt1015_i2s_ops = { .hw_params = mt8192_rt1015_i2s_hw_params, }; -static const struct snd_soc_ops mt8192_rt5682_i2s_ops = { - .hw_params = mt8192_rt5682_i2s_hw_params, +static const struct snd_soc_ops mt8192_rt5682x_i2s_ops = { + .hw_params = mt8192_rt5682x_i2s_hw_params, }; static int mt8192_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) @@ -316,7 +321,7 @@ static int mt8192_rt5682_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - jack, NULL, 0); + jack); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); return ret; @@ -338,7 +343,7 @@ static int mt8192_mt6359_hdmi_init(struct snd_soc_pcm_runtime *rtd) int ret; ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, - &priv->hdmi_jack, NULL, 0); + &priv->hdmi_jack); if (ret) { dev_err(rtd->dev, "HDMI Jack creation failed: %d\n", ret); return ret; @@ -604,17 +609,9 @@ SND_SOC_DAILINK_DEFS(i2s2, DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); -SND_SOC_DAILINK_DEFS(i2s3_rt1015, - DAILINK_COMP_ARRAY(COMP_CPU("I2S3")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT1015_DEV0_NAME, - RT1015_CODEC_DAI), - COMP_CODEC(RT1015_DEV1_NAME, - RT1015_CODEC_DAI)), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(i2s3_rt1015p, +SND_SOC_DAILINK_DEFS(i2s3, DAILINK_COMP_ARRAY(COMP_CPU("I2S3")), - DAILINK_COMP_ARRAY(COMP_CODEC("rt1015p", "HiFi")), + DAILINK_COMP_ARRAY(COMP_EMPTY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(i2s5, @@ -634,14 +631,12 @@ SND_SOC_DAILINK_DEFS(i2s7, SND_SOC_DAILINK_DEFS(i2s8, DAILINK_COMP_ARRAY(COMP_CPU("I2S8")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, - RT5682_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_EMPTY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(i2s9, DAILINK_COMP_ARRAY(COMP_CPU("I2S9")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, - RT5682_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_EMPTY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(connsys_i2s, @@ -929,6 +924,7 @@ static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = { .dpcm_playback = 1, .ignore_suspend = 1, .be_hw_params_fixup = mt8192_i2s_hw_params_fixup, + SND_SOC_DAILINK_REG(i2s3), }, { .name = "I2S5", @@ -962,7 +958,7 @@ static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = { .init = mt8192_rt5682_init, .be_hw_params_fixup = mt8192_i2s_hw_params_fixup, SND_SOC_DAILINK_REG(i2s8), - .ops = &mt8192_rt5682_i2s_ops, + .ops = &mt8192_rt5682x_i2s_ops, }, { .name = "I2S9", @@ -971,7 +967,7 @@ static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = { .ignore_suspend = 1, .be_hw_params_fixup = mt8192_i2s_hw_params_fixup, SND_SOC_DAILINK_REG(i2s9), - .ops = &mt8192_rt5682_i2s_ops, + .ops = &mt8192_rt5682x_i2s_ops, }, { .name = "CONNSYS_I2S", @@ -1051,7 +1047,7 @@ static struct snd_soc_codec_conf rt1015_amp_conf[] = { }; static struct snd_soc_card mt8192_mt6359_rt1015_rt5682_card = { - .name = "mt8192_mt6359_rt1015_rt5682", + .name = RT1015_RT5682_CARD_NAME, .owner = THIS_MODULE, .dai_link = mt8192_mt6359_dai_links, .num_links = ARRAY_SIZE(mt8192_mt6359_dai_links), @@ -1065,14 +1061,13 @@ static struct snd_soc_card mt8192_mt6359_rt1015_rt5682_card = { .num_configs = ARRAY_SIZE(rt1015_amp_conf), }; -static const struct snd_soc_dapm_widget -mt8192_mt6359_rt1015p_rt5682_widgets[] = { +static const struct snd_soc_dapm_widget mt8192_mt6359_rt1015p_rt5682x_widgets[] = { SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), }; -static const struct snd_soc_dapm_route mt8192_mt6359_rt1015p_rt5682_routes[] = { +static const struct snd_soc_dapm_route mt8192_mt6359_rt1015p_rt5682x_routes[] = { /* speaker */ { "Speakers", NULL, "Speaker" }, /* headset */ @@ -1081,74 +1076,107 @@ static const struct snd_soc_dapm_route mt8192_mt6359_rt1015p_rt5682_routes[] = { { "IN1P", NULL, "Headset Mic" }, }; -static const struct snd_kcontrol_new mt8192_mt6359_rt1015p_rt5682_controls[] = { +static const struct snd_kcontrol_new mt8192_mt6359_rt1015p_rt5682x_controls[] = { SOC_DAPM_PIN_SWITCH("Speakers"), SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), }; -static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682_card = { - .name = "mt8192_mt6359_rt1015p_rt5682", +static struct snd_soc_card mt8192_mt6359_rt1015p_rt5682x_card = { .owner = THIS_MODULE, .dai_link = mt8192_mt6359_dai_links, .num_links = ARRAY_SIZE(mt8192_mt6359_dai_links), - .controls = mt8192_mt6359_rt1015p_rt5682_controls, - .num_controls = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682_controls), - .dapm_widgets = mt8192_mt6359_rt1015p_rt5682_widgets, - .num_dapm_widgets = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682_widgets), - .dapm_routes = mt8192_mt6359_rt1015p_rt5682_routes, - .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682_routes), + .controls = mt8192_mt6359_rt1015p_rt5682x_controls, + .num_controls = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_controls), + .dapm_widgets = mt8192_mt6359_rt1015p_rt5682x_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_widgets), + .dapm_routes = mt8192_mt6359_rt1015p_rt5682x_routes, + .num_dapm_routes = ARRAY_SIZE(mt8192_mt6359_rt1015p_rt5682x_routes), }; +static int mt8192_mt6359_card_set_be_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node, + char *link_name) +{ + int ret; + + if (node && strcmp(link->name, link_name) == 0) { + ret = snd_soc_of_get_dai_link_codecs(card->dev, node, link); + if (ret < 0) { + dev_err_probe(card->dev, ret, "get dai link codecs fail\n"); + return ret; + } + } + + return 0; +} + static int mt8192_mt6359_dev_probe(struct platform_device *pdev) { struct snd_soc_card *card; - struct device_node *platform_node, *hdmi_codec; + struct device_node *platform_node, *hdmi_codec, *headset_codec, *speaker_codec; int ret, i; struct snd_soc_dai_link *dai_link; struct mt8192_mt6359_priv *priv; - platform_node = of_parse_phandle(pdev->dev.of_node, - "mediatek,platform", 0); - if (!platform_node) { - dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); + if (!card) return -EINVAL; + card->dev = &pdev->dev; + + if (of_device_is_compatible(pdev->dev.of_node, RT1015P_RT5682_OF_NAME)) + card->name = RT1015P_RT5682_CARD_NAME; + else if (of_device_is_compatible(pdev->dev.of_node, RT1015P_RT5682S_OF_NAME)) + card->name = RT1015P_RT5682S_CARD_NAME; + else + dev_dbg(&pdev->dev, "No need to set card name\n"); + + hdmi_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,hdmi-codec", 0); + if (!hdmi_codec) + dev_dbg(&pdev->dev, "The machine has no hdmi-codec\n"); + + platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0); + if (!platform_node) { + ret = -EINVAL; + dev_err_probe(&pdev->dev, ret, "Property 'platform' missing or invalid\n"); + goto err_platform_node; } - card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); - if (!card) { + speaker_codec = of_get_child_by_name(pdev->dev.of_node, "speaker-codecs"); + if (!speaker_codec) { ret = -EINVAL; - goto put_platform_node; + dev_err_probe(&pdev->dev, ret, "Property 'speaker-codecs' missing or invalid\n"); + goto err_speaker_codec; } - card->dev = &pdev->dev; - hdmi_codec = of_parse_phandle(pdev->dev.of_node, - "mediatek,hdmi-codec", 0); + headset_codec = of_get_child_by_name(pdev->dev.of_node, "headset-codec"); + if (!headset_codec) { + ret = -EINVAL; + dev_err_probe(&pdev->dev, ret, "Property 'headset-codec' missing or invalid\n"); + goto err_headset_codec; + } for_each_card_prelinks(card, i, dai_link) { - if (strcmp(dai_link->name, "I2S3") == 0) { - if (card == &mt8192_mt6359_rt1015_rt5682_card) { - dai_link->ops = &mt8192_rt1015_i2s_ops; - dai_link->cpus = i2s3_rt1015_cpus; - dai_link->num_cpus = - ARRAY_SIZE(i2s3_rt1015_cpus); - dai_link->codecs = i2s3_rt1015_codecs; - dai_link->num_codecs = - ARRAY_SIZE(i2s3_rt1015_codecs); - dai_link->platforms = i2s3_rt1015_platforms; - dai_link->num_platforms = - ARRAY_SIZE(i2s3_rt1015_platforms); - } else if (card == &mt8192_mt6359_rt1015p_rt5682_card) { - dai_link->cpus = i2s3_rt1015p_cpus; - dai_link->num_cpus = - ARRAY_SIZE(i2s3_rt1015p_cpus); - dai_link->codecs = i2s3_rt1015p_codecs; - dai_link->num_codecs = - ARRAY_SIZE(i2s3_rt1015p_codecs); - dai_link->platforms = i2s3_rt1015p_platforms; - dai_link->num_platforms = - ARRAY_SIZE(i2s3_rt1015p_platforms); - } + ret = mt8192_mt6359_card_set_be_link(card, dai_link, speaker_codec, "I2S3"); + if (ret) { + dev_err_probe(&pdev->dev, ret, "%s set speaker_codec fail\n", + dai_link->name); + goto err_probe; + } + + ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S8"); + if (ret) { + dev_err_probe(&pdev->dev, ret, "%s set headset_codec fail\n", + dai_link->name); + goto err_probe; + } + + ret = mt8192_mt6359_card_set_be_link(card, dai_link, headset_codec, "I2S9"); + if (ret) { + dev_err_probe(&pdev->dev, ret, "%s set headset_codec fail\n", + dai_link->name); + goto err_probe; } if (hdmi_codec && strcmp(dai_link->name, "TDM") == 0) { @@ -1156,6 +1184,9 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) dai_link->ignore = 0; } + if (strcmp(dai_link->codecs[0].dai_name, RT1015_CODEC_DAI) == 0) + dai_link->ops = &mt8192_rt1015_i2s_ops; + if (!dai_link->platforms->name) dai_link->platforms->of_node = platform_node; } @@ -1163,34 +1194,44 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; - goto put_hdmi_codec; + goto err_probe; } snd_soc_card_set_drvdata(card, priv); ret = mt8192_afe_gpio_init(&pdev->dev); if (ret) { - dev_err(&pdev->dev, "init gpio error %d\n", ret); - goto put_hdmi_codec; + dev_err_probe(&pdev->dev, ret, "%s init gpio error\n", __func__); + goto err_probe; } ret = devm_snd_soc_register_card(&pdev->dev, card); - -put_hdmi_codec: - of_node_put(hdmi_codec); -put_platform_node: + if (ret) + dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", __func__); + +err_probe: + of_node_put(headset_codec); +err_headset_codec: + of_node_put(speaker_codec); +err_speaker_codec: of_node_put(platform_node); +err_platform_node: + of_node_put(hdmi_codec); return ret; } #ifdef CONFIG_OF static const struct of_device_id mt8192_mt6359_dt_match[] = { { - .compatible = "mediatek,mt8192_mt6359_rt1015_rt5682", + .compatible = RT1015_RT5682_OF_NAME, .data = &mt8192_mt6359_rt1015_rt5682_card, }, { - .compatible = "mediatek,mt8192_mt6359_rt1015p_rt5682", - .data = &mt8192_mt6359_rt1015p_rt5682_card, + .compatible = RT1015P_RT5682_OF_NAME, + .data = &mt8192_mt6359_rt1015p_rt5682x_card, + }, + { + .compatible = RT1015P_RT5682S_OF_NAME, + .data = &mt8192_mt6359_rt1015p_rt5682x_card, }, {} }; diff --git a/sound/soc/mediatek/mt8195/Makefile b/sound/soc/mediatek/mt8195/Makefile index e5f0df5010b6..aae673ec751b 100644 --- a/sound/soc/mediatek/mt8195/Makefile +++ b/sound/soc/mediatek/mt8195/Makefile @@ -12,5 +12,4 @@ snd-soc-mt8195-afe-objs := \ obj-$(CONFIG_SND_SOC_MT8195) += snd-soc-mt8195-afe.o # machine driver -obj-$(CONFIG_SND_SOC_MT8195_MT6359_RT1019_RT5682) += mt8195-mt6359-rt1019-rt5682.o -obj-$(CONFIG_SND_SOC_MT8195_MT6359_RT1011_RT5682) += mt8195-mt6359-rt1011-rt5682.o +obj-$(CONFIG_SND_SOC_MT8195_MT6359) += mt8195-mt6359.o diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c index 151914c873ac..12644ded83d5 100644 --- a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c @@ -213,8 +213,6 @@ static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream, static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret; - dev_dbg(dai->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n", __func__, dai->id, substream->stream, dai->playback_widget->active, dai->capture_widget->active); @@ -222,11 +220,7 @@ static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream, if (dai->playback_widget->active || dai->capture_widget->active) return 0; - ret = mtk_dai_pcm_configure(substream, dai); - if (ret) - return ret; - - return 0; + return mtk_dai_pcm_configure(substream, dai); } static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c deleted file mode 100644 index 5443a29da7b1..000000000000 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1011-rt5682.c +++ /dev/null @@ -1,1198 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// mt8195-mt6359-rt1011-rt5682.c -- -// MT8195-MT6359-RT1011-RT5682 ALSA SoC machine driver -// -// Copyright (c) 2021 MediaTek Inc. -// Author: Trevor Wu <trevor.wu@mediatek.com> -// - -#include <linux/input.h> -#include <linux/module.h> -#include <linux/pm_runtime.h> -#include <sound/jack.h> -#include <sound/pcm_params.h> -#include <sound/rt5682.h> -#include <sound/soc.h> -#include "../../codecs/mt6359.h" -#include "../../codecs/rt1011.h" -#include "../../codecs/rt5682.h" -#include "../common/mtk-afe-platform-driver.h" -#include "mt8195-afe-clk.h" -#include "mt8195-afe-common.h" - -#define RT1011_CODEC_DAI "rt1011-aif" -#define RT1011_DEV0_NAME "rt1011.2-0038" -#define RT1011_DEV1_NAME "rt1011.2-0039" - -#define RT5682_CODEC_DAI "rt5682-aif1" -#define RT5682_DEV0_NAME "rt5682.2-001a" - -#define RT5682S_CODEC_DAI "rt5682s-aif1" -#define RT5682S_DEV0_NAME "rt5682s.2-001a" - -struct mt8195_mt6359_rt1011_rt5682_priv { - struct snd_soc_jack headset_jack; - struct snd_soc_jack dp_jack; - struct snd_soc_jack hdmi_jack; - struct clk *i2so1_mclk; -}; - -static const struct snd_soc_dapm_widget -mt8195_mt6359_rt1011_rt5682_widgets[] = { - SND_SOC_DAPM_SPK("Left Speaker", NULL), - SND_SOC_DAPM_SPK("Right Speaker", NULL), - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), -}; - -static const struct snd_soc_dapm_route mt8195_mt6359_rt1011_rt5682_routes[] = { - /* speaker */ - { "Left Speaker", NULL, "Left SPO" }, - { "Right Speaker", NULL, "Right SPO" }, - /* headset */ - { "Headphone Jack", NULL, "HPOL" }, - { "Headphone Jack", NULL, "HPOR" }, - { "IN1P", NULL, "Headset Mic" }, -}; - -static const struct snd_kcontrol_new mt8195_mt6359_rt1011_rt5682_controls[] = { - SOC_DAPM_PIN_SWITCH("Left Speaker"), - SOC_DAPM_PIN_SWITCH("Right Speaker"), - SOC_DAPM_PIN_SWITCH("Headphone Jack"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), -}; - -static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_card *card = rtd->card; - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - unsigned int rate = params_rate(params); - int bitwidth; - int ret; - - bitwidth = snd_pcm_format_width(params_format(params)); - if (bitwidth < 0) { - dev_err(card->dev, "invalid bit width: %d\n", bitwidth); - return bitwidth; - } - - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth); - if (ret) { - dev_err(card->dev, "failed to set tdm slot\n"); - return ret; - } - - ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK, - rate * 256, rate * 512); - if (ret) { - dev_err(card->dev, "failed to set pll\n"); - return ret; - } - - ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, - rate * 512, SND_SOC_CLOCK_IN); - if (ret) { - dev_err(card->dev, "failed to set sysclk\n"); - return ret; - } - - return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256, - SND_SOC_CLOCK_OUT); -} - -static const struct snd_soc_ops mt8195_rt5682_etdm_ops = { - .hw_params = mt8195_rt5682_etdm_hw_params, -}; - -static int mt8195_rt1011_etdm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai; - struct snd_soc_card *card = rtd->card; - int srate, i, ret = 0; - - srate = params_rate(params); - - for_each_rtd_codec_dais(rtd, i, codec_dai) { - ret = snd_soc_dai_set_pll(codec_dai, 0, RT1011_PLL1_S_BCLK, - 64 * srate, 256 * srate); - if (ret < 0) { - dev_err(card->dev, "codec_dai clock not set\n"); - return ret; - } - - ret = snd_soc_dai_set_sysclk(codec_dai, - RT1011_FS_SYS_PRE_S_PLL1, - 256 * srate, SND_SOC_CLOCK_IN); - if (ret < 0) { - dev_err(card->dev, "codec_dai clock not set\n"); - return ret; - } - } - return ret; -} - -static const struct snd_soc_ops mt8195_rt1011_etdm_ops = { - .hw_params = mt8195_rt1011_etdm_hw_params, -}; - -#define CKSYS_AUD_TOP_CFG 0x032c -#define CKSYS_AUD_TOP_MON 0x0330 - -static int mt8195_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_component *cmpnt_afe = - snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); - struct snd_soc_component *cmpnt_codec = - asoc_rtd_to_codec(rtd, 0)->component; - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe); - struct mt8195_afe_private *afe_priv = afe->platform_priv; - struct mtkaif_param *param = &afe_priv->mtkaif_params; - int chosen_phase_1, chosen_phase_2, chosen_phase_3; - int prev_cycle_1, prev_cycle_2, prev_cycle_3; - int test_done_1, test_done_2, test_done_3; - int cycle_1, cycle_2, cycle_3; - int mtkaif_chosen_phase[MT8195_MTKAIF_MISO_NUM]; - int mtkaif_phase_cycle[MT8195_MTKAIF_MISO_NUM]; - int mtkaif_calibration_num_phase; - bool mtkaif_calibration_ok; - unsigned int monitor; - int counter; - int phase; - int i; - - dev_dbg(afe->dev, "%s(), start\n", __func__); - - param->mtkaif_calibration_ok = false; - for (i = 0; i < MT8195_MTKAIF_MISO_NUM; i++) { - param->mtkaif_chosen_phase[i] = -1; - param->mtkaif_phase_cycle[i] = 0; - mtkaif_chosen_phase[i] = -1; - mtkaif_phase_cycle[i] = 0; - } - - if (IS_ERR(afe_priv->topckgen)) { - dev_info(afe->dev, "%s() Cannot find topckgen controller\n", - __func__); - return 0; - } - - pm_runtime_get_sync(afe->dev); - mt6359_mtkaif_calibration_enable(cmpnt_codec); - - /* set test type to synchronizer pulse */ - regmap_update_bits(afe_priv->topckgen, - CKSYS_AUD_TOP_CFG, 0xffff, 0x4); - mtkaif_calibration_num_phase = 42; /* mt6359: 0 ~ 42 */ - mtkaif_calibration_ok = true; - - for (phase = 0; - phase <= mtkaif_calibration_num_phase && mtkaif_calibration_ok; - phase++) { - mt6359_set_mtkaif_calibration_phase(cmpnt_codec, - phase, phase, phase); - - regmap_update_bits(afe_priv->topckgen, - CKSYS_AUD_TOP_CFG, 0x1, 0x1); - - test_done_1 = 0; - test_done_2 = 0; - test_done_3 = 0; - cycle_1 = -1; - cycle_2 = -1; - cycle_3 = -1; - counter = 0; - while (!(test_done_1 & test_done_2 & test_done_3)) { - regmap_read(afe_priv->topckgen, - CKSYS_AUD_TOP_MON, &monitor); - test_done_1 = (monitor >> 28) & 0x1; - test_done_2 = (monitor >> 29) & 0x1; - test_done_3 = (monitor >> 30) & 0x1; - if (test_done_1 == 1) - cycle_1 = monitor & 0xf; - - if (test_done_2 == 1) - cycle_2 = (monitor >> 4) & 0xf; - - if (test_done_3 == 1) - cycle_3 = (monitor >> 8) & 0xf; - - /* handle if never test done */ - if (++counter > 10000) { - dev_info(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, cycle_3 %d, monitor 0x%x\n", - __func__, - cycle_1, cycle_2, cycle_3, monitor); - mtkaif_calibration_ok = false; - break; - } - } - - if (phase == 0) { - prev_cycle_1 = cycle_1; - prev_cycle_2 = cycle_2; - prev_cycle_3 = cycle_3; - } - - if (cycle_1 != prev_cycle_1 && - mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] < 0) { - mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] = phase - 1; - mtkaif_phase_cycle[MT8195_MTKAIF_MISO_0] = prev_cycle_1; - } - - if (cycle_2 != prev_cycle_2 && - mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] < 0) { - mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] = phase - 1; - mtkaif_phase_cycle[MT8195_MTKAIF_MISO_1] = prev_cycle_2; - } - - if (cycle_3 != prev_cycle_3 && - mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] < 0) { - mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] = phase - 1; - mtkaif_phase_cycle[MT8195_MTKAIF_MISO_2] = prev_cycle_3; - } - - regmap_update_bits(afe_priv->topckgen, - CKSYS_AUD_TOP_CFG, 0x1, 0x0); - - if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] >= 0 && - mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] >= 0 && - mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] >= 0) - break; - } - - if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] < 0) { - mtkaif_calibration_ok = false; - chosen_phase_1 = 0; - } else { - chosen_phase_1 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0]; - } - - if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] < 0) { - mtkaif_calibration_ok = false; - chosen_phase_2 = 0; - } else { - chosen_phase_2 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1]; - } - - if (mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] < 0) { - mtkaif_calibration_ok = false; - chosen_phase_3 = 0; - } else { - chosen_phase_3 = mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2]; - } - - mt6359_set_mtkaif_calibration_phase(cmpnt_codec, - chosen_phase_1, - chosen_phase_2, - chosen_phase_3); - - mt6359_mtkaif_calibration_disable(cmpnt_codec); - pm_runtime_put(afe->dev); - - param->mtkaif_calibration_ok = mtkaif_calibration_ok; - param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_0] = chosen_phase_1; - param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_1] = chosen_phase_2; - param->mtkaif_chosen_phase[MT8195_MTKAIF_MISO_2] = chosen_phase_3; - for (i = 0; i < MT8195_MTKAIF_MISO_NUM; i++) - param->mtkaif_phase_cycle[i] = mtkaif_phase_cycle[i]; - - dev_info(afe->dev, "%s(), end, calibration ok %d\n", - __func__, param->mtkaif_calibration_ok); - - return 0; -} - -static int mt8195_mt6359_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_component *cmpnt_codec = - asoc_rtd_to_codec(rtd, 0)->component; - - /* set mtkaif protocol */ - mt6359_set_mtkaif_protocol(cmpnt_codec, - MT6359_MTKAIF_PROTOCOL_2_CLK_P2); - - /* mtkaif calibration */ - mt8195_mt6359_mtkaif_calibration(rtd); - - return 0; -} - -static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_component *cmpnt_codec = - asoc_rtd_to_codec(rtd, 0)->component; - struct mt8195_mt6359_rt1011_rt5682_priv *priv = - snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_jack *jack = &priv->headset_jack; - struct snd_soc_component *cmpnt_afe = - snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe); - struct mt8195_afe_private *afe_priv = afe->platform_priv; - int ret; - - priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2]; - - ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3, - jack, NULL, 0); - if (ret) { - dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); - return ret; - } - - snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); - snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); - snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); - - ret = snd_soc_component_set_jack(cmpnt_codec, jack, NULL); - if (ret) { - dev_err(rtd->dev, "Headset Jack set failed: %d\n", ret); - return ret; - } - - return 0; -}; - -static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - /* fix BE i2s format to S24_LE, clean param mask first */ - snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); - - params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); - - return 0; -} - -static int mt8195_hdmitx_dptx_startup(struct snd_pcm_substream *substream) -{ - static const unsigned int rates[] = { - 48000 - }; - static const unsigned int channels[] = { - 2, 4, 6, 8 - }; - static const struct snd_pcm_hw_constraint_list constraints_rates = { - .count = ARRAY_SIZE(rates), - .list = rates, - .mask = 0, - }; - static const struct snd_pcm_hw_constraint_list constraints_channels = { - .count = ARRAY_SIZE(channels), - .list = channels, - .mask = 0, - }; - - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - int ret; - - ret = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &constraints_rates); - if (ret < 0) { - dev_err(rtd->dev, "hw_constraint_list rate failed\n"); - return ret; - } - - ret = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - &constraints_channels); - if (ret < 0) { - dev_err(rtd->dev, "hw_constraint_list channel failed\n"); - return ret; - } - - return 0; -} - -static const struct snd_soc_ops mt8195_hdmitx_dptx_playback_ops = { - .startup = mt8195_hdmitx_dptx_startup, -}; - -static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); - - return snd_soc_dai_set_sysclk(cpu_dai, 0, params_rate(params) * 256, - SND_SOC_CLOCK_OUT); -} - -static const struct snd_soc_ops mt8195_dptx_ops = { - .hw_params = mt8195_dptx_hw_params, -}; - -static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd) -{ - struct mt8195_mt6359_rt1011_rt5682_priv *priv = - snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_component *cmpnt_codec = - asoc_rtd_to_codec(rtd, 0)->component; - int ret; - - ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT, - &priv->dp_jack, NULL, 0); - if (ret) - return ret; - - return snd_soc_component_set_jack(cmpnt_codec, &priv->dp_jack, NULL); -} - -static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd) -{ - struct mt8195_mt6359_rt1011_rt5682_priv *priv = - snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_component *cmpnt_codec = - asoc_rtd_to_codec(rtd, 0)->component; - int ret; - - ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, - &priv->hdmi_jack, NULL, 0); - if (ret) - return ret; - - return snd_soc_component_set_jack(cmpnt_codec, &priv->hdmi_jack, NULL); -} - -static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) - -{ - /* fix BE i2s format to S24_LE, clean param mask first */ - snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); - - params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); - - return 0; -} - -static int mt8195_playback_startup(struct snd_pcm_substream *substream) -{ - static const unsigned int rates[] = { - 48000 - }; - static const unsigned int channels[] = { - 2 - }; - static const struct snd_pcm_hw_constraint_list constraints_rates = { - .count = ARRAY_SIZE(rates), - .list = rates, - .mask = 0, - }; - static const struct snd_pcm_hw_constraint_list constraints_channels = { - .count = ARRAY_SIZE(channels), - .list = channels, - .mask = 0, - }; - - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - int ret; - - ret = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &constraints_rates); - if (ret < 0) { - dev_err(rtd->dev, "hw_constraint_list rate failed\n"); - return ret; - } - - ret = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - &constraints_channels); - if (ret < 0) { - dev_err(rtd->dev, "hw_constraint_list channel failed\n"); - return ret; - } - - return 0; -} - -static const struct snd_soc_ops mt8195_playback_ops = { - .startup = mt8195_playback_startup, -}; - -static int mt8195_capture_startup(struct snd_pcm_substream *substream) -{ - static const unsigned int rates[] = { - 48000 - }; - static const unsigned int channels[] = { - 1, 2 - }; - static const struct snd_pcm_hw_constraint_list constraints_rates = { - .count = ARRAY_SIZE(rates), - .list = rates, - .mask = 0, - }; - static const struct snd_pcm_hw_constraint_list constraints_channels = { - .count = ARRAY_SIZE(channels), - .list = channels, - .mask = 0, - }; - - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - int ret; - - ret = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &constraints_rates); - if (ret < 0) { - dev_err(rtd->dev, "hw_constraint_list rate failed\n"); - return ret; - } - - ret = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - &constraints_channels); - if (ret < 0) { - dev_err(rtd->dev, "hw_constraint_list channel failed\n"); - return ret; - } - - return 0; -} - -static const struct snd_soc_ops mt8195_capture_ops = { - .startup = mt8195_capture_startup, -}; - -static int mt8195_set_bias_level_post(struct snd_soc_card *card, - struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) -{ - struct snd_soc_component *component = dapm->component; - struct mt8195_mt6359_rt1011_rt5682_priv *priv = - snd_soc_card_get_drvdata(card); - int ret; - - /* - * It's required to control mclk directly in the set_bias_level_post - * function for rt5682 and rt5682s codec, or the unexpected pop happens - * at the end of playback. - */ - if (!component || - (strcmp(component->name, RT5682_DEV0_NAME) && - strcmp(component->name, RT5682S_DEV0_NAME))) - return 0; - - switch (level) { - case SND_SOC_BIAS_OFF: - if (!__clk_is_enabled(priv->i2so1_mclk)) - return 0; - - clk_disable_unprepare(priv->i2so1_mclk); - dev_dbg(card->dev, "Disable i2so1 mclk\n"); - break; - case SND_SOC_BIAS_ON: - ret = clk_prepare_enable(priv->i2so1_mclk); - if (ret) { - dev_err(card->dev, "Can't enable i2so1 mclk: %d\n", ret); - return ret; - } - dev_dbg(card->dev, "Enable i2so1 mclk\n"); - break; - default: - break; - } - - return 0; -} - -enum { - DAI_LINK_DL2_FE, - DAI_LINK_DL3_FE, - DAI_LINK_DL6_FE, - DAI_LINK_DL7_FE, - DAI_LINK_DL8_FE, - DAI_LINK_DL10_FE, - DAI_LINK_DL11_FE, - DAI_LINK_UL1_FE, - DAI_LINK_UL2_FE, - DAI_LINK_UL3_FE, - DAI_LINK_UL4_FE, - DAI_LINK_UL5_FE, - DAI_LINK_UL6_FE, - DAI_LINK_UL8_FE, - DAI_LINK_UL9_FE, - DAI_LINK_UL10_FE, - DAI_LINK_DL_SRC_BE, - DAI_LINK_DPTX_BE, - DAI_LINK_ETDM1_IN_BE, - DAI_LINK_ETDM2_IN_BE, - DAI_LINK_ETDM1_OUT_BE, - DAI_LINK_ETDM2_OUT_BE, - DAI_LINK_ETDM3_OUT_BE, - DAI_LINK_PCM1_BE, - DAI_LINK_UL_SRC1_BE, - DAI_LINK_UL_SRC2_BE, -}; - -/* FE */ -SND_SOC_DAILINK_DEFS(DL2_FE, - DAILINK_COMP_ARRAY(COMP_CPU("DL2")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(DL3_FE, - DAILINK_COMP_ARRAY(COMP_CPU("DL3")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(DL6_FE, - DAILINK_COMP_ARRAY(COMP_CPU("DL6")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(DL7_FE, - DAILINK_COMP_ARRAY(COMP_CPU("DL7")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(DL8_FE, - DAILINK_COMP_ARRAY(COMP_CPU("DL8")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(DL10_FE, - DAILINK_COMP_ARRAY(COMP_CPU("DL10")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(DL11_FE, - DAILINK_COMP_ARRAY(COMP_CPU("DL11")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(UL1_FE, - DAILINK_COMP_ARRAY(COMP_CPU("UL1")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(UL2_FE, - DAILINK_COMP_ARRAY(COMP_CPU("UL2")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(UL3_FE, - DAILINK_COMP_ARRAY(COMP_CPU("UL3")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(UL4_FE, - DAILINK_COMP_ARRAY(COMP_CPU("UL4")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(UL5_FE, - DAILINK_COMP_ARRAY(COMP_CPU("UL5")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(UL6_FE, - DAILINK_COMP_ARRAY(COMP_CPU("UL6")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(UL8_FE, - DAILINK_COMP_ARRAY(COMP_CPU("UL8")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(UL9_FE, - DAILINK_COMP_ARRAY(COMP_CPU("UL9")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(UL10_FE, - DAILINK_COMP_ARRAY(COMP_CPU("UL10")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -/* BE */ -SND_SOC_DAILINK_DEFS(DL_SRC_BE, - DAILINK_COMP_ARRAY(COMP_CPU("DL_SRC")), - DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound", - "mt6359-snd-codec-aif1")), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(DPTX_BE, - DAILINK_COMP_ARRAY(COMP_CPU("DPTX")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(ETDM1_IN_BE, - DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_IN")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(ETDM2_IN_BE, - DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE, - DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE, - DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_OUT")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT1011_DEV0_NAME, - RT1011_CODEC_DAI), - COMP_CODEC(RT1011_DEV1_NAME, - RT1011_CODEC_DAI)), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(ETDM3_OUT_BE, - DAILINK_COMP_ARRAY(COMP_CPU("ETDM3_OUT")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(PCM1_BE, - DAILINK_COMP_ARRAY(COMP_CPU("PCM1")), - DAILINK_COMP_ARRAY(COMP_DUMMY()), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(UL_SRC1_BE, - DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC1")), - DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound", - "mt6359-snd-codec-aif1"), - COMP_CODEC("dmic-codec", - "dmic-hifi")), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -SND_SOC_DAILINK_DEFS(UL_SRC2_BE, - DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC2")), - DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound", - "mt6359-snd-codec-aif2")), - DAILINK_COMP_ARRAY(COMP_EMPTY())); - -static struct snd_soc_dai_link mt8195_mt6359_rt1011_rt5682_dai_links[] = { - /* FE */ - [DAI_LINK_DL2_FE] = { - .name = "DL2_FE", - .stream_name = "DL2 Playback", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_playback = 1, - .ops = &mt8195_playback_ops, - SND_SOC_DAILINK_REG(DL2_FE), - }, - [DAI_LINK_DL3_FE] = { - .name = "DL3_FE", - .stream_name = "DL3 Playback", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_playback = 1, - .ops = &mt8195_playback_ops, - SND_SOC_DAILINK_REG(DL3_FE), - }, - [DAI_LINK_DL6_FE] = { - .name = "DL6_FE", - .stream_name = "DL6 Playback", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_playback = 1, - .ops = &mt8195_playback_ops, - SND_SOC_DAILINK_REG(DL6_FE), - }, - [DAI_LINK_DL7_FE] = { - .name = "DL7_FE", - .stream_name = "DL7 Playback", - .trigger = { - SND_SOC_DPCM_TRIGGER_PRE, - SND_SOC_DPCM_TRIGGER_PRE, - }, - .dynamic = 1, - .dpcm_playback = 1, - SND_SOC_DAILINK_REG(DL7_FE), - }, - [DAI_LINK_DL8_FE] = { - .name = "DL8_FE", - .stream_name = "DL8 Playback", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_playback = 1, - .ops = &mt8195_playback_ops, - SND_SOC_DAILINK_REG(DL8_FE), - }, - [DAI_LINK_DL10_FE] = { - .name = "DL10_FE", - .stream_name = "DL10 Playback", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_playback = 1, - .ops = &mt8195_hdmitx_dptx_playback_ops, - SND_SOC_DAILINK_REG(DL10_FE), - }, - [DAI_LINK_DL11_FE] = { - .name = "DL11_FE", - .stream_name = "DL11 Playback", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_playback = 1, - .ops = &mt8195_playback_ops, - SND_SOC_DAILINK_REG(DL11_FE), - }, - [DAI_LINK_UL1_FE] = { - .name = "UL1_FE", - .stream_name = "UL1 Capture", - .trigger = { - SND_SOC_DPCM_TRIGGER_PRE, - SND_SOC_DPCM_TRIGGER_PRE, - }, - .dynamic = 1, - .dpcm_capture = 1, - SND_SOC_DAILINK_REG(UL1_FE), - }, - [DAI_LINK_UL2_FE] = { - .name = "UL2_FE", - .stream_name = "UL2 Capture", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_capture = 1, - .ops = &mt8195_capture_ops, - SND_SOC_DAILINK_REG(UL2_FE), - }, - [DAI_LINK_UL3_FE] = { - .name = "UL3_FE", - .stream_name = "UL3 Capture", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_capture = 1, - .ops = &mt8195_capture_ops, - SND_SOC_DAILINK_REG(UL3_FE), - }, - [DAI_LINK_UL4_FE] = { - .name = "UL4_FE", - .stream_name = "UL4 Capture", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_capture = 1, - .ops = &mt8195_capture_ops, - SND_SOC_DAILINK_REG(UL4_FE), - }, - [DAI_LINK_UL5_FE] = { - .name = "UL5_FE", - .stream_name = "UL5 Capture", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_capture = 1, - .ops = &mt8195_capture_ops, - SND_SOC_DAILINK_REG(UL5_FE), - }, - [DAI_LINK_UL6_FE] = { - .name = "UL6_FE", - .stream_name = "UL6 Capture", - .trigger = { - SND_SOC_DPCM_TRIGGER_PRE, - SND_SOC_DPCM_TRIGGER_PRE, - }, - .dynamic = 1, - .dpcm_capture = 1, - SND_SOC_DAILINK_REG(UL6_FE), - }, - [DAI_LINK_UL8_FE] = { - .name = "UL8_FE", - .stream_name = "UL8 Capture", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_capture = 1, - .ops = &mt8195_capture_ops, - SND_SOC_DAILINK_REG(UL8_FE), - }, - [DAI_LINK_UL9_FE] = { - .name = "UL9_FE", - .stream_name = "UL9 Capture", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_capture = 1, - .ops = &mt8195_capture_ops, - SND_SOC_DAILINK_REG(UL9_FE), - }, - [DAI_LINK_UL10_FE] = { - .name = "UL10_FE", - .stream_name = "UL10 Capture", - .trigger = { - SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST, - }, - .dynamic = 1, - .dpcm_capture = 1, - .ops = &mt8195_capture_ops, - SND_SOC_DAILINK_REG(UL10_FE), - }, - /* BE */ - [DAI_LINK_DL_SRC_BE] = { - .name = "DL_SRC_BE", - .init = mt8195_mt6359_init, - .no_pcm = 1, - .dpcm_playback = 1, - SND_SOC_DAILINK_REG(DL_SRC_BE), - }, - [DAI_LINK_DPTX_BE] = { - .name = "DPTX_BE", - .no_pcm = 1, - .dpcm_playback = 1, - .ops = &mt8195_dptx_ops, - .be_hw_params_fixup = mt8195_dptx_hw_params_fixup, - SND_SOC_DAILINK_REG(DPTX_BE), - }, - [DAI_LINK_ETDM1_IN_BE] = { - .name = "ETDM1_IN_BE", - .no_pcm = 1, - .dai_fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .dpcm_capture = 1, - SND_SOC_DAILINK_REG(ETDM1_IN_BE), - }, - [DAI_LINK_ETDM2_IN_BE] = { - .name = "ETDM2_IN_BE", - .no_pcm = 1, - .dai_fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .dpcm_capture = 1, - .init = mt8195_rt5682_init, - .ops = &mt8195_rt5682_etdm_ops, - .be_hw_params_fixup = mt8195_etdm_hw_params_fixup, - SND_SOC_DAILINK_REG(ETDM2_IN_BE), - }, - [DAI_LINK_ETDM1_OUT_BE] = { - .name = "ETDM1_OUT_BE", - .no_pcm = 1, - .dai_fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .dpcm_playback = 1, - .ops = &mt8195_rt5682_etdm_ops, - .be_hw_params_fixup = mt8195_etdm_hw_params_fixup, - SND_SOC_DAILINK_REG(ETDM1_OUT_BE), - }, - [DAI_LINK_ETDM2_OUT_BE] = { - .name = "ETDM2_OUT_BE", - .no_pcm = 1, - .dai_fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .dpcm_playback = 1, - .ops = &mt8195_rt1011_etdm_ops, - .be_hw_params_fixup = mt8195_etdm_hw_params_fixup, - SND_SOC_DAILINK_REG(ETDM2_OUT_BE), - }, - [DAI_LINK_ETDM3_OUT_BE] = { - .name = "ETDM3_OUT_BE", - .no_pcm = 1, - .dai_fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .dpcm_playback = 1, - SND_SOC_DAILINK_REG(ETDM3_OUT_BE), - }, - [DAI_LINK_PCM1_BE] = { - .name = "PCM1_BE", - .no_pcm = 1, - .dai_fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .dpcm_playback = 1, - .dpcm_capture = 1, - SND_SOC_DAILINK_REG(PCM1_BE), - }, - [DAI_LINK_UL_SRC1_BE] = { - .name = "UL_SRC1_BE", - .no_pcm = 1, - .dpcm_capture = 1, - SND_SOC_DAILINK_REG(UL_SRC1_BE), - }, - [DAI_LINK_UL_SRC2_BE] = { - .name = "UL_SRC2_BE", - .no_pcm = 1, - .dpcm_capture = 1, - SND_SOC_DAILINK_REG(UL_SRC2_BE), - }, -}; - -static struct snd_soc_codec_conf rt1011_amp_conf[] = { - { - .dlc = COMP_CODEC_CONF(RT1011_DEV0_NAME), - .name_prefix = "Left", - }, - { - .dlc = COMP_CODEC_CONF(RT1011_DEV1_NAME), - .name_prefix = "Right", - }, -}; - -static struct snd_soc_card mt8195_mt6359_rt1011_rt5682_soc_card = { - .name = "mt8195_r1011_5682", - .owner = THIS_MODULE, - .dai_link = mt8195_mt6359_rt1011_rt5682_dai_links, - .num_links = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_dai_links), - .controls = mt8195_mt6359_rt1011_rt5682_controls, - .num_controls = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_controls), - .dapm_widgets = mt8195_mt6359_rt1011_rt5682_widgets, - .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_widgets), - .dapm_routes = mt8195_mt6359_rt1011_rt5682_routes, - .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1011_rt5682_routes), - .codec_conf = rt1011_amp_conf, - .num_configs = ARRAY_SIZE(rt1011_amp_conf), - .set_bias_level_post = mt8195_set_bias_level_post, -}; - -static int mt8195_mt6359_rt1011_rt5682_dev_probe(struct platform_device *pdev) -{ - struct snd_soc_card *card = &mt8195_mt6359_rt1011_rt5682_soc_card; - struct snd_soc_dai_link *dai_link; - struct mt8195_mt6359_rt1011_rt5682_priv *priv; - struct device_node *platform_node, *dp_node, *hdmi_node; - int is5682s = 0; - int ret, i; - - card->dev = &pdev->dev; - ret = snd_soc_of_parse_card_name(card, "model"); - if (ret) { - dev_err(&pdev->dev, "%s new card name parsing error %d\n", - __func__, ret); - return ret; - } - - if (strstr(card->name, "_5682s")) - is5682s = 1; - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - platform_node = of_parse_phandle(pdev->dev.of_node, - "mediatek,platform", 0); - if (!platform_node) { - dev_dbg(&pdev->dev, "Property 'platform' missing or invalid\n"); - return -EINVAL; - } - - dp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,dptx-codec", 0); - hdmi_node = of_parse_phandle(pdev->dev.of_node, - "mediatek,hdmi-codec", 0); - - for_each_card_prelinks(card, i, dai_link) { - if (!dai_link->platforms->name) - dai_link->platforms->of_node = platform_node; - - if (strcmp(dai_link->name, "DPTX_BE") == 0) { - if (!dp_node) { - dev_dbg(&pdev->dev, "No property 'dptx-codec'\n"); - } else { - dai_link->codecs->of_node = dp_node; - dai_link->codecs->name = NULL; - dai_link->codecs->dai_name = "i2s-hifi"; - dai_link->init = mt8195_dptx_codec_init; - } - } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { - if (!hdmi_node) { - dev_dbg(&pdev->dev, "No property 'hdmi-codec'\n"); - } else { - dai_link->codecs->of_node = hdmi_node; - dai_link->codecs->name = NULL; - dai_link->codecs->dai_name = "i2s-hifi"; - dai_link->init = mt8195_hdmi_codec_init; - } - } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 || - strcmp(dai_link->name, "ETDM2_IN_BE") == 0) { - dai_link->codecs->name = - is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME; - dai_link->codecs->dai_name = - is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI; - } - } - - snd_soc_card_set_drvdata(card, priv); - - ret = devm_snd_soc_register_card(&pdev->dev, card); - - of_node_put(platform_node); - of_node_put(dp_node); - of_node_put(hdmi_node); - return ret; -} - -#ifdef CONFIG_OF -static const struct of_device_id mt8195_mt6359_rt1011_rt5682_dt_match[] = { - {.compatible = "mediatek,mt8195_mt6359_rt1011_rt5682",}, - {} -}; -#endif - -static const struct dev_pm_ops mt8195_mt6359_rt1011_rt5682_pm_ops = { - .poweroff = snd_soc_poweroff, - .restore = snd_soc_resume, -}; - -static struct platform_driver mt8195_mt6359_rt1011_rt5682_driver = { - .driver = { - .name = "mt8195_mt6359_rt1011_rt5682", -#ifdef CONFIG_OF - .of_match_table = mt8195_mt6359_rt1011_rt5682_dt_match, -#endif - .pm = &mt8195_mt6359_rt1011_rt5682_pm_ops, - }, - .probe = mt8195_mt6359_rt1011_rt5682_dev_probe, -}; - -module_platform_driver(mt8195_mt6359_rt1011_rt5682_driver); - -/* Module information */ -MODULE_DESCRIPTION("MT8195-MT6359-RT1011-RT5682 ALSA SoC machine driver"); -MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("mt8195_mt6359_rt1011_rt5682 soc card"); diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c index e3146311722f..54a00b0699b1 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c @@ -1,30 +1,43 @@ // SPDX-License-Identifier: GPL-2.0 /* - * mt8195-mt6359-rt1019-rt5682.c -- - * MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver + * mt8195-mt6359.c -- + * MT8195-MT6359 ALSA SoC machine driver code * - * Copyright (c) 2021 MediaTek Inc. + * Copyright (c) 2022 MediaTek Inc. * Author: Trevor Wu <trevor.wu@mediatek.com> * YC Hung <yc.hung@mediatek.com> */ #include <linux/input.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/pm_runtime.h> #include <sound/jack.h> #include <sound/pcm_params.h> #include <sound/rt5682.h> -#include <sound/sof.h> #include <sound/soc.h> #include "../../codecs/mt6359.h" +#include "../../codecs/rt1011.h" #include "../../codecs/rt5682.h" #include "../common/mtk-afe-platform-driver.h" #include "mt8195-afe-clk.h" #include "mt8195-afe-common.h" +#define RT1011_SPEAKER_AMP_PRESENT BIT(0) +#define RT1019_SPEAKER_AMP_PRESENT BIT(1) +#define MAX98390_SPEAKER_AMP_PRESENT BIT(2) + +#define RT1011_CODEC_DAI "rt1011-aif" +#define RT1011_DEV0_NAME "rt1011.2-0038" +#define RT1011_DEV1_NAME "rt1011.2-0039" + #define RT1019_CODEC_DAI "HiFi" #define RT1019_DEV0_NAME "rt1019p" +#define MAX98390_CODEC_DAI "max98390-aif1" +#define MAX98390_DEV0_NAME "max98390.2-0038" /* right */ +#define MAX98390_DEV1_NAME "max98390.2-0039" /* left */ + #define RT5682_CODEC_DAI "rt5682-aif1" #define RT5682_DEV0_NAME "rt5682.2-001a" @@ -36,6 +49,11 @@ #define SOF_DMA_UL4 "SOF_DMA_UL4" #define SOF_DMA_UL5 "SOF_DMA_UL5" +struct mt8195_card_data { + const char *name; + unsigned long quirk; +}; + struct sof_conn_stream { const char *normal_link; const char *sof_link; @@ -43,17 +61,15 @@ struct sof_conn_stream { int stream_dir; }; -struct mt8195_mt6359_rt1019_rt5682_priv { +struct mt8195_mt6359_priv { struct snd_soc_jack headset_jack; struct snd_soc_jack dp_jack; struct snd_soc_jack hdmi_jack; struct clk *i2so1_mclk; }; -static const struct snd_soc_dapm_widget - mt8195_mt6359_rt1019_rt5682_widgets[] = { - SND_SOC_DAPM_SPK("Speakers", NULL), - SND_SOC_DAPM_HP("Headphone Jack", NULL), +static const struct snd_soc_dapm_widget mt8195_mt6359_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER(SOF_DMA_DL3, SND_SOC_NOPM, 0, 0, NULL, 0), @@ -61,12 +77,10 @@ static const struct snd_soc_dapm_widget SND_SOC_DAPM_MIXER(SOF_DMA_UL5, SND_SOC_NOPM, 0, 0, NULL, 0), }; -static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = { - /* speaker */ - { "Speakers", NULL, "Speaker" }, +static const struct snd_soc_dapm_route mt8195_mt6359_routes[] = { /* headset */ - { "Headphone Jack", NULL, "HPOL" }, - { "Headphone Jack", NULL, "HPOR" }, + { "Headphone", NULL, "HPOL" }, + { "Headphone", NULL, "HPOR" }, { "IN1P", NULL, "Headset Mic" }, /* SOF Uplink */ {SOF_DMA_UL4, NULL, "O034"}, @@ -80,55 +94,41 @@ static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = { {"I021", NULL, SOF_DMA_DL3}, }; -static const struct snd_kcontrol_new mt8195_mt6359_rt1019_rt5682_controls[] = { - SOC_DAPM_PIN_SWITCH("Speakers"), - SOC_DAPM_PIN_SWITCH("Headphone Jack"), +static const struct snd_kcontrol_new mt8195_mt6359_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), }; -static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_card *card = rtd->card; - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - unsigned int rate = params_rate(params); - int bitwidth; - int ret; +static const struct snd_soc_dapm_widget mt8195_dual_speaker_widgets[] = { + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), +}; - bitwidth = snd_pcm_format_width(params_format(params)); - if (bitwidth < 0) { - dev_err(card->dev, "invalid bit width: %d\n", bitwidth); - return bitwidth; - } +static const struct snd_kcontrol_new mt8195_dual_speaker_controls[] = { + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth); - if (ret) { - dev_err(card->dev, "failed to set tdm slot\n"); - return ret; - } +static const struct snd_soc_dapm_widget mt8195_speaker_widgets[] = { + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; - ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK, - rate * 256, rate * 512); - if (ret) { - dev_err(card->dev, "failed to set pll\n"); - return ret; - } +static const struct snd_kcontrol_new mt8195_speaker_controls[] = { + SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; - ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, - rate * 512, SND_SOC_CLOCK_IN); - if (ret) { - dev_err(card->dev, "failed to set sysclk\n"); - return ret; - } +static const struct snd_soc_dapm_route mt8195_rt1011_routes[] = { + { "Left Spk", NULL, "Left SPO" }, + { "Right Spk", NULL, "Right SPO" }, +}; - return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256, - SND_SOC_CLOCK_OUT); -} +static const struct snd_soc_dapm_route mt8195_rt1019_routes[] = { + { "Ext Spk", NULL, "Speaker" }, +}; -static const struct snd_soc_ops mt8195_rt5682_etdm_ops = { - .hw_params = mt8195_rt5682_etdm_hw_params, +static const struct snd_soc_dapm_route mt8195_max98390_routes[] = { + { "Left Spk", NULL, "Left BE_OUT" }, + { "Right Spk", NULL, "Right BE_OUT" }, }; #define CKSYS_AUD_TOP_CFG 0x032c @@ -143,20 +143,20 @@ static int mt8195_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe); struct mt8195_afe_private *afe_priv = afe->platform_priv; struct mtkaif_param *param = &afe_priv->mtkaif_params; - int phase; - unsigned int monitor; - int mtkaif_calibration_num_phase; + int chosen_phase_1, chosen_phase_2, chosen_phase_3; + int prev_cycle_1, prev_cycle_2, prev_cycle_3; int test_done_1, test_done_2, test_done_3; int cycle_1, cycle_2, cycle_3; - int prev_cycle_1, prev_cycle_2, prev_cycle_3; - int chosen_phase_1, chosen_phase_2, chosen_phase_3; - int counter; - bool mtkaif_calibration_ok; int mtkaif_chosen_phase[MT8195_MTKAIF_MISO_NUM]; int mtkaif_phase_cycle[MT8195_MTKAIF_MISO_NUM]; + int mtkaif_calibration_num_phase; + bool mtkaif_calibration_ok; + unsigned int monitor; + int counter; + int phase; int i; - dev_info(afe->dev, "%s(), start\n", __func__); + dev_dbg(afe->dev, "%s(), start\n", __func__); param->mtkaif_calibration_ok = false; for (i = 0; i < MT8195_MTKAIF_MISO_NUM; i++) { @@ -312,57 +312,6 @@ static int mt8195_mt6359_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_component *cmpnt_codec = - asoc_rtd_to_codec(rtd, 0)->component; - struct mt8195_mt6359_rt1019_rt5682_priv *priv = - snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_jack *jack = &priv->headset_jack; - struct snd_soc_component *cmpnt_afe = - snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe); - struct mt8195_afe_private *afe_priv = afe->platform_priv; - int ret; - - priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2]; - - ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3, - jack, NULL, 0); - if (ret) { - dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); - return ret; - } - - snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); - snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); - snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); - - ret = snd_soc_component_set_jack(cmpnt_codec, jack, NULL); - if (ret) { - dev_err(rtd->dev, "Headset Jack set failed: %d\n", ret); - return ret; - } - - return 0; -}; - -static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - /* fix BE i2s format to S24_LE, clean param mask first */ - snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); - - params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); - - return 0; -} - static int mt8195_hdmitx_dptx_startup(struct snd_pcm_substream *substream) { static const unsigned int rates[] = { @@ -414,11 +363,8 @@ static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); - unsigned int rate = params_rate(params); - unsigned int mclk_fs_ratio = 256; - unsigned int mclk_fs = rate * mclk_fs_ratio; - return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs, + return snd_soc_dai_set_sysclk(cpu_dai, 0, params_rate(params) * 256, SND_SOC_CLOCK_OUT); } @@ -428,14 +374,13 @@ static const struct snd_soc_ops mt8195_dptx_ops = { static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd) { - struct mt8195_mt6359_rt1019_rt5682_priv *priv = - snd_soc_card_get_drvdata(rtd->card); + struct mt8195_mt6359_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_component *cmpnt_codec = asoc_rtd_to_codec(rtd, 0)->component; - int ret = 0; + int ret; ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT, - &priv->dp_jack, NULL, 0); + &priv->dp_jack); if (ret) return ret; @@ -444,14 +389,13 @@ static int mt8195_dptx_codec_init(struct snd_soc_pcm_runtime *rtd) static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd) { - struct mt8195_mt6359_rt1019_rt5682_priv *priv = - snd_soc_card_get_drvdata(rtd->card); + struct mt8195_mt6359_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_component *cmpnt_codec = asoc_rtd_to_codec(rtd, 0)->component; - int ret = 0; + int ret; ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, - &priv->hdmi_jack, NULL, 0); + &priv->hdmi_jack); if (ret) return ret; @@ -460,7 +404,6 @@ static int mt8195_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd) static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) - { /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), @@ -563,12 +506,223 @@ static const struct snd_soc_ops mt8195_capture_ops = { .startup = mt8195_capture_startup, }; +static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + unsigned int rate = params_rate(params); + int bitwidth; + int ret; + + bitwidth = snd_pcm_format_width(params_format(params)); + if (bitwidth < 0) { + dev_err(card->dev, "invalid bit width: %d\n", bitwidth); + return bitwidth; + } + + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x00, 0x0, 0x2, bitwidth); + if (ret) { + dev_err(card->dev, "failed to set tdm slot\n"); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK, + rate * 256, rate * 512); + if (ret) { + dev_err(card->dev, "failed to set pll\n"); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, + rate * 512, SND_SOC_CLOCK_IN); + if (ret) { + dev_err(card->dev, "failed to set sysclk\n"); + return ret; + } + + return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256, + SND_SOC_CLOCK_OUT); +} + +static const struct snd_soc_ops mt8195_rt5682_etdm_ops = { + .hw_params = mt8195_rt5682_etdm_hw_params, +}; + +static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *cmpnt_codec = + asoc_rtd_to_codec(rtd, 0)->component; + struct mt8195_mt6359_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_jack *jack = &priv->headset_jack; + struct snd_soc_component *cmpnt_afe = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe); + struct mt8195_afe_private *afe_priv = afe->platform_priv; + int ret; + + priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2]; + + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + jack); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + + ret = snd_soc_component_set_jack(cmpnt_codec, jack, NULL); + if (ret) { + dev_err(rtd->dev, "Headset Jack set failed: %d\n", ret); + return ret; + } + + return 0; +}; + +static int mt8195_rt1011_etdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; + struct snd_soc_card *card = rtd->card; + int srate, i, ret; + + srate = params_rate(params); + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_pll(codec_dai, 0, RT1011_PLL1_S_BCLK, + 64 * srate, 256 * srate); + if (ret < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, + RT1011_FS_SYS_PRE_S_PLL1, + 256 * srate, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return ret; + } + } + return 0; +} + +static const struct snd_soc_ops mt8195_rt1011_etdm_ops = { + .hw_params = mt8195_rt1011_etdm_hw_params, +}; + +static int mt8195_rt1011_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, mt8195_dual_speaker_widgets, + ARRAY_SIZE(mt8195_dual_speaker_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_add_card_controls(card, mt8195_dual_speaker_controls, + ARRAY_SIZE(mt8195_dual_speaker_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, mt8195_rt1011_routes, + ARRAY_SIZE(mt8195_rt1011_routes)); + if (ret) + dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret); + + return ret; +} + +static int mt8195_rt1019_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, mt8195_speaker_widgets, + ARRAY_SIZE(mt8195_speaker_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_add_card_controls(card, mt8195_speaker_controls, + ARRAY_SIZE(mt8195_speaker_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, mt8195_rt1019_routes, + ARRAY_SIZE(mt8195_rt1019_routes)); + if (ret) + dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret); + + return ret; +} + +static int mt8195_max98390_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, mt8195_dual_speaker_widgets, + ARRAY_SIZE(mt8195_dual_speaker_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add dapm controls, ret %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_add_card_controls(card, mt8195_dual_speaker_controls, + ARRAY_SIZE(mt8195_dual_speaker_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, mt8195_max98390_routes, + ARRAY_SIZE(mt8195_max98390_routes)); + if (ret) + dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret); + + return ret; +} + +static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + /* fix BE i2s format to S24_LE, clean param mask first */ + snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + static int mt8195_set_bias_level_post(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { struct snd_soc_component *component = dapm->component; - struct mt8195_mt6359_rt1019_rt5682_priv *priv = - snd_soc_card_get_drvdata(card); + struct mt8195_mt6359_priv *priv = snd_soc_card_get_drvdata(card); int ret; /* @@ -581,7 +735,6 @@ static int mt8195_set_bias_level_post(struct snd_soc_card *card, strcmp(component->name, RT5682S_DEV0_NAME))) return 0; - switch (level) { case SND_SOC_BIAS_OFF: if (!__clk_is_enabled(priv->i2so1_mclk)) @@ -753,8 +906,7 @@ SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE, SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_OUT")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT1019_DEV0_NAME, - RT1019_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM3_OUT_BE, @@ -801,6 +953,23 @@ SND_SOC_DAILINK_DEFS(AFE_SOF_UL5, DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); +/* codec */ +SND_SOC_DAILINK_DEF(rt1019_comps, + DAILINK_COMP_ARRAY(COMP_CODEC(RT1019_DEV0_NAME, + RT1019_CODEC_DAI))); + +SND_SOC_DAILINK_DEF(rt1011_comps, + DAILINK_COMP_ARRAY(COMP_CODEC(RT1011_DEV0_NAME, + RT1011_CODEC_DAI), + COMP_CODEC(RT1011_DEV1_NAME, + RT1011_CODEC_DAI))); + +SND_SOC_DAILINK_DEF(max98390_comps, + DAILINK_COMP_ARRAY(COMP_CODEC(MAX98390_DEV0_NAME, + MAX98390_CODEC_DAI), + COMP_CODEC(MAX98390_DEV1_NAME, + MAX98390_CODEC_DAI))); + static const struct sof_conn_stream g_sof_conn_streams[] = { { "ETDM2_OUT_BE", "AFE_SOF_DL2", SOF_DMA_DL2, SNDRV_PCM_STREAM_PLAYBACK}, { "ETDM1_OUT_BE", "AFE_SOF_DL3", SOF_DMA_DL3, SNDRV_PCM_STREAM_PLAYBACK}, @@ -808,128 +977,7 @@ static const struct sof_conn_stream g_sof_conn_streams[] = { { "ETDM2_IN_BE", "AFE_SOF_UL5", SOF_DMA_UL5, SNDRV_PCM_STREAM_CAPTURE}, }; -/* fixup the BE DAI link to match any values from topology */ -static int mt8195_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_card *card = rtd->card; - struct snd_soc_dai_link *sof_dai_link = NULL; - struct snd_soc_pcm_runtime *runtime; - struct snd_soc_dai *cpu_dai; - int i, j, ret = 0; - - for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) { - const struct sof_conn_stream *conn = &g_sof_conn_streams[i]; - - if (strcmp(rtd->dai_link->name, conn->normal_link)) - continue; - - for_each_card_rtds(card, runtime) { - if (strcmp(runtime->dai_link->name, conn->sof_link)) - continue; - - for_each_rtd_cpu_dais(runtime, j, cpu_dai) { - if (cpu_dai->stream_active[conn->stream_dir] > 0) { - sof_dai_link = runtime->dai_link; - break; - } - } - break; - } - - if (sof_dai_link && sof_dai_link->be_hw_params_fixup) - ret = sof_dai_link->be_hw_params_fixup(runtime, params); - - break; - } - - if (!strcmp(rtd->dai_link->name, "ETDM2_IN_BE") || - !strcmp(rtd->dai_link->name, "ETDM1_OUT_BE")) { - mt8195_etdm_hw_params_fixup(runtime, params); - } - - return ret; -} - -static int mt8195_mt6359_rt1019_rt5682_card_late_probe(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *runtime; - struct snd_soc_component *sof_comp = NULL; - int i; - - /* 1. find sof component */ - for_each_card_rtds(card, runtime) { - for (i = 0; i < runtime->num_components; i++) { - if (!runtime->components[i]->driver->name) - continue; - if (!strcmp(runtime->components[i]->driver->name, "sof-audio-component")) { - sof_comp = runtime->components[i]; - break; - } - } - } - - if (!sof_comp) { - dev_info(card->dev, " probe without component\n"); - return 0; - } - /* 2. add route path and fixup callback */ - for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) { - const struct sof_conn_stream *conn = &g_sof_conn_streams[i]; - struct snd_soc_pcm_runtime *sof_rtd = NULL; - struct snd_soc_pcm_runtime *normal_rtd = NULL; - struct snd_soc_pcm_runtime *rtd = NULL; - - for_each_card_rtds(card, rtd) { - if (!strcmp(rtd->dai_link->name, conn->sof_link)) { - sof_rtd = rtd; - continue; - } - if (!strcmp(rtd->dai_link->name, conn->normal_link)) { - normal_rtd = rtd; - continue; - } - if (normal_rtd && sof_rtd) - break; - } - if (normal_rtd && sof_rtd) { - int j; - struct snd_soc_dai *cpu_dai; - - for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) { - struct snd_soc_dapm_route route; - struct snd_soc_dapm_path *p = NULL; - struct snd_soc_dapm_widget *play_widget = - cpu_dai->playback_widget; - struct snd_soc_dapm_widget *cap_widget = - cpu_dai->capture_widget; - memset(&route, 0, sizeof(route)); - if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && - cap_widget) { - snd_soc_dapm_widget_for_each_sink_path(cap_widget, p) { - route.source = conn->sof_dma; - route.sink = p->sink->name; - snd_soc_dapm_add_routes(&card->dapm, &route, 1); - } - } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && - play_widget){ - snd_soc_dapm_widget_for_each_source_path(play_widget, p) { - route.source = p->source->name; - route.sink = conn->sof_dma; - snd_soc_dapm_add_routes(&card->dapm, &route, 1); - } - } else { - dev_err(cpu_dai->dev, "stream dir and widget not pair\n"); - } - } - normal_rtd->dai_link->be_hw_params_fixup = mt8195_dai_link_fixup; - } - } - - return 0; -} - -static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { +static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = { /* FE */ [DAI_LINK_DL2_FE] = { .name = "DL2_FE", @@ -1234,20 +1282,162 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { }, }; -static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = { - .name = "mt8195_r1019_5682", +static struct snd_soc_codec_conf rt1011_codec_conf[] = { + { + .dlc = COMP_CODEC_CONF(RT1011_DEV0_NAME), + .name_prefix = "Left", + }, + { + .dlc = COMP_CODEC_CONF(RT1011_DEV1_NAME), + .name_prefix = "Right", + }, +}; + +static struct snd_soc_codec_conf max98390_codec_conf[] = { + { + .dlc = COMP_CODEC_CONF(MAX98390_DEV0_NAME), + .name_prefix = "Right", + }, + { + .dlc = COMP_CODEC_CONF(MAX98390_DEV1_NAME), + .name_prefix = "Left", + }, +}; + +static struct snd_soc_card mt8195_mt6359_soc_card = { .owner = THIS_MODULE, - .dai_link = mt8195_mt6359_rt1019_rt5682_dai_links, - .num_links = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links), - .controls = mt8195_mt6359_rt1019_rt5682_controls, - .num_controls = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_controls), - .dapm_widgets = mt8195_mt6359_rt1019_rt5682_widgets, - .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_widgets), - .dapm_routes = mt8195_mt6359_rt1019_rt5682_routes, - .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_routes), + .dai_link = mt8195_mt6359_dai_links, + .num_links = ARRAY_SIZE(mt8195_mt6359_dai_links), + .controls = mt8195_mt6359_controls, + .num_controls = ARRAY_SIZE(mt8195_mt6359_controls), + .dapm_widgets = mt8195_mt6359_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_widgets), + .dapm_routes = mt8195_mt6359_routes, + .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_routes), .set_bias_level_post = mt8195_set_bias_level_post, }; +/* fixup the BE DAI link to match any values from topology */ +static int mt8195_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai_link *sof_dai_link = NULL; + struct snd_soc_pcm_runtime *runtime; + struct snd_soc_dai *cpu_dai; + int i, j, ret = 0; + + for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) { + const struct sof_conn_stream *conn = &g_sof_conn_streams[i]; + + if (strcmp(rtd->dai_link->name, conn->normal_link)) + continue; + + for_each_card_rtds(card, runtime) { + if (strcmp(runtime->dai_link->name, conn->sof_link)) + continue; + + for_each_rtd_cpu_dais(runtime, j, cpu_dai) { + if (cpu_dai->stream_active[conn->stream_dir] > 0) { + sof_dai_link = runtime->dai_link; + break; + } + } + break; + } + + if (sof_dai_link && sof_dai_link->be_hw_params_fixup) + ret = sof_dai_link->be_hw_params_fixup(runtime, params); + + break; + } + + if (!strcmp(rtd->dai_link->name, "ETDM2_IN_BE") || + !strcmp(rtd->dai_link->name, "ETDM1_OUT_BE")) { + mt8195_etdm_hw_params_fixup(runtime, params); + } + + return ret; +} + +static int mt8195_mt6359_card_late_probe(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *runtime; + struct snd_soc_component *sof_comp = NULL; + int i; + + /* 1. find sof component */ + for_each_card_rtds(card, runtime) { + for (i = 0; i < runtime->num_components; i++) { + if (!runtime->components[i]->driver->name) + continue; + if (!strcmp(runtime->components[i]->driver->name, "sof-audio-component")) { + sof_comp = runtime->components[i]; + break; + } + } + } + + if (!sof_comp) { + dev_info(card->dev, " probe without component\n"); + return 0; + } + /* 2. add route path and fixup callback */ + for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) { + const struct sof_conn_stream *conn = &g_sof_conn_streams[i]; + struct snd_soc_pcm_runtime *sof_rtd = NULL; + struct snd_soc_pcm_runtime *normal_rtd = NULL; + struct snd_soc_pcm_runtime *rtd = NULL; + + for_each_card_rtds(card, rtd) { + if (!strcmp(rtd->dai_link->name, conn->sof_link)) { + sof_rtd = rtd; + continue; + } + if (!strcmp(rtd->dai_link->name, conn->normal_link)) { + normal_rtd = rtd; + continue; + } + if (normal_rtd && sof_rtd) + break; + } + if (normal_rtd && sof_rtd) { + int j; + struct snd_soc_dai *cpu_dai; + + for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) { + struct snd_soc_dapm_route route; + struct snd_soc_dapm_path *p = NULL; + struct snd_soc_dapm_widget *play_widget = + cpu_dai->playback_widget; + struct snd_soc_dapm_widget *cap_widget = + cpu_dai->capture_widget; + memset(&route, 0, sizeof(route)); + if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && + cap_widget) { + snd_soc_dapm_widget_for_each_sink_path(cap_widget, p) { + route.source = conn->sof_dma; + route.sink = p->sink->name; + snd_soc_dapm_add_routes(&card->dapm, &route, 1); + } + } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && + play_widget){ + snd_soc_dapm_widget_for_each_source_path(play_widget, p) { + route.source = p->source->name; + route.sink = conn->sof_dma; + snd_soc_dapm_add_routes(&card->dapm, &route, 1); + } + } else { + dev_err(cpu_dai->dev, "stream dir and widget not pair\n"); + } + } + normal_rtd->dai_link->be_hw_params_fixup = mt8195_dai_link_fixup; + } + } + + return 0; +} + static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node *np, const char *propname) { @@ -1258,7 +1448,7 @@ static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node num_links = of_property_count_strings(np, "mediatek,dai-link"); - if (num_links < 0 || num_links > ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links)) { + if (num_links < 0 || num_links > ARRAY_SIZE(mt8195_mt6359_dai_links)) { dev_dbg(dev, "number of dai-link is invalid\n"); return -EINVAL; } @@ -1278,9 +1468,9 @@ static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node return -EINVAL; } - for (j = 0; j < ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links); j++) { - if (!strcmp(dai_name, mt8195_mt6359_rt1019_rt5682_dai_links[j].name)) { - memcpy(link, &mt8195_mt6359_rt1019_rt5682_dai_links[j], + for (j = 0; j < ARRAY_SIZE(mt8195_mt6359_dai_links); j++) { + if (!strcmp(dai_name, mt8195_mt6359_dai_links[j].name)) { + memcpy(link, &mt8195_mt6359_dai_links[j], sizeof(struct snd_soc_dai_link)); link++; card->num_links++; @@ -1295,17 +1485,19 @@ static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node return 0; } -static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) +static int mt8195_mt6359_dev_probe(struct platform_device *pdev) { - struct snd_soc_card *card = &mt8195_mt6359_rt1019_rt5682_soc_card; + struct snd_soc_card *card = &mt8195_mt6359_soc_card; struct snd_soc_dai_link *dai_link; - struct mt8195_mt6359_rt1019_rt5682_priv *priv; + struct mt8195_mt6359_priv *priv; struct device_node *platform_node, *adsp_node, *dp_node, *hdmi_node; + struct mt8195_card_data *card_data; int is5682s = 0; int init6359 = 0; int sof_on = 0; int ret, i; + card_data = (struct mt8195_card_data *)of_device_get_match_data(&pdev->dev); card->dev = &pdev->dev; ret = snd_soc_of_parse_card_name(card, "model"); @@ -1315,6 +1507,9 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) return ret; } + if (!card->name) + card->name = card_data->name; + if (strstr(card->name, "_5682s")) is5682s = 1; @@ -1322,6 +1517,18 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) { + ret = mt8195_dailink_parse_of(card, pdev->dev.of_node, + "mediatek,dai-link"); + if (ret) { + dev_dbg(&pdev->dev, "Parse dai-link fail\n"); + return -EINVAL; + } + } else { + if (!sof_on) + card->num_links = DAI_LINK_REGULAR_NUM; + } + platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0); if (!platform_node) { @@ -1337,19 +1544,6 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) hdmi_node = of_parse_phandle(pdev->dev.of_node, "mediatek,hdmi-codec", 0); - if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) { - ret = mt8195_dailink_parse_of(card, pdev->dev.of_node, - "mediatek,dai-link"); - if (ret) { - dev_dbg(&pdev->dev, "Parse dai-link fail\n"); - ret = -EINVAL; - goto put_node; - } - } else { - if (!sof_on) - card->num_links = DAI_LINK_REGULAR_NUM; - } - for_each_card_prelinks(card, i, dai_link) { if (!dai_link->platforms->name) { if (!strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")) && sof_on) @@ -1389,17 +1583,42 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) dai_link->init = mt8195_mt6359_init; init6359 = 1; } + } else if (strcmp(dai_link->name, "ETDM2_OUT_BE") == 0) { + switch (card_data->quirk) { + case RT1011_SPEAKER_AMP_PRESENT: + dai_link->codecs = rt1011_comps; + dai_link->num_codecs = ARRAY_SIZE(rt1011_comps); + dai_link->init = mt8195_rt1011_init; + dai_link->ops = &mt8195_rt1011_etdm_ops; + dai_link->be_hw_params_fixup = mt8195_etdm_hw_params_fixup; + card->codec_conf = rt1011_codec_conf; + card->num_configs = ARRAY_SIZE(rt1011_codec_conf); + break; + case RT1019_SPEAKER_AMP_PRESENT: + dai_link->codecs = rt1019_comps; + dai_link->num_codecs = ARRAY_SIZE(rt1019_comps); + dai_link->init = mt8195_rt1019_init; + break; + case MAX98390_SPEAKER_AMP_PRESENT: + dai_link->codecs = max98390_comps; + dai_link->num_codecs = ARRAY_SIZE(max98390_comps); + dai_link->init = mt8195_max98390_init; + card->codec_conf = max98390_codec_conf; + card->num_configs = ARRAY_SIZE(max98390_codec_conf); + break; + default: + break; + } } } if (sof_on) - card->late_probe = mt8195_mt6359_rt1019_rt5682_card_late_probe; + card->late_probe = mt8195_mt6359_card_late_probe; snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); -put_node: of_node_put(platform_node); of_node_put(adsp_node); of_node_put(dp_node); @@ -1407,34 +1626,56 @@ put_node: return ret; } -#ifdef CONFIG_OF -static const struct of_device_id mt8195_mt6359_rt1019_rt5682_dt_match[] = { - {.compatible = "mediatek,mt8195_mt6359_rt1019_rt5682",}, - {} +static struct mt8195_card_data mt8195_mt6359_rt1019_rt5682_card = { + .name = "mt8195_r1019_5682", + .quirk = RT1019_SPEAKER_AMP_PRESENT, +}; + +static struct mt8195_card_data mt8195_mt6359_rt1011_rt5682_card = { + .name = "mt8195_r1011_5682", + .quirk = RT1011_SPEAKER_AMP_PRESENT, +}; + +static struct mt8195_card_data mt8195_mt6359_max98390_rt5682_card = { + .name = "mt8195_m98390_r5682", + .quirk = MAX98390_SPEAKER_AMP_PRESENT, +}; + +static const struct of_device_id mt8195_mt6359_dt_match[] = { + { + .compatible = "mediatek,mt8195_mt6359_rt1019_rt5682", + .data = &mt8195_mt6359_rt1019_rt5682_card, + }, + { + .compatible = "mediatek,mt8195_mt6359_rt1011_rt5682", + .data = &mt8195_mt6359_rt1011_rt5682_card, + }, + { + .compatible = "mediatek,mt8195_mt6359_max98390_rt5682", + .data = &mt8195_mt6359_max98390_rt5682_card, + }, + {}, }; -#endif -static const struct dev_pm_ops mt8195_mt6359_rt1019_rt5682_pm_ops = { +static const struct dev_pm_ops mt8195_mt6359_pm_ops = { .poweroff = snd_soc_poweroff, .restore = snd_soc_resume, }; -static struct platform_driver mt8195_mt6359_rt1019_rt5682_driver = { +static struct platform_driver mt8195_mt6359_driver = { .driver = { - .name = "mt8195_mt6359_rt1019_rt5682", -#ifdef CONFIG_OF - .of_match_table = mt8195_mt6359_rt1019_rt5682_dt_match, -#endif - .pm = &mt8195_mt6359_rt1019_rt5682_pm_ops, + .name = "mt8195_mt6359", + .of_match_table = mt8195_mt6359_dt_match, + .pm = &mt8195_mt6359_pm_ops, }, - .probe = mt8195_mt6359_rt1019_rt5682_dev_probe, + .probe = mt8195_mt6359_dev_probe, }; -module_platform_driver(mt8195_mt6359_rt1019_rt5682_driver); +module_platform_driver(mt8195_mt6359_driver); /* Module information */ -MODULE_DESCRIPTION("MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver"); +MODULE_DESCRIPTION("MT8195-MT6359 ALSA SoC machine driver"); MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>"); MODULE_AUTHOR("YC Hung <yc.hung@mediatek.com>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("mt8195_mt6359_rt1019_rt5682 soc card"); +MODULE_ALIAS("mt8195_mt6359 soc card"); diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 879c1221a809..7afe1a1acc56 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -754,6 +754,7 @@ static int mxs_saif_probe(struct platform_device *pdev) saif->master_id = saif->id; } else { ret = of_alias_get_id(master, "saif"); + of_node_put(master); if (ret < 0) return ret; else diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index cecbec2a09d7..a045693d5bc2 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -45,7 +45,7 @@ config SND_PXA2XX_SOC_CORGI tristate "SoC Audio support for Sharp Zaurus SL-C7x0" depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx && I2C select SND_PXA2XX_SOC_I2S - select SND_SOC_WM8731 + select SND_SOC_WM8731_I2C help Say Y if you want to add support for SoC audio on Sharp Zaurus SL-C7x0 models (Corgi, Shepherd, Husky). @@ -71,7 +71,7 @@ config SND_PXA2XX_SOC_POODLE tristate "SoC Audio support for Poodle" depends on SND_PXA2XX_SOC && MACH_POODLE && I2C select SND_PXA2XX_SOC_I2S - select SND_SOC_WM8731 + select SND_SOC_WM8731_I2C help Say Y if you want to add support for SoC audio on Sharp Zaurus SL-5600 model (Poodle). diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c index 7334fac758de..9a816156f012 100644 --- a/sound/soc/pxa/hx4700.c +++ b/sound/soc/pxa/hx4700.c @@ -122,9 +122,9 @@ static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd) int err; /* Jack detection API stuff */ - err = snd_soc_card_jack_new(rtd->card, "Headphone Jack", - SND_JACK_HEADPHONE, &hs_jack, hs_jack_pin, - ARRAY_SIZE(hs_jack_pin)); + err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &hs_jack, + hs_jack_pin, ARRAY_SIZE(hs_jack_pin)); if (err) return err; diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index b92ea1a0453f..65257f7fe4c4 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -71,9 +71,10 @@ static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd) int err; /* Jack detection API stuff */ - err = snd_soc_card_jack_new(rtd->card, "Headphone Jack", - SND_JACK_HEADPHONE, &hs_jack, hs_jack_pins, - ARRAY_SIZE(hs_jack_pins)); + err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &hs_jack, + hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (err) return err; diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c index d5f2961b1a3e..6cc970bb2aac 100644 --- a/sound/soc/pxa/ttc-dkb.c +++ b/sound/soc/pxa/ttc-dkb.c @@ -64,12 +64,14 @@ static int ttc_pm860x_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; /* Headset jack detection */ - snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, - &hs_jack, hs_jack_pins, ARRAY_SIZE(hs_jack_pins)); - snd_soc_card_jack_new(rtd->card, "Microphone Jack", SND_JACK_MICROPHONE, - &mic_jack, mic_jack_pins, - ARRAY_SIZE(mic_jack_pins)); + snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2, + &hs_jack, + hs_jack_pins, ARRAY_SIZE(hs_jack_pins)); + snd_soc_card_jack_new_pins(rtd->card, "Microphone Jack", + SND_JACK_MICROPHONE, &mic_jack, + mic_jack_pins, ARRAY_SIZE(mic_jack_pins)); /* headphone, microphone detection & headset short detection */ pm860x_hs_jack_detect(component, &hs_jack, SND_JACK_HEADPHONE, diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index edf2b9eec5b8..f4a7cfe22115 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c @@ -132,9 +132,10 @@ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd) int ret; /* Jack detection API stuff */ - ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET, - &hs_jack, hs_jack_pins, - ARRAY_SIZE(hs_jack_pins)); + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET, &hs_jack, + hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (ret) goto err; diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 28d0dfb4033c..750653404ba3 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -197,6 +197,8 @@ config SND_SOC_SC7280 select SND_SOC_LPASS_MACRO_COMMON imply SND_SOC_LPASS_RX_MACRO imply SND_SOC_LPASS_TX_MACRO + select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S help Add support for audio on Qualcomm Technologies Inc. SC7280 SoC-based systems. diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index f9d69375320e..b0a4f7ca2751 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -96,7 +96,7 @@ static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s) SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4, - &pdata->jack, NULL, 0); + &pdata->jack); if (rval < 0) { dev_err(card->dev, "Unable to add Headphone Jack\n"); diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 74d62f377dfd..f03a7ae49d50 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -1160,7 +1160,7 @@ static int lpass_platform_prealloc_cdc_dma_buffer(struct snd_soc_component *comp break; } - buf->area = (unsigned char * __force)memremap(buf->addr, buf->bytes, MEMREMAP_WT); + buf->area = (unsigned char * __force)memremap(buf->addr, buf->bytes, MEMREMAP_WC); return 0; } diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c index 37225ef2563a..efccb5c0b3e0 100644 --- a/sound/soc/qcom/sc7180.c +++ b/sound/soc/qcom/sc7180.c @@ -57,7 +57,7 @@ static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADPHONE | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pdata->hs_jack, NULL, 0); + &pdata->hs_jack); if (rval < 0) { dev_err(card->dev, "Unable to add Headset Jack\n"); @@ -89,7 +89,7 @@ static int sc7180_hdmi_init(struct snd_soc_pcm_runtime *rtd) rval = snd_soc_card_jack_new( card, "HDMI Jack", SND_JACK_LINEOUT, - &pdata->hdmi_jack, NULL, 0); + &pdata->hdmi_jack); if (rval < 0) { dev_err(card->dev, "Unable to add HDMI Jack\n"); diff --git a/sound/soc/qcom/sc7280.c b/sound/soc/qcom/sc7280.c index bd0bf9c8cb28..34cdb99d4ed6 100644 --- a/sound/soc/qcom/sc7280.c +++ b/sound/soc/qcom/sc7280.c @@ -12,14 +12,21 @@ #include <sound/jack.h> #include <sound/pcm.h> #include <sound/soc.h> +#include <sound/rt5682s.h> #include <linux/soundwire/sdw.h> +#include "../codecs/rt5682.h" +#include "../codecs/rt5682s.h" #include "common.h" #include "lpass.h" +#define DEFAULT_MCLK_RATE 19200000 +#define RT5682_PLL_FREQ (48000 * 512) + struct sc7280_snd_data { struct snd_soc_card card; struct sdw_stream_runtime *sruntime[LPASS_MAX_PORTS]; + u32 pri_mi2s_clk_count; struct snd_soc_jack hs_jack; struct snd_soc_jack hdmi_jack; bool jack_setup; @@ -50,7 +57,7 @@ static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5, - &pdata->hs_jack, NULL, 0); + &pdata->hs_jack); if (rval < 0) { dev_err(card->dev, "Unable to add Headset Jack\n"); @@ -69,6 +76,7 @@ static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd) pdata->jack_setup = true; } switch (cpu_dai->id) { + case MI2S_PRIMARY: case LPASS_CDC_DMA_RX0: case LPASS_CDC_DMA_TX3: for_each_rtd_codec_dais(rtd, i, codec_dai) { @@ -96,7 +104,7 @@ static int sc7280_hdmi_init(struct snd_soc_pcm_runtime *rtd) int rval; rval = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT, - &pdata->hdmi_jack, NULL, 0); + &pdata->hdmi_jack); if (rval < 0) { dev_err(card->dev, "Unable to add HDMI Jack\n"); @@ -110,11 +118,51 @@ static int sc7280_hdmi_init(struct snd_soc_pcm_runtime *rtd) return snd_soc_component_set_jack(component, &pdata->hdmi_jack, NULL); } +static int sc7280_rt5682_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_card *card = rtd->card; + struct sc7280_snd_data *data = snd_soc_card_get_drvdata(card); + int ret; + + if (++data->pri_mi2s_clk_count == 1) { + snd_soc_dai_set_sysclk(cpu_dai, + LPASS_MCLK0, + DEFAULT_MCLK_RATE, + SNDRV_PCM_STREAM_PLAYBACK); + } + snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_CBC_CFC | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_I2S); + + ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL2, RT5682S_PLL_S_MCLK, + DEFAULT_MCLK_RATE, RT5682_PLL_FREQ); + if (ret) { + dev_err(rtd->dev, "can't set codec pll: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL2, + RT5682_PLL_FREQ, + SND_SOC_CLOCK_IN); + + if (ret) { + dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", + ret); + return ret; + } + + return 0; +} + static int sc7280_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); switch (cpu_dai->id) { + case MI2S_PRIMARY: case LPASS_CDC_DMA_TX3: return sc7280_headset_init(rtd); case LPASS_CDC_DMA_RX0: @@ -227,10 +275,54 @@ static int sc7280_snd_hw_free(struct snd_pcm_substream *substream) return 0; } +static void sc7280_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct sc7280_snd_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + + switch (cpu_dai->id) { + case MI2S_PRIMARY: + if (--data->pri_mi2s_clk_count == 0) { + snd_soc_dai_set_sysclk(cpu_dai, + LPASS_MCLK0, + 0, + SNDRV_PCM_STREAM_PLAYBACK); + } + break; + default: + break; + } +} + +static int sc7280_snd_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + int ret = 0; + + switch (cpu_dai->id) { + case MI2S_PRIMARY: + ret = sc7280_rt5682_init(rtd); + break; + default: + break; + } + return ret; +} + static const struct snd_soc_ops sc7280_ops = { + .startup = sc7280_snd_startup, .hw_params = sc7280_snd_hw_params, .hw_free = sc7280_snd_hw_free, .prepare = sc7280_snd_prepare, + .shutdown = sc7280_snd_shutdown, +}; + +static const struct snd_soc_dapm_widget sc7280_snd_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), }; static int sc7280_snd_platform_probe(struct platform_device *pdev) @@ -252,6 +344,9 @@ static int sc7280_snd_platform_probe(struct platform_device *pdev) card->driver_name = "SC7280"; card->dev = dev; + card->dapm_widgets = sc7280_snd_widgets; + card->num_dapm_widgets = ARRAY_SIZE(sc7280_snd_widgets); + ret = qcom_snd_parse_of(card); if (ret) return ret; diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 5c1d13eccbee..61fda790f375 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -247,7 +247,7 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_HEADPHONE | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pdata->jack, NULL, 0); + &pdata->jack); if (rval < 0) { dev_err(card->dev, "Unable to add Headphone Jack\n"); diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index 114a29e01c0f..6e1184c8b672 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -41,7 +41,7 @@ static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd) SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5, - &data->jack, NULL, 0); + &data->jack); if (rval < 0) { dev_err(card->dev, "Unable to add Headphone Jack\n"); diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c index b052642ea620..bcdeddeba80c 100644 --- a/sound/soc/rockchip/rk3288_hdmi_analog.c +++ b/sound/soc/rockchip/rk3288_hdmi_analog.c @@ -124,10 +124,10 @@ static int rk_init(struct snd_soc_pcm_runtime *runtime) /* Enable Headset Jack detection */ if (gpio_is_valid(machine->gpio_hp_det)) { - snd_soc_card_jack_new(runtime->card, "Headphone Jack", - SND_JACK_HEADPHONE, &headphone_jack, - headphone_jack_pins, - ARRAY_SIZE(headphone_jack_pins)); + snd_soc_card_jack_new_pins(runtime->card, "Headphone Jack", + SND_JACK_HEADPHONE, &headphone_jack, + headphone_jack_pins, + ARRAY_SIZE(headphone_jack_pins)); rk_hp_jack_gpio.gpio = machine->gpio_hp_det; snd_soc_jack_add_gpios(&headphone_jack, 1, &rk_hp_jack_gpio); } diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index eeef3ed70037..2540b9ba37c8 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -174,7 +174,7 @@ static int rockchip_sound_cdndp_init(struct snd_soc_pcm_runtime *rtd) /* Enable jack detection. */ ret = snd_soc_card_jack_new(card, "DP Jack", SND_JACK_LINEOUT, - &cdn_dp_card_jack, NULL, 0); + &cdn_dp_card_jack); if (ret) { dev_err(card->dev, "Can't create DP Jack %d\n", ret); return ret; @@ -204,13 +204,13 @@ static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd) } /* Enable Headset and 4 Buttons Jack detection */ - ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &rockchip_sound_jack, - rockchip_sound_jack_pins, - ARRAY_SIZE(rockchip_sound_jack_pins)); + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &rockchip_sound_jack, + rockchip_sound_jack_pins, + ARRAY_SIZE(rockchip_sound_jack_pins)); if (ret) { dev_err(rtd->card->dev, "New Headset Jack failed! (%d)\n", ret); diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index c8f1a28a92b7..150ac524a590 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -231,7 +231,7 @@ static int rk_hdmi_init(struct snd_soc_pcm_runtime *runtime) /* enable jack detection */ ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT, - &rk_hdmi_jack, NULL, 0); + &rk_hdmi_jack); if (ret) { dev_err(card->dev, "Can't new HDMI Jack %d\n", ret); return ret; @@ -345,13 +345,13 @@ static int rk_98090_headset_init(struct snd_soc_component *component) int ret; /* Enable Headset and 4 Buttons Jack detection */ - ret = snd_soc_card_jack_new(component->card, "Headset Jack", - SND_JACK_HEADSET | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &headset_jack, - headset_jack_pins, - ARRAY_SIZE(headset_jack_pins)); + ret = snd_soc_card_jack_new_pins(component->card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &headset_jack, + headset_jack_pins, + ARRAY_SIZE(headset_jack_pins)); if (ret) return ret; diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c index 16ca2ad92426..d07cc5c813f2 100644 --- a/sound/soc/rockchip/rockchip_rt5645.c +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -107,7 +107,7 @@ static int rk_init(struct snd_soc_pcm_runtime *runtime) SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &headset_jack, NULL, 0); + &headset_jack); if (ret) { dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret); return ret; diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 5265e546b124..bb0cf4244e00 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -343,7 +343,7 @@ static int aries_late_probe(struct snd_soc_card *card) struct aries_wm8994_data *priv = snd_soc_card_get_drvdata(card); int ret, irq; - ret = snd_soc_card_jack_new(card, "Dock", SND_JACK_LINEOUT, + ret = snd_soc_card_jack_new_pins(card, "Dock", SND_JACK_LINEOUT, &aries_dock, dock_pins, ARRAY_SIZE(dock_pins)); if (ret) return ret; @@ -361,7 +361,7 @@ static int aries_late_probe(struct snd_soc_card *card) else snd_soc_jack_report(&aries_dock, 0, SND_JACK_LINEOUT); - ret = snd_soc_card_jack_new(card, "Headset", + ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0, &aries_headset, jack_pins, ARRAY_SIZE(jack_pins)); @@ -585,10 +585,10 @@ static int aries_audio_probe(struct platform_device *pdev) extcon_np = of_parse_phandle(np, "extcon", 0); priv->usb_extcon = extcon_find_edev_by_node(extcon_np); + of_node_put(extcon_np); if (IS_ERR(priv->usb_extcon)) return dev_err_probe(dev, PTR_ERR(priv->usb_extcon), "Failed to get extcon device"); - of_node_put(extcon_np); priv->adc = devm_iio_channel_get(dev, "headset-detect"); if (IS_ERR(priv->adc)) diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c index 8b83f39c3ac9..76998a4a4cad 100644 --- a/sound/soc/samsung/bells.c +++ b/sound/soc/samsung/bells.c @@ -386,11 +386,11 @@ static struct snd_soc_codec_conf bells_codec_conf[] = { }, }; -static struct snd_soc_dapm_widget bells_widgets[] = { +static const struct snd_soc_dapm_widget bells_widgets[] = { SND_SOC_DAPM_MIC("DMIC", NULL), }; -static struct snd_soc_dapm_route bells_routes[] = { +static const struct snd_soc_dapm_route bells_routes[] = { { "Sub CLK_SYS", NULL, "OPCLK" }, { "CLKIN", NULL, "OPCLK" }, diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index c994e67d1eaf..907266aee839 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -151,7 +151,8 @@ static const struct snd_soc_dapm_route audio_map[] = { static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd) { - snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, + snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins)); snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 34067cc314ff..411e25cec591 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -228,7 +228,7 @@ static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("WM1250 Output"), }; -static struct snd_soc_dapm_widget widgets[] = { +static const struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("AMIC", NULL), @@ -239,7 +239,7 @@ static struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), }; -static struct snd_soc_dapm_route audio_paths[] = { +static const struct snd_soc_dapm_route audio_paths[] = { { "Headphone", NULL, "HPOUT1L" }, { "Headphone", NULL, "HPOUT1R" }, @@ -285,7 +285,7 @@ static int littlemill_late_probe(struct snd_soc_card *card) SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5, - &littlemill_headset, NULL, 0); + &littlemill_headset); if (ret) return ret; diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 7b12ccd2a9b2..b44f5e92224f 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -51,10 +51,11 @@ static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT | - SND_JACK_HEADSET | SND_JACK_BTN_0, - &lowland_headset, lowland_headset_pins, - ARRAY_SIZE(lowland_headset_pins)); + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset", + SND_JACK_LINEOUT | SND_JACK_HEADSET | + SND_JACK_BTN_0, + &lowland_headset, lowland_headset_pins, + ARRAY_SIZE(lowland_headset_pins)); if (ret) return ret; @@ -140,7 +141,7 @@ static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), }; -static struct snd_soc_dapm_widget widgets[] = { +static const struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), @@ -150,7 +151,7 @@ static struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_MIC("Main DMIC", NULL), }; -static struct snd_soc_dapm_route audio_paths[] = { +static const struct snd_soc_dapm_route audio_paths[] = { { "Sub IN1", NULL, "HPOUT2L" }, { "Sub IN2", NULL, "HPOUT2R" }, diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index 5e9dc18687cc..6931b9a45b3e 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -309,7 +309,7 @@ static int midas_late_probe(struct snd_soc_card *card) SND_JACK_HEADSET | SND_JACK_MECHANICAL | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5, - &priv->headset_jack, NULL, 0); + &priv->headset_jack); if (ret) return ret; diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 6ea1c8cc9167..ff3acc94a454 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -201,7 +201,8 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream, static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd) { - snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, + snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins)); snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c index cee39ad16667..29bf917242fe 100644 --- a/sound/soc/samsung/smartq_wm8987.c +++ b/sound/soc/samsung/smartq_wm8987.c @@ -139,10 +139,10 @@ static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_nc_pin(dapm, "ROUT1"); /* Headphone jack detection */ - err = snd_soc_card_jack_new(rtd->card, "Headphone Jack", - SND_JACK_HEADPHONE, &smartq_jack, - smartq_jack_pins, - ARRAY_SIZE(smartq_jack_pins)); + err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &smartq_jack, + smartq_jack_pins, + ARRAY_SIZE(smartq_jack_pins)); if (err) return err; diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 226c359892e9..47b6d19e43ff 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -467,8 +467,7 @@ static int spdif_remove(struct platform_device *pdev) iounmap(spdif->regs); mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (mem_res) - release_mem_region(mem_res->start, resource_size(mem_res)); + release_mem_region(mem_res->start, resource_size(mem_res)); clk_disable_unprepare(spdif->sclk); clk_disable_unprepare(spdif->pclk); diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 37b1f4f60b21..69d7b0115b38 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -156,10 +156,12 @@ static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd) pr_err("Failed to request HP_SEL GPIO: %d\n", ret); gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity); - ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT | - SND_JACK_HEADSET | SND_JACK_BTN_0, - &speyside_headset, speyside_headset_pins, - ARRAY_SIZE(speyside_headset_pins)); + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset", + SND_JACK_LINEOUT | SND_JACK_HEADSET | + SND_JACK_BTN_0, + &speyside_headset, + speyside_headset_pins, + ARRAY_SIZE(speyside_headset_pins)); if (ret) return ret; @@ -261,7 +263,7 @@ static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), }; -static struct snd_soc_dapm_widget widgets[] = { +static const struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), @@ -271,7 +273,7 @@ static struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_MIC("Main DMIC", NULL), }; -static struct snd_soc_dapm_route audio_paths[] = { +static const struct snd_soc_dapm_route audio_paths[] = { { "IN1RN", NULL, "MICB1" }, { "IN1RP", NULL, "MICB1" }, { "IN1RN", NULL, "MICB2" }, diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index 8d3149a47a4c..9287a1d0eef1 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -130,7 +130,7 @@ static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("DMIC"), }; -static struct snd_soc_dapm_widget widgets[] = { +static const struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), @@ -140,7 +140,7 @@ static struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_SPK("Main Speaker", NULL), }; -static struct snd_soc_dapm_route audio_paths[] = { +static const struct snd_soc_dapm_route audio_paths[] = { { "Headphone", NULL, "HPOUTL" }, { "Headphone", NULL, "HPOUTR" }, @@ -189,10 +189,10 @@ static int tobermory_late_probe(struct snd_soc_card *card) if (ret < 0) return ret; - ret = snd_soc_card_jack_new(card, "Headset", SND_JACK_HEADSET | - SND_JACK_BTN_0, &tobermory_headset, - tobermory_headset_pins, - ARRAY_SIZE(tobermory_headset_pins)); + ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | + SND_JACK_BTN_0, &tobermory_headset, + tobermory_headset_pins, + ARRAY_SIZE(tobermory_headset_pins)); if (ret) return ret; diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index ae46f187cc2a..97916e3ca9dd 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -47,7 +47,7 @@ config SND_SOC_RCAR config SND_SOC_RZ tristate "RZ/G2L series SSIF-2 support" - depends on ARCH_R9A07G044 || COMPILE_TEST + depends on ARCH_RZG2L || COMPILE_TEST help This option enables RZ/G2L SSIF-2 sound support. diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 6a8fe0da7670..eb762ab94d3e 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -755,7 +755,7 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); /* set clock master for audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_CBP_CFP: rdai->clk_master = 0; break; @@ -1159,6 +1159,7 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name, struct device_node *capture) { struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device *dev = rsnd_priv_to_dev(priv); struct device_node *np; int i; @@ -1169,7 +1170,11 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name, for_each_child_of_node(node, np) { struct rsnd_mod *mod; - i = rsnd_node_fixed_index(np, name, i); + i = rsnd_node_fixed_index(dev, np, name, i); + if (i < 0) { + of_node_put(np); + break; + } mod = mod_get(priv, i); @@ -1183,7 +1188,7 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name, of_node_put(node); } -int rsnd_node_fixed_index(struct device_node *node, char *name, int idx) +int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *name, int idx) { char node_name[16]; @@ -1210,6 +1215,8 @@ int rsnd_node_fixed_index(struct device_node *node, char *name, int idx) return idx; } + dev_err(dev, "strange node numbering (%s)", + of_node_full_name(node)); return -EINVAL; } @@ -1221,10 +1228,8 @@ int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name i = 0; for_each_child_of_node(node, np) { - i = rsnd_node_fixed_index(np, name, i); + i = rsnd_node_fixed_index(dev, np, name, i); if (i < 0) { - dev_err(dev, "strange node numbering (%s)", - of_node_full_name(node)); of_node_put(np); return 0; } diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 03e0d4eca781..463ab237d7bd 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -240,12 +240,19 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name, struct rsnd_mod *mod, char *x) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); struct dma_chan *chan = NULL; struct device_node *np; int i = 0; for_each_child_of_node(of_node, np) { - i = rsnd_node_fixed_index(np, name, i); + i = rsnd_node_fixed_index(dev, np, name, i); + if (i < 0) { + chan = NULL; + of_node_put(np); + break; + } if (i == rsnd_mod_id_raw(mod) && (!chan)) chan = of_dma_request_slave_channel(np, x); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 6580bab0e229..d9cd190d7e19 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -460,7 +460,7 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name, struct device_node *playback, struct device_node *capture); int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name); -int rsnd_node_fixed_index(struct device_node *node, char *name, int idx); +int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *name, int idx); int rsnd_channel_normalization(int chan); #define rsnd_runtime_channel_original(io) \ diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 42a100c6303d..0ea84ae57c6a 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -676,7 +676,12 @@ int rsnd_src_probe(struct rsnd_priv *priv) if (!of_device_is_available(np)) goto skip; - i = rsnd_node_fixed_index(np, SRC_NAME, i); + i = rsnd_node_fixed_index(dev, np, SRC_NAME, i); + if (i < 0) { + ret = -EINVAL; + of_node_put(np); + goto rsnd_src_probe_done; + } src = rsnd_src_get(priv, i); diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 87e606f688d3..43c5e27dc5c8 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -1105,6 +1105,7 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, struct device_node *capture) { struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device *dev = rsnd_priv_to_dev(priv); struct device_node *node; struct device_node *np; int i; @@ -1117,7 +1118,11 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, for_each_child_of_node(node, np) { struct rsnd_mod *mod; - i = rsnd_node_fixed_index(np, SSI_NAME, i); + i = rsnd_node_fixed_index(dev, np, SSI_NAME, i); + if (i < 0) { + of_node_put(np); + break; + } mod = rsnd_ssi_mod_get(priv, i); @@ -1182,7 +1187,12 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) if (!of_device_is_available(np)) goto skip; - i = rsnd_node_fixed_index(np, SSI_NAME, i); + i = rsnd_node_fixed_index(dev, np, SSI_NAME, i); + if (i < 0) { + ret = -EINVAL; + of_node_put(np); + goto rsnd_ssi_probe_done; + } ssi = rsnd_ssi_get(priv, i); diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 0d8f97633dd2..4b8a63e336c7 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -102,6 +102,8 @@ bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod) shift = 1; offset = 1; break; + default: + goto out; } for (i = 0; i < 4; i++) { @@ -120,7 +122,7 @@ bool rsnd_ssiu_busif_err_status_clear(struct rsnd_mod *mod) } rsnd_mod_write(mod, reg, val); } - +out: return error; } @@ -460,6 +462,7 @@ void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, struct device_node *capture) { struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device *dev = rsnd_priv_to_dev(priv); struct device_node *node = rsnd_ssiu_of_node(priv); struct rsnd_dai_stream *io_p = &rdai->playback; struct rsnd_dai_stream *io_c = &rdai->capture; @@ -472,7 +475,11 @@ void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, for_each_child_of_node(node, np) { struct rsnd_mod *mod; - i = rsnd_node_fixed_index(np, SSIU_NAME, i); + i = rsnd_node_fixed_index(dev, np, SSIU_NAME, i); + if (i < 0) { + of_node_put(np); + break; + } mod = rsnd_ssiu_mod_get(priv, i); diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index e8edaed05d4c..e392de7a262e 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -59,9 +59,7 @@ #define SSIFSR_RDC_MASK 0x3f #define SSIFSR_RDC_SHIFT 8 -#define SSIFSR_TDC(x) (((x) & 0x1f) << 24) #define SSIFSR_TDE BIT(16) -#define SSIFSR_RDC(x) (((x) & 0x1f) << 8) #define SSIFSR_RDF BIT(0) #define SSIOFR_LRCONT BIT(8) @@ -978,22 +976,24 @@ static int rz_ssi_probe(struct platform_device *pdev) /* Error Interrupt */ ssi->irq_int = platform_get_irq_byname(pdev, "int_req"); - if (ssi->irq_int < 0) - return dev_err_probe(&pdev->dev, -ENODEV, - "Unable to get SSI int_req IRQ\n"); + if (ssi->irq_int < 0) { + rz_ssi_release_dma_channels(ssi); + return ssi->irq_int; + } ret = devm_request_irq(&pdev->dev, ssi->irq_int, &rz_ssi_interrupt, 0, dev_name(&pdev->dev), ssi); - if (ret < 0) + if (ret < 0) { + rz_ssi_release_dma_channels(ssi); return dev_err_probe(&pdev->dev, ret, "irq request error (int_req)\n"); + } if (!rz_ssi_is_dma_enabled(ssi)) { /* Tx and Rx interrupts (pio only) */ ssi->irq_tx = platform_get_irq_byname(pdev, "dma_tx"); if (ssi->irq_tx < 0) - return dev_err_probe(&pdev->dev, -ENODEV, - "Unable to get SSI dma_tx IRQ\n"); + return ssi->irq_tx; ret = devm_request_irq(&pdev->dev, ssi->irq_tx, &rz_ssi_interrupt, 0, @@ -1004,8 +1004,7 @@ static int rz_ssi_probe(struct platform_device *pdev) ssi->irq_rx = platform_get_irq_byname(pdev, "dma_rx"); if (ssi->irq_rx < 0) - return dev_err_probe(&pdev->dev, -ENODEV, - "Unable to get SSI dma_rx IRQ\n"); + return ssi->irq_rx; ret = devm_request_irq(&pdev->dev, ssi->irq_rx, &rz_ssi_interrupt, 0, @@ -1016,13 +1015,16 @@ static int rz_ssi_probe(struct platform_device *pdev) } ssi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(ssi->rstc)) + if (IS_ERR(ssi->rstc)) { + rz_ssi_release_dma_channels(ssi); return PTR_ERR(ssi->rstc); + } reset_control_deassert(ssi->rstc); pm_runtime_enable(&pdev->dev); ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) { + rz_ssi_release_dma_channels(ssi); pm_runtime_disable(ssi->dev); reset_control_assert(ssi->rstc); return dev_err_probe(ssi->dev, ret, "pm_runtime_resume_and_get failed\n"); diff --git a/sound/soc/soc-card.c b/sound/soc/soc-card.c index 41c586b86dc3..4158f5aacfd3 100644 --- a/sound/soc/soc-card.c +++ b/sound/soc/soc-card.c @@ -42,8 +42,42 @@ struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, } EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); +static int jack_new(struct snd_soc_card *card, const char *id, int type, + struct snd_soc_jack *jack, bool initial_kctl) +{ + mutex_init(&jack->mutex); + jack->card = card; + INIT_LIST_HEAD(&jack->pins); + INIT_LIST_HEAD(&jack->jack_zones); + BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); + + return snd_jack_new(card->snd_card, id, type, &jack->jack, initial_kctl, false); +} + /** - * snd_soc_card_jack_new - Create a new jack + * snd_soc_card_jack_new - Create a new jack without pins + * @card: ASoC card + * @id: an identifying string for this jack + * @type: a bitmask of enum snd_jack_type values that can be detected by + * this jack + * @jack: structure to use for the jack + * + * Creates a new jack object without pins. If adding pins later, + * snd_soc_card_jack_new_pins() should be used instead with 0 as num_pins + * argument. + * + * Returns zero if successful, or a negative error code on failure. + * On success jack will be initialised. + */ +int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, + struct snd_soc_jack *jack) +{ + return soc_card_ret(card, jack_new(card, id, type, jack, true)); +} +EXPORT_SYMBOL_GPL(snd_soc_card_jack_new); + +/** + * snd_soc_card_jack_new_pins - Create a new jack with pins * @card: ASoC card * @id: an identifying string for this jack * @type: a bitmask of enum snd_jack_type values that can be detected by @@ -52,24 +86,20 @@ EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); * @pins: Array of jack pins to be added to the jack or NULL * @num_pins: Number of elements in the @pins array * - * Creates a new jack object. + * Creates a new jack object with pins. If not adding pins, + * snd_soc_card_jack_new() should be used instead. * * Returns zero if successful, or a negative error code on failure. * On success jack will be initialised. */ -int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, - struct snd_soc_jack *jack, - struct snd_soc_jack_pin *pins, unsigned int num_pins) +int snd_soc_card_jack_new_pins(struct snd_soc_card *card, const char *id, + int type, struct snd_soc_jack *jack, + struct snd_soc_jack_pin *pins, + unsigned int num_pins) { int ret; - mutex_init(&jack->mutex); - jack->card = card; - INIT_LIST_HEAD(&jack->pins); - INIT_LIST_HEAD(&jack->jack_zones); - BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); - - ret = snd_jack_new(card->snd_card, id, type, &jack->jack, false, false); + ret = jack_new(card, id, type, jack, false); if (ret) goto end; @@ -78,7 +108,7 @@ int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, end: return soc_card_ret(card, ret); } -EXPORT_SYMBOL_GPL(snd_soc_card_jack_new); +EXPORT_SYMBOL_GPL(snd_soc_card_jack_new_pins); int snd_soc_card_suspend_pre(struct snd_soc_card *card) { diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index c0664f94990c..e12f8244242b 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -932,6 +932,20 @@ int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream) return 0; } +static bool snd_soc_component_is_codec_on_rtd(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_component *component) +{ + struct snd_soc_dai *dai; + int i; + + for_each_rtd_codec_dais(rtd, i, dai) { + if (dai->component == component) + return true; + } + + return false; +} + void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream, snd_pcm_sframes_t *cpu_delay, snd_pcm_sframes_t *codec_delay) @@ -953,7 +967,7 @@ void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream, delay = component->driver->delay(component, substream); - if (snd_soc_component_is_codec(component)) + if (snd_soc_component_is_codec_on_rtd(rtd, component)) *codec_delay = max(*codec_delay, delay); else *cpu_delay = max(*cpu_delay, delay); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 8c7da82a62ca..9574f86dd4de 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1230,7 +1230,7 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, /* * Flip the polarity for the "CPU" end of a CODEC<->CODEC link */ - inv_dai_fmt = snd_soc_daifmt_clock_provider_fliped(dai_fmt); + inv_dai_fmt = snd_soc_daifmt_clock_provider_flipped(dai_fmt); for_each_rtd_cpu_dais(rtd, i, cpu_dai) { unsigned int fmt = dai_fmt; @@ -2497,7 +2497,7 @@ static int snd_soc_register_dais(struct snd_soc_component *component, for (i = 0; i < count; i++) { dai = snd_soc_register_dai(component, dai_drv + i, count == 1 && - !snd_soc_component_is_codec(component)); + !component->driver->non_legacy_dai_naming); if (dai == NULL) { ret = -ENOMEM; goto err; @@ -2763,6 +2763,11 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, "ASoC: Property '%s' does not exist\n", propname); return -EINVAL; } + if (!num_widgets) { + dev_err(card->dev, "ASoC: Property '%s's length is zero\n", + propname); + return -EINVAL; + } if (num_widgets & 1) { dev_err(card->dev, "ASoC: Property '%s' length is not even\n", propname); @@ -2770,11 +2775,6 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, } num_widgets /= 2; - if (!num_widgets) { - dev_err(card->dev, "ASoC: Property '%s's length is zero\n", - propname); - return -EINVAL; - } widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets), GFP_KERNEL); @@ -3035,7 +3035,7 @@ int snd_soc_of_parse_aux_devs(struct snd_soc_card *card, const char *propname) } EXPORT_SYMBOL_GPL(snd_soc_of_parse_aux_devs); -unsigned int snd_soc_daifmt_clock_provider_fliped(unsigned int dai_fmt) +unsigned int snd_soc_daifmt_clock_provider_flipped(unsigned int dai_fmt) { unsigned int inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; @@ -3056,7 +3056,7 @@ unsigned int snd_soc_daifmt_clock_provider_fliped(unsigned int dai_fmt) return inv_dai_fmt; } -EXPORT_SYMBOL_GPL(snd_soc_daifmt_clock_provider_fliped); +EXPORT_SYMBOL_GPL(snd_soc_daifmt_clock_provider_flipped); unsigned int snd_soc_daifmt_clock_provider_from_bitmap(unsigned int bit_frame) { @@ -3395,6 +3395,86 @@ err: } EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs); +/* + * snd_soc_of_put_dai_link_cpus - Dereference device nodes in the codecs array + * @dai_link: DAI link + * + * Dereference device nodes acquired by snd_soc_of_get_dai_link_cpus(). + */ +void snd_soc_of_put_dai_link_cpus(struct snd_soc_dai_link *dai_link) +{ + struct snd_soc_dai_link_component *component; + int index; + + for_each_link_cpus(dai_link, index, component) { + if (!component->of_node) + break; + of_node_put(component->of_node); + component->of_node = NULL; + } +} +EXPORT_SYMBOL_GPL(snd_soc_of_put_dai_link_cpus); + +/* + * snd_soc_of_get_dai_link_cpus - Parse a list of CPU DAIs in the devicetree + * @dev: Card device + * @of_node: Device node + * @dai_link: DAI link + * + * Is analogous to snd_soc_of_get_dai_link_codecs but parses a list of CPU DAIs + * instead. + * + * Returns 0 for success + */ +int snd_soc_of_get_dai_link_cpus(struct device *dev, + struct device_node *of_node, + struct snd_soc_dai_link *dai_link) +{ + struct of_phandle_args args; + struct snd_soc_dai_link_component *component; + char *name; + int index, num_codecs, ret; + + /* Count the number of CODECs */ + name = "sound-dai"; + num_codecs = of_count_phandle_with_args(of_node, name, + "#sound-dai-cells"); + if (num_codecs <= 0) { + if (num_codecs == -ENOENT) + dev_err(dev, "No 'sound-dai' property\n"); + else + dev_err(dev, "Bad phandle in 'sound-dai'\n"); + return num_codecs; + } + component = devm_kcalloc(dev, + num_codecs, sizeof(*component), + GFP_KERNEL); + if (!component) + return -ENOMEM; + dai_link->cpus = component; + dai_link->num_cpus = num_codecs; + + /* Parse the list */ + for_each_link_cpus(dai_link, index, component) { + ret = of_parse_phandle_with_args(of_node, name, + "#sound-dai-cells", + index, &args); + if (ret) + goto err; + component->of_node = args.np; + ret = snd_soc_get_dai_name(&args, &component->dai_name); + if (ret < 0) + goto err; + } + return 0; +err: + snd_soc_of_put_dai_link_codecs(dai_link); + dai_link->cpus = NULL; + dai_link->num_cpus = 0; + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_cpus); + static int __init snd_soc_init(void) { snd_soc_debugfs_init(); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index ca917a849c42..869c76506b66 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3437,7 +3437,6 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, update.val = val; card->update = &update; } - change |= reg_change; ret = soc_dapm_mixer_update_power(card, kcontrol, connect, rconnect); @@ -3539,7 +3538,6 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.val = val; card->update = &update; } - change |= reg_change; ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e); diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 285441d6aeed..87858462bba9 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -79,29 +79,19 @@ static int dmaengine_pcm_hw_params(struct snd_soc_component *component, { struct dmaengine_pcm *pcm = soc_component_to_pcm(component); struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); - int (*prepare_slave_config)(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct dma_slave_config *slave_config); struct dma_slave_config slave_config; + int ret; - memset(&slave_config, 0, sizeof(slave_config)); - - if (!pcm->config) - prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config; - else - prepare_slave_config = pcm->config->prepare_slave_config; + if (!pcm->config->prepare_slave_config) + return 0; - if (prepare_slave_config) { - int ret = prepare_slave_config(substream, params, &slave_config); - if (ret) - return ret; + memset(&slave_config, 0, sizeof(slave_config)); - ret = dmaengine_slave_config(chan, &slave_config); - if (ret) - return ret; - } + ret = pcm->config->prepare_slave_config(substream, params, &slave_config); + if (ret) + return ret; - return 0; + return dmaengine_slave_config(chan, &slave_config); } static int @@ -121,7 +111,7 @@ dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, return -EINVAL; } - if (pcm->config && pcm->config->pcm_hardware) + if (pcm->config->pcm_hardware) return snd_soc_set_runtime_hwparams(substream, pcm->config->pcm_hardware); @@ -188,7 +178,6 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( { struct dmaengine_pcm *pcm = soc_component_to_pcm(component); struct snd_dmaengine_dai_dma_data *dma_data; - dma_filter_fn fn = NULL; if (rtd->num_cpus > 1) { dev_err(rtd->dev, @@ -201,13 +190,11 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0]) return pcm->chan[0]; - if (pcm->config && pcm->config->compat_request_channel) + if (pcm->config->compat_request_channel) return pcm->config->compat_request_channel(rtd, substream); - if (pcm->config) - fn = pcm->config->compat_filter_fn; - - return snd_dmaengine_pcm_request_channel(fn, dma_data->filter_data); + return snd_dmaengine_pcm_request_channel(pcm->config->compat_filter_fn, + dma_data->filter_data); } static bool dmaengine_pcm_can_report_residue(struct device *dev, @@ -239,12 +226,12 @@ static int dmaengine_pcm_new(struct snd_soc_component *component, size_t max_buffer_size; unsigned int i; - if (config && config->prealloc_buffer_size) + if (config->prealloc_buffer_size) prealloc_buffer_size = config->prealloc_buffer_size; else prealloc_buffer_size = prealloc_buffer_size_kbytes * 1024; - if (config && config->pcm_hardware && config->pcm_hardware->buffer_bytes_max) + if (config->pcm_hardware && config->pcm_hardware->buffer_bytes_max) max_buffer_size = config->pcm_hardware->buffer_bytes_max; else max_buffer_size = SIZE_MAX; @@ -254,7 +241,7 @@ static int dmaengine_pcm_new(struct snd_soc_component *component, if (!substream) continue; - if (!pcm->chan[i] && config && config->chan_names[i]) + if (!pcm->chan[i] && config->chan_names[i]) pcm->chan[i] = dma_request_slave_channel(dev, config->chan_names[i]); @@ -367,10 +354,10 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, struct dma_chan *chan; if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || (!dev->of_node && - !(config && config->dma_dev && config->dma_dev->of_node))) + !(config->dma_dev && config->dma_dev->of_node))) return 0; - if (config && config->dma_dev) { + if (config->dma_dev) { /* * If this warning is seen, it probably means that your Linux * device structure does not match your HW device structure. @@ -387,7 +374,7 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, name = "rx-tx"; else name = dmaengine_pcm_dma_channel_names[i]; - if (config && config->chan_names[i]) + if (config->chan_names[i]) name = config->chan_names[i]; chan = dma_request_chan(dev, name); if (IS_ERR(chan)) { @@ -425,6 +412,10 @@ static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm) } } +static const struct snd_dmaengine_pcm_config snd_dmaengine_pcm_default_config = { + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, +}; + /** * snd_dmaengine_pcm_register - Register a dmaengine based PCM device * @dev: The parent device for the PCM device @@ -445,6 +436,8 @@ int snd_dmaengine_pcm_register(struct device *dev, #ifdef CONFIG_DEBUG_FS pcm->component.debugfs_prefix = "dma"; #endif + if (!config) + config = &snd_dmaengine_pcm_default_config; pcm->config = config; pcm->flags = flags; @@ -452,7 +445,7 @@ int snd_dmaengine_pcm_register(struct device *dev, if (ret) goto err_free_dma; - if (config && config->process) + if (config->process) driver = &dmaengine_pcm_component_process; else driver = &dmaengine_pcm_component; diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index d798765d168c..fcece5ca38c6 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -126,7 +126,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_get_type); /** * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack * - * @jack: ASoC jack + * @jack: ASoC jack created with snd_soc_card_jack_new_pins() * @count: Number of pins * @pins: Array of pins * diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 11c9853e9e80..6f43db35a5c8 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2090,6 +2090,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd) { struct snd_soc_pcm_runtime *be; + bool pause_stop_transition; struct snd_soc_dpcm *dpcm; unsigned long flags; int ret = 0; @@ -2121,6 +2122,13 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, if (be->dpcm[stream].be_start != 1) goto next; + if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_PAUSED) + ret = soc_pcm_trigger(be_substream, + SNDRV_PCM_TRIGGER_PAUSE_RELEASE); + else + ret = soc_pcm_trigger(be_substream, + SNDRV_PCM_TRIGGER_START); + ret = soc_pcm_trigger(be_substream, cmd); if (ret) { be->dpcm[stream].be_start--; @@ -2148,10 +2156,12 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (!be->dpcm[stream].be_start && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) && - (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) goto next; + fe->dpcm[stream].fe_pause = false; + be->dpcm[stream].be_pause--; + be->dpcm[stream].be_start++; if (be->dpcm[stream].be_start != 1) goto next; @@ -2175,14 +2185,33 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, if (be->dpcm[stream].be_start != 0) goto next; - ret = soc_pcm_trigger(be_substream, cmd); + pause_stop_transition = false; + if (fe->dpcm[stream].fe_pause) { + pause_stop_transition = true; + fe->dpcm[stream].fe_pause = false; + be->dpcm[stream].be_pause--; + } + + if (be->dpcm[stream].be_pause != 0) + ret = soc_pcm_trigger(be_substream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); + else + ret = soc_pcm_trigger(be_substream, SNDRV_PCM_TRIGGER_STOP); + if (ret) { if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START) be->dpcm[stream].be_start++; + if (pause_stop_transition) { + fe->dpcm[stream].fe_pause = true; + be->dpcm[stream].be_pause++; + } goto next; } - be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; + if (be->dpcm[stream].be_pause != 0) + be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; + else + be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; + break; case SNDRV_PCM_TRIGGER_SUSPEND: if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) @@ -2204,6 +2233,9 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) goto next; + fe->dpcm[stream].fe_pause = true; + be->dpcm[stream].be_pause++; + be->dpcm[stream].be_start--; if (be->dpcm[stream].be_start != 0) goto next; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 3bb90a819650..3f9d314fba16 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -40,7 +40,7 @@ */ #define SOC_TPLG_PASS_MANIFEST 0 #define SOC_TPLG_PASS_VENDOR 1 -#define SOC_TPLG_PASS_MIXER 2 +#define SOC_TPLG_PASS_CONTROL 2 #define SOC_TPLG_PASS_WIDGET 3 #define SOC_TPLG_PASS_PCM_DAI 4 #define SOC_TPLG_PASS_GRAPH 5 @@ -104,13 +104,13 @@ static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size, return 0; } -static inline int soc_tplg_is_eof(struct soc_tplg *tplg) +static inline bool soc_tplg_is_eof(struct soc_tplg *tplg) { const u8 *end = tplg->hdr_pos; if (end >= tplg->fw->data + tplg->fw->size) - return 1; - return 0; + return true; + return false; } static inline unsigned long soc_tplg_get_hdr_offset(struct soc_tplg *tplg) @@ -237,7 +237,7 @@ static inline void soc_control_err(struct soc_tplg *tplg, struct snd_soc_tplg_ctl_hdr *hdr, const char *name) { dev_err(tplg->dev, - "ASoC: no complete mixer IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n", + "ASoC: no complete control IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n", name, hdr->ops.get, hdr->ops.put, hdr->ops.info, soc_tplg_get_offset(tplg)); } @@ -360,7 +360,7 @@ static void remove_mixer(struct snd_soc_component *comp, { struct snd_card *card = comp->card->snd_card; - if (pass != SOC_TPLG_PASS_MIXER) + if (pass != SOC_TPLG_PASS_CONTROL) return; if (dobj->ops && dobj->ops->control_unload) @@ -376,7 +376,7 @@ static void remove_enum(struct snd_soc_component *comp, { struct snd_card *card = comp->card->snd_card; - if (pass != SOC_TPLG_PASS_MIXER) + if (pass != SOC_TPLG_PASS_CONTROL) return; if (dobj->ops && dobj->ops->control_unload) @@ -392,7 +392,7 @@ static void remove_bytes(struct snd_soc_component *comp, { struct snd_card *card = comp->card->snd_card; - if (pass != SOC_TPLG_PASS_MIXER) + if (pass != SOC_TPLG_PASS_CONTROL) return; if (dobj->ops && dobj->ops->control_unload) @@ -618,7 +618,7 @@ int snd_soc_tplg_widget_bind_event(struct snd_soc_dapm_widget *w, EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event); /* optionally pass new dynamic kcontrol to component driver. */ -static int soc_tplg_init_kcontrol(struct soc_tplg *tplg, +static int soc_tplg_control_load(struct soc_tplg *tplg, struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr) { if (tplg->ops && tplg->ops->control_load) @@ -676,175 +676,156 @@ static int soc_tplg_create_tlv(struct soc_tplg *tplg, return 0; } -static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count, - size_t size) +static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size) { struct snd_soc_tplg_bytes_control *be; struct soc_bytes_ext *sbe; struct snd_kcontrol_new kc; - int i; - int err = 0; + int ret = 0; if (soc_tplg_check_elem_count(tplg, sizeof(struct snd_soc_tplg_bytes_control), - count, size, "mixer bytes")) + 1, size, "mixer bytes")) return -EINVAL; - for (i = 0; i < count; i++) { - be = (struct snd_soc_tplg_bytes_control *)tplg->pos; + be = (struct snd_soc_tplg_bytes_control *)tplg->pos; - /* validate kcontrol */ - if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; + /* validate kcontrol */ + if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; - sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL); - if (sbe == NULL) - return -ENOMEM; + sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL); + if (sbe == NULL) + return -ENOMEM; - tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + - le32_to_cpu(be->priv.size)); + tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + + le32_to_cpu(be->priv.size)); - dev_dbg(tplg->dev, - "ASoC: adding bytes kcontrol %s with access 0x%x\n", - be->hdr.name, be->hdr.access); - - memset(&kc, 0, sizeof(kc)); - kc.name = be->hdr.name; - kc.private_value = (long)sbe; - kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc.access = le32_to_cpu(be->hdr.access); - - sbe->max = le32_to_cpu(be->max); - sbe->dobj.type = SND_SOC_DOBJ_BYTES; - sbe->dobj.ops = tplg->ops; - INIT_LIST_HEAD(&sbe->dobj.list); - - /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg); - if (err) { - soc_control_err(tplg, &be->hdr, be->hdr.name); - break; - } + dev_dbg(tplg->dev, + "ASoC: adding bytes kcontrol %s with access 0x%x\n", + be->hdr.name, be->hdr.access); - /* pass control to driver for optional further init */ - err = soc_tplg_init_kcontrol(tplg, &kc, - (struct snd_soc_tplg_ctl_hdr *)be); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to init %s\n", - be->hdr.name); - break; - } + memset(&kc, 0, sizeof(kc)); + kc.name = be->hdr.name; + kc.private_value = (long)sbe; + kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc.access = le32_to_cpu(be->hdr.access); - /* register control here */ - err = soc_tplg_add_kcontrol(tplg, &kc, - &sbe->dobj.control.kcontrol); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to add %s\n", - be->hdr.name); - break; - } + sbe->max = le32_to_cpu(be->max); + sbe->dobj.type = SND_SOC_DOBJ_BYTES; + sbe->dobj.ops = tplg->ops; + INIT_LIST_HEAD(&sbe->dobj.list); - list_add(&sbe->dobj.list, &tplg->comp->dobj_list); + /* map io handlers */ + ret = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg); + if (ret) { + soc_control_err(tplg, &be->hdr, be->hdr.name); + goto err; } - return err; + /* pass control to driver for optional further init */ + ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)be); + if (ret < 0) { + dev_err(tplg->dev, "ASoC: failed to init %s\n", be->hdr.name); + goto err; + } + + /* register control here */ + ret = soc_tplg_add_kcontrol(tplg, &kc, &sbe->dobj.control.kcontrol); + if (ret < 0) { + dev_err(tplg->dev, "ASoC: failed to add %s\n", be->hdr.name); + goto err; + } + + list_add(&sbe->dobj.list, &tplg->comp->dobj_list); + +err: + return ret; } -static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count, - size_t size) +static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size) { struct snd_soc_tplg_mixer_control *mc; struct soc_mixer_control *sm; struct snd_kcontrol_new kc; - int i; - int err = 0; + int ret = 0; if (soc_tplg_check_elem_count(tplg, sizeof(struct snd_soc_tplg_mixer_control), - count, size, "mixers")) + 1, size, "mixers")) return -EINVAL; - for (i = 0; i < count; i++) { - mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; + mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; - /* validate kcontrol */ - if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; + /* validate kcontrol */ + if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; - sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL); - if (sm == NULL) - return -ENOMEM; - tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) + - le32_to_cpu(mc->priv.size)); + sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL); + if (sm == NULL) + return -ENOMEM; + tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) + + le32_to_cpu(mc->priv.size)); - dev_dbg(tplg->dev, - "ASoC: adding mixer kcontrol %s with access 0x%x\n", - mc->hdr.name, mc->hdr.access); - - memset(&kc, 0, sizeof(kc)); - kc.name = mc->hdr.name; - kc.private_value = (long)sm; - kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc.access = le32_to_cpu(mc->hdr.access); - - /* we only support FL/FR channel mapping atm */ - sm->reg = tplc_chan_get_reg(tplg, mc->channel, - SNDRV_CHMAP_FL); - sm->rreg = tplc_chan_get_reg(tplg, mc->channel, - SNDRV_CHMAP_FR); - sm->shift = tplc_chan_get_shift(tplg, mc->channel, - SNDRV_CHMAP_FL); - sm->rshift = tplc_chan_get_shift(tplg, mc->channel, - SNDRV_CHMAP_FR); - - sm->max = le32_to_cpu(mc->max); - sm->min = le32_to_cpu(mc->min); - sm->invert = le32_to_cpu(mc->invert); - sm->platform_max = le32_to_cpu(mc->platform_max); - sm->dobj.index = tplg->index; - sm->dobj.ops = tplg->ops; - sm->dobj.type = SND_SOC_DOBJ_MIXER; - INIT_LIST_HEAD(&sm->dobj.list); - - /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg); - if (err) { - soc_control_err(tplg, &mc->hdr, mc->hdr.name); - break; - } + dev_dbg(tplg->dev, + "ASoC: adding mixer kcontrol %s with access 0x%x\n", + mc->hdr.name, mc->hdr.access); - /* create any TLV data */ - err = soc_tplg_create_tlv(tplg, &kc, &mc->hdr); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", - mc->hdr.name); - break; - } + memset(&kc, 0, sizeof(kc)); + kc.name = mc->hdr.name; + kc.private_value = (long)sm; + kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc.access = le32_to_cpu(mc->hdr.access); - /* pass control to driver for optional further init */ - err = soc_tplg_init_kcontrol(tplg, &kc, - (struct snd_soc_tplg_ctl_hdr *) mc); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to init %s\n", - mc->hdr.name); - break; - } + /* we only support FL/FR channel mapping atm */ + sm->reg = tplc_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL); + sm->rreg = tplc_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR); + sm->shift = tplc_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL); + sm->rshift = tplc_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR); - /* register control here */ - err = soc_tplg_add_kcontrol(tplg, &kc, - &sm->dobj.control.kcontrol); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to add %s\n", - mc->hdr.name); - break; - } + sm->max = le32_to_cpu(mc->max); + sm->min = le32_to_cpu(mc->min); + sm->invert = le32_to_cpu(mc->invert); + sm->platform_max = le32_to_cpu(mc->platform_max); + sm->dobj.index = tplg->index; + sm->dobj.ops = tplg->ops; + sm->dobj.type = SND_SOC_DOBJ_MIXER; + INIT_LIST_HEAD(&sm->dobj.list); + + /* map io handlers */ + ret = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg); + if (ret) { + soc_control_err(tplg, &mc->hdr, mc->hdr.name); + goto err; + } - list_add(&sm->dobj.list, &tplg->comp->dobj_list); + /* create any TLV data */ + ret = soc_tplg_create_tlv(tplg, &kc, &mc->hdr); + if (ret < 0) { + dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", mc->hdr.name); + goto err; } - return err; + /* pass control to driver for optional further init */ + ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)mc); + if (ret < 0) { + dev_err(tplg->dev, "ASoC: failed to init %s\n", mc->hdr.name); + goto err; + } + + /* register control here */ + ret = soc_tplg_add_kcontrol(tplg, &kc, &sm->dobj.control.kcontrol); + if (ret < 0) { + dev_err(tplg->dev, "ASoC: failed to add %s\n", mc->hdr.name); + goto err; + } + + list_add(&sm->dobj.list, &tplg->comp->dobj_list); + +err: + return ret; } static int soc_tplg_denum_create_texts(struct soc_tplg *tplg, struct soc_enum *se, @@ -911,117 +892,108 @@ static int soc_tplg_denum_create_values(struct soc_tplg *tplg, struct soc_enum * return 0; } -static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count, - size_t size) +static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size) { struct snd_soc_tplg_enum_control *ec; struct soc_enum *se; struct snd_kcontrol_new kc; - int i; - int err = 0; + int ret = 0; if (soc_tplg_check_elem_count(tplg, sizeof(struct snd_soc_tplg_enum_control), - count, size, "enums")) + 1, size, "enums")) return -EINVAL; - for (i = 0; i < count; i++) { - ec = (struct snd_soc_tplg_enum_control *)tplg->pos; + ec = (struct snd_soc_tplg_enum_control *)tplg->pos; - /* validate kcontrol */ - if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == - SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return -EINVAL; + /* validate kcontrol */ + if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; - se = devm_kzalloc(tplg->dev, (sizeof(*se)), GFP_KERNEL); - if (se == NULL) - return -ENOMEM; + se = devm_kzalloc(tplg->dev, (sizeof(*se)), GFP_KERNEL); + if (se == NULL) + return -ENOMEM; - tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + - le32_to_cpu(ec->priv.size)); + tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + + le32_to_cpu(ec->priv.size)); - dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", - ec->hdr.name, ec->items); + dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", + ec->hdr.name, ec->items); - memset(&kc, 0, sizeof(kc)); - kc.name = ec->hdr.name; - kc.private_value = (long)se; - kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kc.access = le32_to_cpu(ec->hdr.access); + memset(&kc, 0, sizeof(kc)); + kc.name = ec->hdr.name; + kc.private_value = (long)se; + kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc.access = le32_to_cpu(ec->hdr.access); - se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); - se->shift_l = tplc_chan_get_shift(tplg, ec->channel, - SNDRV_CHMAP_FL); - se->shift_r = tplc_chan_get_shift(tplg, ec->channel, - SNDRV_CHMAP_FL); + se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); + se->shift_l = tplc_chan_get_shift(tplg, ec->channel, + SNDRV_CHMAP_FL); + se->shift_r = tplc_chan_get_shift(tplg, ec->channel, + SNDRV_CHMAP_FL); - se->mask = le32_to_cpu(ec->mask); - se->dobj.index = tplg->index; - se->dobj.type = SND_SOC_DOBJ_ENUM; - se->dobj.ops = tplg->ops; - INIT_LIST_HEAD(&se->dobj.list); + se->mask = le32_to_cpu(ec->mask); + se->dobj.index = tplg->index; + se->dobj.type = SND_SOC_DOBJ_ENUM; + se->dobj.ops = tplg->ops; + INIT_LIST_HEAD(&se->dobj.list); - switch (le32_to_cpu(ec->hdr.ops.info)) { - case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: - case SND_SOC_TPLG_CTL_ENUM_VALUE: - err = soc_tplg_denum_create_values(tplg, se, ec); - if (err < 0) { - dev_err(tplg->dev, - "ASoC: could not create values for %s\n", - ec->hdr.name); - goto err_denum; - } - fallthrough; - case SND_SOC_TPLG_CTL_ENUM: - case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: - err = soc_tplg_denum_create_texts(tplg, se, ec); - if (err < 0) { - dev_err(tplg->dev, - "ASoC: could not create texts for %s\n", - ec->hdr.name); - goto err_denum; - } - break; - default: - err = -EINVAL; + switch (le32_to_cpu(ec->hdr.ops.info)) { + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + ret = soc_tplg_denum_create_values(tplg, se, ec); + if (ret < 0) { dev_err(tplg->dev, - "ASoC: invalid enum control type %d for %s\n", - ec->hdr.ops.info, ec->hdr.name); - goto err_denum; - } - - /* map io handlers */ - err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg); - if (err) { - soc_control_err(tplg, &ec->hdr, ec->hdr.name); - goto err_denum; - } - - /* pass control to driver for optional further init */ - err = soc_tplg_init_kcontrol(tplg, &kc, - (struct snd_soc_tplg_ctl_hdr *) ec); - if (err < 0) { - dev_err(tplg->dev, "ASoC: failed to init %s\n", + "ASoC: could not create values for %s\n", ec->hdr.name); - goto err_denum; + goto err; } - - /* register control here */ - err = soc_tplg_add_kcontrol(tplg, - &kc, &se->dobj.control.kcontrol); - if (err < 0) { - dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n", + fallthrough; + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: + ret = soc_tplg_denum_create_texts(tplg, se, ec); + if (ret < 0) { + dev_err(tplg->dev, + "ASoC: could not create texts for %s\n", ec->hdr.name); - goto err_denum; + goto err; } + break; + default: + ret = -EINVAL; + dev_err(tplg->dev, + "ASoC: invalid enum control type %d for %s\n", + ec->hdr.ops.info, ec->hdr.name); + goto err; + } - list_add(&se->dobj.list, &tplg->comp->dobj_list); + /* map io handlers */ + ret = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg); + if (ret) { + soc_control_err(tplg, &ec->hdr, ec->hdr.name); + goto err; } - return 0; -err_denum: - return err; + /* pass control to driver for optional further init */ + ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)ec); + if (ret < 0) { + dev_err(tplg->dev, "ASoC: failed to init %s\n", ec->hdr.name); + goto err; + } + + /* register control here */ + ret = soc_tplg_add_kcontrol(tplg, &kc, &se->dobj.control.kcontrol); + if (ret < 0) { + dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n", ec->hdr.name); + goto err; + } + + list_add(&se->dobj.list, &tplg->comp->dobj_list); + +err: + return ret; } static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, @@ -1049,20 +1021,17 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, case SND_SOC_TPLG_CTL_RANGE: case SND_SOC_TPLG_DAPM_CTL_VOLSW: case SND_SOC_TPLG_DAPM_CTL_PIN: - ret = soc_tplg_dmixer_create(tplg, 1, - le32_to_cpu(hdr->payload_size)); + ret = soc_tplg_dmixer_create(tplg, le32_to_cpu(hdr->payload_size)); break; case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: - ret = soc_tplg_denum_create(tplg, 1, - le32_to_cpu(hdr->payload_size)); + ret = soc_tplg_denum_create(tplg, le32_to_cpu(hdr->payload_size)); break; case SND_SOC_TPLG_CTL_BYTES: - ret = soc_tplg_dbytes_create(tplg, 1, - le32_to_cpu(hdr->payload_size)); + ret = soc_tplg_dbytes_create(tplg, le32_to_cpu(hdr->payload_size)); break; default: soc_bind_err(tplg, control_hdr, i); @@ -1224,8 +1193,7 @@ static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_ } /* pass control to driver for optional further init */ - err = soc_tplg_init_kcontrol(tplg, kc, - (struct snd_soc_tplg_ctl_hdr *)mc); + err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)mc); if (err < 0) { dev_err(tplg->dev, "ASoC: failed to init %s\n", mc->hdr.name); @@ -1309,8 +1277,7 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k } /* pass control to driver for optional further init */ - err = soc_tplg_init_kcontrol(tplg, kc, - (struct snd_soc_tplg_ctl_hdr *)ec); + err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)ec); if (err < 0) { dev_err(tplg->dev, "ASoC: failed to init %s\n", ec->hdr.name); @@ -1362,8 +1329,7 @@ static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_ } /* pass control to driver for optional further init */ - err = soc_tplg_init_kcontrol(tplg, kc, - (struct snd_soc_tplg_ctl_hdr *)be); + err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)be); if (err < 0) { dev_err(tplg->dev, "ASoC: failed to init %s\n", be->hdr.name); @@ -2498,7 +2464,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg, case SND_SOC_TPLG_TYPE_MIXER: case SND_SOC_TPLG_TYPE_ENUM: case SND_SOC_TPLG_TYPE_BYTES: - hdr_pass = SOC_TPLG_PASS_MIXER; + hdr_pass = SOC_TPLG_PASS_CONTROL; elem_load = soc_tplg_kcontrol_elems_load; break; case SND_SOC_TPLG_TYPE_DAPM_GRAPH: @@ -2550,10 +2516,8 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg) { int ret; - tplg->pass = SOC_TPLG_PASS_START; - /* process the header types from start to end */ - while (tplg->pass <= SOC_TPLG_PASS_END) { + for (tplg->pass = SOC_TPLG_PASS_START; tplg->pass <= SOC_TPLG_PASS_END; tplg->pass++) { struct snd_soc_tplg_hdr *hdr; tplg->hdr_pos = tplg->fw->data; @@ -2585,8 +2549,6 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg) hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos; } - /* next data type pass */ - tplg->pass++; } /* signal DAPM we are complete */ @@ -2653,10 +2615,10 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp) { struct snd_card *card = comp->card->snd_card; struct snd_soc_dobj *dobj, *next_dobj; - int pass = SOC_TPLG_PASS_END; + int pass; /* process the header types from end to start */ - while (pass >= SOC_TPLG_PASS_START) { + for (pass = SOC_TPLG_PASS_END; pass >= SOC_TPLG_PASS_START; pass--) { /* remove mixer controls */ down_write(&card->controls_rwsem); @@ -2699,7 +2661,6 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp) } } up_write(&card->controls_rwsem); - pass--; } /* let caller know if FW can be freed when no objects are left */ diff --git a/sound/soc/soc-utils-test.c b/sound/soc/soc-utils-test.c new file mode 100644 index 000000000000..5ad8e23af49a --- /dev/null +++ b/sound/soc/soc-utils-test.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2022 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <kunit/test.h> +#include <linux/module.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <uapi/sound/asound.h> + +static const struct { + u32 rate; + snd_pcm_format_t fmt; + u8 channels; + u8 tdm_width; + u8 tdm_slots; + u8 slot_multiple; + u32 bclk; +} tdm_params_to_bclk_cases[] = { + /* rate fmt channels tdm_width tdm_slots slot_multiple bclk */ + + /* From params only */ + { 8000, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 0, 0, 128000 }, + { 8000, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 0, 0, 256000 }, + { 8000, SNDRV_PCM_FORMAT_S24_LE, 1, 0, 0, 0, 192000 }, + { 8000, SNDRV_PCM_FORMAT_S24_LE, 2, 0, 0, 0, 384000 }, + { 8000, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 0, 0, 256000 }, + { 8000, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 0, 0, 512000 }, + { 44100, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 0, 0, 705600 }, + { 44100, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 0, 0, 1411200 }, + { 44100, SNDRV_PCM_FORMAT_S24_LE, 1, 0, 0, 0, 1058400 }, + { 44100, SNDRV_PCM_FORMAT_S24_LE, 2, 0, 0, 0, 2116800 }, + { 44100, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 0, 0, 1411200 }, + { 44100, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 0, 0, 2822400 }, + { 384000, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 0, 0, 6144000 }, + { 384000, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 0, 0, 12288000 }, + { 384000, SNDRV_PCM_FORMAT_S24_LE, 1, 0, 0, 0, 9216000 }, + { 384000, SNDRV_PCM_FORMAT_S24_LE, 2, 0, 0, 0, 18432000 }, + { 384000, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 0, 0, 12288000 }, + { 384000, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 0, 0, 24576000 }, + + /* I2S from params */ + { 8000, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 0, 2, 256000 }, + { 8000, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 0, 2, 256000 }, + { 8000, SNDRV_PCM_FORMAT_S24_LE, 1, 0, 0, 2, 384000 }, + { 8000, SNDRV_PCM_FORMAT_S24_LE, 2, 0, 0, 2, 384000 }, + { 8000, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 0, 2, 512000 }, + { 8000, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 0, 2, 512000 }, + { 44100, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 0, 2, 1411200 }, + { 44100, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 0, 2, 1411200 }, + { 44100, SNDRV_PCM_FORMAT_S24_LE, 1, 0, 0, 2, 2116800 }, + { 44100, SNDRV_PCM_FORMAT_S24_LE, 2, 0, 0, 2, 2116800 }, + { 44100, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 0, 2, 2822400 }, + { 44100, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 0, 2, 2822400 }, + { 384000, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 0, 2, 12288000 }, + { 384000, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 0, 2, 12288000 }, + { 384000, SNDRV_PCM_FORMAT_S24_LE, 1, 0, 0, 2, 18432000 }, + { 384000, SNDRV_PCM_FORMAT_S24_LE, 2, 0, 0, 2, 18432000 }, + { 384000, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 0, 2, 24576000 }, + { 384000, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 0, 2, 24576000 }, + + /* Fixed 8-slot TDM, other values from params */ + { 8000, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 8, 0, 1024000 }, + { 8000, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 8, 0, 1024000 }, + { 8000, SNDRV_PCM_FORMAT_S16_LE, 3, 0, 8, 0, 1024000 }, + { 8000, SNDRV_PCM_FORMAT_S16_LE, 4, 0, 8, 0, 1024000 }, + { 8000, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 8, 0, 2048000 }, + { 8000, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 8, 0, 2048000 }, + { 8000, SNDRV_PCM_FORMAT_S32_LE, 3, 0, 8, 0, 2048000 }, + { 8000, SNDRV_PCM_FORMAT_S32_LE, 4, 0, 8, 0, 2048000 }, + { 384000, SNDRV_PCM_FORMAT_S16_LE, 1, 0, 8, 0, 49152000 }, + { 384000, SNDRV_PCM_FORMAT_S16_LE, 2, 0, 8, 0, 49152000 }, + { 384000, SNDRV_PCM_FORMAT_S16_LE, 3, 0, 8, 0, 49152000 }, + { 384000, SNDRV_PCM_FORMAT_S16_LE, 4, 0, 8, 0, 49152000 }, + { 384000, SNDRV_PCM_FORMAT_S32_LE, 1, 0, 8, 0, 98304000 }, + { 384000, SNDRV_PCM_FORMAT_S32_LE, 2, 0, 8, 0, 98304000 }, + { 384000, SNDRV_PCM_FORMAT_S32_LE, 3, 0, 8, 0, 98304000 }, + { 384000, SNDRV_PCM_FORMAT_S32_LE, 4, 0, 8, 0, 98304000 }, + + /* Fixed 32-bit TDM, other values from params */ + { 8000, SNDRV_PCM_FORMAT_S16_LE, 1, 32, 0, 0, 256000 }, + { 8000, SNDRV_PCM_FORMAT_S16_LE, 2, 32, 0, 0, 512000 }, + { 8000, SNDRV_PCM_FORMAT_S16_LE, 3, 32, 0, 0, 768000 }, + { 8000, SNDRV_PCM_FORMAT_S16_LE, 4, 32, 0, 0, 1024000 }, + { 8000, SNDRV_PCM_FORMAT_S32_LE, 1, 32, 0, 0, 256000 }, + { 8000, SNDRV_PCM_FORMAT_S32_LE, 2, 32, 0, 0, 512000 }, + { 8000, SNDRV_PCM_FORMAT_S32_LE, 3, 32, 0, 0, 768000 }, + { 8000, SNDRV_PCM_FORMAT_S32_LE, 4, 32, 0, 0, 1024000 }, + { 384000, SNDRV_PCM_FORMAT_S16_LE, 1, 32, 0, 0, 12288000 }, + { 384000, SNDRV_PCM_FORMAT_S16_LE, 2, 32, 0, 0, 24576000 }, + { 384000, SNDRV_PCM_FORMAT_S16_LE, 3, 32, 0, 0, 36864000 }, + { 384000, SNDRV_PCM_FORMAT_S16_LE, 4, 32, 0, 0, 49152000 }, + { 384000, SNDRV_PCM_FORMAT_S32_LE, 1, 32, 0, 0, 12288000 }, + { 384000, SNDRV_PCM_FORMAT_S32_LE, 2, 32, 0, 0, 24576000 }, + { 384000, SNDRV_PCM_FORMAT_S32_LE, 3, 32, 0, 0, 36864000 }, + { 384000, SNDRV_PCM_FORMAT_S32_LE, 4, 32, 0, 0, 49152000 }, + + /* Fixed 6-slot 24-bit TDM, other values from params */ + { 8000, SNDRV_PCM_FORMAT_S16_LE, 1, 24, 6, 0, 1152000 }, + { 8000, SNDRV_PCM_FORMAT_S16_LE, 2, 24, 6, 0, 1152000 }, + { 8000, SNDRV_PCM_FORMAT_S16_LE, 3, 24, 6, 0, 1152000 }, + { 8000, SNDRV_PCM_FORMAT_S16_LE, 4, 24, 6, 0, 1152000 }, + { 8000, SNDRV_PCM_FORMAT_S24_LE, 1, 24, 6, 0, 1152000 }, + { 8000, SNDRV_PCM_FORMAT_S24_LE, 2, 24, 6, 0, 1152000 }, + { 8000, SNDRV_PCM_FORMAT_S24_LE, 3, 24, 6, 0, 1152000 }, + { 8000, SNDRV_PCM_FORMAT_S24_LE, 4, 24, 6, 0, 1152000 }, + { 192000, SNDRV_PCM_FORMAT_S16_LE, 1, 24, 6, 0, 27648000 }, + { 192000, SNDRV_PCM_FORMAT_S16_LE, 2, 24, 6, 0, 27648000 }, + { 192000, SNDRV_PCM_FORMAT_S16_LE, 3, 24, 6, 0, 27648000 }, + { 192000, SNDRV_PCM_FORMAT_S16_LE, 4, 24, 6, 0, 27648000 }, + { 192000, SNDRV_PCM_FORMAT_S24_LE, 1, 24, 6, 0, 27648000 }, + { 192000, SNDRV_PCM_FORMAT_S24_LE, 2, 24, 6, 0, 27648000 }, + { 192000, SNDRV_PCM_FORMAT_S24_LE, 3, 24, 6, 0, 27648000 }, + { 192000, SNDRV_PCM_FORMAT_S24_LE, 4, 24, 6, 0, 27648000 }, +}; + +static void test_tdm_params_to_bclk_one(struct kunit *test, + unsigned int rate, snd_pcm_format_t fmt, + unsigned int channels, + unsigned int tdm_width, unsigned int tdm_slots, + unsigned int slot_multiple, + unsigned int expected_bclk) +{ + struct snd_pcm_hw_params params; + int got_bclk; + + _snd_pcm_hw_params_any(¶ms); + snd_mask_none(hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT)); + hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_RATE)->min = rate; + hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_RATE)->max = rate; + hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS)->min = channels; + hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS)->max = channels; + params_set_format(¶ms, fmt); + + got_bclk = snd_soc_tdm_params_to_bclk(¶ms, tdm_width, tdm_slots, slot_multiple); + pr_debug("%s: r=%u sb=%u ch=%u tw=%u ts=%u sm=%u expected=%u got=%d\n", + __func__, + rate, params_width(¶ms), channels, tdm_width, tdm_slots, slot_multiple, + expected_bclk, got_bclk); + KUNIT_ASSERT_EQ(test, expected_bclk, (unsigned int)got_bclk); +} + +static void test_tdm_params_to_bclk(struct kunit *test) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tdm_params_to_bclk_cases); ++i) { + test_tdm_params_to_bclk_one(test, + tdm_params_to_bclk_cases[i].rate, + tdm_params_to_bclk_cases[i].fmt, + tdm_params_to_bclk_cases[i].channels, + tdm_params_to_bclk_cases[i].tdm_width, + tdm_params_to_bclk_cases[i].tdm_slots, + tdm_params_to_bclk_cases[i].slot_multiple, + tdm_params_to_bclk_cases[i].bclk); + + if (tdm_params_to_bclk_cases[i].slot_multiple > 0) + continue; + + /* Slot multiple 1 should have the same effect as multiple 0 */ + test_tdm_params_to_bclk_one(test, + tdm_params_to_bclk_cases[i].rate, + tdm_params_to_bclk_cases[i].fmt, + tdm_params_to_bclk_cases[i].channels, + tdm_params_to_bclk_cases[i].tdm_width, + tdm_params_to_bclk_cases[i].tdm_slots, + 1, + tdm_params_to_bclk_cases[i].bclk); + } +} + +static struct kunit_case soc_utils_test_cases[] = { + KUNIT_CASE(test_tdm_params_to_bclk), + {} +}; + +static struct kunit_suite soc_utils_test_suite = { + .name = "soc-utils", + .test_cases = soc_utils_test_cases, +}; + +kunit_test_suites(&soc_utils_test_suite); + +MODULE_DESCRIPTION("ASoC soc-utils kunit test"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index a4efe7e52a8b..594cb311ff30 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -9,6 +9,7 @@ #include <linux/platform_device.h> #include <linux/export.h> +#include <linux/math.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -52,6 +53,50 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *params) } EXPORT_SYMBOL_GPL(snd_soc_params_to_bclk); +/** + * snd_soc_tdm_params_to_bclk - calculate bclk from params and tdm slot info. + * + * Calculate the bclk from the params sample rate and the tdm slot count and + * tdm slot width. Either or both of tdm_width and tdm_slots can be 0. + * + * If tdm_width == 0 and tdm_slots > 0: the params_width will be used. + * If tdm_width > 0 and tdm_slots == 0: the params_channels will be used + * as the slot count. + * Both tdm_width and tdm_slots are 0: this is equivalent to calling + * snd_soc_params_to_bclk(). + * + * If slot_multiple > 1 the slot count (or params_channels if tdm_slots == 0) + * will be rounded up to a multiple of this value. This is mainly useful for + * I2S mode, which has a left and right phase so the number of slots is always + * a multiple of 2. + * + * @params: Pointer to struct_pcm_hw_params. + * @tdm_width: Width in bits of the tdm slots. + * @tdm_slots: Number of tdm slots per frame. + * @slot_multiple: If >1 roundup slot count to a multiple of this value. + * + * Return: bclk frequency in Hz, else a negative error code if params format + * is invalid. + */ +int snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params, + int tdm_width, int tdm_slots, int slot_multiple) +{ + if (!tdm_slots) + tdm_slots = params_channels(params); + + if (slot_multiple > 1) + tdm_slots = roundup(tdm_slots, slot_multiple); + + if (!tdm_width) { + tdm_width = snd_pcm_format_width(params_format(params)); + if (tdm_width < 0) + return tdm_width; + } + + return snd_soc_calc_bclk(params_rate(params), tdm_width, 1, tdm_slots); +} +EXPORT_SYMBOL_GPL(snd_soc_tdm_params_to_bclk); + static const struct snd_pcm_hardware dummy_dma_hardware = { /* Random values to keep userspace happy when checking constraints */ .info = SNDRV_PCM_INFO_INTERLEAVED | diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 18acbc001b9a..92b5e83601be 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,7 +2,9 @@ snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\ - ipc3-topology.o ipc3.o ipc3-control.o ipc3-pcm.o + ipc3-topology.o ipc3-control.o ipc3.o ipc3-pcm.o ipc3-loader.o\ + ipc3-dtrace.o\ + ipc4.o ipc4-loader.o ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),) snd-sof-objs += sof-client.o endif diff --git a/sound/soc/sof/amd/acp-trace.c b/sound/soc/sof/amd/acp-trace.c index 903b6cc3dda3..c9482b27cbe3 100644 --- a/sound/soc/sof/amd/acp-trace.c +++ b/sound/soc/sof/amd/acp-trace.c @@ -34,7 +34,7 @@ int acp_sof_trace_release(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_NS(acp_sof_trace_release, SND_SOC_SOF_AMD_COMMON); -int acp_sof_trace_init(struct snd_sof_dev *sdev, +int acp_sof_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct sof_ipc_dma_trace_params_ext *dtrace_params) { struct acp_dsp_stream *stream; @@ -46,7 +46,7 @@ int acp_sof_trace_init(struct snd_sof_dev *sdev, if (!stream) return -ENODEV; - stream->dmab = &sdev->dmatb; + stream->dmab = dmab; stream->num_pages = NUM_PAGES; ret = acp_dsp_stream_config(sdev, stream); diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 71d71c152342..0c272573df97 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -138,23 +138,75 @@ int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr, return ret; } -static int psp_fw_validate(struct acp_dev_data *adata) +/* + * psp_mbox_ready- function to poll ready bit of psp mbox + * @adata: acp device data + * @ack: bool variable to check ready bit status or psp ack + */ + +static int psp_mbox_ready(struct acp_dev_data *adata, bool ack) { struct snd_sof_dev *sdev = adata->dev; int timeout; u32 data; - smn_write(adata->smn_dev, MP0_C2PMSG_26_REG, MBOX_ACP_SHA_DMA_COMMAND); - for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) { msleep(20); - smn_read(adata->smn_dev, MP0_C2PMSG_26_REG, &data); + smn_read(adata->smn_dev, MP0_C2PMSG_114_REG, &data); if (data & MBOX_READY_MASK) return 0; } - dev_err(sdev->dev, "FW validation timedout: status %x\n", data & MBOX_STATUS_MASK); - return -ETIMEDOUT; + dev_err(sdev->dev, "PSP error status %x\n", data & MBOX_STATUS_MASK); + + if (ack) + return -ETIMEDOUT; + + return -EBUSY; +} + +/* + * psp_send_cmd - function to send psp command over mbox + * @adata: acp device data + * @cmd: non zero integer value for command type + */ + +static int psp_send_cmd(struct acp_dev_data *adata, int cmd) +{ + struct snd_sof_dev *sdev = adata->dev; + int ret, timeout; + u32 data; + + if (!cmd) + return -EINVAL; + + /* Get a non-zero Doorbell value from PSP */ + for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) { + msleep(MBOX_DELAY); + smn_read(adata->smn_dev, MP0_C2PMSG_73_REG, &data); + if (data) + break; + } + + if (!timeout) { + dev_err(sdev->dev, "Failed to get Doorbell from MBOX %x\n", MP0_C2PMSG_73_REG); + return -EINVAL; + } + + /* Check if PSP is ready for new command */ + ret = psp_mbox_ready(adata, 0); + if (ret) + return ret; + + smn_write(adata->smn_dev, MP0_C2PMSG_114_REG, cmd); + + /* Ring the Doorbell for PSP */ + smn_write(adata->smn_dev, MP0_C2PMSG_73_REG, data); + + /* Check MBOX ready as PSP ack */ + ret = psp_mbox_ready(adata, 1); + + return ret; } int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, @@ -196,7 +248,7 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, return ret; } - ret = psp_fw_validate(adata); + ret = psp_send_cmd(adata, MBOX_ACP_SHA_DMA_COMMAND); if (ret) return ret; diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 35e46fe6676a..291b44c54bcc 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -57,8 +57,10 @@ #define ACP_SHA_STAT 0x8000 #define ACP_PSP_TIMEOUT_COUNTER 5 #define ACP_EXT_INTR_ERROR_STAT 0x20000000 -#define MP0_C2PMSG_26_REG 0x03810570 -#define MBOX_ACP_SHA_DMA_COMMAND 0x330000 +#define MP0_C2PMSG_114_REG 0x3810AC8 +#define MP0_C2PMSG_73_REG 0x3810A24 +#define MBOX_ACP_SHA_DMA_COMMAND 0x70000 +#define MBOX_DELAY 1000 #define MBOX_READY_MASK 0x80000000 #define MBOX_STATUS_MASK 0xFFFF @@ -204,13 +206,13 @@ int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substr struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params); -extern const struct snd_sof_dsp_ops sof_renoir_ops; +extern struct snd_sof_dsp_ops sof_renoir_ops; /* Machine configuration */ int snd_amd_acp_find_config(struct pci_dev *pci); /* Trace */ -int acp_sof_trace_init(struct snd_sof_dev *sdev, +int acp_sof_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct sof_ipc_dma_trace_params_ext *dtrace_params); int acp_sof_trace_release(struct snd_sof_dev *sdev); diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c index 392ffbdf6417..d5d9bcc2c997 100644 --- a/sound/soc/sof/amd/pci-rn.c +++ b/sound/soc/sof/amd/pci-rn.c @@ -54,9 +54,17 @@ static const struct sof_dev_desc renoir_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &renoir_chip_info, - .default_fw_path = "amd/sof", - .default_tplg_path = "amd/sof-tplg", - .default_fw_filename = "sof-rn.ri", + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "amd/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "amd/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-rn.ri", + }, .nocodec_tplg_filename = "sof-acp.tplg", .ops = &sof_renoir_ops, }; @@ -93,6 +101,7 @@ static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci res = devm_kzalloc(&pci->dev, sizeof(struct resource) * ARRAY_SIZE(renoir_res), GFP_KERNEL); if (!res) { sof_pci_remove(pci); + platform_device_unregister(dmic_dev); return -ENOMEM; } diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c index 409fd57448b8..70190365328c 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -123,7 +123,7 @@ static struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev } /* AMD Renoir DSP ops */ -const struct snd_sof_dsp_ops sof_renoir_ops = { +struct snd_sof_dsp_ops sof_renoir_ops = { /* probe and remove */ .probe = amd_sof_acp_probe, .remove = amd_sof_acp_remove, @@ -136,9 +136,6 @@ const struct snd_sof_dsp_ops sof_renoir_ops = { .block_read = acp_dsp_block_read, .block_write = acp_dsp_block_write, - /* Module loading */ - .load_module = snd_sof_parse_module_memcpy, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, .pre_fw_run = acp_dsp_pre_fw_run, @@ -152,7 +149,6 @@ const struct snd_sof_dsp_ops sof_renoir_ops = { .ipc_msg_data = acp_sof_ipc_msg_data, .get_mailbox_offset = acp_sof_ipc_get_mailbox_offset, .irq_thread = acp_sof_ipc_irq_thread, - .fw_ready = sof_fw_ready, /* DAI drivers */ .drv = renoir_sof_dai, diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c index a8e908e50101..47639b6344c8 100644 --- a/sound/soc/sof/compress.c +++ b/sound/soc/sof/compress.c @@ -147,8 +147,7 @@ static int sof_compr_free(struct snd_soc_component *component, stream.comp_id = spcm->stream[cstream->direction].comp_id; if (spcm->prepared[cstream->direction]) { - ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, - &stream, sizeof(stream), + ret = sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply)); if (!ret) spcm->prepared[cstream->direction] = false; @@ -209,7 +208,7 @@ static int sof_compr_set_params(struct snd_soc_component *component, snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32) >> 3; pcm.params.host_period_bytes = params->buffer.fragment_size; - ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), + ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm), &ipc_params_reply, sizeof(ipc_params_reply)); if (ret < 0) { dev_err(component->dev, "error ipc failed\n"); @@ -268,8 +267,7 @@ static int sof_compr_trigger(struct snd_soc_component *component, break; } - return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, - &stream, sizeof(stream), + return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply)); } diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index de1778c4002b..e0e9efd25d34 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -15,36 +15,6 @@ #include "sof-priv.h" #include "sof-audio.h" -static void update_mute_led(struct snd_sof_control *scontrol, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int temp = 0; - int mask; - int i; - - mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - for (i = 0; i < scontrol->num_channels; i++) { - if (ucontrol->value.integer.value[i]) { - temp |= mask; - break; - } - } - - if (temp == scontrol->led_ctl.led_value) - return; - - scontrol->led_ctl.led_value = temp; - -#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) - if (!scontrol->led_ctl.direction) - ledtrig_audio_set(LED_AUDIO_MUTE, temp ? LED_OFF : LED_ON); - else - ledtrig_audio_set(LED_AUDIO_MICMUTE, temp ? LED_OFF : LED_ON); -#endif -} - int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -121,9 +91,6 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - if (scontrol->led_ctl.use_led) - update_mute_led(scontrol, kcontrol, ucontrol); - if (tplg_ops->control->switch_put) return tplg_ops->control->switch_put(scontrol, ucontrol); @@ -220,10 +187,9 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; int ret, err; - ret = pm_runtime_get_sync(scomp->dev); + ret = pm_runtime_resume_and_get(scomp->dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(scomp->dev, "%s: failed to resume %d\n", __func__, ret); - pm_runtime_put_noidle(scomp->dev); return ret; } diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index e91631618bff..53719c04658f 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -250,14 +250,13 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) } if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) { - sdev->dtrace_is_supported = true; + sdev->fw_trace_is_supported = true; - /* init DMA trace */ - ret = snd_sof_init_trace(sdev); + /* init firmware tracing */ + ret = sof_fw_trace_init(sdev); if (ret < 0) { /* non fatal */ - dev_warn(sdev->dev, - "warning: failed to initialize trace %d\n", + dev_warn(sdev->dev, "failed to initialize firmware tracing %d\n", ret); } } else { @@ -308,7 +307,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) sof_machine_err: snd_sof_machine_unregister(sdev, plat_data); fw_trace_err: - snd_sof_free_trace(sdev); + sof_fw_trace_free(sdev); fw_run_err: snd_sof_fw_unload(sdev); fw_load_err: @@ -342,6 +341,7 @@ static void sof_probe_work(struct work_struct *work) int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) { struct snd_sof_dev *sdev; + int ret; sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL); if (!sdev) @@ -357,11 +357,23 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->first_boot = true; dev_set_drvdata(dev, sdev); + /* check IPC support */ + if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) { + dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n", + plat_data->ipc_type, plat_data->desc->ipc_supported_mask); + return -EINVAL; + } + + /* init ops, if necessary */ + ret = sof_ops_init(sdev); + if (ret < 0) + return ret; + /* check all mandatory ops */ if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run || !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware || - !sof_ops(sdev)->ipc_msg_data || !sof_ops(sdev)->fw_ready) { + !sof_ops(sdev)->ipc_msg_data) { dev_err(dev, "error: missing mandatory ops\n"); return -EINVAL; } @@ -434,7 +446,7 @@ int snd_sof_device_remove(struct device *dev) snd_sof_machine_unregister(sdev, pdata); if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { - snd_sof_free_trace(sdev); + sof_fw_trace_free(sdev); ret = snd_sof_dsp_power_down_notify(sdev); if (ret < 0) dev_warn(dev, "error: %d failed to prepare DSP for device removal", diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 7b1139961a99..cf1271eb29b2 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -229,14 +229,13 @@ static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_s if (!reply) return -ENOMEM; - ret = pm_runtime_get_sync(sdev->dev); + ret = pm_runtime_resume_and_get(sdev->dev); if (ret < 0 && ret != -EACCES) { - pm_runtime_put_noidle(sdev->dev); dev_err(sdev->dev, "error: enabling device failed: %d\n", ret); goto error; } - ret = sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE); + ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE); pm_runtime_mark_last_busy(sdev->dev); pm_runtime_put_autosuspend(sdev->dev); if (ret < 0 || reply->rhdr.error < 0) { @@ -331,7 +330,7 @@ EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init); int snd_sof_dbg_init(struct snd_sof_dev *sdev) { - const struct snd_sof_dsp_ops *ops = sof_ops(sdev); + struct snd_sof_dsp_ops *ops = sof_ops(sdev); const struct snd_sof_debugfs_map *map; int i; int err; @@ -444,6 +443,6 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) snd_sof_ipc_dump(sdev); snd_sof_dsp_dbg_dump(sdev, "Firmware exception", SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); - snd_sof_trace_notify_for_error(sdev); + sof_fw_trace_fw_crashed(sdev); } EXPORT_SYMBOL(snd_sof_handle_fw_exception); diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 825bd2b9b7a1..2844d9a8040a 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -487,7 +487,7 @@ static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev, } /* i.MX8 ops */ -static const struct snd_sof_dsp_ops sof_imx8_ops = { +static struct snd_sof_dsp_ops sof_imx8_ops = { /* probe and remove */ .probe = imx8_probe, .remove = imx8_remove, @@ -504,16 +504,14 @@ static const struct snd_sof_dsp_ops sof_imx8_ops = { /* ipc */ .send_msg = imx8_send_msg, - .fw_ready = sof_fw_ready, .get_mailbox_offset = imx8_get_mailbox_offset, .get_window_offset = imx8_get_window_offset, .ipc_msg_data = sof_ipc_msg_data, .set_stream_data_offset = sof_set_stream_data_offset, - /* module loading */ - .load_module = snd_sof_parse_module_memcpy, .get_bar_index = imx8_get_bar_index, + /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, @@ -550,7 +548,7 @@ static const struct snd_sof_dsp_ops sof_imx8_ops = { }; /* i.MX8X ops */ -static const struct snd_sof_dsp_ops sof_imx8x_ops = { +static struct snd_sof_dsp_ops sof_imx8x_ops = { /* probe and remove */ .probe = imx8_probe, .remove = imx8_remove, @@ -567,16 +565,14 @@ static const struct snd_sof_dsp_ops sof_imx8x_ops = { /* ipc */ .send_msg = imx8_send_msg, - .fw_ready = sof_fw_ready, .get_mailbox_offset = imx8_get_mailbox_offset, .get_window_offset = imx8_get_window_offset, .ipc_msg_data = sof_ipc_msg_data, .set_stream_data_offset = sof_set_stream_data_offset, - /* module loading */ - .load_module = snd_sof_parse_module_memcpy, .get_bar_index = imx8_get_bar_index, + /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, @@ -613,17 +609,33 @@ static const struct snd_sof_dsp_ops sof_imx8x_ops = { }; static struct sof_dev_desc sof_of_imx8qxp_desc = { - .default_fw_path = "imx/sof", - .default_tplg_path = "imx/sof-tplg", - .default_fw_filename = "sof-imx8x.ri", + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "imx/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "imx/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-imx8x.ri", + }, .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", .ops = &sof_imx8x_ops, }; static struct sof_dev_desc sof_of_imx8qm_desc = { - .default_fw_path = "imx/sof", - .default_tplg_path = "imx/sof-tplg", - .default_fw_filename = "sof-imx8.ri", + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "imx/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "imx/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-imx8.ri", + }, .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", .ops = &sof_imx8_ops, }; diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index 803d6be6b4fb..1243f8a6141e 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -412,7 +412,7 @@ static int imx8m_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state } /* i.MX8 ops */ -static const struct snd_sof_dsp_ops sof_imx8m_ops = { +static struct snd_sof_dsp_ops sof_imx8m_ops = { /* probe and remove */ .probe = imx8m_probe, .remove = imx8m_remove, @@ -430,16 +430,14 @@ static const struct snd_sof_dsp_ops sof_imx8m_ops = { /* ipc */ .send_msg = imx8m_send_msg, - .fw_ready = sof_fw_ready, .get_mailbox_offset = imx8m_get_mailbox_offset, .get_window_offset = imx8m_get_window_offset, .ipc_msg_data = sof_ipc_msg_data, .set_stream_data_offset = sof_set_stream_data_offset, - /* module loading */ - .load_module = snd_sof_parse_module_memcpy, .get_bar_index = imx8m_get_bar_index, + /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, @@ -473,9 +471,17 @@ static const struct snd_sof_dsp_ops sof_imx8m_ops = { }; static struct sof_dev_desc sof_of_imx8mp_desc = { - .default_fw_path = "imx/sof", - .default_tplg_path = "imx/sof-tplg", - .default_fw_filename = "sof-imx8m.ri", + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "imx/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "imx/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-imx8m.ri", + }, .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", .ops = &sof_imx8m_ops, }; diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 172419392b33..0def2aa5581d 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -260,7 +260,7 @@ config SND_SOC_SOF_HDA 'select' statements at a higher level. config SND_SOC_SOF_HDA_PROBES - bool + tristate select SND_SOC_SOF_DEBUG_PROBES help The option enables the data probing for Intel(R) Skylake and newer diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index 1f473d4d8416..b9d51dc39ffa 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -6,7 +6,7 @@ snd-sof-acpi-intel-bdw-objs := bdw.o snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ hda-dai.o hda-bus.o \ - apl.o cnl.o tgl.o icl.o + apl.o cnl.o tgl.o icl.o hda-common-ops.o snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o snd-sof-intel-hda-objs := hda-codec.o diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 6721c8f95161..0cea280a6d2d 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -15,6 +15,8 @@ * Hardware interface for audio DSP on Apollolake and GeminiLake */ +#include <sound/sof/ext_manifest4.h> +#include "../ipc4-priv.h" #include "../sof-priv.h" #include "hda.h" #include "../sof-audio.h" @@ -26,108 +28,62 @@ static const struct snd_sof_debugfs_map apl_dsp_debugfs[] = { }; /* apollolake ops */ -const struct snd_sof_dsp_ops sof_apl_ops = { +struct snd_sof_dsp_ops sof_apl_ops; +EXPORT_SYMBOL_NS(sof_apl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); + +int sof_apl_ops_init(struct snd_sof_dev *sdev) +{ + /* common defaults */ + memcpy(&sof_apl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); + /* probe/remove/shutdown */ - .probe = hda_dsp_probe, - .remove = hda_dsp_remove, - .shutdown = hda_dsp_shutdown, - - /* Register IO */ - .write = sof_io_write, - .read = sof_io_read, - .write64 = sof_io_write64, - .read64 = sof_io_read64, - - /* Block IO */ - .block_read = sof_block_read, - .block_write = sof_block_write, - - /* Mailbox IO */ - .mailbox_read = sof_mailbox_read, - .mailbox_write = sof_mailbox_write, - - /* doorbell */ - .irq_thread = hda_dsp_ipc_irq_thread, - - /* ipc */ - .send_msg = hda_dsp_ipc_send_msg, - .fw_ready = sof_fw_ready, - .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset, - .get_window_offset = hda_dsp_ipc_get_window_offset, - - .ipc_msg_data = hda_ipc_msg_data, - .set_stream_data_offset = hda_set_stream_data_offset, - - /* machine driver */ - .machine_select = hda_machine_select, - .machine_register = sof_machine_register, - .machine_unregister = sof_machine_unregister, - .set_mach_params = hda_set_mach_params, + sof_apl_ops.shutdown = hda_dsp_shutdown; + + if (sdev->pdata->ipc_type == SOF_IPC) { + /* doorbell */ + sof_apl_ops.irq_thread = hda_dsp_ipc_irq_thread; + + /* ipc */ + sof_apl_ops.send_msg = hda_dsp_ipc_send_msg; + } + + if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) { + struct sof_ipc4_fw_data *ipc4_data; + + sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + if (!sdev->private) + return -ENOMEM; + + ipc4_data = sdev->private; + ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET; + + /* doorbell */ + sof_apl_ops.irq_thread = hda_dsp_ipc4_irq_thread; + + /* ipc */ + sof_apl_ops.send_msg = hda_dsp_ipc4_send_msg; + } + + /* set DAI driver ops */ + hda_set_dai_drv_ops(sdev, &sof_apl_ops); /* debug */ - .debug_map = apl_dsp_debugfs, - .debug_map_count = ARRAY_SIZE(apl_dsp_debugfs), - .dbg_dump = hda_dsp_dump, - .ipc_dump = hda_ipc_dump, - .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - - /* stream callbacks */ - .pcm_open = hda_dsp_pcm_open, - .pcm_close = hda_dsp_pcm_close, - .pcm_hw_params = hda_dsp_pcm_hw_params, - .pcm_hw_free = hda_dsp_stream_hw_free, - .pcm_trigger = hda_dsp_pcm_trigger, - .pcm_pointer = hda_dsp_pcm_pointer, - .pcm_ack = hda_dsp_pcm_ack, - - /* firmware loading */ - .load_firmware = snd_sof_load_firmware_raw, + sof_apl_ops.debug_map = apl_dsp_debugfs; + sof_apl_ops.debug_map_count = ARRAY_SIZE(apl_dsp_debugfs); + sof_apl_ops.ipc_dump = hda_ipc_dump; /* firmware run */ - .run = hda_dsp_cl_boot_firmware, + sof_apl_ops.run = hda_dsp_cl_boot_firmware; /* pre/post fw run */ - .pre_fw_run = hda_dsp_pre_fw_run, - .post_fw_run = hda_dsp_post_fw_run, - - /* parse platform specific extended manifest */ - .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, + sof_apl_ops.post_fw_run = hda_dsp_post_fw_run; /* dsp core get/put */ - .core_get = hda_dsp_core_get, - - /* trace callback */ - .trace_init = hda_dsp_trace_init, - .trace_release = hda_dsp_trace_release, - .trace_trigger = hda_dsp_trace_trigger, - - /* client ops */ - .register_ipc_clients = hda_register_clients, - .unregister_ipc_clients = hda_unregister_clients, - - /* DAI drivers */ - .drv = skl_dai, - .num_drv = SOF_SKL_NUM_DAIS, - - /* PM */ - .suspend = hda_dsp_suspend, - .resume = hda_dsp_resume, - .runtime_suspend = hda_dsp_runtime_suspend, - .runtime_resume = hda_dsp_runtime_resume, - .runtime_idle = hda_dsp_runtime_idle, - .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, - .set_power_state = hda_dsp_set_power_state, - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - - .dsp_arch_ops = &sof_xtensa_arch_ops, + sof_apl_ops.core_get = hda_dsp_core_get; + + return 0; }; -EXPORT_SYMBOL_NS(sof_apl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); +EXPORT_SYMBOL_NS(sof_apl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON); const struct sof_intel_dsp_desc apl_chip_info = { /* Apollolake */ @@ -139,9 +95,12 @@ const struct sof_intel_dsp_desc apl_chip_info = { .ipc_ack = HDA_DSP_REG_HIPCIE, .ipc_ack_mask = HDA_DSP_REG_HIPCIE_DONE, .ipc_ctl = HDA_DSP_REG_HIPCCTL, + .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS, .rom_init_timeout = 150, .ssp_count = APL_SSP_COUNT, .ssp_base_offset = APL_SSP_BASE_OFFSET, .quirks = SOF_INTEL_PROCEN_FMT_QUIRK, + .check_ipc_irq = hda_dsp_check_ipc_irq, + .hw_ip_version = SOF_INTEL_CAVS_1_5_PLUS, }; EXPORT_SYMBOL_NS(apl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index fb9682b2fe32..26df780c702e 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -567,7 +567,7 @@ static struct snd_soc_dai_driver bdw_dai[] = { }; /* broadwell ops */ -static const struct snd_sof_dsp_ops sof_bdw_ops = { +static struct snd_sof_dsp_ops sof_bdw_ops = { /*Device init */ .probe = bdw_probe, @@ -591,7 +591,6 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = { /* ipc */ .send_msg = bdw_send_msg, - .fw_ready = sof_fw_ready, .get_mailbox_offset = bdw_get_mailbox_offset, .get_window_offset = bdw_get_window_offset, @@ -614,9 +613,6 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = { .pcm_open = sof_stream_pcm_open, .pcm_close = sof_stream_pcm_close, - /* Module loading */ - .load_module = snd_sof_parse_module_memcpy, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, @@ -637,6 +633,7 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = { static const struct sof_intel_dsp_desc bdw_chip_info = { .cores_num = 1, .host_managed_cores_mask = 1, + .hw_ip_version = SOF_INTEL_BROADWELL, }; static const struct sof_dev_desc sof_acpi_broadwell_desc = { @@ -646,9 +643,17 @@ static const struct sof_dev_desc sof_acpi_broadwell_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = 0, .chip_info = &bdw_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-bdw.ri", + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-bdw.ri", + }, .nocodec_tplg_filename = "sof-bdw-nocodec.tplg", .ops = &sof_bdw_ops, }; diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index bb84a4aa587a..4ed8381eceda 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -216,7 +216,7 @@ irq: } /* baytrail ops */ -static const struct snd_sof_dsp_ops sof_byt_ops = { +static struct snd_sof_dsp_ops sof_byt_ops = { /* device init */ .probe = byt_acpi_probe, .remove = byt_remove, @@ -245,7 +245,6 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { /* ipc */ .send_msg = atom_send_msg, - .fw_ready = sof_fw_ready, .get_mailbox_offset = atom_get_mailbox_offset, .get_window_offset = atom_get_window_offset, @@ -268,9 +267,6 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { .pcm_open = sof_stream_pcm_open, .pcm_close = sof_stream_pcm_close, - /* module loading */ - .load_module = snd_sof_parse_module_memcpy, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, @@ -295,10 +291,11 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { static const struct sof_intel_dsp_desc byt_chip_info = { .cores_num = 1, .host_managed_cores_mask = 1, + .hw_ip_version = SOF_INTEL_BAYTRAIL, }; /* cherrytrail and braswell ops */ -static const struct snd_sof_dsp_ops sof_cht_ops = { +static struct snd_sof_dsp_ops sof_cht_ops = { /* device init */ .probe = byt_acpi_probe, .remove = byt_remove, @@ -327,7 +324,6 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { /* ipc */ .send_msg = atom_send_msg, - .fw_ready = sof_fw_ready, .get_mailbox_offset = atom_get_mailbox_offset, .get_window_offset = atom_get_window_offset, @@ -350,9 +346,6 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { .pcm_open = sof_stream_pcm_open, .pcm_close = sof_stream_pcm_close, - /* module loading */ - .load_module = snd_sof_parse_module_memcpy, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, @@ -378,6 +371,7 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { static const struct sof_intel_dsp_desc cht_chip_info = { .cores_num = 1, .host_managed_cores_mask = 1, + .hw_ip_version = SOF_INTEL_BAYTRAIL, }; /* BYTCR uses different IRQ index */ @@ -388,9 +382,17 @@ static const struct sof_dev_desc sof_acpi_baytrailcr_desc = { .resindex_imr_base = 2, .irqindex_host_ipc = 0, .chip_info = &byt_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-byt.ri", + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-byt.ri", + }, .nocodec_tplg_filename = "sof-byt-nocodec.tplg", .ops = &sof_byt_ops, }; @@ -402,9 +404,17 @@ static const struct sof_dev_desc sof_acpi_baytrail_desc = { .resindex_imr_base = 2, .irqindex_host_ipc = 5, .chip_info = &byt_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-byt.ri", + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-byt.ri", + }, .nocodec_tplg_filename = "sof-byt-nocodec.tplg", .ops = &sof_byt_ops, }; @@ -416,9 +426,17 @@ static const struct sof_dev_desc sof_acpi_cherrytrail_desc = { .resindex_imr_base = 2, .irqindex_host_ipc = 5, .chip_info = &cht_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-cht.ri", + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-cht.ri", + }, .nocodec_tplg_filename = "sof-cht-nocodec.tplg", .ops = &sof_cht_ops, }; diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 6a96470b967f..cd6e5f8a5eb4 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -15,6 +15,9 @@ * Hardware interface for audio DSP on Cannonlake. */ +#include <sound/sof/ext_manifest4.h> +#include <sound/sof/ipc4/header.h> +#include "../ipc4-priv.h" #include "../ops.h" #include "hda.h" #include "hda-ipc.h" @@ -29,6 +32,68 @@ static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = { static void cnl_ipc_host_done(struct snd_sof_dev *sdev); static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev); +irqreturn_t cnl_ipc4_irq_thread(int irq, void *context) +{ + struct sof_ipc4_msg notification_data = {{ 0 }}; + struct snd_sof_dev *sdev = context; + bool ipc_irq = false; + u32 hipcida, hipctdr; + + hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA); + if (hipcida & CNL_DSP_REG_HIPCIDA_DONE) { + /* DSP received the message */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCCTL, + CNL_DSP_REG_HIPCCTL_DONE, 0); + cnl_ipc_dsp_done(sdev); + + ipc_irq = true; + } + + hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR); + if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) { + /* Message from DSP (reply or notification) */ + u32 hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDD); + u32 primary = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK; + u32 extension = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK; + + if (primary & SOF_IPC4_MSG_DIR_MASK) { + /* Reply received */ + struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data; + + data->primary = primary; + data->extension = extension; + + spin_lock_irq(&sdev->ipc_lock); + + snd_sof_ipc_get_reply(sdev); + snd_sof_ipc_reply(sdev, data->primary); + + spin_unlock_irq(&sdev->ipc_lock); + } else { + /* Notification received */ + notification_data.primary = primary; + notification_data.extension = extension; + + sdev->ipc->msg.rx_data = ¬ification_data; + snd_sof_ipc_msgs_rx(sdev); + sdev->ipc->msg.rx_data = NULL; + } + + /* Let DSP know that we have finished processing the message */ + cnl_ipc_host_done(sdev); + + ipc_irq = true; + } + + if (!ipc_irq) + /* This interrupt is not shared so no need to return IRQ_NONE. */ + dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); + + return IRQ_HANDLED; +} + irqreturn_t cnl_ipc_irq_thread(int irq, void *context) { struct snd_sof_dev *sdev = context; @@ -176,6 +241,22 @@ static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg, return false; } +int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc4_msg *msg_data = msg->msg_data; + + /* send the message via mailbox */ + if (msg_data->data_size) + sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr, + msg_data->data_size); + + snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDD, msg_data->extension); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, + msg_data->primary | CNL_DSP_REG_HIPCIDR_BUSY); + + return 0; +} + int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; @@ -244,108 +325,63 @@ void cnl_ipc_dump(struct snd_sof_dev *sdev) } /* cannonlake ops */ -const struct snd_sof_dsp_ops sof_cnl_ops = { +struct snd_sof_dsp_ops sof_cnl_ops; +EXPORT_SYMBOL_NS(sof_cnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); + +int sof_cnl_ops_init(struct snd_sof_dev *sdev) +{ + /* common defaults */ + memcpy(&sof_cnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); + /* probe/remove/shutdown */ - .probe = hda_dsp_probe, - .remove = hda_dsp_remove, - .shutdown = hda_dsp_shutdown, + sof_cnl_ops.shutdown = hda_dsp_shutdown; + + /* ipc */ + if (sdev->pdata->ipc_type == SOF_IPC) { + /* doorbell */ + sof_cnl_ops.irq_thread = cnl_ipc_irq_thread; - /* Register IO */ - .write = sof_io_write, - .read = sof_io_read, - .write64 = sof_io_write64, - .read64 = sof_io_read64, + /* ipc */ + sof_cnl_ops.send_msg = cnl_ipc_send_msg; + } - /* Block IO */ - .block_read = sof_block_read, - .block_write = sof_block_write, + if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) { + struct sof_ipc4_fw_data *ipc4_data; - /* Mailbox IO */ - .mailbox_read = sof_mailbox_read, - .mailbox_write = sof_mailbox_write, + sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + if (!sdev->private) + return -ENOMEM; - /* doorbell */ - .irq_thread = cnl_ipc_irq_thread, + ipc4_data = sdev->private; + ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET; - /* ipc */ - .send_msg = cnl_ipc_send_msg, - .fw_ready = sof_fw_ready, - .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset, - .get_window_offset = hda_dsp_ipc_get_window_offset, + /* doorbell */ + sof_cnl_ops.irq_thread = cnl_ipc4_irq_thread; - .ipc_msg_data = hda_ipc_msg_data, - .set_stream_data_offset = hda_set_stream_data_offset, + /* ipc */ + sof_cnl_ops.send_msg = cnl_ipc4_send_msg; + } - /* machine driver */ - .machine_select = hda_machine_select, - .machine_register = sof_machine_register, - .machine_unregister = sof_machine_unregister, - .set_mach_params = hda_set_mach_params, + /* set DAI driver ops */ + hda_set_dai_drv_ops(sdev, &sof_cnl_ops); /* debug */ - .debug_map = cnl_dsp_debugfs, - .debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs), - .dbg_dump = hda_dsp_dump, - .ipc_dump = cnl_ipc_dump, - .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - - /* stream callbacks */ - .pcm_open = hda_dsp_pcm_open, - .pcm_close = hda_dsp_pcm_close, - .pcm_hw_params = hda_dsp_pcm_hw_params, - .pcm_hw_free = hda_dsp_stream_hw_free, - .pcm_trigger = hda_dsp_pcm_trigger, - .pcm_pointer = hda_dsp_pcm_pointer, - .pcm_ack = hda_dsp_pcm_ack, - - /* firmware loading */ - .load_firmware = snd_sof_load_firmware_raw, + sof_cnl_ops.debug_map = cnl_dsp_debugfs; + sof_cnl_ops.debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs); + sof_cnl_ops.ipc_dump = cnl_ipc_dump; /* pre/post fw run */ - .pre_fw_run = hda_dsp_pre_fw_run, - .post_fw_run = hda_dsp_post_fw_run, + sof_cnl_ops.post_fw_run = hda_dsp_post_fw_run; - /* parse platform specific extended manifest */ - .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, + /* firmware run */ + sof_cnl_ops.run = hda_dsp_cl_boot_firmware; /* dsp core get/put */ - .core_get = hda_dsp_core_get, + sof_cnl_ops.core_get = hda_dsp_core_get; - /* firmware run */ - .run = hda_dsp_cl_boot_firmware, - - /* trace callback */ - .trace_init = hda_dsp_trace_init, - .trace_release = hda_dsp_trace_release, - .trace_trigger = hda_dsp_trace_trigger, - - /* client ops */ - .register_ipc_clients = hda_register_clients, - .unregister_ipc_clients = hda_unregister_clients, - - /* DAI drivers */ - .drv = skl_dai, - .num_drv = SOF_SKL_NUM_DAIS, - - /* PM */ - .suspend = hda_dsp_suspend, - .resume = hda_dsp_resume, - .runtime_suspend = hda_dsp_runtime_suspend, - .runtime_resume = hda_dsp_runtime_resume, - .runtime_idle = hda_dsp_runtime_idle, - .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, - .set_power_state = hda_dsp_set_power_state, - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - - .dsp_arch_ops = &sof_xtensa_arch_ops, + return 0; }; -EXPORT_SYMBOL_NS(sof_cnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); +EXPORT_SYMBOL_NS(sof_cnl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON); const struct sof_intel_dsp_desc cnl_chip_info = { /* Cannonlake */ @@ -357,12 +393,15 @@ const struct sof_intel_dsp_desc cnl_chip_info = { .ipc_ack = CNL_DSP_REG_HIPCIDA, .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS, .rom_init_timeout = 300, .ssp_count = CNL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, .sdw_shim_base = SDW_SHIM_BASE, .sdw_alh_base = SDW_ALH_BASE, .check_sdw_irq = hda_common_check_sdw_irq, + .check_ipc_irq = hda_dsp_check_ipc_irq, + .hw_ip_version = SOF_INTEL_CAVS_1_8, }; EXPORT_SYMBOL_NS(cnl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); @@ -383,11 +422,14 @@ const struct sof_intel_dsp_desc jsl_chip_info = { .ipc_ack = CNL_DSP_REG_HIPCIDA, .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS, .rom_init_timeout = 300, .ssp_count = ICL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, .sdw_shim_base = SDW_SHIM_BASE, .sdw_alh_base = SDW_ALH_BASE, .check_sdw_irq = hda_common_check_sdw_irq, + .check_ipc_irq = hda_dsp_check_ipc_irq, + .hw_ip_version = SOF_INTEL_CAVS_2_0, }; EXPORT_SYMBOL_NS(jsl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c new file mode 100644 index 000000000000..b2326396c870 --- /dev/null +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. +// + +/* + * common ops for SKL+ HDAudio platforms + */ + +#include "../sof-priv.h" +#include "hda.h" +#include "../sof-audio.h" + +struct snd_sof_dsp_ops sof_hda_common_ops = { + /* probe/remove/shutdown */ + .probe = hda_dsp_probe, + .remove = hda_dsp_remove, + + /* Register IO */ + .write = sof_io_write, + .read = sof_io_read, + .write64 = sof_io_write64, + .read64 = sof_io_read64, + + /* Block IO */ + .block_read = sof_block_read, + .block_write = sof_block_write, + + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, + + /* ipc */ + .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset, + .get_window_offset = hda_dsp_ipc_get_window_offset, + + .ipc_msg_data = hda_ipc_msg_data, + .set_stream_data_offset = hda_set_stream_data_offset, + + /* machine driver */ + .machine_select = hda_machine_select, + .machine_register = sof_machine_register, + .machine_unregister = sof_machine_unregister, + .set_mach_params = hda_set_mach_params, + + /* debug */ + .dbg_dump = hda_dsp_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, + + /* stream callbacks */ + .pcm_open = hda_dsp_pcm_open, + .pcm_close = hda_dsp_pcm_close, + .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_hw_free = hda_dsp_stream_hw_free, + .pcm_trigger = hda_dsp_pcm_trigger, + .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, + + /* firmware loading */ + .load_firmware = snd_sof_load_firmware_raw, + + /* pre/post fw run */ + .pre_fw_run = hda_dsp_pre_fw_run, + + /* firmware run */ + .run = hda_dsp_cl_boot_firmware, + + /* parse platform specific extended manifest */ + .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, + + /* dsp core get/put */ + + /* trace callback */ + .trace_init = hda_dsp_trace_init, + .trace_release = hda_dsp_trace_release, + .trace_trigger = hda_dsp_trace_trigger, + + /* client ops */ + .register_ipc_clients = hda_register_clients, + .unregister_ipc_clients = hda_unregister_clients, + + /* DAI drivers */ + .drv = skl_dai, + .num_drv = SOF_SKL_NUM_DAIS, + + /* PM */ + .suspend = hda_dsp_suspend, + .resume = hda_dsp_resume, + .runtime_suspend = hda_dsp_runtime_suspend, + .runtime_resume = hda_dsp_runtime_resume, + .runtime_idle = hda_dsp_runtime_idle, + .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, + .set_power_state = hda_dsp_set_power_state, + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + + .dsp_arch_ops = &sof_xtensa_arch_ops, +}; diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index f9cb9f1f0237..9823230d2ef4 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -50,8 +50,8 @@ static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd, } static struct hdac_ext_stream * - hda_link_stream_assign(struct hdac_bus *bus, - struct snd_pcm_substream *substream) +hda_link_stream_assign(struct hdac_bus *bus, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct sof_intel_hda_stream *hda_stream; @@ -128,6 +128,40 @@ static struct hdac_ext_stream * return res; } +static int hda_link_dma_cleanup(struct snd_pcm_substream *substream, + struct hdac_stream *hstream, + struct snd_soc_dai *cpu_dai, + struct snd_soc_dai *codec_dai, + bool trigger_suspend_stop) +{ + struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream); + struct hdac_bus *bus = hstream->bus; + struct sof_intel_hda_stream *hda_stream; + struct hdac_ext_link *link; + int stream_tag; + + link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name); + if (!link) + return -EINVAL; + + if (trigger_suspend_stop) + snd_hdac_ext_link_stream_clear(hext_stream); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + stream_tag = hdac_stream(hext_stream)->stream_tag; + snd_hdac_ext_link_clear_stream_id(link, stream_tag); + } + snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); + snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); + hext_stream->link_prepared = 0; + + /* free the host DMA channel reserved by hostless streams */ + hda_stream = hstream_to_sof_hda_stream(hext_stream); + hda_stream->host_reserved = 0; + + return 0; +} + static int hda_link_dma_params(struct hdac_ext_stream *hext_stream, struct hda_pipe_params *params) { @@ -162,61 +196,28 @@ static int hda_link_dma_params(struct hdac_ext_stream *hext_stream, return 0; } -static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream, - struct snd_soc_dapm_widget *w, - int channel, bool widget_setup) -{ - struct snd_sof_dai_config_data data; - - data.dai_data = channel; - - /* set up/free DAI widget and send DAI_CONFIG IPC */ - if (widget_setup) - return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data); - - return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data); -} - -static int hda_link_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct hdac_stream *hstream = substream->runtime->private_data; - struct hdac_bus *bus = hstream->bus; struct hdac_ext_stream *hext_stream; struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - struct sof_intel_hda_stream *hda_stream; struct hda_pipe_params p_params = {0}; - struct snd_soc_dapm_widget *w; + struct hdac_bus *bus = hstream->bus; struct hdac_ext_link *link; - int stream_tag; - int ret; /* get stored dma data if resuming from system suspend */ - hext_stream = snd_soc_dai_get_dma_data(dai, substream); + hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream); if (!hext_stream) { hext_stream = hda_link_stream_assign(bus, substream); if (!hext_stream) return -EBUSY; - snd_soc_dai_set_dma_data(dai, substream, (void *)hext_stream); + snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream); } - stream_tag = hdac_stream(hext_stream)->stream_tag; - - hda_stream = hstream_to_sof_hda_stream(hext_stream); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; - - /* set up the DAI widget and send the DAI_CONFIG with the new tag */ - ret = hda_link_dai_widget_update(hda_stream, w, stream_tag - 1, true); - if (ret < 0) - return ret; - link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name); if (!link) return -EINVAL; @@ -239,26 +240,118 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, return hda_link_dma_params(hext_stream, &p_params); } -static int hda_link_pcm_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int hda_link_dma_prepare(struct snd_pcm_substream *substream) { - struct hdac_ext_stream *hext_stream = - snd_soc_dai_get_dma_data(dai, substream); - struct snd_sof_dev *sdev = - snd_soc_component_get_drvdata(dai->component); struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); int stream = substream->stream; - if (hext_stream->link_prepared) + return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params); +} + +static int hda_link_dma_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream); + int ret; + + dev_dbg(cpu_dai->dev, "%s: cmd=%d\n", __func__, cmd); + if (!hext_stream) + return 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_hdac_ext_link_stream_start(hext_stream); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = hda_link_dma_cleanup(substream, hstream, cpu_dai, codec_dai, true); + if (ret < 0) + return ret; + + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + snd_hdac_ext_link_stream_clear(hext_stream); + + break; + default: + return -EINVAL; + } + return 0; +} + +static int hda_link_dma_hw_free(struct snd_pcm_substream *substream) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct hdac_ext_stream *hext_stream; + + hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream); + if (!hext_stream) + return 0; + + return hda_link_dma_cleanup(substream, hstream, cpu_dai, codec_dai, false); +} + +static int hda_dai_widget_update(struct snd_soc_dapm_widget *w, + int channel, bool widget_setup) +{ + struct snd_sof_dai_config_data data; + + data.dai_data = channel; + + /* set up/free DAI widget and send DAI_CONFIG IPC */ + if (widget_setup) + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data); + + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data); +} + +static int hda_dai_hw_params_update(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *hext_stream; + struct snd_soc_dapm_widget *w; + int stream_tag; + + hext_stream = snd_soc_dai_get_dma_data(dai, substream); + if (!hext_stream) + return -EINVAL; + + stream_tag = hdac_stream(hext_stream)->stream_tag; + + w = snd_soc_dai_get_widget(dai, substream->stream); + + /* set up the DAI widget and send the DAI_CONFIG with the new tag */ + return hda_dai_widget_update(w, stream_tag - 1, true); +} + +static int hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *hext_stream = + snd_soc_dai_get_dma_data(dai, substream); + int ret; + + if (hext_stream && hext_stream->link_prepared) return 0; - dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream); + ret = hda_link_dma_hw_params(substream, params); + if (ret < 0) + return ret; - return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params, - dai); + return hda_dai_hw_params_update(substream, params, dai); } -static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) + +static int hda_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) { struct snd_sof_widget *swidget = w->dobj.private; struct snd_soc_component *component = swidget->scomp; @@ -276,132 +369,134 @@ static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) return ret; } -static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) +static int ipc3_hda_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(dai, substream); - struct sof_intel_hda_stream *hda_stream; - struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dapm_widget *w; - struct hdac_ext_link *link; - struct hdac_stream *hstream; - struct hdac_bus *bus; - int stream_tag; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int stream = substream->stream; int ret; - hstream = substream->runtime->private_data; - bus = hstream->bus; - rtd = asoc_substream_to_rtd(substream); + if (hext_stream && hext_stream->link_prepared) + return 0; - link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name); - if (!link) - return -EINVAL; + dev_dbg(sdev->dev, "%s: prepare stream dir %d\n", __func__, substream->stream); - hda_stream = hstream_to_sof_hda_stream(hext_stream); + ret = hda_link_dma_prepare(substream); + if (ret < 0) + return ret; + + return hda_dai_hw_params_update(substream, &rtd->dpcm[stream].hw_params, dai); +} - dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); +static int hda_dai_hw_free_ipc(int stream, /* direction */ + struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_widget *w; + + w = snd_soc_dai_get_widget(dai, stream); + + /* free the link DMA channel in the FW and the DAI widget */ + return hda_dai_widget_update(w, DMA_CHAN_INVALID, false); +} + +static int ipc3_hda_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_widget *w; + int ret; + + ret = hda_link_dma_trigger(substream, cmd); + if (ret < 0) + return ret; w = snd_soc_dai_get_widget(dai, substream->stream); + dev_dbg(dai->dev, "%s: cmd=%d\n", __func__, cmd); switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - snd_hdac_ext_link_stream_start(hext_stream); - break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - snd_hdac_ext_link_stream_clear(hext_stream); - /* * free DAI widget during stop/suspend to keep widget use_count's balanced. */ - ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false); + ret = hda_dai_hw_free_ipc(substream->stream, dai); if (ret < 0) return ret; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - stream_tag = hdac_stream(hext_stream)->stream_tag; - snd_hdac_ext_link_clear_stream_id(link, stream_tag); - } - - hext_stream->link_prepared = 0; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - snd_hdac_ext_link_stream_clear(hext_stream); - - ret = hda_link_dai_config_pause_push_ipc(w); + ret = hda_dai_config_pause_push_ipc(w); if (ret < 0) return ret; break; + default: - return -EINVAL; + break; } return 0; } -static int hda_link_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { - unsigned int stream_tag; - struct sof_intel_hda_stream *hda_stream; - struct hdac_bus *bus; - struct hdac_ext_link *link; - struct hdac_stream *hstream; - struct snd_soc_pcm_runtime *rtd; - struct hdac_ext_stream *hext_stream; - struct snd_soc_dapm_widget *w; int ret; - hstream = substream->runtime->private_data; - bus = hstream->bus; - rtd = asoc_substream_to_rtd(substream); - hext_stream = snd_soc_dai_get_dma_data(dai, substream); + ret = hda_link_dma_hw_free(substream); + if (ret < 0) + return ret; - if (!hext_stream) { - dev_dbg(dai->dev, - "%s: hext_stream is not assigned\n", __func__); - return -EINVAL; - } + return hda_dai_hw_free_ipc(substream->stream, dai); +} - hda_stream = hstream_to_sof_hda_stream(hext_stream); +static const struct snd_soc_dai_ops ipc3_hda_dai_ops = { + .hw_params = hda_dai_hw_params, + .hw_free = hda_dai_hw_free, + .trigger = ipc3_hda_dai_trigger, + .prepare = ipc3_hda_dai_prepare, +}; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; +static int hda_dai_suspend(struct hdac_bus *bus) +{ + struct snd_soc_pcm_runtime *rtd; + struct hdac_ext_stream *hext_stream; + struct hdac_stream *s; + int ret; - /* free the link DMA channel in the FW and the DAI widget */ - ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false); - if (ret < 0) - return ret; + /* set internal flag for BE */ + list_for_each_entry(s, &bus->stream_list, list) { - link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name); - if (!link) - return -EINVAL; + hext_stream = stream_to_hdac_ext_stream(s); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - stream_tag = hdac_stream(hext_stream)->stream_tag; - snd_hdac_ext_link_clear_stream_id(link, stream_tag); + /* + * clear stream. This should already be taken care for running + * streams when the SUSPEND trigger is called. But paused + * streams do not get suspended, so this needs to be done + * explicitly during suspend. + */ + if (hext_stream->link_substream) { + struct snd_soc_dai *cpu_dai; + struct snd_soc_dai *codec_dai; + + rtd = asoc_substream_to_rtd(hext_stream->link_substream); + cpu_dai = asoc_rtd_to_cpu(rtd, 0); + codec_dai = asoc_rtd_to_codec(rtd, 0); + + ret = hda_link_dma_cleanup(hext_stream->link_substream, s, + cpu_dai, codec_dai, false); + if (ret < 0) + return ret; + + /* for consistency with TRIGGER_SUSPEND we free DAI resources */ + ret = hda_dai_hw_free_ipc(hdac_stream(hext_stream)->direction, cpu_dai); + if (ret < 0) + return ret; + } } - snd_soc_dai_set_dma_data(dai, substream, NULL); - snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); - hext_stream->link_prepared = 0; - - /* free the host DMA channel reserved by hostless streams */ - hda_stream->host_reserved = 0; - return 0; } - -static const struct snd_soc_dai_ops hda_link_dai_ops = { - .hw_params = hda_link_hw_params, - .hw_free = hda_link_hw_free, - .trigger = hda_link_pcm_trigger, - .prepare = hda_link_pcm_prepare, -}; - #endif /* only one flag used so far to harden hw_params/hw_free/trigger/prepare */ @@ -414,10 +509,7 @@ static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd { struct snd_soc_dapm_widget *w; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; + w = snd_soc_dai_get_widget(dai, substream->stream); if (setup) return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL); @@ -478,8 +570,8 @@ static int ssp_dai_prepare(struct snd_pcm_substream *substream, return ssp_dai_setup(substream, dai, true); } -static int ssp_dai_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) +static int ipc3_ssp_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) { if (cmd != SNDRV_PCM_TRIGGER_SUSPEND) return 0; @@ -507,15 +599,39 @@ static void ssp_dai_shutdown(struct snd_pcm_substream *substream, kfree(dma_data); } -static const struct snd_soc_dai_ops ssp_dai_ops = { +static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = { .startup = ssp_dai_startup, .hw_params = ssp_dai_hw_params, .prepare = ssp_dai_prepare, - .trigger = ssp_dai_trigger, + .trigger = ipc3_ssp_dai_trigger, .hw_free = ssp_dai_hw_free, .shutdown = ssp_dai_shutdown, }; +void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) +{ + int i; + + switch (sdev->pdata->ipc_type) { + case SOF_IPC: + for (i = 0; i < ops->num_drv; i++) { + if (strstr(ops->drv[i].name, "SSP")) { + ops->drv[i].ops = &ipc3_ssp_dai_ops; + continue; + } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + if (strstr(ops->drv[i].name, "iDisp") || + strstr(ops->drv[i].name, "Analog") || + strstr(ops->drv[i].name, "Digital")) + ops->drv[i].ops = &ipc3_hda_dai_ops; +#endif + } + break; + default: + break; + } +} + /* * common dai driver for skl+ platforms. * some products who use this DAI array only physically have a subset of @@ -524,7 +640,6 @@ static const struct snd_soc_dai_ops ssp_dai_ops = { struct snd_soc_dai_driver skl_dai[] = { { .name = "SSP0 Pin", - .ops = &ssp_dai_ops, .playback = { .channels_min = 1, .channels_max = 8, @@ -536,7 +651,6 @@ struct snd_soc_dai_driver skl_dai[] = { }, { .name = "SSP1 Pin", - .ops = &ssp_dai_ops, .playback = { .channels_min = 1, .channels_max = 8, @@ -548,7 +662,6 @@ struct snd_soc_dai_driver skl_dai[] = { }, { .name = "SSP2 Pin", - .ops = &ssp_dai_ops, .playback = { .channels_min = 1, .channels_max = 8, @@ -560,7 +673,6 @@ struct snd_soc_dai_driver skl_dai[] = { }, { .name = "SSP3 Pin", - .ops = &ssp_dai_ops, .playback = { .channels_min = 1, .channels_max = 8, @@ -572,7 +684,6 @@ struct snd_soc_dai_driver skl_dai[] = { }, { .name = "SSP4 Pin", - .ops = &ssp_dai_ops, .playback = { .channels_min = 1, .channels_max = 8, @@ -584,7 +695,6 @@ struct snd_soc_dai_driver skl_dai[] = { }, { .name = "SSP5 Pin", - .ops = &ssp_dai_ops, .playback = { .channels_min = 1, .channels_max = 8, @@ -611,7 +721,6 @@ struct snd_soc_dai_driver skl_dai[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) { .name = "iDisp1 Pin", - .ops = &hda_link_dai_ops, .playback = { .channels_min = 1, .channels_max = 8, @@ -619,7 +728,6 @@ struct snd_soc_dai_driver skl_dai[] = { }, { .name = "iDisp2 Pin", - .ops = &hda_link_dai_ops, .playback = { .channels_min = 1, .channels_max = 8, @@ -627,7 +735,6 @@ struct snd_soc_dai_driver skl_dai[] = { }, { .name = "iDisp3 Pin", - .ops = &hda_link_dai_ops, .playback = { .channels_min = 1, .channels_max = 8, @@ -635,7 +742,6 @@ struct snd_soc_dai_driver skl_dai[] = { }, { .name = "iDisp4 Pin", - .ops = &hda_link_dai_ops, .playback = { .channels_min = 1, .channels_max = 8, @@ -643,7 +749,6 @@ struct snd_soc_dai_driver skl_dai[] = { }, { .name = "Analog CPU DAI", - .ops = &hda_link_dai_ops, .playback = { .channels_min = 1, .channels_max = 16, @@ -655,7 +760,6 @@ struct snd_soc_dai_driver skl_dai[] = { }, { .name = "Digital CPU DAI", - .ops = &hda_link_dai_ops, .playback = { .channels_min = 1, .channels_max = 16, @@ -667,7 +771,6 @@ struct snd_soc_dai_driver skl_dai[] = { }, { .name = "Alt Analog CPU DAI", - .ops = &hda_link_dai_ops, .playback = { .channels_min = 1, .channels_max = 16, @@ -679,3 +782,22 @@ struct snd_soc_dai_driver skl_dai[] = { }, #endif }; + +int hda_dsp_dais_suspend(struct snd_sof_dev *sdev) +{ + /* + * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core + * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state. + * Since the component suspend is called last, we can trap this corner case + * and force the DAIs to release their resources. + */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + int ret; + + ret = hda_dai_suspend(sof_to_bus(sdev)); + if (ret < 0) + return ret; +#endif + + return 0; +} diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 8ddde60c56b3..000ea906670c 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -363,9 +363,8 @@ static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags) pm_gate.flags = flags; /* send pm_gate ipc to dsp */ - return sof_ipc_tx_message_no_pm(sdev->ipc, pm_gate.hdr.cmd, - &pm_gate, sizeof(pm_gate), &reply, - sizeof(reply)); + return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate), + &reply, sizeof(reply)); } static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value) @@ -433,7 +432,7 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev, * when the DSP enters D0I3 while the system is in S0 * for debug purpose. */ - if (!sdev->dtrace_is_supported || + if (!sdev->fw_trace_is_supported || !hda_enable_trace_D0I3_S0 || sdev->system_suspend_target != SOF_SUSPEND_NONE) flags = HDA_PM_NO_DMA_TRACE; @@ -895,44 +894,14 @@ int hda_dsp_shutdown(struct snd_sof_dev *sdev) int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - struct hdac_bus *bus = sof_to_bus(sdev); - struct snd_soc_pcm_runtime *rtd; - struct hdac_ext_stream *hext_stream; - struct hdac_ext_link *link; - struct hdac_stream *s; - const char *name; - int stream_tag; - - /* set internal flag for BE */ - list_for_each_entry(s, &bus->stream_list, list) { - hext_stream = stream_to_hdac_ext_stream(s); - - /* - * clear stream. This should already be taken care for running - * streams when the SUSPEND trigger is called. But paused - * streams do not get suspended, so this needs to be done - * explicitly during suspend. - */ - if (hext_stream->link_substream) { - rtd = asoc_substream_to_rtd(hext_stream->link_substream); - name = asoc_rtd_to_codec(rtd, 0)->component->name; - link = snd_hdac_ext_bus_get_link(bus, name); - if (!link) - return -EINVAL; - - hext_stream->link_prepared = 0; + int ret; - if (hdac_stream(hext_stream)->direction == - SNDRV_PCM_STREAM_CAPTURE) - continue; + /* make sure all DAI resources are freed */ + ret = hda_dsp_dais_suspend(sdev); + if (ret < 0) + dev_warn(sdev->dev, "%s: failure in hda_dsp_dais_suspend\n", __func__); - stream_tag = hdac_stream(hext_stream)->stream_tag; - snd_hdac_ext_link_clear_stream_id(link, stream_tag); - } - } -#endif - return 0; + return ret; } void hda_dsp_d0i3_work(struct work_struct *work) @@ -985,8 +954,7 @@ int hda_dsp_core_get(struct snd_sof_dev *sdev, int core) return 0; /* Now notify DSP for secondary cores */ - ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, - &pm_core_config, sizeof(pm_core_config), + ret = sof_ipc_tx_message(sdev->ipc, &pm_core_config, sizeof(pm_core_config), &pm_core_config, sizeof(pm_core_config)); if (ret < 0) { dev_err(sdev->dev, "failed to enable secondary core '%d' failed with %d\n", diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 0395638c43ae..f08011249955 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -15,6 +15,7 @@ * Hardware interface for generic Intel audio DSP HDA IP */ +#include <sound/sof/ipc4/header.h> #include "../ops.h" #include "hda.h" @@ -65,6 +66,22 @@ int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) return 0; } +int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct sof_ipc4_msg *msg_data = msg->msg_data; + + /* send the message via mailbox */ + if (msg_data->data_size) + sof_mailbox_write(sdev, sdev->host_box.offset, msg_data->data_ptr, + msg_data->data_size); + + snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE, msg_data->extension); + snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, + msg_data->primary | HDA_DSP_REG_HIPCI_BUSY); + + return 0; +} + void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) { struct snd_sof_ipc_msg *msg = sdev->msg; @@ -100,6 +117,71 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) } } +irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context) +{ + struct sof_ipc4_msg notification_data = {{ 0 }}; + struct snd_sof_dev *sdev = context; + bool ipc_irq = false; + u32 hipcie, hipct; + + hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE); + if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { + /* DSP received the message */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL, + HDA_DSP_REG_HIPCCTL_DONE, 0); + hda_dsp_ipc_dsp_done(sdev); + + ipc_irq = true; + } + + hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); + if (hipct & HDA_DSP_REG_HIPCT_BUSY) { + /* Message from DSP (reply or notification) */ + u32 hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCTE); + u32 primary = hipct & HDA_DSP_REG_HIPCT_MSG_MASK; + u32 extension = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK; + + /* mask BUSY interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL, + HDA_DSP_REG_HIPCCTL_BUSY, 0); + + if (primary & SOF_IPC4_MSG_DIR_MASK) { + /* Reply received */ + struct sof_ipc4_msg *data = sdev->ipc->msg.reply_data; + + data->primary = primary; + data->extension = extension; + + spin_lock_irq(&sdev->ipc_lock); + + snd_sof_ipc_get_reply(sdev); + snd_sof_ipc_reply(sdev, data->primary); + + spin_unlock_irq(&sdev->ipc_lock); + } else { + /* Notification received */ + + notification_data.primary = primary; + notification_data.extension = extension; + sdev->ipc->msg.rx_data = ¬ification_data; + snd_sof_ipc_msgs_rx(sdev); + sdev->ipc->msg.rx_data = NULL; + } + + /* Let DSP know that we have finished processing the message */ + hda_dsp_ipc_host_done(sdev); + + ipc_irq = true; + } + + if (!ipc_irq) + /* This interrupt is not shared so no need to return IRQ_NONE. */ + dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); + + return IRQ_HANDLED; +} + /* IPC handler thread */ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) { diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 2ac5d9d0719b..64290125d7cd 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -24,8 +24,6 @@ #include "../sof-priv.h" #include "hda.h" -#define HDA_CL_STREAM_FORMAT 0x40 - static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; @@ -43,9 +41,9 @@ static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev) } } -static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, - unsigned int size, struct snd_dma_buffer *dmab, - int direction) +struct hdac_ext_stream *hda_cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, + unsigned int size, struct snd_dma_buffer *dmab, + int direction) { struct hdac_ext_stream *hext_stream; struct hdac_stream *hstream; @@ -101,14 +99,14 @@ out_put: * status on core 1, so power up core 1 also momentarily, keep it in * reset/stall and then turn it off */ -static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) +static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; - unsigned int status; + unsigned int status, target_status; + u32 flags, ipc_hdr, j; unsigned long mask; char *dump_msg; - u32 flags, j; int ret; /* step 1: power up corex */ @@ -121,10 +119,12 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) hda_ssp_set_cbp_cfp(sdev); - /* step 2: purge FW request */ - snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, - chip->ipc_req_mask | (HDA_DSP_IPC_PURGE_FW | - ((stream_tag - 1) << 9))); + /* step 2: Send ROM_CONTROL command (stream_tag is ignored for IMR boot) */ + ipc_hdr = chip->ipc_req_mask | HDA_DSP_ROM_IPC_CONTROL; + if (!imr_boot) + ipc_hdr |= HDA_DSP_ROM_IPC_PURGE_FW | ((stream_tag - 1) << 9); + + snd_sof_dsp_write(sdev, HDA_DSP_BAR, chip->ipc_req, ipc_hdr); /* step 3: unset core 0 reset state & unstall/run core 0 */ ret = hda_dsp_core_run(sdev, BIT(0)); @@ -171,11 +171,20 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) /* step 6: enable IPC interrupts */ hda_dsp_ipc_int_enable(sdev); - /* step 7: wait for ROM init */ + /* + * step 7: + * - Cold/Full boot: wait for ROM init to proceed to download the firmware + * - IMR boot: wait for ROM firmware entered (firmware booted up from IMR) + */ + if (imr_boot) + target_status = HDA_DSP_ROM_FW_ENTERED; + else + target_status = HDA_DSP_ROM_INIT; + ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, - HDA_DSP_SRAM_REG_ROM_STATUS, status, + chip->rom_status_reg, status, ((status & HDA_DSP_ROM_STS_MASK) - == HDA_DSP_ROM_INIT), + == target_status), HDA_DSP_REG_POLL_INTERVAL_US, chip->rom_init_timeout * USEC_PER_MSEC); @@ -190,8 +199,8 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, - "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n", - __func__); + "%s: timeout with rom_status_reg (%#x) read\n", + __func__, chip->rom_status_reg); err: flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL; @@ -236,8 +245,8 @@ static int cl_trigger(struct snd_sof_dev *sdev, } } -static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, - struct hdac_ext_stream *hext_stream) +int hda_cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, + struct hdac_ext_stream *hext_stream) { struct hdac_stream *hstream = &hext_stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); @@ -268,8 +277,10 @@ static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, return ret; } -static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream) +int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; unsigned int reg; int ret, status; @@ -280,7 +291,7 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_str } status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, - HDA_DSP_SRAM_REG_ROM_STATUS, reg, + chip->rom_status_reg, reg, ((reg & HDA_DSP_ROM_STS_MASK) == HDA_DSP_ROM_FW_ENTERED), HDA_DSP_REG_POLL_INTERVAL_US, @@ -293,8 +304,8 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_str if (status < 0) { dev_err(sdev->dev, - "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n", - __func__); + "%s: timeout with rom_status_reg (%#x) read\n", + __func__, chip->rom_status_reg); } ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP); @@ -313,6 +324,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) struct hdac_ext_stream *iccmax_stream; struct hdac_bus *bus = sof_to_bus(sdev); struct firmware stripped_firmware; + struct snd_dma_buffer dmab_bdl; int ret, ret1; u8 original_gb; @@ -327,8 +339,8 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset; /* prepare capture stream for ICCMAX */ - iccmax_stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, - &sdev->dmab_bdl, SNDRV_PCM_STREAM_CAPTURE); + iccmax_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, + &dmab_bdl, SNDRV_PCM_STREAM_CAPTURE); if (IS_ERR(iccmax_stream)) { dev_err(sdev->dev, "error: dma prepare for ICCMAX stream failed\n"); return PTR_ERR(iccmax_stream); @@ -340,7 +352,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) * Perform iccmax stream cleanup. This should be done even if firmware loading fails. * If the cleanup also fails, we return the initial error */ - ret1 = cl_cleanup(sdev, &sdev->dmab_bdl, iccmax_stream); + ret1 = hda_cl_cleanup(sdev, &dmab_bdl, iccmax_stream); if (ret1 < 0) { dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n"); @@ -357,32 +369,11 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) static int hda_dsp_boot_imr(struct snd_sof_dev *sdev) { - struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - const struct sof_intel_dsp_desc *chip = hda->desc; - unsigned long mask; - u32 j; int ret; - /* power up & unstall/run the cores to run the firmware */ - ret = hda_dsp_enable_core(sdev, chip->init_core_mask); - if (ret < 0) { - dev_err(sdev->dev, "dsp core start failed %d\n", ret); - return -EIO; - } - - /* set enabled cores mask and increment ref count for cores in init_core_mask */ - sdev->enabled_cores_mask |= chip->init_core_mask; - mask = sdev->enabled_cores_mask; - for_each_set_bit(j, &mask, SOF_MAX_DSP_NUM_CORES) - sdev->dsp_core_ref_count[j]++; - - hda_ssp_set_cbp_cfp(sdev); - - /* enable IPC interrupts */ - hda_dsp_ipc_int_enable(sdev); - - /* process wakes */ - hda_sdw_process_wakeen(sdev); + ret = cl_dsp_init(sdev, 0, true); + if (!ret) + hda_sdw_process_wakeen(sdev); return ret; } @@ -395,13 +386,17 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) const struct sof_intel_dsp_desc *chip_info; struct hdac_ext_stream *hext_stream; struct firmware stripped_firmware; + struct snd_dma_buffer dmab; int ret, ret1, i; - if ((sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT) && - !(sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT)) && - !sdev->first_boot) { + if (hda->imrboot_supported && !sdev->first_boot) { dev_dbg(sdev->dev, "IMR restore supported, booting from IMR directly\n"); - return hda_dsp_boot_imr(sdev); + hda->boot_iteration = 0; + ret = hda_dsp_boot_imr(sdev); + if (!ret) + return 0; + + dev_warn(sdev->dev, "IMR restore failed, trying to cold boot\n"); } chip_info = desc->chip_info; @@ -418,14 +413,15 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) init_waitqueue_head(&sdev->boot_wait); /* prepare DMA for code loader stream */ - hext_stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, - &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK); + hext_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, + stripped_firmware.size, + &dmab, SNDRV_PCM_STREAM_PLAYBACK); if (IS_ERR(hext_stream)) { dev_err(sdev->dev, "error: dma prepare for fw loading failed\n"); return PTR_ERR(hext_stream); } - memcpy(sdev->dmab.area, stripped_firmware.data, + memcpy(dmab.area, stripped_firmware.data, stripped_firmware.size); /* try ROM init a few times before giving up */ @@ -434,7 +430,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) "Attempting iteration %d of Core En/ROM load...\n", i); hda->boot_iteration = i + 1; - ret = cl_dsp_init(sdev, hext_stream->hstream.stream_tag); + ret = cl_dsp_init(sdev, hext_stream->hstream.stream_tag, false); /* don't retry anymore if successful */ if (!ret) @@ -473,7 +469,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) * Continue with code loading and firmware boot */ hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS; - ret = cl_copy_fw(sdev, hext_stream); + ret = hda_cl_copy_fw(sdev, hext_stream); if (!ret) dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); else @@ -486,7 +482,7 @@ cleanup: * This should be done even if firmware loading fails. * If the cleanup also fails, we return the initial error */ - ret1 = cl_cleanup(sdev, &sdev->dmab, hext_stream); + ret1 = hda_cl_cleanup(sdev, &dmab, hext_stream); if (ret1 < 0) { dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); @@ -522,12 +518,19 @@ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev) int ret; if (sdev->first_boot) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + ret = hda_sdw_startup(sdev); if (ret < 0) { dev_err(sdev->dev, "error: could not startup SoundWire links\n"); return ret; } + + /* Check if IMR boot is usable */ + if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT) && + sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT) + hdev->imrboot_supported = true; } hda_sdw_int_enable(sdev, true); diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index 755ef1d835e0..cbb9bd7770e6 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -36,7 +36,7 @@ static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer return ret; } -int hda_dsp_trace_init(struct snd_sof_dev *sdev, +int hda_dsp_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct sof_ipc_dma_trace_params_ext *dtrace_params) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; @@ -57,7 +57,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, * initialize capture stream, set BDL address and return corresponding * stream tag which will be sent to the firmware by IPC message. */ - ret = hda_dsp_trace_prepare(sdev, &sdev->dmatb); + ret = hda_dsp_trace_prepare(sdev, dmab); if (ret < 0) { dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret); hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 9c97c80a7f48..bc07df1fc39f 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -406,11 +406,13 @@ static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = { static void hda_dsp_get_status(struct snd_sof_dev *sdev, const char *level) { + const struct sof_intel_dsp_desc *chip; u32 status; int i; + chip = get_chip_info(sdev->pdata); status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_SRAM_REG_ROM_STATUS); + chip->rom_status_reg); for (i = 0; i < ARRAY_SIZE(hda_dsp_rom_msg); i++) { if (status == hda_dsp_rom_msg[i].code) { @@ -456,13 +458,15 @@ static void hda_dsp_get_registers(struct snd_sof_dev *sdev, static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, const char *level, u32 flags) { + const struct sof_intel_dsp_desc *chip; char msg[128]; int len = 0; u32 value; int i; + chip = get_chip_info(sdev->pdata); for (i = 0; i < HDA_EXT_ROM_STATUS_SIZE; i++) { - value = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_ROM_STATUS + i * 0x4); + value = snd_sof_dsp_read(sdev, HDA_DSP_BAR, chip->rom_status_reg + i * 0x4); len += snprintf(msg + len, sizeof(msg) - len, " 0x%x", value); } @@ -493,6 +497,17 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) } } +static bool hda_check_ipc_irq(struct snd_sof_dev *sdev) +{ + const struct sof_intel_dsp_desc *chip; + + chip = get_chip_info(sdev->pdata); + if (chip && chip->check_ipc_irq) + return chip->check_ipc_irq(sdev); + + return false; +} + void hda_ipc_irq_dump(struct snd_sof_dev *sdev) { struct hdac_bus *bus = sof_to_bus(sdev); @@ -584,14 +599,13 @@ static int hda_init(struct snd_sof_dev *sdev) static int check_dmic_num(struct snd_sof_dev *sdev) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct nhlt_acpi_table *nhlt; int dmic_num = 0; - nhlt = intel_nhlt_init(sdev->dev); - if (nhlt) { + nhlt = hdev->nhlt; + if (nhlt) dmic_num = intel_nhlt_get_dmic_geo(sdev->dev, nhlt); - intel_nhlt_free(nhlt); - } /* allow for module parameter override */ if (dmic_num_override != -1) { @@ -611,10 +625,11 @@ static int check_dmic_num(struct snd_sof_dev *sdev) static int check_nhlt_ssp_mask(struct snd_sof_dev *sdev) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct nhlt_acpi_table *nhlt; int ssp_mask = 0; - nhlt = intel_nhlt_init(sdev->dev); + nhlt = hdev->nhlt; if (!nhlt) return ssp_mask; @@ -623,7 +638,6 @@ static int check_nhlt_ssp_mask(struct snd_sof_dev *sdev) if (ssp_mask) dev_info(sdev->dev, "NHLT_DEVICE_I2S detected, ssp_mask %#x\n", ssp_mask); } - intel_nhlt_free(nhlt); return ssp_mask; } @@ -816,7 +830,7 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context) if (hda_dsp_check_stream_irq(sdev)) hda_dsp_stream_threaded_handler(irq, sdev); - if (hda_dsp_check_ipc_irq(sdev)) + if (hda_check_ipc_irq(sdev)) sof_ops(sdev)->irq_thread(irq, sdev); if (hda_dsp_check_sdw_irq(sdev)) @@ -984,6 +998,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work); + hdev->nhlt = intel_nhlt_init(sdev->dev); + return 0; free_ipc_irq: @@ -1009,6 +1025,10 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) const struct sof_intel_dsp_desc *chip = hda->desc; struct hdac_bus *bus = sof_to_bus(sdev); struct pci_dev *pci = to_pci_dev(sdev->dev); + struct nhlt_acpi_table *nhlt = hda->nhlt; + + if (nhlt) + intel_nhlt_free(nhlt); /* cancel any attempt for DSP D0I3 */ cancel_delayed_work_sync(&hda->d0i3_work); @@ -1274,7 +1294,7 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; mach->mach_params.platform = dev_name(sdev->dev); - pdata->fw_filename = pdata->desc->default_fw_filename; + pdata->fw_filename = pdata->desc->default_fw_filename[pdata->ipc_type]; pdata->tplg_filename = mach->sof_tplg_filename; /* diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 05e5e158614a..3e0f7b0c586a 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -210,7 +210,9 @@ #define HDA_DSP_ROM_USER_EXCEPTION 0xBEEF0000 #define HDA_DSP_ROM_UNEXPECTED_RESET 0xDECAF000 #define HDA_DSP_ROM_NULL_FW_ENTRY 0x4c4c4e55 -#define HDA_DSP_IPC_PURGE_FW 0x01004000 + +#define HDA_DSP_ROM_IPC_CONTROL 0x01000000 +#define HDA_DSP_ROM_IPC_PURGE_FW 0x00004000 /* various timeout values */ #define HDA_DSP_PU_TIMEOUT 50 @@ -223,8 +225,8 @@ #define HDA_DSP_REG_POLL_INTERVAL_US 500 /* 0.5 msec */ #define HDA_DSP_REG_POLL_RETRY_COUNT 50 -#define HDA_DSP_ADSPIC_IPC 1 -#define HDA_DSP_ADSPIS_IPC 1 +#define HDA_DSP_ADSPIC_IPC BIT(0) +#define HDA_DSP_ADSPIS_IPC BIT(0) /* Intel HD Audio General DSP Registers */ #define HDA_DSP_GEN_BASE 0x0 @@ -268,8 +270,8 @@ /* HIPCTE */ #define HDA_DSP_REG_HIPCTE_MSG_MASK 0x3FFFFFFF -#define HDA_DSP_ADSPIC_CL_DMA 0x2 -#define HDA_DSP_ADSPIS_CL_DMA 0x2 +#define HDA_DSP_ADSPIC_CL_DMA BIT(1) +#define HDA_DSP_ADSPIS_CL_DMA BIT(1) /* Delay before scheduling D0i3 entry */ #define BXT_D0I3_DELAY 5000 @@ -416,6 +418,8 @@ enum sof_hda_D0_substate { /* represents DSP HDA controller frontend - i.e. host facing control */ struct sof_intel_hda_dev { + bool imrboot_supported; + int boot_iteration; struct hda_bus hbus; @@ -449,6 +453,9 @@ struct sof_intel_hda_dev { /* FW clock config, 0:HPRO, 1:LPRO */ bool clk_config_lpro; + + /* Intel NHLT information */ + struct nhlt_acpi_table *nhlt; }; static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s) @@ -588,6 +595,13 @@ int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir); */ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev); +int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream); +struct hdac_ext_stream *hda_cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, + unsigned int size, struct snd_dma_buffer *dmab, + int direction); +int hda_cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, + struct hdac_ext_stream *hext_stream); +#define HDA_CL_STREAM_FORMAT 0x40 /* pre and post fw run ops */ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev); @@ -644,7 +658,7 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; } /* * Trace Control. */ -int hda_dsp_trace_init(struct snd_sof_dev *sdev, +int hda_dsp_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct sof_ipc_dma_trace_params_ext *dtrace_params); int hda_dsp_trace_release(struct snd_sof_dev *sdev); int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); @@ -683,18 +697,24 @@ static inline bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev) /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[]; +int hda_dsp_dais_suspend(struct snd_sof_dev *sdev); /* * Platform Specific HW abstraction Ops. */ -extern const struct snd_sof_dsp_ops sof_apl_ops; -extern const struct snd_sof_dsp_ops sof_cnl_ops; -extern const struct snd_sof_dsp_ops sof_tgl_ops; -extern const struct snd_sof_dsp_ops sof_icl_ops; +extern struct snd_sof_dsp_ops sof_hda_common_ops; + +extern struct snd_sof_dsp_ops sof_apl_ops; +int sof_apl_ops_init(struct snd_sof_dev *sdev); +extern struct snd_sof_dsp_ops sof_cnl_ops; +int sof_cnl_ops_init(struct snd_sof_dev *sdev); +extern struct snd_sof_dsp_ops sof_tgl_ops; +int sof_tgl_ops_init(struct snd_sof_dev *sdev); +extern struct snd_sof_dsp_ops sof_icl_ops; +int sof_icl_ops_init(struct snd_sof_dev *sdev); extern const struct sof_intel_dsp_desc apl_chip_info; extern const struct sof_intel_dsp_desc cnl_chip_info; -extern const struct sof_intel_dsp_desc skl_chip_info; extern const struct sof_intel_dsp_desc icl_chip_info; extern const struct sof_intel_dsp_desc tgl_chip_info; extern const struct sof_intel_dsp_desc tglh_chip_info; @@ -742,4 +762,12 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_f extern int sof_hda_position_quirk; +void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops); + +/* IPC4 */ +irqreturn_t cnl_ipc4_irq_thread(int irq, void *context); +int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); +irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context); +int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); + #endif diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index b44a649bfc0b..f19517dffd62 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -56,11 +56,18 @@ static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev) int ret; if (sdev->first_boot) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + ret = hda_sdw_startup(sdev); if (ret < 0) { dev_err(sdev->dev, "error: could not startup SoundWire links\n"); return ret; } + + /* Check if IMR boot is usable */ + if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT) && + sdev->fw_ready.flags & SOF_IPC_INFO_D3_PERSISTENT) + hdev->imrboot_supported = true; } hda_sdw_int_enable(sdev, true); @@ -88,109 +95,44 @@ static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev) } /* Icelake ops */ -const struct snd_sof_dsp_ops sof_icl_ops = { - /* probe/remove/shutdown */ - .probe = hda_dsp_probe, - .remove = hda_dsp_remove, - .shutdown = hda_dsp_shutdown, - - /* Register IO */ - .write = sof_io_write, - .read = sof_io_read, - .write64 = sof_io_write64, - .read64 = sof_io_read64, +struct snd_sof_dsp_ops sof_icl_ops; +EXPORT_SYMBOL_NS(sof_icl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); - /* Block IO */ - .block_read = sof_block_read, - .block_write = sof_block_write, +int sof_icl_ops_init(struct snd_sof_dev *sdev) +{ + /* common defaults */ + memcpy(&sof_icl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); - /* Mailbox IO */ - .mailbox_read = sof_mailbox_read, - .mailbox_write = sof_mailbox_write, + /* probe/remove/shutdown */ + sof_icl_ops.shutdown = hda_dsp_shutdown; /* doorbell */ - .irq_thread = cnl_ipc_irq_thread, + sof_icl_ops.irq_thread = cnl_ipc_irq_thread; /* ipc */ - .send_msg = cnl_ipc_send_msg, - .fw_ready = sof_fw_ready, - .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset, - .get_window_offset = hda_dsp_ipc_get_window_offset, - - .ipc_msg_data = hda_ipc_msg_data, - .set_stream_data_offset = hda_set_stream_data_offset, - - /* machine driver */ - .machine_select = hda_machine_select, - .machine_register = sof_machine_register, - .machine_unregister = sof_machine_unregister, - .set_mach_params = hda_set_mach_params, + sof_icl_ops.send_msg = cnl_ipc_send_msg; /* debug */ - .debug_map = icl_dsp_debugfs, - .debug_map_count = ARRAY_SIZE(icl_dsp_debugfs), - .dbg_dump = hda_dsp_dump, - .ipc_dump = cnl_ipc_dump, - .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - - /* stream callbacks */ - .pcm_open = hda_dsp_pcm_open, - .pcm_close = hda_dsp_pcm_close, - .pcm_hw_params = hda_dsp_pcm_hw_params, - .pcm_hw_free = hda_dsp_stream_hw_free, - .pcm_trigger = hda_dsp_pcm_trigger, - .pcm_pointer = hda_dsp_pcm_pointer, - .pcm_ack = hda_dsp_pcm_ack, - - /* firmware loading */ - .load_firmware = snd_sof_load_firmware_raw, + sof_icl_ops.debug_map = icl_dsp_debugfs; + sof_icl_ops.debug_map_count = ARRAY_SIZE(icl_dsp_debugfs); + sof_icl_ops.ipc_dump = cnl_ipc_dump; /* pre/post fw run */ - .pre_fw_run = hda_dsp_pre_fw_run, - .post_fw_run = icl_dsp_post_fw_run, + sof_icl_ops.post_fw_run = icl_dsp_post_fw_run; - /* parse platform specific extended manifest */ - .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, + /* firmware run */ + sof_icl_ops.run = hda_dsp_cl_boot_firmware_iccmax; + sof_icl_ops.stall = icl_dsp_core_stall; /* dsp core get/put */ - .core_get = hda_dsp_core_get, + sof_icl_ops.core_get = hda_dsp_core_get; - /* firmware run */ - .run = hda_dsp_cl_boot_firmware_iccmax, - .stall = icl_dsp_core_stall, - - /* trace callback */ - .trace_init = hda_dsp_trace_init, - .trace_release = hda_dsp_trace_release, - .trace_trigger = hda_dsp_trace_trigger, - - /* client ops */ - .register_ipc_clients = hda_register_clients, - .unregister_ipc_clients = hda_unregister_clients, - - /* DAI drivers */ - .drv = skl_dai, - .num_drv = SOF_SKL_NUM_DAIS, - - /* PM */ - .suspend = hda_dsp_suspend, - .resume = hda_dsp_resume, - .runtime_suspend = hda_dsp_runtime_suspend, - .runtime_resume = hda_dsp_runtime_resume, - .runtime_idle = hda_dsp_runtime_idle, - .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, - .set_power_state = hda_dsp_set_power_state, - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - - .dsp_arch_ops = &sof_xtensa_arch_ops, + /* set DAI driver ops */ + hda_set_dai_drv_ops(sdev, &sof_icl_ops); + + return 0; }; -EXPORT_SYMBOL_NS(sof_icl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); +EXPORT_SYMBOL_NS(sof_icl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON); const struct sof_intel_dsp_desc icl_chip_info = { /* Icelake */ @@ -202,11 +144,14 @@ const struct sof_intel_dsp_desc icl_chip_info = { .ipc_ack = CNL_DSP_REG_HIPCIDA, .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS, .rom_init_timeout = 300, .ssp_count = ICL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, .sdw_shim_base = SDW_SHIM_BASE, .sdw_alh_base = SDW_ALH_BASE, .check_sdw_irq = hda_common_check_sdw_irq, + .check_ipc_irq = hda_dsp_check_ipc_irq, + .hw_ip_version = SOF_INTEL_CAVS_2_0, }; EXPORT_SYMBOL_NS(icl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c index a023b3cc0af4..2de3658eb817 100644 --- a/sound/soc/sof/intel/pci-apl.c +++ b/sound/soc/sof/intel/pci-apl.c @@ -27,11 +27,23 @@ static const struct sof_dev_desc bxt_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &apl_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-apl.ri", + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + [SOF_INTEL_IPC4] = "intel/avs/apl", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + [SOF_INTEL_IPC4] = "intel/avs-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-apl.ri", + [SOF_INTEL_IPC4] = "dsp_basefw.bin", + }, .nocodec_tplg_filename = "sof-apl-nocodec.tplg", .ops = &sof_apl_ops, + .ops_init = sof_apl_ops_init, }; static const struct sof_dev_desc glk_desc = { @@ -42,11 +54,23 @@ static const struct sof_dev_desc glk_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &apl_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-glk.ri", + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + [SOF_INTEL_IPC4] = "intel/avs/glk", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + [SOF_INTEL_IPC4] = "intel/avs-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-glk.ri", + [SOF_INTEL_IPC4] = "dsp_basefw.bin", + }, .nocodec_tplg_filename = "sof-glk-nocodec.tplg", .ops = &sof_apl_ops, + .ops_init = sof_apl_ops_init, }; /* PCI IDs */ diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c index 40cf1cd00042..87e587aef9c9 100644 --- a/sound/soc/sof/intel/pci-cnl.c +++ b/sound/soc/sof/intel/pci-cnl.c @@ -28,11 +28,23 @@ static const struct sof_dev_desc cnl_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &cnl_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-cnl.ri", + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + [SOF_INTEL_IPC4] = "intel/avs/cnl", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + [SOF_INTEL_IPC4] = "intel/avs-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-cnl.ri", + [SOF_INTEL_IPC4] = "dsp_basefw.bin", + }, .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, + .ops_init = sof_cnl_ops_init, }; static const struct sof_dev_desc cfl_desc = { @@ -44,11 +56,23 @@ static const struct sof_dev_desc cfl_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &cnl_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-cfl.ri", + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + [SOF_INTEL_IPC4] = "intel/avs/cnl", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + [SOF_INTEL_IPC4] = "intel/avs-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-cfl.ri", + [SOF_INTEL_IPC4] = "dsp_basefw.bin", + }, .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, + .ops_init = sof_cnl_ops_init, }; static const struct sof_dev_desc cml_desc = { @@ -60,11 +84,23 @@ static const struct sof_dev_desc cml_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &cnl_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-cml.ri", + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + [SOF_INTEL_IPC4] = "intel/avs/cnl", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + [SOF_INTEL_IPC4] = "intel/avs-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-cml.ri", + [SOF_INTEL_IPC4] = "dsp_basefw.bin", + }, .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, + .ops_init = sof_cnl_ops_init, }; /* PCI IDs */ diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c index 39c84121b313..1c7f16ce531e 100644 --- a/sound/soc/sof/intel/pci-icl.c +++ b/sound/soc/sof/intel/pci-icl.c @@ -28,11 +28,23 @@ static const struct sof_dev_desc icl_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &icl_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-icl.ri", + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + [SOF_INTEL_IPC4] = "intel/avs/icl", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + [SOF_INTEL_IPC4] = "intel/avs-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-icl.ri", + [SOF_INTEL_IPC4] = "dsp_basefw.bin", + }, .nocodec_tplg_filename = "sof-icl-nocodec.tplg", .ops = &sof_icl_ops, + .ops_init = sof_icl_ops_init, }; static const struct sof_dev_desc jsl_desc = { @@ -43,11 +55,23 @@ static const struct sof_dev_desc jsl_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &jsl_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-jsl.ri", + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + [SOF_INTEL_IPC4] = "intel/avs/jsl", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + [SOF_INTEL_IPC4] = "intel/avs-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-jsl.ri", + [SOF_INTEL_IPC4] = "dsp_basefw.bin", + }, .nocodec_tplg_filename = "sof-jsl-nocodec.tplg", .ops = &sof_cnl_ops, + .ops_init = sof_cnl_ops_init, }; /* PCI IDs */ diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index feaec251adc8..58a9bd92a237 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -28,11 +28,23 @@ static const struct sof_dev_desc tgl_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &tgl_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-tgl.ri", + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + [SOF_INTEL_IPC4] = "intel/avs/tgl", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + [SOF_INTEL_IPC4] = "intel/avs-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-tgl.ri", + [SOF_INTEL_IPC4] = "dsp_basefw.bin", + }, .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", .ops = &sof_tgl_ops, + .ops_init = sof_tgl_ops_init, }; static const struct sof_dev_desc tglh_desc = { @@ -44,11 +56,23 @@ static const struct sof_dev_desc tglh_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &tglh_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-tgl-h.ri", + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + [SOF_INTEL_IPC4] = "intel/avs/tgl-h", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + [SOF_INTEL_IPC4] = "intel/avs-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-tgl-h.ri", + [SOF_INTEL_IPC4] = "dsp_basefw.bin", + }, .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", .ops = &sof_tgl_ops, + .ops_init = sof_tgl_ops_init, }; static const struct sof_dev_desc ehl_desc = { @@ -59,11 +83,23 @@ static const struct sof_dev_desc ehl_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &ehl_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-ehl.ri", + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + [SOF_INTEL_IPC4] = "intel/avs/ehl", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + [SOF_INTEL_IPC4] = "intel/avs-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-ehl.ri", + [SOF_INTEL_IPC4] = "dsp_basefw.bin", + }, .nocodec_tplg_filename = "sof-ehl-nocodec.tplg", .ops = &sof_tgl_ops, + .ops_init = sof_tgl_ops_init, }; static const struct sof_dev_desc adls_desc = { @@ -75,11 +111,23 @@ static const struct sof_dev_desc adls_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &adls_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-adl-s.ri", + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + [SOF_INTEL_IPC4] = "intel/avs/adl-s", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + [SOF_INTEL_IPC4] = "intel/avs-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-adl-s.ri", + [SOF_INTEL_IPC4] = "dsp_basefw.bin", + }, .nocodec_tplg_filename = "sof-adl-nocodec.tplg", .ops = &sof_tgl_ops, + .ops_init = sof_tgl_ops_init, }; static const struct sof_dev_desc adl_desc = { @@ -91,11 +139,23 @@ static const struct sof_dev_desc adl_desc = { .resindex_imr_base = -1, .irqindex_host_ipc = -1, .chip_info = &tgl_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-adl.ri", + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + [SOF_INTEL_IPC4] = "intel/avs/adl", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + [SOF_INTEL_IPC4] = "intel/avs-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-adl.ri", + [SOF_INTEL_IPC4] = "dsp_basefw.bin", + }, .nocodec_tplg_filename = "sof-adl-nocodec.tplg", .ops = &sof_tgl_ops, + .ops_init = sof_tgl_ops_init, }; /* PCI IDs */ @@ -116,6 +176,12 @@ static const struct pci_device_id sof_pci_ids[] = { .driver_data = (unsigned long)&adl_desc}, { PCI_DEVICE(0x8086, 0x51cd), /* ADL-P */ .driver_data = (unsigned long)&adl_desc}, + { PCI_DEVICE(0x8086, 0x51c9), /* ADL-PS */ + .driver_data = (unsigned long)&adl_desc}, + { PCI_DEVICE(0x8086, 0x51ca), /* RPL-P */ + .driver_data = (unsigned long)&adl_desc}, + { PCI_DEVICE(0x8086, 0x51cb), /* RPL-P */ + .driver_data = (unsigned long)&adl_desc}, { PCI_DEVICE(0x8086, 0x51cc), /* ADL-M */ .driver_data = (unsigned long)&adl_desc}, { PCI_DEVICE(0x8086, 0x54c8), /* ADL-N */ @@ -140,4 +206,3 @@ module_pci_driver(snd_sof_pci_intel_tgl_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HDA_COMMON); MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV); - diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c index 6efef225973f..f0f6d9ba8803 100644 --- a/sound/soc/sof/intel/pci-tng.c +++ b/sound/soc/sof/intel/pci-tng.c @@ -75,7 +75,11 @@ static int tangier_pci_probe(struct snd_sof_dev *sdev) /* LPE base */ base = pci_resource_start(pci, desc->resindex_lpe_base) - IRAM_OFFSET; - size = PCI_BAR_SIZE; + size = pci_resource_len(pci, desc->resindex_lpe_base); + if (size < PCI_BAR_SIZE) { + dev_err(sdev->dev, "error: I/O region is too small.\n"); + return -ENODEV; + } dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); sdev->bar[DSP_BAR] = devm_ioremap(sdev->dev, base, size); @@ -132,7 +136,7 @@ irq: return ret; } -const struct snd_sof_dsp_ops sof_tng_ops = { +struct snd_sof_dsp_ops sof_tng_ops = { /* device init */ .probe = tangier_pci_probe, @@ -160,7 +164,6 @@ const struct snd_sof_dsp_ops sof_tng_ops = { /* ipc */ .send_msg = atom_send_msg, - .fw_ready = sof_fw_ready, .get_mailbox_offset = atom_get_mailbox_offset, .get_window_offset = atom_get_window_offset, @@ -183,9 +186,6 @@ const struct snd_sof_dsp_ops sof_tng_ops = { .pcm_open = sof_stream_pcm_open, .pcm_close = sof_stream_pcm_close, - /* module loading */ - .load_module = snd_sof_parse_module_memcpy, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, @@ -206,6 +206,7 @@ const struct snd_sof_dsp_ops sof_tng_ops = { const struct sof_intel_dsp_desc tng_chip_info = { .cores_num = 1, .host_managed_cores_mask = 1, + .hw_ip_version = SOF_INTEL_TANGIER, }; static const struct sof_dev_desc tng_desc = { @@ -215,9 +216,17 @@ static const struct sof_dev_desc tng_desc = { .resindex_imr_base = 0, .irqindex_host_ipc = -1, .chip_info = &tng_chip_info, - .default_fw_path = "intel/sof", - .default_tplg_path = "intel/sof-tplg", - .default_fw_filename = "sof-byt.ri", + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "intel/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "intel/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-byt.ri", + }, .nocodec_tplg_filename = "sof-byt.tplg", .ops = &sof_tng_ops, }; diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h index f36cd9d5eb94..1fd7b485d821 100644 --- a/sound/soc/sof/intel/shim.h +++ b/sound/soc/sof/intel/shim.h @@ -11,6 +11,17 @@ #ifndef __SOF_INTEL_SHIM_H #define __SOF_INTEL_SHIM_H +enum sof_intel_hw_ip_version { + SOF_INTEL_TANGIER, + SOF_INTEL_BAYTRAIL, + SOF_INTEL_BROADWELL, + SOF_INTEL_CAVS_1_5, /* SkyLake, KabyLake, AmberLake */ + SOF_INTEL_CAVS_1_5_PLUS,/* ApolloLake, GeminiLake */ + SOF_INTEL_CAVS_1_8, /* CannonLake, CometLake, CoffeeLake */ + SOF_INTEL_CAVS_2_0, /* IceLake, JasperLake */ + SOF_INTEL_CAVS_2_5, /* TigerLake, AlderLake */ +}; + /* * SHIM registers for BYT, BSW, CHT, BDW */ @@ -164,16 +175,19 @@ struct sof_intel_dsp_desc { int ipc_ack; int ipc_ack_mask; int ipc_ctl; + int rom_status_reg; int rom_init_timeout; int ssp_count; /* ssp count of the platform */ int ssp_base_offset; /* base address of the SSPs */ u32 sdw_shim_base; u32 sdw_alh_base; u32 quirks; + enum sof_intel_hw_ip_version hw_ip_version; bool (*check_sdw_irq)(struct snd_sof_dev *sdev); + bool (*check_ipc_irq)(struct snd_sof_dev *sdev); }; -extern const struct snd_sof_dsp_ops sof_tng_ops; +extern struct snd_sof_dsp_ops sof_tng_ops; extern const struct sof_intel_dsp_desc tng_chip_info; diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index cb1c319d5bee..1ddc492f1b13 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -9,6 +9,8 @@ * Hardware interface for audio DSP on Tigerlake. */ +#include <sound/sof/ext_manifest4.h> +#include "../ipc4-priv.h" #include "../ops.h" #include "hda.h" #include "hda-ipc.h" @@ -35,8 +37,7 @@ static int tgl_dsp_core_get(struct snd_sof_dev *sdev, int core) return hda_dsp_enable_core(sdev, BIT(core)); /* notify DSP for secondary cores */ - return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, - &pm_core_config, sizeof(pm_core_config), + return sof_ipc_tx_message(sdev->ipc, &pm_core_config, sizeof(pm_core_config), &pm_core_config, sizeof(pm_core_config)); } @@ -55,115 +56,68 @@ static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core) return hda_dsp_core_reset_power_down(sdev, BIT(core)); /* notify DSP for secondary cores */ - return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, - &pm_core_config, sizeof(pm_core_config), + return sof_ipc_tx_message(sdev->ipc, &pm_core_config, sizeof(pm_core_config), &pm_core_config, sizeof(pm_core_config)); } /* Tigerlake ops */ -const struct snd_sof_dsp_ops sof_tgl_ops = { +struct snd_sof_dsp_ops sof_tgl_ops; +EXPORT_SYMBOL_NS(sof_tgl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); + +int sof_tgl_ops_init(struct snd_sof_dev *sdev) +{ + /* common defaults */ + memcpy(&sof_tgl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); + /* probe/remove/shutdown */ - .probe = hda_dsp_probe, - .remove = hda_dsp_remove, - .shutdown = hda_dsp_shutdown, - - /* Register IO */ - .write = sof_io_write, - .read = sof_io_read, - .write64 = sof_io_write64, - .read64 = sof_io_read64, - - /* Block IO */ - .block_read = sof_block_read, - .block_write = sof_block_write, - - /* Mailbox IO */ - .mailbox_read = sof_mailbox_read, - .mailbox_write = sof_mailbox_write, - - /* doorbell */ - .irq_thread = cnl_ipc_irq_thread, - - /* ipc */ - .send_msg = cnl_ipc_send_msg, - .fw_ready = sof_fw_ready, - .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset, - .get_window_offset = hda_dsp_ipc_get_window_offset, - - .ipc_msg_data = hda_ipc_msg_data, - .set_stream_data_offset = hda_set_stream_data_offset, - - /* machine driver */ - .machine_select = hda_machine_select, - .machine_register = sof_machine_register, - .machine_unregister = sof_machine_unregister, - .set_mach_params = hda_set_mach_params, + sof_tgl_ops.shutdown = hda_dsp_shutdown; + + if (sdev->pdata->ipc_type == SOF_IPC) { + /* doorbell */ + sof_tgl_ops.irq_thread = cnl_ipc_irq_thread; + + /* ipc */ + sof_tgl_ops.send_msg = cnl_ipc_send_msg; + } + + if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) { + struct sof_ipc4_fw_data *ipc4_data; + + sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL); + if (!sdev->private) + return -ENOMEM; + + ipc4_data = sdev->private; + ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET; + + /* doorbell */ + sof_tgl_ops.irq_thread = cnl_ipc4_irq_thread; + + /* ipc */ + sof_tgl_ops.send_msg = cnl_ipc4_send_msg; + } + + /* set DAI driver ops */ + hda_set_dai_drv_ops(sdev, &sof_tgl_ops); /* debug */ - .debug_map = tgl_dsp_debugfs, - .debug_map_count = ARRAY_SIZE(tgl_dsp_debugfs), - .dbg_dump = hda_dsp_dump, - .ipc_dump = cnl_ipc_dump, - .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - - /* stream callbacks */ - .pcm_open = hda_dsp_pcm_open, - .pcm_close = hda_dsp_pcm_close, - .pcm_hw_params = hda_dsp_pcm_hw_params, - .pcm_hw_free = hda_dsp_stream_hw_free, - .pcm_trigger = hda_dsp_pcm_trigger, - .pcm_pointer = hda_dsp_pcm_pointer, - .pcm_ack = hda_dsp_pcm_ack, - - /* firmware loading */ - .load_firmware = snd_sof_load_firmware_raw, + sof_tgl_ops.debug_map = tgl_dsp_debugfs; + sof_tgl_ops.debug_map_count = ARRAY_SIZE(tgl_dsp_debugfs); + sof_tgl_ops.ipc_dump = cnl_ipc_dump; /* pre/post fw run */ - .pre_fw_run = hda_dsp_pre_fw_run, - .post_fw_run = hda_dsp_post_fw_run, + sof_tgl_ops.post_fw_run = hda_dsp_post_fw_run; - /* parse platform specific extended manifest */ - .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, + /* firmware run */ + sof_tgl_ops.run = hda_dsp_cl_boot_firmware_iccmax; /* dsp core get/put */ - .core_get = tgl_dsp_core_get, - .core_put = tgl_dsp_core_put, + sof_tgl_ops.core_get = tgl_dsp_core_get; + sof_tgl_ops.core_put = tgl_dsp_core_put; - /* firmware run */ - .run = hda_dsp_cl_boot_firmware_iccmax, - - /* trace callback */ - .trace_init = hda_dsp_trace_init, - .trace_release = hda_dsp_trace_release, - .trace_trigger = hda_dsp_trace_trigger, - - /* client ops */ - .register_ipc_clients = hda_register_clients, - .unregister_ipc_clients = hda_unregister_clients, - - /* DAI drivers */ - .drv = skl_dai, - .num_drv = SOF_SKL_NUM_DAIS, - - /* PM */ - .suspend = hda_dsp_suspend, - .resume = hda_dsp_resume, - .runtime_suspend = hda_dsp_runtime_suspend, - .runtime_resume = hda_dsp_runtime_resume, - .runtime_idle = hda_dsp_runtime_idle, - .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, - .set_power_state = hda_dsp_set_power_state, - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - - .dsp_arch_ops = &sof_xtensa_arch_ops, + return 0; }; -EXPORT_SYMBOL_NS(sof_tgl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); +EXPORT_SYMBOL_NS(sof_tgl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON); const struct sof_intel_dsp_desc tgl_chip_info = { /* Tigerlake , Alderlake */ @@ -175,12 +129,15 @@ const struct sof_intel_dsp_desc tgl_chip_info = { .ipc_ack = CNL_DSP_REG_HIPCIDA, .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS, .rom_init_timeout = 300, .ssp_count = ICL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, .sdw_shim_base = SDW_SHIM_BASE, .sdw_alh_base = SDW_ALH_BASE, .check_sdw_irq = hda_common_check_sdw_irq, + .check_ipc_irq = hda_dsp_check_ipc_irq, + .hw_ip_version = SOF_INTEL_CAVS_2_5, }; EXPORT_SYMBOL_NS(tgl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); @@ -194,12 +151,15 @@ const struct sof_intel_dsp_desc tglh_chip_info = { .ipc_ack = CNL_DSP_REG_HIPCIDA, .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS, .rom_init_timeout = 300, .ssp_count = ICL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, .sdw_shim_base = SDW_SHIM_BASE, .sdw_alh_base = SDW_ALH_BASE, .check_sdw_irq = hda_common_check_sdw_irq, + .check_ipc_irq = hda_dsp_check_ipc_irq, + .hw_ip_version = SOF_INTEL_CAVS_2_5, }; EXPORT_SYMBOL_NS(tglh_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); @@ -213,12 +173,15 @@ const struct sof_intel_dsp_desc ehl_chip_info = { .ipc_ack = CNL_DSP_REG_HIPCIDA, .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS, .rom_init_timeout = 300, .ssp_count = ICL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, .sdw_shim_base = SDW_SHIM_BASE, .sdw_alh_base = SDW_ALH_BASE, .check_sdw_irq = hda_common_check_sdw_irq, + .check_ipc_irq = hda_dsp_check_ipc_irq, + .hw_ip_version = SOF_INTEL_CAVS_2_5, }; EXPORT_SYMBOL_NS(ehl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); @@ -232,11 +195,14 @@ const struct sof_intel_dsp_desc adls_chip_info = { .ipc_ack = CNL_DSP_REG_HIPCIDA, .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .rom_status_reg = HDA_DSP_SRAM_REG_ROM_STATUS, .rom_init_timeout = 300, .ssp_count = ICL_SSP_COUNT, .ssp_base_offset = CNL_SSP_BASE_OFFSET, .sdw_shim_base = SDW_SHIM_BASE, .sdw_alh_base = SDW_ALH_BASE, .check_sdw_irq = hda_common_check_sdw_irq, + .check_ipc_irq = hda_dsp_check_ipc_irq, + .hw_ip_version = SOF_INTEL_CAVS_2_5, }; EXPORT_SYMBOL_NS(adls_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 5f5753608c79..c5aef5fc056b 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -17,294 +17,35 @@ #include "sof-priv.h" #include "sof-audio.h" #include "ops.h" -#include "ipc3-ops.h" -typedef void (*ipc_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf); - -static void ipc_trace_message(struct snd_sof_dev *sdev, void *msg_buf); -static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf); - -/* - * IPC message Tx/Rx message handling. +/** + * sof_ipc_send_msg - generic function to prepare and send one IPC message + * @sdev: pointer to SOF core device struct + * @msg_data: pointer to a message to send + * @msg_bytes: number of bytes in the message + * @reply_bytes: number of bytes available for the reply. + * The buffer for the reply data is not passed to this + * function, the available size is an information for the + * reply handling functions. + * + * On success the function returns 0, otherwise negative error number. + * + * Note: higher level sdev->ipc->tx_mutex must be held to make sure that + * transfers are synchronized. */ - -struct sof_ipc_ctrl_data_params { - size_t msg_bytes; - size_t hdr_bytes; - size_t pl_size; - size_t elems; - u32 num_msg; - u8 *src; - u8 *dst; -}; - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC) -static void ipc_log_header(struct device *dev, u8 *text, u32 cmd) -{ - u8 *str; - u8 *str2 = NULL; - u32 glb; - u32 type; - bool vdbg = false; - - glb = cmd & SOF_GLB_TYPE_MASK; - type = cmd & SOF_CMD_TYPE_MASK; - - switch (glb) { - case SOF_IPC_GLB_REPLY: - str = "GLB_REPLY"; break; - case SOF_IPC_GLB_COMPOUND: - str = "GLB_COMPOUND"; break; - case SOF_IPC_GLB_TPLG_MSG: - str = "GLB_TPLG_MSG"; - switch (type) { - case SOF_IPC_TPLG_COMP_NEW: - str2 = "COMP_NEW"; break; - case SOF_IPC_TPLG_COMP_FREE: - str2 = "COMP_FREE"; break; - case SOF_IPC_TPLG_COMP_CONNECT: - str2 = "COMP_CONNECT"; break; - case SOF_IPC_TPLG_PIPE_NEW: - str2 = "PIPE_NEW"; break; - case SOF_IPC_TPLG_PIPE_FREE: - str2 = "PIPE_FREE"; break; - case SOF_IPC_TPLG_PIPE_CONNECT: - str2 = "PIPE_CONNECT"; break; - case SOF_IPC_TPLG_PIPE_COMPLETE: - str2 = "PIPE_COMPLETE"; break; - case SOF_IPC_TPLG_BUFFER_NEW: - str2 = "BUFFER_NEW"; break; - case SOF_IPC_TPLG_BUFFER_FREE: - str2 = "BUFFER_FREE"; break; - default: - str2 = "unknown type"; break; - } - break; - case SOF_IPC_GLB_PM_MSG: - str = "GLB_PM_MSG"; - switch (type) { - case SOF_IPC_PM_CTX_SAVE: - str2 = "CTX_SAVE"; break; - case SOF_IPC_PM_CTX_RESTORE: - str2 = "CTX_RESTORE"; break; - case SOF_IPC_PM_CTX_SIZE: - str2 = "CTX_SIZE"; break; - case SOF_IPC_PM_CLK_SET: - str2 = "CLK_SET"; break; - case SOF_IPC_PM_CLK_GET: - str2 = "CLK_GET"; break; - case SOF_IPC_PM_CLK_REQ: - str2 = "CLK_REQ"; break; - case SOF_IPC_PM_CORE_ENABLE: - str2 = "CORE_ENABLE"; break; - case SOF_IPC_PM_GATE: - str2 = "GATE"; break; - default: - str2 = "unknown type"; break; - } - break; - case SOF_IPC_GLB_COMP_MSG: - str = "GLB_COMP_MSG"; - switch (type) { - case SOF_IPC_COMP_SET_VALUE: - str2 = "SET_VALUE"; break; - case SOF_IPC_COMP_GET_VALUE: - str2 = "GET_VALUE"; break; - case SOF_IPC_COMP_SET_DATA: - str2 = "SET_DATA"; break; - case SOF_IPC_COMP_GET_DATA: - str2 = "GET_DATA"; break; - default: - str2 = "unknown type"; break; - } - break; - case SOF_IPC_GLB_STREAM_MSG: - str = "GLB_STREAM_MSG"; - switch (type) { - case SOF_IPC_STREAM_PCM_PARAMS: - str2 = "PCM_PARAMS"; break; - case SOF_IPC_STREAM_PCM_PARAMS_REPLY: - str2 = "PCM_REPLY"; break; - case SOF_IPC_STREAM_PCM_FREE: - str2 = "PCM_FREE"; break; - case SOF_IPC_STREAM_TRIG_START: - str2 = "TRIG_START"; break; - case SOF_IPC_STREAM_TRIG_STOP: - str2 = "TRIG_STOP"; break; - case SOF_IPC_STREAM_TRIG_PAUSE: - str2 = "TRIG_PAUSE"; break; - case SOF_IPC_STREAM_TRIG_RELEASE: - str2 = "TRIG_RELEASE"; break; - case SOF_IPC_STREAM_TRIG_DRAIN: - str2 = "TRIG_DRAIN"; break; - case SOF_IPC_STREAM_TRIG_XRUN: - str2 = "TRIG_XRUN"; break; - case SOF_IPC_STREAM_POSITION: - vdbg = true; - str2 = "POSITION"; break; - case SOF_IPC_STREAM_VORBIS_PARAMS: - str2 = "VORBIS_PARAMS"; break; - case SOF_IPC_STREAM_VORBIS_FREE: - str2 = "VORBIS_FREE"; break; - default: - str2 = "unknown type"; break; - } - break; - case SOF_IPC_FW_READY: - str = "FW_READY"; break; - case SOF_IPC_GLB_DAI_MSG: - str = "GLB_DAI_MSG"; - switch (type) { - case SOF_IPC_DAI_CONFIG: - str2 = "CONFIG"; break; - case SOF_IPC_DAI_LOOPBACK: - str2 = "LOOPBACK"; break; - default: - str2 = "unknown type"; break; - } - break; - case SOF_IPC_GLB_TRACE_MSG: - str = "GLB_TRACE_MSG"; - switch (type) { - case SOF_IPC_TRACE_DMA_PARAMS: - str2 = "DMA_PARAMS"; break; - case SOF_IPC_TRACE_DMA_POSITION: - str2 = "DMA_POSITION"; break; - case SOF_IPC_TRACE_DMA_PARAMS_EXT: - str2 = "DMA_PARAMS_EXT"; break; - case SOF_IPC_TRACE_FILTER_UPDATE: - str2 = "FILTER_UPDATE"; break; - case SOF_IPC_TRACE_DMA_FREE: - str2 = "DMA_FREE"; break; - default: - str2 = "unknown type"; break; - } - break; - case SOF_IPC_GLB_TEST_MSG: - str = "GLB_TEST_MSG"; - switch (type) { - case SOF_IPC_TEST_IPC_FLOOD: - str2 = "IPC_FLOOD"; break; - default: - str2 = "unknown type"; break; - } - break; - case SOF_IPC_GLB_DEBUG: - str = "GLB_DEBUG"; - switch (type) { - case SOF_IPC_DEBUG_MEM_USAGE: - str2 = "MEM_USAGE"; break; - default: - str2 = "unknown type"; break; - } - break; - case SOF_IPC_GLB_PROBE: - str = "GLB_PROBE"; - switch (type) { - case SOF_IPC_PROBE_INIT: - str2 = "INIT"; break; - case SOF_IPC_PROBE_DEINIT: - str2 = "DEINIT"; break; - case SOF_IPC_PROBE_DMA_ADD: - str2 = "DMA_ADD"; break; - case SOF_IPC_PROBE_DMA_INFO: - str2 = "DMA_INFO"; break; - case SOF_IPC_PROBE_DMA_REMOVE: - str2 = "DMA_REMOVE"; break; - case SOF_IPC_PROBE_POINT_ADD: - str2 = "POINT_ADD"; break; - case SOF_IPC_PROBE_POINT_INFO: - str2 = "POINT_INFO"; break; - case SOF_IPC_PROBE_POINT_REMOVE: - str2 = "POINT_REMOVE"; break; - default: - str2 = "unknown type"; break; - } - break; - default: - str = "unknown GLB command"; break; - } - - if (str2) { - if (vdbg) - dev_vdbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2); - else - dev_dbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2); - } else { - dev_dbg(dev, "%s: 0x%x: %s\n", text, cmd, str); - } -} -#else -static inline void ipc_log_header(struct device *dev, u8 *text, u32 cmd) -{ - if ((cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_TRACE_MSG) - dev_dbg(dev, "%s: 0x%x\n", text, cmd); -} -#endif - -/* wait for IPC message reply */ -static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, - void *reply_data) -{ - struct snd_sof_dev *sdev = ipc->sdev; - struct sof_ipc_cmd_hdr *hdr = msg->msg_data; - int ret; - - /* wait for DSP IPC completion */ - ret = wait_event_timeout(msg->waitq, msg->ipc_complete, - msecs_to_jiffies(sdev->ipc_timeout)); - - if (ret == 0) { - dev_err(sdev->dev, - "ipc tx timed out for %#x (msg/reply size: %d/%zu)\n", - hdr->cmd, hdr->size, msg->reply_size); - snd_sof_handle_fw_exception(ipc->sdev); - ret = -ETIMEDOUT; - } else { - ret = msg->reply_error; - if (ret < 0) { - dev_err(sdev->dev, - "ipc tx error for %#x (msg/reply size: %d/%zu): %d\n", - hdr->cmd, hdr->size, msg->reply_size, ret); - } else { - ipc_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd); - if (msg->reply_size) - /* copy the data returned from DSP */ - memcpy(reply_data, msg->reply_data, - msg->reply_size); - } - - /* re-enable dumps after successful IPC tx */ - if (sdev->ipc_dump_printed) { - sdev->dbg_dump_printed = false; - sdev->ipc_dump_printed = false; - } - } - - return ret; -} - -/* send IPC message from host to DSP */ -static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, - void *msg_data, size_t msg_bytes, - void *reply_data, size_t reply_bytes) +int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, + size_t reply_bytes) { - struct sof_ipc_cmd_hdr *hdr = msg_data; - struct snd_sof_dev *sdev = ipc->sdev; + struct snd_sof_ipc *ipc = sdev->ipc; struct snd_sof_ipc_msg *msg; int ret; - if (!msg_data || msg_bytes < sizeof(*hdr)) { - dev_err_ratelimited(sdev->dev, "No IPC message to send\n"); - return -EINVAL; - } - if (ipc->disable_ipc_tx || sdev->fw_state != SOF_FW_BOOT_COMPLETE) return -ENODEV; /* - * The spin-lock is also still needed to protect message objects against - * other atomic contexts. + * The spin-lock is needed to protect message objects against other + * atomic contexts. */ spin_lock_irq(&sdev->ipc_lock); @@ -327,38 +68,19 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, spin_unlock_irq(&sdev->ipc_lock); - if (ret) { - dev_err_ratelimited(sdev->dev, - "error: ipc tx failed with error %d\n", - ret); - return ret; - } - - ipc_log_header(sdev->dev, "ipc tx", hdr->cmd); - - /* now wait for completion */ - return tx_wait_done(ipc, msg, reply_data); + return ret; } /* send IPC message from host to DSP */ -int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, - void *msg_data, size_t msg_bytes, void *reply_data, - size_t reply_bytes) +int sof_ipc_tx_message(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes, + void *reply_data, size_t reply_bytes) { - const struct sof_dsp_power_state target_state = { - .state = SOF_DSP_PM_D0, - }; - int ret; - - /* ensure the DSP is in D0 before sending a new IPC */ - ret = snd_sof_dsp_set_power_state(ipc->sdev, &target_state); - if (ret < 0) { - dev_err(ipc->sdev->dev, "error: resuming DSP %d\n", ret); - return ret; - } + if (msg_bytes > ipc->max_payload_size || + reply_bytes > ipc->max_payload_size) + return -ENOBUFS; - return sof_ipc_tx_message_no_pm(ipc, header, msg_data, msg_bytes, - reply_data, reply_bytes); + return ipc->ops->tx_msg(ipc->sdev, msg_data, msg_bytes, reply_data, + reply_bytes, false); } EXPORT_SYMBOL(sof_ipc_tx_message); @@ -367,86 +89,32 @@ EXPORT_SYMBOL(sof_ipc_tx_message); * This will be used for IPC's that can be handled by the DSP * even in a low-power D0 substate. */ -int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, - void *msg_data, size_t msg_bytes, +int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes) { - int ret; - - if (msg_bytes > SOF_IPC_MSG_MAX_SIZE || - reply_bytes > SOF_IPC_MSG_MAX_SIZE) + if (msg_bytes > ipc->max_payload_size || + reply_bytes > ipc->max_payload_size) return -ENOBUFS; - /* Serialise IPC TX */ - mutex_lock(&ipc->tx_mutex); - - ret = sof_ipc_tx_message_unlocked(ipc, msg_data, msg_bytes, - reply_data, reply_bytes); - - mutex_unlock(&ipc->tx_mutex); - - return ret; + return ipc->ops->tx_msg(ipc->sdev, msg_data, msg_bytes, reply_data, + reply_bytes, true); } EXPORT_SYMBOL(sof_ipc_tx_message_no_pm); /* Generic helper function to retrieve the reply */ void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev) { - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - /* * Sometimes, there is unexpected reply ipc arriving. The reply * ipc belongs to none of the ipcs sent from driver. * In this case, the driver must ignore the ipc. */ - if (!msg) { + if (!sdev->msg) { dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); return; } - /* get the generic reply */ - snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, &reply, - sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else if (!reply.hdr.size) { - /* Reply should always be >= sizeof(struct sof_ipc_reply) */ - if (msg->reply_size) - dev_err(sdev->dev, - "empty reply received, expected %zu bytes\n", - msg->reply_size); - else - dev_err(sdev->dev, "empty reply received\n"); - - ret = -EINVAL; - } else if (msg->reply_size > 0) { - if (reply.hdr.size == msg->reply_size) { - ret = 0; - } else if (reply.hdr.size < msg->reply_size) { - dev_dbg(sdev->dev, - "reply size (%u) is less than expected (%zu)\n", - reply.hdr.size, msg->reply_size); - - msg->reply_size = reply.hdr.size; - ret = 0; - } else { - dev_err(sdev->dev, - "reply size (%u) exceeds the buffer size (%zu)\n", - reply.hdr.size, msg->reply_size); - ret = -EINVAL; - } - - /* get the full message if reply.hdr.size <= msg->reply_size */ - if (!ret) - snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; + sdev->msg->reply_error = sdev->ipc->ops->get_reply(sdev); } EXPORT_SYMBOL(snd_sof_ipc_get_reply); @@ -468,550 +136,11 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) } EXPORT_SYMBOL(snd_sof_ipc_reply); -static void ipc_comp_notification(struct snd_sof_dev *sdev, void *msg_buf) -{ - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - struct sof_ipc_cmd_hdr *hdr = msg_buf; - u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; - - switch (msg_type) { - case SOF_IPC_COMP_GET_VALUE: - case SOF_IPC_COMP_GET_DATA: - break; - default: - dev_err(sdev->dev, "error: unhandled component message %#x\n", msg_type); - return; - } - - if (tplg_ops->control->update) - tplg_ops->control->update(sdev, msg_buf); -} - -/* DSP firmware has sent host a message */ -void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) -{ - ipc_rx_callback rx_callback = NULL; - struct sof_ipc_cmd_hdr hdr; - void *msg_buf; - u32 cmd; - int err; - - /* read back header */ - err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr)); - if (err < 0) { - dev_warn(sdev->dev, "failed to read IPC header: %d\n", err); - return; - } - - if (hdr.size < sizeof(hdr)) { - dev_err(sdev->dev, "The received message size is invalid\n"); - return; - } - - ipc_log_header(sdev->dev, "ipc rx", hdr.cmd); - - cmd = hdr.cmd & SOF_GLB_TYPE_MASK; - - /* check message type */ - switch (cmd) { - case SOF_IPC_GLB_REPLY: - dev_err(sdev->dev, "error: ipc reply unknown\n"); - break; - case SOF_IPC_FW_READY: - /* check for FW boot completion */ - if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) { - err = sof_ops(sdev)->fw_ready(sdev, cmd); - if (err < 0) - sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED); - else - sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK); - - /* wake up firmware loader */ - wake_up(&sdev->boot_wait); - } - break; - case SOF_IPC_GLB_COMPOUND: - case SOF_IPC_GLB_TPLG_MSG: - case SOF_IPC_GLB_PM_MSG: - break; - case SOF_IPC_GLB_COMP_MSG: - rx_callback = ipc_comp_notification; - break; - case SOF_IPC_GLB_STREAM_MSG: - rx_callback = ipc_stream_message; - break; - case SOF_IPC_GLB_TRACE_MSG: - rx_callback = ipc_trace_message; - break; - default: - dev_err(sdev->dev, "%s: Unknown DSP message: 0x%x\n", __func__, cmd); - break; - } - - /* read the full message */ - msg_buf = kmalloc(hdr.size, GFP_KERNEL); - if (!msg_buf) - return; - - err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size); - if (err < 0) { - dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err); - } else { - /* Call local handler for the message */ - if (rx_callback) - rx_callback(sdev, msg_buf); - - /* Notify registered clients */ - sof_client_ipc_rx_dispatcher(sdev, msg_buf); - } - - kfree(msg_buf); - - ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd); -} -EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); - -/* - * IPC trace mechanism. - */ - -static void ipc_trace_message(struct snd_sof_dev *sdev, void *msg_buf) -{ - struct sof_ipc_cmd_hdr *hdr = msg_buf; - u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; - - switch (msg_type) { - case SOF_IPC_TRACE_DMA_POSITION: - snd_sof_trace_update_pos(sdev, msg_buf); - break; - default: - dev_err(sdev->dev, "error: unhandled trace message %#x\n", msg_type); - break; - } -} - -/* - * IPC stream position. - */ - -static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) -{ - struct snd_soc_component *scomp = sdev->component; - struct snd_sof_pcm_stream *stream; - struct sof_ipc_stream_posn posn; - struct snd_sof_pcm *spcm; - int direction, ret; - - spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); - if (!spcm) { - dev_err(sdev->dev, - "error: period elapsed for unknown stream, msg_id %d\n", - msg_id); - return; - } - - stream = &spcm->stream[direction]; - ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); - if (ret < 0) { - dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); - return; - } - - dev_vdbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n", - posn.host_posn, posn.dai_posn, posn.wallclock); - - memcpy(&stream->posn, &posn, sizeof(posn)); - - if (spcm->pcm.compress) - snd_sof_compr_fragment_elapsed(stream->cstream); - else if (stream->substream->runtime && - !stream->substream->runtime->no_period_wakeup) - /* only inform ALSA for period_wakeup mode */ - snd_sof_pcm_period_elapsed(stream->substream); -} - -/* DSP notifies host of an XRUN within FW */ -static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) -{ - struct snd_soc_component *scomp = sdev->component; - struct snd_sof_pcm_stream *stream; - struct sof_ipc_stream_posn posn; - struct snd_sof_pcm *spcm; - int direction, ret; - - spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); - if (!spcm) { - dev_err(sdev->dev, "error: XRUN for unknown stream, msg_id %d\n", - msg_id); - return; - } - - stream = &spcm->stream[direction]; - ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); - if (ret < 0) { - dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret); - return; - } - - dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n", - posn.host_posn, posn.xrun_comp_id, posn.xrun_size); - -#if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP) - /* stop PCM on XRUN - used for pipeline debug */ - memcpy(&stream->posn, &posn, sizeof(posn)); - snd_pcm_stop_xrun(stream->substream); -#endif -} - -/* stream notifications from DSP FW */ -static void ipc_stream_message(struct snd_sof_dev *sdev, void *msg_buf) -{ - struct sof_ipc_cmd_hdr *hdr = msg_buf; - u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; - u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd); - - switch (msg_type) { - case SOF_IPC_STREAM_POSITION: - ipc_period_elapsed(sdev, msg_id); - break; - case SOF_IPC_STREAM_TRIG_XRUN: - ipc_xrun(sdev, msg_id); - break; - default: - dev_err(sdev->dev, "error: unhandled stream message %#x\n", - msg_id); - break; - } -} - -/* get stream position IPC - use faster MMIO method if available on platform */ -int snd_sof_ipc_stream_posn(struct snd_soc_component *scomp, - struct snd_sof_pcm *spcm, int direction, - struct sof_ipc_stream_posn *posn) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_stream stream; - int err; - - /* read position via slower IPC */ - stream.hdr.size = sizeof(stream); - stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_POSITION; - stream.comp_id = spcm->stream[direction].comp_id; - - /* send IPC to the DSP */ - err = sof_ipc_tx_message(sdev->ipc, - stream.hdr.cmd, &stream, sizeof(stream), posn, - sizeof(*posn)); - if (err < 0) { - dev_err(sdev->dev, "error: failed to get stream %d position\n", - stream.comp_id); - return err; - } - - return 0; -} -EXPORT_SYMBOL(snd_sof_ipc_stream_posn); - -static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type, - struct sof_ipc_ctrl_data *src, - struct sof_ipc_ctrl_data *dst, - struct sof_ipc_ctrl_data_params *sparams) -{ - switch (ctrl_type) { - case SOF_CTRL_TYPE_VALUE_CHAN_GET: - case SOF_CTRL_TYPE_VALUE_CHAN_SET: - sparams->src = (u8 *)src->chanv; - sparams->dst = (u8 *)dst->chanv; - break; - case SOF_CTRL_TYPE_DATA_GET: - case SOF_CTRL_TYPE_DATA_SET: - sparams->src = (u8 *)src->data->data; - sparams->dst = (u8 *)dst->data->data; - break; - default: - return -EINVAL; - } - - /* calculate payload size and number of messages */ - sparams->pl_size = SOF_IPC_MSG_MAX_SIZE - sparams->hdr_bytes; - sparams->num_msg = DIV_ROUND_UP(sparams->msg_bytes, sparams->pl_size); - - return 0; -} - -static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, - struct sof_ipc_ctrl_data *cdata, - struct sof_ipc_ctrl_data_params *sparams, - bool set) -{ - struct sof_ipc_ctrl_data *partdata; - size_t send_bytes; - size_t offset = 0; - size_t msg_bytes; - size_t pl_size; - int err; - int i; - - /* allocate max ipc size because we have at least one */ - partdata = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); - if (!partdata) - return -ENOMEM; - - if (set) - err = sof_get_ctrl_copy_params(cdata->type, cdata, partdata, - sparams); - else - err = sof_get_ctrl_copy_params(cdata->type, partdata, cdata, - sparams); - if (err < 0) { - kfree(partdata); - return err; - } - - msg_bytes = sparams->msg_bytes; - pl_size = sparams->pl_size; - - /* copy the header data */ - memcpy(partdata, cdata, sparams->hdr_bytes); - - /* Serialise IPC TX */ - mutex_lock(&sdev->ipc->tx_mutex); - - /* copy the payload data in a loop */ - for (i = 0; i < sparams->num_msg; i++) { - send_bytes = min(msg_bytes, pl_size); - partdata->num_elems = send_bytes; - partdata->rhdr.hdr.size = sparams->hdr_bytes + send_bytes; - partdata->msg_index = i; - msg_bytes -= send_bytes; - partdata->elems_remaining = msg_bytes; - - if (set) - memcpy(sparams->dst, sparams->src + offset, send_bytes); - - err = sof_ipc_tx_message_unlocked(sdev->ipc, - partdata, - partdata->rhdr.hdr.size, - partdata, - partdata->rhdr.hdr.size); - if (err < 0) - break; - - if (!set) - memcpy(sparams->dst + offset, sparams->src, send_bytes); - - offset += pl_size; - } - - mutex_unlock(&sdev->ipc->tx_mutex); - - kfree(partdata); - return err; -} - -/* - * IPC get()/set() for kcontrols. - */ -int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set) -{ - struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_fw_ready *ready = &sdev->fw_ready; - struct sof_ipc_fw_version *v = &ready->version; - struct sof_ipc_ctrl_data_params sparams; - enum sof_ipc_ctrl_type ctrl_type; - struct snd_sof_widget *swidget; - bool widget_found = false; - size_t send_bytes; - u32 ipc_cmd; - int err; - - list_for_each_entry(swidget, &sdev->widget_list, list) { - if (swidget->comp_id == scontrol->comp_id) { - widget_found = true; - break; - } - } - - if (!widget_found) { - dev_err(sdev->dev, "error: can't find widget with id %d\n", scontrol->comp_id); - return -EINVAL; - } - - /* - * Volatile controls should always be part of static pipelines and the widget use_count - * would always be > 0 in this case. For the others, just return the cached value if the - * widget is not set up. - */ - if (!swidget->use_count) - return 0; - - /* read or write firmware volume */ - if (scontrol->readback_offset != 0) { - /* write/read value header via mmaped region */ - send_bytes = sizeof(struct sof_ipc_ctrl_value_chan) * - cdata->num_elems; - if (set) - err = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, - scontrol->readback_offset, - cdata->chanv, send_bytes); - - else - err = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_IRAM, - scontrol->readback_offset, - cdata->chanv, send_bytes); - - if (err) - dev_err_once(sdev->dev, "error: %s TYPE_IRAM failed\n", - set ? "write to" : "read from"); - return err; - } - - /* - * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the - * direction - * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently - * for ctrl_type - */ - if (cdata->cmd == SOF_CTRL_CMD_BINARY) { - ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA; - ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET; - } else { - ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE; - ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET; - } - - cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; - cdata->type = ctrl_type; - cdata->comp_id = scontrol->comp_id; - cdata->msg_index = 0; - - /* calculate header and data size */ - switch (cdata->type) { - case SOF_CTRL_TYPE_VALUE_CHAN_GET: - case SOF_CTRL_TYPE_VALUE_CHAN_SET: - sparams.msg_bytes = scontrol->num_channels * - sizeof(struct sof_ipc_ctrl_value_chan); - sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data); - sparams.elems = scontrol->num_channels; - break; - case SOF_CTRL_TYPE_DATA_GET: - case SOF_CTRL_TYPE_DATA_SET: - sparams.msg_bytes = cdata->data->size; - sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data) + - sizeof(struct sof_abi_hdr); - sparams.elems = cdata->data->size; - break; - default: - return -EINVAL; - } - - cdata->rhdr.hdr.size = sparams.msg_bytes + sparams.hdr_bytes; - cdata->num_elems = sparams.elems; - cdata->elems_remaining = 0; - - /* send normal size ipc in one part */ - if (cdata->rhdr.hdr.size <= SOF_IPC_MSG_MAX_SIZE) { - err = sof_ipc_tx_message(sdev->ipc, cdata->rhdr.hdr.cmd, cdata, - cdata->rhdr.hdr.size, cdata, - cdata->rhdr.hdr.size); - - if (err < 0) - dev_err(sdev->dev, "error: set/get ctrl ipc comp %d\n", - cdata->comp_id); - - return err; - } - - /* data is bigger than max ipc size, chop into smaller pieces */ - dev_dbg(sdev->dev, "large ipc size %u, control size %u\n", - cdata->rhdr.hdr.size, scontrol->size); - - /* large messages is only supported from ABI 3.3.0 onwards */ - if (v->abi_version < SOF_ABI_VER(3, 3, 0)) { - dev_err(sdev->dev, "error: incompatible FW ABI version\n"); - return -EINVAL; - } - - err = sof_set_get_large_ctrl_data(sdev, cdata, &sparams, set); - - if (err < 0) - dev_err(sdev->dev, "error: set/get large ctrl ipc comp %d\n", - cdata->comp_id); - - return err; -} -EXPORT_SYMBOL(snd_sof_ipc_set_get_comp_data); - -int snd_sof_ipc_valid(struct snd_sof_dev *sdev) -{ - struct sof_ipc_fw_ready *ready = &sdev->fw_ready; - struct sof_ipc_fw_version *v = &ready->version; - - dev_info(sdev->dev, - "Firmware info: version %d:%d:%d-%s\n", v->major, v->minor, - v->micro, v->tag); - dev_info(sdev->dev, - "Firmware: ABI %d:%d:%d Kernel ABI %d:%d:%d\n", - SOF_ABI_VERSION_MAJOR(v->abi_version), - SOF_ABI_VERSION_MINOR(v->abi_version), - SOF_ABI_VERSION_PATCH(v->abi_version), - SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH); - - if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, v->abi_version)) { - dev_err(sdev->dev, "error: incompatible FW ABI version\n"); - return -EINVAL; - } - - if (SOF_ABI_VERSION_MINOR(v->abi_version) > SOF_ABI_MINOR) { - if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) { - dev_warn(sdev->dev, "warn: FW ABI is more recent than kernel\n"); - } else { - dev_err(sdev->dev, "error: FW ABI is more recent than kernel\n"); - return -EINVAL; - } - } - - if (ready->flags & SOF_IPC_INFO_BUILD) { - dev_info(sdev->dev, - "Firmware debug build %d on %s-%s - options:\n" - " GDB: %s\n" - " lock debug: %s\n" - " lock vdebug: %s\n", - v->build, v->date, v->time, - (ready->flags & SOF_IPC_INFO_GDB) ? - "enabled" : "disabled", - (ready->flags & SOF_IPC_INFO_LOCKS) ? - "enabled" : "disabled", - (ready->flags & SOF_IPC_INFO_LOCKSV) ? - "enabled" : "disabled"); - } - - /* copy the fw_version into debugfs at first boot */ - memcpy(&sdev->fw_version, v, sizeof(*v)); - - return 0; -} -EXPORT_SYMBOL(snd_sof_ipc_valid); - -int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg; - - msg = &sdev->ipc->msg; - - msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); - if (!msg->reply_data) - return -ENOMEM; - - return 0; -} - struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) { struct snd_sof_ipc *ipc; struct snd_sof_ipc_msg *msg; + const struct sof_ipc_ops *ops; ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL); if (!ipc) @@ -1031,11 +160,33 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) * versions, this will need to be modified to use the selected version at runtime. */ ipc->ops = &ipc3_ops; + ops = ipc->ops; /* check for mandatory ops */ - if (!ipc->ops->pcm || !ipc->ops->tplg || !ipc->ops->tplg->widget || - !ipc->ops->tplg->control) { - dev_err(sdev->dev, "Invalid IPC ops\n"); + if (!ops->tx_msg || !ops->rx_msg || !ops->set_get_data || !ops->get_reply) { + dev_err(sdev->dev, "Missing IPC message handling ops\n"); + return NULL; + } + + if (!ops->fw_loader || !ops->fw_loader->validate || + !ops->fw_loader->parse_ext_manifest) { + dev_err(sdev->dev, "Missing IPC firmware loading ops\n"); + return NULL; + } + + if (!ops->pcm) { + dev_err(sdev->dev, "Missing IPC PCM ops\n"); + return NULL; + } + + if (!ops->tplg || !ops->tplg->widget || !ops->tplg->control) { + dev_err(sdev->dev, "Missing IPC topology ops\n"); + return NULL; + } + + if (ops->fw_tracing && (!ops->fw_tracing->init || !ops->fw_tracing->suspend || + !ops->fw_tracing->resume)) { + dev_err(sdev->dev, "Missing firmware tracing ops\n"); return NULL; } diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index cdd5ad860a94..3fdc0d854e65 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -9,26 +9,85 @@ #include "sof-priv.h" #include "sof-audio.h" -#include "ipc3-ops.h" +#include "ipc3-priv.h" -static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) +/* IPC set()/get() for kcontrols. */ +static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set) { - if (value >= size) - return volume_map[size - 1]; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp); + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + const struct sof_ipc_ops *iops = sdev->ipc->ops; + enum sof_ipc_ctrl_type ctrl_type; + struct snd_sof_widget *swidget; + bool widget_found = false; + u32 ipc_cmd, msg_bytes; - return volume_map[value]; -} + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->comp_id == scontrol->comp_id) { + widget_found = true; + break; + } + } -static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) -{ - int i; + if (!widget_found) { + dev_err(sdev->dev, "%s: can't find widget with id %d\n", __func__, + scontrol->comp_id); + return -EINVAL; + } + + /* + * Volatile controls should always be part of static pipelines and the widget use_count + * would always be > 0 in this case. For the others, just return the cached value if the + * widget is not set up. + */ + if (!swidget->use_count) + return 0; + + /* + * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the + * direction + * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently + * for ctrl_type + */ + if (cdata->cmd == SOF_CTRL_CMD_BINARY) { + ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA; + ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET; + } else { + ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE; + ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET; + } + + cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; + cdata->type = ctrl_type; + cdata->comp_id = scontrol->comp_id; + cdata->msg_index = 0; + + /* calculate header and data size */ + switch (cdata->type) { + case SOF_CTRL_TYPE_VALUE_CHAN_GET: + case SOF_CTRL_TYPE_VALUE_CHAN_SET: + cdata->num_elems = scontrol->num_channels; - for (i = 0; i < size; i++) { - if (volume_map[i] >= value) - return i; + msg_bytes = scontrol->num_channels * + sizeof(struct sof_ipc_ctrl_value_chan); + msg_bytes += sizeof(struct sof_ipc_ctrl_data); + break; + case SOF_CTRL_TYPE_DATA_GET: + case SOF_CTRL_TYPE_DATA_SET: + cdata->num_elems = cdata->data->size; + + msg_bytes = cdata->data->size; + msg_bytes += sizeof(struct sof_ipc_ctrl_data) + + sizeof(struct sof_abi_hdr); + break; + default: + return -EINVAL; } - return i - 1; + cdata->rhdr.hdr.size = msg_bytes; + cdata->elems_remaining = 0; + + return iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); } static void snd_sof_refresh_control(struct snd_sof_control *scontrol) @@ -49,7 +108,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) /* refresh the component data from DSP */ scontrol->comp_data_dirty = false; - ret = snd_sof_ipc_set_get_comp_data(scontrol, false); + ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); if (ret < 0) { dev_err(scomp->dev, "Failed to get control data: %d\n", ret); @@ -97,7 +156,7 @@ static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) { - int ret = snd_sof_ipc_set_get_comp_data(scontrol, true); + int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); if (ret < 0) { dev_err(scomp->dev, "Failed to set mixer updates for %s\n", @@ -145,7 +204,7 @@ static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) { - int ret = snd_sof_ipc_set_get_comp_data(scontrol, true); + int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); if (ret < 0) { dev_err(scomp->dev, "Failed to set mixer updates for %s\n", @@ -193,7 +252,7 @@ static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol, /* notify DSP of enum updates */ if (pm_runtime_active(scomp->dev)) { - int ret = snd_sof_ipc_set_get_comp_data(scontrol, true); + int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); if (ret < 0) { dev_err(scomp->dev, "Failed to set enum updates for %s\n", @@ -265,7 +324,7 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) - return snd_sof_ipc_set_get_comp_data(scontrol, true); + return sof_ipc3_set_get_kcontrol_data(scontrol, true); return 0; } @@ -379,7 +438,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) - return snd_sof_ipc_set_get_comp_data(scontrol, true); + return sof_ipc3_set_get_kcontrol_data(scontrol, true); return 0; } @@ -409,7 +468,7 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol, cdata->data->abi = SOF_ABI_VERSION; /* get all the component data from DSP */ - ret = snd_sof_ipc_set_get_comp_data(scontrol, false); + ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); if (ret < 0) return ret; @@ -578,6 +637,60 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ snd_ctl_notify_one(swidget->scomp->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, kc, 0); } +static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget) +{ + struct snd_sof_control *scontrol; + int ret; + + /* set up all controls for the widget */ + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) + if (scontrol->comp_id == swidget->comp_id) { + /* set kcontrol data in DSP */ + ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); + if (ret < 0) { + dev_err(sdev->dev, + "kcontrol %d set up failed for widget %s\n", + scontrol->comp_id, swidget->widget->name); + return ret; + } + + /* + * Read back the data from the DSP for static widgets. + * This is particularly useful for binary kcontrols + * associated with static pipeline widgets to initialize + * the data size to match that in the DSP. + */ + if (swidget->dynamic_pipeline_widget) + continue; + + ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); + if (ret < 0) + dev_warn(sdev->dev, + "kcontrol %d read failed for widget %s\n", + scontrol->comp_id, swidget->widget->name); + } + + return 0; +} + +static int +sof_ipc3_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size) +{ + int i; + + /* init the volume table */ + scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL); + if (!scontrol->volume_table) + return -ENOMEM; + + /* populate the volume table */ + for (i = 0; i < size ; i++) + scontrol->volume_table[i] = vol_compute_gain(i, tlv); + + return 0; +} + const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { .volume_put = sof_ipc3_volume_put, .volume_get = sof_ipc3_volume_get, @@ -591,4 +704,6 @@ const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { .bytes_ext_get = sof_ipc3_bytes_ext_get, .bytes_ext_volatile_get = sof_ipc3_bytes_ext_volatile_get, .update = sof_ipc3_control_update, + .widget_kcontrol_setup = sof_ipc3_widget_kcontrol_setup, + .set_up_volume_table = sof_ipc3_set_up_volume_table, }; diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c new file mode 100644 index 000000000000..b4e1343f9138 --- /dev/null +++ b/sound/soc/sof/ipc3-dtrace.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> + +#include <linux/debugfs.h> +#include <linux/sched/signal.h> +#include "sof-priv.h" +#include "sof-audio.h" +#include "ops.h" +#include "sof-utils.h" +#include "ipc3-priv.h" + +#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 +#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024 + +enum sof_dtrace_state { + SOF_DTRACE_DISABLED, + SOF_DTRACE_STOPPED, + SOF_DTRACE_ENABLED, +}; + +struct sof_dtrace_priv { + struct snd_dma_buffer dmatb; + struct snd_dma_buffer dmatp; + int dma_trace_pages; + wait_queue_head_t trace_sleep; + u32 host_offset; + bool dtrace_error; + bool dtrace_draining; + enum sof_dtrace_state dtrace_state; +}; + +static int trace_filter_append_elem(struct snd_sof_dev *sdev, u32 key, u32 value, + struct sof_ipc_trace_filter_elem *elem_list, + int capacity, int *counter) +{ + if (*counter >= capacity) + return -ENOMEM; + + elem_list[*counter].key = key; + elem_list[*counter].value = value; + ++*counter; + + return 0; +} + +static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line, + struct sof_ipc_trace_filter_elem *elem, + int capacity, int *counter) +{ + int log_level, pipe_id, comp_id, read, ret; + int len = strlen(line); + int cnt = *counter; + u32 uuid_id; + + /* ignore empty content */ + ret = sscanf(line, " %n", &read); + if (!ret && read == len) + return len; + + ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read); + if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) { + dev_err(sdev->dev, "Invalid trace filter entry '%s'\n", line); + return -EINVAL; + } + + if (uuid_id > 0) { + ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID, + uuid_id, elem, capacity, &cnt); + if (ret) + return ret; + } + if (pipe_id >= 0) { + ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE, + pipe_id, elem, capacity, &cnt); + if (ret) + return ret; + } + if (comp_id >= 0) { + ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP, + comp_id, elem, capacity, &cnt); + if (ret) + return ret; + } + + ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL | + SOF_IPC_TRACE_FILTER_ELEM_FIN, + log_level, elem, capacity, &cnt); + if (ret) + return ret; + + /* update counter only when parsing whole entry passed */ + *counter = cnt; + + return len; +} + +static int trace_filter_parse(struct snd_sof_dev *sdev, char *string, + int *out_elem_cnt, + struct sof_ipc_trace_filter_elem **out) +{ + static const char entry_delimiter[] = ";"; + char *entry = string; + int capacity = 0; + int entry_len; + int cnt = 0; + + /* + * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY + * IPC elements, depending on content. Calculate IPC elements capacity + * for the input string where each element is set. + */ + while (entry) { + capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY; + entry = strchr(entry + 1, entry_delimiter[0]); + } + *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL); + if (!*out) + return -ENOMEM; + + /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */ + while ((entry = strsep(&string, entry_delimiter))) { + entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt); + if (entry_len < 0) { + dev_err(sdev->dev, + "Parsing filter entry '%s' failed with %d\n", + entry, entry_len); + return -EINVAL; + } + } + + *out_elem_cnt = cnt; + + return 0; +} + +static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems, + struct sof_ipc_trace_filter_elem *elems) +{ + struct sof_ipc_trace_filter *msg; + struct sof_ipc_reply reply; + size_t size; + int ret; + + size = struct_size(msg, elems, num_elems); + if (size > SOF_IPC_MSG_MAX_SIZE) + return -EINVAL; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->hdr.size = size; + msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE; + msg->elem_cnt = num_elems; + memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems)); + + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(sdev->dev); + dev_err(sdev->dev, "enabling device failed: %d\n", ret); + goto error; + } + ret = sof_ipc_tx_message(sdev->ipc, msg, msg->hdr.size, &reply, sizeof(reply)); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + +error: + kfree(msg); + return ret ? ret : reply.error; +} + +static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user *from, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct sof_ipc_trace_filter_elem *elems = NULL; + struct snd_sof_dev *sdev = dfse->sdev; + loff_t pos = 0; + int num_elems; + char *string; + int ret; + + if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) { + dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count, + TRACE_FILTER_MAX_CONFIG_STRING_LENGTH); + return -EINVAL; + } + + string = kmalloc(count + 1, GFP_KERNEL); + if (!string) + return -ENOMEM; + + /* assert null termination */ + string[count] = 0; + ret = simple_write_to_buffer(string, count, &pos, from, count); + if (ret < 0) + goto error; + + ret = trace_filter_parse(sdev, string, &num_elems, &elems); + if (ret < 0) + goto error; + + if (num_elems) { + ret = ipc3_trace_update_filter(sdev, num_elems, elems); + if (ret < 0) { + dev_err(sdev->dev, "Filter update failed: %d\n", ret); + goto error; + } + } + ret = count; +error: + kfree(string); + kfree(elems); + return ret; +} + +static const struct file_operations sof_dfs_trace_filter_fops = { + .open = simple_open, + .write = dfsentry_trace_filter_write, + .llseek = default_llseek, +}; + +static int debugfs_create_trace_filter(struct snd_sof_dev *sdev) +{ + struct snd_sof_dfsentry *dfse; + + dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + dfse->sdev = sdev; + dfse->type = SOF_DFSENTRY_TYPE_BUF; + + debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse, + &sof_dfs_trace_filter_fops); + /* add to dfsentry list */ + list_add(&dfse->list, &sdev->dfsentry_list); + + return 0; +} + +static size_t sof_dtrace_avail(struct snd_sof_dev *sdev, + loff_t pos, size_t buffer_size) +{ + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + loff_t host_offset = READ_ONCE(priv->host_offset); + + /* + * If host offset is less than local pos, it means write pointer of + * host DMA buffer has been wrapped. We should output the trace data + * at the end of host DMA buffer at first. + */ + if (host_offset < pos) + return buffer_size - pos; + + /* If there is available trace data now, it is unnecessary to wait. */ + if (host_offset > pos) + return host_offset - pos; + + return 0; +} + +static size_t sof_wait_dtrace_avail(struct snd_sof_dev *sdev, loff_t pos, + size_t buffer_size) +{ + size_t ret = sof_dtrace_avail(sdev, pos, buffer_size); + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + wait_queue_entry_t wait; + + /* data immediately available */ + if (ret) + return ret; + + if (priv->dtrace_state != SOF_DTRACE_ENABLED && priv->dtrace_draining) { + /* + * tracing has ended and all traces have been + * read by client, return EOF + */ + priv->dtrace_draining = false; + return 0; + } + + /* wait for available trace data from FW */ + init_waitqueue_entry(&wait, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&priv->trace_sleep, &wait); + + if (!signal_pending(current)) { + /* set timeout to max value, no error code */ + schedule_timeout(MAX_SCHEDULE_TIMEOUT); + } + remove_wait_queue(&priv->trace_sleep, &wait); + + return sof_dtrace_avail(sdev, pos, buffer_size); +} + +static ssize_t dfsentry_dtrace_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + unsigned long rem; + loff_t lpos = *ppos; + size_t avail, buffer_size = dfse->size; + u64 lpos_64; + + /* make sure we know about any failures on the DSP side */ + priv->dtrace_error = false; + + /* check pos and count */ + if (lpos < 0) + return -EINVAL; + if (!count) + return 0; + + /* check for buffer wrap and count overflow */ + lpos_64 = lpos; + lpos = do_div(lpos_64, buffer_size); + + /* get available count based on current host offset */ + avail = sof_wait_dtrace_avail(sdev, lpos, buffer_size); + if (priv->dtrace_error) { + dev_err(sdev->dev, "trace IO error\n"); + return -EIO; + } + + /* make sure count is <= avail */ + if (count > avail) + count = avail; + + /* + * make sure that all trace data is available for the CPU as the trace + * data buffer might be allocated from non consistent memory. + * Note: snd_dma_buffer_sync() is called for normal audio playback and + * capture streams also. + */ + snd_dma_buffer_sync(&priv->dmatb, SNDRV_DMA_SYNC_CPU); + /* copy available trace data to debugfs */ + rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count); + if (rem) + return -EFAULT; + + *ppos += count; + + /* move debugfs reading position */ + return count; +} + +static int dfsentry_dtrace_release(struct inode *inode, struct file *file) +{ + struct snd_sof_dfsentry *dfse = inode->i_private; + struct snd_sof_dev *sdev = dfse->sdev; + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + + /* avoid duplicate traces at next open */ + if (priv->dtrace_state != SOF_DTRACE_ENABLED) + priv->host_offset = 0; + + return 0; +} + +static const struct file_operations sof_dfs_dtrace_fops = { + .open = simple_open, + .read = dfsentry_dtrace_read, + .llseek = default_llseek, + .release = dfsentry_dtrace_release, +}; + +static int debugfs_create_dtrace(struct snd_sof_dev *sdev) +{ + struct sof_dtrace_priv *priv; + struct snd_sof_dfsentry *dfse; + int ret; + + if (!sdev) + return -EINVAL; + + priv = sdev->fw_trace_data; + + ret = debugfs_create_trace_filter(sdev); + if (ret < 0) + dev_warn(sdev->dev, "failed to create filter debugfs file: %d", ret); + + dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + dfse->type = SOF_DFSENTRY_TYPE_BUF; + dfse->buf = priv->dmatb.area; + dfse->size = priv->dmatb.bytes; + dfse->sdev = sdev; + + debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse, + &sof_dfs_dtrace_fops); + + return 0; +} + +static int ipc3_dtrace_enable(struct snd_sof_dev *sdev) +{ + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + struct sof_ipc_fw_ready *ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &ready->version; + struct sof_ipc_dma_trace_params_ext params; + struct sof_ipc_reply ipc_reply; + int ret; + + if (!sdev->fw_trace_is_supported) + return 0; + + if (priv->dtrace_state == SOF_DTRACE_ENABLED || !priv->dma_trace_pages) + return -EINVAL; + + if (priv->dtrace_state == SOF_DTRACE_STOPPED) + goto start; + + /* set IPC parameters */ + params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG; + /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */ + if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) { + params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext); + params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT; + params.timestamp_ns = ktime_get(); /* in nanosecond */ + } else { + params.hdr.size = sizeof(struct sof_ipc_dma_trace_params); + params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS; + } + params.buffer.phy_addr = priv->dmatp.addr; + params.buffer.size = priv->dmatb.bytes; + params.buffer.pages = priv->dma_trace_pages; + params.stream_tag = 0; + + priv->host_offset = 0; + priv->dtrace_draining = false; + + ret = sof_dtrace_host_init(sdev, &priv->dmatb, ¶ms); + if (ret < 0) { + dev_err(sdev->dev, "Host dtrace init failed: %d\n", ret); + return ret; + } + dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag); + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, ¶ms, sizeof(params), &ipc_reply, sizeof(ipc_reply)); + if (ret < 0) { + dev_err(sdev->dev, "can't set params for DMA for trace %d\n", ret); + goto trace_release; + } + +start: + ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(sdev->dev, "Host dtrace trigger start failed: %d\n", ret); + goto trace_release; + } + + priv->dtrace_state = SOF_DTRACE_ENABLED; + + return 0; + +trace_release: + sof_dtrace_host_release(sdev); + return ret; +} + +static int ipc3_dtrace_init(struct snd_sof_dev *sdev) +{ + struct sof_dtrace_priv *priv; + int ret; + + /* dtrace is only supported with SOF_IPC */ + if (sdev->pdata->ipc_type != SOF_IPC) + return -EOPNOTSUPP; + + if (sdev->fw_trace_data) { + dev_err(sdev->dev, "fw_trace_data has been already allocated\n"); + return -EBUSY; + } + + priv = devm_kzalloc(sdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + sdev->fw_trace_data = priv; + + /* set false before start initialization */ + priv->dtrace_state = SOF_DTRACE_DISABLED; + + /* allocate trace page table buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, + PAGE_SIZE, &priv->dmatp); + if (ret < 0) { + dev_err(sdev->dev, "can't alloc page table for trace %d\n", ret); + return ret; + } + + /* allocate trace data buffer */ + ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev, + DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE, + &priv->dmatb); + if (ret < 0) { + dev_err(sdev->dev, "can't alloc buffer for trace %d\n", ret); + goto page_err; + } + + /* create compressed page table for audio firmware */ + ret = snd_sof_create_page_table(sdev->dev, &priv->dmatb, + priv->dmatp.area, priv->dmatb.bytes); + if (ret < 0) + goto table_err; + + priv->dma_trace_pages = ret; + dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n", __func__, + priv->dma_trace_pages); + + if (sdev->first_boot) { + ret = debugfs_create_dtrace(sdev); + if (ret < 0) + goto table_err; + } + + init_waitqueue_head(&priv->trace_sleep); + + ret = ipc3_dtrace_enable(sdev); + if (ret < 0) + goto table_err; + + return 0; +table_err: + priv->dma_trace_pages = 0; + snd_dma_free_pages(&priv->dmatb); +page_err: + snd_dma_free_pages(&priv->dmatp); + return ret; +} + +int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_posn *posn) +{ + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + + if (!sdev->fw_trace_is_supported) + return 0; + + if (priv->dtrace_state == SOF_DTRACE_ENABLED && + priv->host_offset != posn->host_offset) { + priv->host_offset = posn->host_offset; + wake_up(&priv->trace_sleep); + } + + if (posn->overflow != 0) + dev_err(sdev->dev, + "DSP trace buffer overflow %u bytes. Total messages %d\n", + posn->overflow, posn->messages); + + return 0; +} + +/* an error has occurred within the DSP that prevents further trace */ +static void ipc3_dtrace_fw_crashed(struct snd_sof_dev *sdev) +{ + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + + if (priv->dtrace_state == SOF_DTRACE_ENABLED) { + priv->dtrace_error = true; + wake_up(&priv->trace_sleep); + } +} + +static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop) +{ + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + struct sof_ipc_fw_ready *ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &ready->version; + struct sof_ipc_cmd_hdr hdr; + struct sof_ipc_reply ipc_reply; + int ret; + + if (!sdev->fw_trace_is_supported || priv->dtrace_state == SOF_DTRACE_DISABLED) + return; + + ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); + if (ret < 0) + dev_err(sdev->dev, "Host dtrace trigger stop failed: %d\n", ret); + priv->dtrace_state = SOF_DTRACE_STOPPED; + + /* + * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from + * ABI 3.20.0 onwards + */ + if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) { + hdr.size = sizeof(hdr); + hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE; + + ret = sof_ipc_tx_message(sdev->ipc, &hdr, hdr.size, + &ipc_reply, sizeof(ipc_reply)); + if (ret < 0) + dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret); + } + + if (only_stop) + goto out; + + ret = sof_dtrace_host_release(sdev); + if (ret < 0) + dev_err(sdev->dev, "Host dtrace release failed %d\n", ret); + + priv->dtrace_state = SOF_DTRACE_DISABLED; + +out: + priv->dtrace_draining = true; + wake_up(&priv->trace_sleep); +} + +static void ipc3_dtrace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state) +{ + ipc3_dtrace_release(sdev, pm_state.event == SOF_DSP_PM_D0); +} + +static int ipc3_dtrace_resume(struct snd_sof_dev *sdev) +{ + return ipc3_dtrace_enable(sdev); +} + +static void ipc3_dtrace_free(struct snd_sof_dev *sdev) +{ + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + + /* release trace */ + ipc3_dtrace_release(sdev, false); + + if (priv->dma_trace_pages) { + snd_dma_free_pages(&priv->dmatb); + snd_dma_free_pages(&priv->dmatp); + priv->dma_trace_pages = 0; + } +} + +const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops = { + .init = ipc3_dtrace_init, + .free = ipc3_dtrace_free, + .fw_crashed = ipc3_dtrace_fw_crashed, + .suspend = ipc3_dtrace_suspend, + .resume = ipc3_dtrace_resume, +}; diff --git a/sound/soc/sof/ipc3-loader.c b/sound/soc/sof/ipc3-loader.c new file mode 100644 index 000000000000..f3c741b49519 --- /dev/null +++ b/sound/soc/sof/ipc3-loader.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. + +#include <linux/firmware.h> +#include "sof-priv.h" +#include "sof-audio.h" +#include "ipc3-priv.h" +#include "ops.h" + +static int ipc3_fw_ext_man_get_version(struct snd_sof_dev *sdev, + const struct sof_ext_man_elem_header *hdr) +{ + const struct sof_ext_man_fw_version *v = + container_of(hdr, struct sof_ext_man_fw_version, hdr); + + memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version)); + sdev->fw_ready.flags = v->flags; + + /* log ABI versions and check FW compatibility */ + return sof_ipc3_validate_fw_version(sdev); +} + +static int ipc3_fw_ext_man_get_windows(struct snd_sof_dev *sdev, + const struct sof_ext_man_elem_header *hdr) +{ + const struct sof_ext_man_window *w; + + w = container_of(hdr, struct sof_ext_man_window, hdr); + + return sof_ipc3_get_ext_windows(sdev, &w->ipc_window.ext_hdr); +} + +static int ipc3_fw_ext_man_get_cc_info(struct snd_sof_dev *sdev, + const struct sof_ext_man_elem_header *hdr) +{ + const struct sof_ext_man_cc_version *cc; + + cc = container_of(hdr, struct sof_ext_man_cc_version, hdr); + + return sof_ipc3_get_cc_info(sdev, &cc->cc_version.ext_hdr); +} + +static int ipc3_fw_ext_man_get_dbg_abi_info(struct snd_sof_dev *sdev, + const struct sof_ext_man_elem_header *hdr) +{ + const struct ext_man_dbg_abi *dbg_abi = + container_of(hdr, struct ext_man_dbg_abi, hdr); + + if (sdev->first_boot) + dev_dbg(sdev->dev, + "Firmware: DBG_ABI %d:%d:%d\n", + SOF_ABI_VERSION_MAJOR(dbg_abi->dbg_abi.abi_dbg_version), + SOF_ABI_VERSION_MINOR(dbg_abi->dbg_abi.abi_dbg_version), + SOF_ABI_VERSION_PATCH(dbg_abi->dbg_abi.abi_dbg_version)); + + return 0; +} + +static int ipc3_fw_ext_man_get_config_data(struct snd_sof_dev *sdev, + const struct sof_ext_man_elem_header *hdr) +{ + const struct sof_ext_man_config_data *config = + container_of(hdr, struct sof_ext_man_config_data, hdr); + const struct sof_config_elem *elem; + int elems_counter; + int elems_size; + int ret = 0; + int i; + + /* calculate elements counter */ + elems_size = config->hdr.size - sizeof(struct sof_ext_man_elem_header); + elems_counter = elems_size / sizeof(struct sof_config_elem); + + dev_dbg(sdev->dev, "%s can hold up to %d config elements\n", + __func__, elems_counter); + + for (i = 0; i < elems_counter; ++i) { + elem = &config->elems[i]; + dev_dbg(sdev->dev, "%s get index %d token %d val %d\n", + __func__, i, elem->token, elem->value); + switch (elem->token) { + case SOF_EXT_MAN_CONFIG_EMPTY: + /* unused memory space is zero filled - mapped to EMPTY elements */ + break; + case SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE: + /* TODO: use ipc msg size from config data */ + break; + case SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN: + if (sdev->first_boot && elem->value) + ret = snd_sof_dbg_memory_info_init(sdev); + break; + default: + dev_info(sdev->dev, + "Unknown firmware configuration token %d value %d", + elem->token, elem->value); + break; + } + if (ret < 0) { + dev_err(sdev->dev, + "%s: processing failed for token %d value %#x, %d\n", + __func__, elem->token, elem->value, ret); + return ret; + } + } + + return 0; +} + +static ssize_t ipc3_fw_ext_man_size(const struct firmware *fw) +{ + const struct sof_ext_man_header *head; + + head = (struct sof_ext_man_header *)fw->data; + + /* + * assert fw size is big enough to contain extended manifest header, + * it prevents from reading unallocated memory from `head` in following + * step. + */ + if (fw->size < sizeof(*head)) + return -EINVAL; + + /* + * When fw points to extended manifest, + * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER. + */ + if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER) + return head->full_size; + + /* otherwise given fw don't have an extended manifest */ + return 0; +} + +static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + const struct firmware *fw = plat_data->fw; + const struct sof_ext_man_elem_header *elem_hdr; + const struct sof_ext_man_header *head; + ssize_t ext_man_size; + ssize_t remaining; + uintptr_t iptr; + int ret = 0; + + head = (struct sof_ext_man_header *)fw->data; + remaining = head->full_size - head->header_size; + ext_man_size = ipc3_fw_ext_man_size(fw); + + /* Assert firmware starts with extended manifest */ + if (ext_man_size <= 0) + return ext_man_size; + + /* incompatible version */ + if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION, + head->header_version)) { + dev_err(sdev->dev, + "extended manifest version %#x differ from used %#x\n", + head->header_version, SOF_EXT_MAN_VERSION); + return -EINVAL; + } + + /* get first extended manifest element header */ + iptr = (uintptr_t)fw->data + head->header_size; + + while (remaining > sizeof(*elem_hdr)) { + elem_hdr = (struct sof_ext_man_elem_header *)iptr; + + dev_dbg(sdev->dev, "found sof_ext_man header type %d size %#x\n", + elem_hdr->type, elem_hdr->size); + + if (elem_hdr->size < sizeof(*elem_hdr) || + elem_hdr->size > remaining) { + dev_err(sdev->dev, + "invalid sof_ext_man header size, type %d size %#x\n", + elem_hdr->type, elem_hdr->size); + return -EINVAL; + } + + /* process structure data */ + switch (elem_hdr->type) { + case SOF_EXT_MAN_ELEM_FW_VERSION: + ret = ipc3_fw_ext_man_get_version(sdev, elem_hdr); + break; + case SOF_EXT_MAN_ELEM_WINDOW: + ret = ipc3_fw_ext_man_get_windows(sdev, elem_hdr); + break; + case SOF_EXT_MAN_ELEM_CC_VERSION: + ret = ipc3_fw_ext_man_get_cc_info(sdev, elem_hdr); + break; + case SOF_EXT_MAN_ELEM_DBG_ABI: + ret = ipc3_fw_ext_man_get_dbg_abi_info(sdev, elem_hdr); + break; + case SOF_EXT_MAN_ELEM_CONFIG_DATA: + ret = ipc3_fw_ext_man_get_config_data(sdev, elem_hdr); + break; + case SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA: + ret = snd_sof_dsp_parse_platform_ext_manifest(sdev, elem_hdr); + break; + default: + dev_info(sdev->dev, + "unknown sof_ext_man header type %d size %#x\n", + elem_hdr->type, elem_hdr->size); + break; + } + + if (ret < 0) { + dev_err(sdev->dev, + "failed to parse sof_ext_man header type %d size %#x\n", + elem_hdr->type, elem_hdr->size); + return ret; + } + + remaining -= elem_hdr->size; + iptr += elem_hdr->size; + } + + if (remaining) { + dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n"); + return -EINVAL; + } + + return ext_man_size; +} + +/* generic module parser for mmaped DSPs */ +static int sof_ipc3_parse_module_memcpy(struct snd_sof_dev *sdev, + struct snd_sof_mod_hdr *module) +{ + struct snd_sof_blk_hdr *block; + int count, ret; + u32 offset; + size_t remaining; + + dev_dbg(sdev->dev, "new module size %#x blocks %#x type %#x\n", + module->size, module->num_blocks, module->type); + + block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module)); + + /* module->size doesn't include header size */ + remaining = module->size; + for (count = 0; count < module->num_blocks; count++) { + /* check for wrap */ + if (remaining < sizeof(*block)) { + dev_err(sdev->dev, "not enough data remaining\n"); + return -EINVAL; + } + + /* minus header size of block */ + remaining -= sizeof(*block); + + if (block->size == 0) { + dev_warn(sdev->dev, + "warning: block %d size zero\n", count); + dev_warn(sdev->dev, " type %#x offset %#x\n", + block->type, block->offset); + continue; + } + + switch (block->type) { + case SOF_FW_BLK_TYPE_RSRVD0: + case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14: + continue; /* not handled atm */ + case SOF_FW_BLK_TYPE_IRAM: + case SOF_FW_BLK_TYPE_DRAM: + case SOF_FW_BLK_TYPE_SRAM: + offset = block->offset; + break; + default: + dev_err(sdev->dev, "%s: bad type %#x for block %#x\n", + __func__, block->type, count); + return -EINVAL; + } + + dev_dbg(sdev->dev, "block %d type %#x size %#x ==> offset %#x\n", + count, block->type, block->size, offset); + + /* checking block->size to avoid unaligned access */ + if (block->size % sizeof(u32)) { + dev_err(sdev->dev, "%s: invalid block size %#x\n", + __func__, block->size); + return -EINVAL; + } + ret = snd_sof_dsp_block_write(sdev, block->type, offset, + block + 1, block->size); + if (ret < 0) { + dev_err(sdev->dev, "%s: write to block type %#x failed\n", + __func__, block->type); + return ret; + } + + if (remaining < block->size) { + dev_err(sdev->dev, "%s: not enough data remaining\n", __func__); + return -EINVAL; + } + + /* minus body size of block */ + remaining -= block->size; + /* next block */ + block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block) + + block->size); + } + + return 0; +} + +static int sof_ipc3_load_fw_to_dsp(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + const struct firmware *fw = plat_data->fw; + struct snd_sof_fw_header *header; + struct snd_sof_mod_hdr *module; + int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr); + size_t remaining; + int ret, count; + + if (!plat_data->fw) + return -EINVAL; + + header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset); + load_module = sof_ops(sdev)->load_module; + if (!load_module) { + dev_dbg(sdev->dev, "%s: Using generic module loading\n", __func__); + load_module = sof_ipc3_parse_module_memcpy; + } else { + dev_dbg(sdev->dev, "%s: Using custom module loading\n", __func__); + } + + /* parse each module */ + module = (struct snd_sof_mod_hdr *)(fw->data + plat_data->fw_offset + + sizeof(*header)); + remaining = fw->size - sizeof(*header) - plat_data->fw_offset; + /* check for wrap */ + if (remaining > fw->size) { + dev_err(sdev->dev, "%s: fw size smaller than header size\n", __func__); + return -EINVAL; + } + + for (count = 0; count < header->num_modules; count++) { + /* check for wrap */ + if (remaining < sizeof(*module)) { + dev_err(sdev->dev, "%s: not enough data for a module\n", + __func__); + return -EINVAL; + } + + /* minus header size of module */ + remaining -= sizeof(*module); + + /* module */ + ret = load_module(sdev, module); + if (ret < 0) { + dev_err(sdev->dev, "%s: invalid module %d\n", __func__, count); + return ret; + } + + if (remaining < module->size) { + dev_err(sdev->dev, "%s: not enough data remaining\n", __func__); + return -EINVAL; + } + + /* minus body size of module */ + remaining -= module->size; + module = (struct snd_sof_mod_hdr *)((u8 *)module + + sizeof(*module) + module->size); + } + + return 0; +} + +static int sof_ipc3_validate_firmware(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + const struct firmware *fw = plat_data->fw; + struct snd_sof_fw_header *header; + size_t fw_size = fw->size - plat_data->fw_offset; + + if (fw->size <= plat_data->fw_offset) { + dev_err(sdev->dev, + "firmware size must be greater than firmware offset\n"); + return -EINVAL; + } + + /* Read the header information from the data pointer */ + header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset); + + /* verify FW sig */ + if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) { + dev_err(sdev->dev, "invalid firmware signature\n"); + return -EINVAL; + } + + /* check size is valid */ + if (fw_size != header->file_size + sizeof(*header)) { + dev_err(sdev->dev, + "invalid filesize mismatch got 0x%zx expected 0x%zx\n", + fw_size, header->file_size + sizeof(*header)); + return -EINVAL; + } + + dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n", + header->file_size, header->num_modules, + header->abi, sizeof(*header)); + + return 0; +} + +const struct sof_ipc_fw_loader_ops ipc3_loader_ops = { + .validate = sof_ipc3_validate_firmware, + .parse_ext_manifest = sof_ipc3_fw_parse_ext_man, + .load_fw_to_dsp = sof_ipc3_load_fw_to_dsp, +}; diff --git a/sound/soc/sof/ipc3-ops.h b/sound/soc/sof/ipc3-ops.h deleted file mode 100644 index a4784626a3d7..000000000000 --- a/sound/soc/sof/ipc3-ops.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ -/* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * Copyright(c) 2021 Intel Corporation. All rights reserved. - * - * Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> - */ - -#ifndef __SOUND_SOC_SOF_IPC3_OPS_H -#define __SOUND_SOC_SOF_IPC3_OPS_H - -#include "sof-priv.h" - -extern const struct sof_ipc_tplg_ops ipc3_tplg_ops; -extern const struct sof_ipc_ops ipc3_ops; -extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops; -extern const struct sof_ipc_pcm_ops ipc3_pcm_ops; - -#endif diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 58b75943cf6d..c8774a891d6f 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -8,7 +8,7 @@ // #include <sound/pcm_params.h> -#include "ipc3-ops.h" +#include "ipc3-priv.h" #include "ops.h" #include "sof-priv.h" #include "sof-audio.h" @@ -34,8 +34,7 @@ static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component, stream.comp_id = spcm->stream[substream->stream].comp_id; /* send IPC to the DSP */ - return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, - sizeof(stream), &reply, sizeof(reply)); + return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply)); } static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, @@ -119,7 +118,7 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag); /* send hw_params IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), + ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm), &ipc_params_reply, sizeof(ipc_params_reply)); if (ret < 0) { dev_err(component->dev, "HW params ipc failed for stream %d\n", @@ -175,8 +174,7 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, } /* send IPC to the DSP */ - return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, - sizeof(stream), &reply, sizeof(reply)); + return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply)); } static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, diff --git a/sound/soc/sof/ipc3-priv.h b/sound/soc/sof/ipc3-priv.h new file mode 100644 index 000000000000..f5044202f3c5 --- /dev/null +++ b/sound/soc/sof/ipc3-priv.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Intel Corporation. All rights reserved. + */ + +#ifndef __SOUND_SOC_SOF_IPC3_PRIV_H +#define __SOUND_SOC_SOF_IPC3_PRIV_H + +#include "sof-priv.h" + +/* IPC3 specific ops */ +extern const struct sof_ipc_pcm_ops ipc3_pcm_ops; +extern const struct sof_ipc_tplg_ops ipc3_tplg_ops; +extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops; +extern const struct sof_ipc_fw_loader_ops ipc3_loader_ops; +extern const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops; + +/* helpers for fw_ready and ext_manifest parsing */ +int sof_ipc3_get_ext_windows(struct snd_sof_dev *sdev, + const struct sof_ipc_ext_data_hdr *ext_hdr); +int sof_ipc3_get_cc_info(struct snd_sof_dev *sdev, + const struct sof_ipc_ext_data_hdr *ext_hdr); +int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev); + +/* dtrace position update */ +int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_posn *posn); + +/* dtrace platform callback wrappers */ +static inline int sof_dtrace_host_init(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmatb, + struct sof_ipc_dma_trace_params_ext *dtrace_params) +{ + struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops; + + if (dsp_ops->trace_init) + return dsp_ops->trace_init(sdev, dmatb, dtrace_params); + + return 0; +} + +static inline int sof_dtrace_host_release(struct snd_sof_dev *sdev) +{ + struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops; + + if (dsp_ops->trace_release) + return dsp_ops->trace_release(sdev); + + return 0; +} + +static inline int sof_dtrace_host_trigger(struct snd_sof_dev *sdev, int cmd) +{ + struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops; + + if (dsp_ops->trace_trigger) + return dsp_ops->trace_trigger(sdev, cmd); + + return 0; +} + +#endif diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 2f8450a8c0a1..043554d7cb4a 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -11,7 +11,7 @@ #include <sound/pcm_params.h> #include "sof-priv.h" #include "sof-audio.h" -#include "ipc3-ops.h" +#include "ipc3-priv.h" #include "ops.h" /* Full volume for default values */ @@ -20,7 +20,8 @@ struct sof_widget_data { int ctrl_type; int ipc_cmd; - struct sof_abi_hdr *pdata; + void *pdata; + size_t pdata_size; struct snd_sof_control *control; }; @@ -784,16 +785,26 @@ static int sof_get_control_data(struct snd_soc_component *scomp, } cdata = wdata[i].control->ipc_control_data; - wdata[i].pdata = cdata->data; - if (!wdata[i].pdata) - return -EINVAL; - /* make sure data is valid - data can be updated at runtime */ - if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES && - wdata[i].pdata->magic != SOF_ABI_MAGIC) - return -EINVAL; + if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES) { + /* make sure data is valid - data can be updated at runtime */ + if (cdata->data->magic != SOF_ABI_MAGIC) + return -EINVAL; + + wdata[i].pdata = cdata->data->data; + wdata[i].pdata_size = cdata->data->size; + } else { + /* points to the control data union */ + wdata[i].pdata = cdata->chanv; + /* + * wdata[i].control->size is calculated with struct_size + * and includes the size of struct sof_ipc_ctrl_data + */ + wdata[i].pdata_size = wdata[i].control->size - + sizeof(struct sof_ipc_ctrl_data); + } - *size += wdata[i].pdata->size; + *size += wdata[i].pdata_size; /* get data type */ switch (cdata->cmd) { @@ -876,10 +887,12 @@ static int sof_process_load(struct snd_soc_component *scomp, */ if (ipc_data_size) { for (i = 0; i < widget->num_kcontrols; i++) { - memcpy(&process->data[offset], - wdata[i].pdata->data, - wdata[i].pdata->size); - offset += wdata[i].pdata->size; + if (!wdata[i].pdata_size) + continue; + + memcpy(&process->data[offset], wdata[i].pdata, + wdata[i].pdata_size); + offset += wdata[i].pdata_size; } } @@ -1551,8 +1564,7 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * sroute->sink_widget->widget->name); /* send ipc */ - ret = sof_ipc_tx_message(sdev->ipc, connect.hdr.cmd, &connect, sizeof(connect), - &reply, sizeof(reply)); + ret = sof_ipc_tx_message(sdev->ipc, &connect, sizeof(connect), &reply, sizeof(reply)); if (ret < 0) dev_err(sdev->dev, "%s: route %s -> %s failed\n", __func__, sroute->src_widget->widget->name, sroute->sink_widget->widget->name); @@ -1592,6 +1604,7 @@ static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ if (scontrol->priv_size > 0) { memcpy(cdata->data, scontrol->priv, scontrol->priv_size); kfree(scontrol->priv); + scontrol->priv = NULL; if (cdata->data->magic != SOF_ABI_MAGIC) { dev_err(sdev->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic); @@ -1697,7 +1710,7 @@ static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_contro fcomp.id = scontrol->comp_id; /* send IPC to the DSP */ - return sof_ipc_tx_message(sdev->ipc, fcomp.hdr.cmd, &fcomp, sizeof(fcomp), NULL, 0); + return sof_ipc_tx_message(sdev->ipc, &fcomp, sizeof(fcomp), NULL, 0); } /* send pcm params ipc */ @@ -1749,7 +1762,7 @@ static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, in } /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), + ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm), &ipc_params_reply, sizeof(ipc_params_reply)); if (ret < 0) dev_err(scomp->dev, "%s: PCM params failed for %s\n", __func__, @@ -1773,8 +1786,7 @@ static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int c stream.comp_id = swidget->comp_id; /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, - sizeof(stream), &reply, sizeof(reply)); + ret = sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply)); if (ret < 0) dev_err(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name); @@ -1902,8 +1914,7 @@ static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_w ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE; ready.comp_id = swidget->comp_id; - ret = sof_ipc_tx_message(sdev->ipc, ready.hdr.cmd, &ready, sizeof(ready), &reply, - sizeof(reply)); + ret = sof_ipc_tx_message(sdev->ipc, &ready, sizeof(ready), &reply, sizeof(reply)); if (ret < 0) return ret; @@ -1939,7 +1950,7 @@ static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget break; } - ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free), + ret = sof_ipc_tx_message(sdev->ipc, &ipc_free, sizeof(ipc_free), &reply, sizeof(reply)); if (ret < 0) dev_err(sdev->dev, "failed to free widget %s\n", swidget->widget->name); @@ -2003,7 +2014,7 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * /* only send the IPC if the widget is set up in the DSP */ if (swidget->use_count > 0) { - ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, + ret = sof_ipc_tx_message(sdev->ipc, config, config->hdr.size, &reply, sizeof(reply)); if (ret < 0) dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name); @@ -2028,7 +2039,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget struct sof_dai_private_data *dai_data = dai->private; struct sof_ipc_comp *comp = &dai_data->comp_dai->comp; - ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, dai_data->comp_dai, + ret = sof_ipc_tx_message(sdev->ipc, dai_data->comp_dai, comp->hdr.size, &reply, sizeof(reply)); break; } @@ -2037,8 +2048,8 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget struct sof_ipc_pipe_new *pipeline; pipeline = swidget->private; - ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, - sizeof(*pipeline), &reply, sizeof(reply)); + ret = sof_ipc_tx_message(sdev->ipc, pipeline, sizeof(*pipeline), + &reply, sizeof(reply)); break; } default: @@ -2046,7 +2057,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget struct sof_ipc_cmd_hdr *hdr; hdr = swidget->private; - ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size, + ret = sof_ipc_tx_message(sdev->ipc, swidget->private, hdr->size, &reply, sizeof(reply)); break; } @@ -2249,6 +2260,18 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif list_for_each_entry(sroute, &sdev->route_list, list) sroute->setup = false; + /* + * before suspending, make sure the refcounts are all zeroed out. There's no way + * to recover at this point but this will help root cause bad sequences leading to + * more issues on resume + */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->use_count != 0) { + dev_err(sdev->dev, "%s: widget %s is still in use: count %d\n", + __func__, swidget->widget->name, swidget->use_count); + } + } + return 0; } diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index 03e914b62728..dff5feaad370 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -7,8 +7,1035 @@ // // +#include <sound/sof/stream.h> +#include <sound/sof/control.h> #include "sof-priv.h" -#include "ipc3-ops.h" +#include "sof-audio.h" +#include "ipc3-priv.h" +#include "ops.h" + +typedef void (*ipc3_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf); + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC) +static void ipc3_log_header(struct device *dev, u8 *text, u32 cmd) +{ + u8 *str; + u8 *str2 = NULL; + u32 glb; + u32 type; + bool vdbg = false; + + glb = cmd & SOF_GLB_TYPE_MASK; + type = cmd & SOF_CMD_TYPE_MASK; + + switch (glb) { + case SOF_IPC_GLB_REPLY: + str = "GLB_REPLY"; break; + case SOF_IPC_GLB_COMPOUND: + str = "GLB_COMPOUND"; break; + case SOF_IPC_GLB_TPLG_MSG: + str = "GLB_TPLG_MSG"; + switch (type) { + case SOF_IPC_TPLG_COMP_NEW: + str2 = "COMP_NEW"; break; + case SOF_IPC_TPLG_COMP_FREE: + str2 = "COMP_FREE"; break; + case SOF_IPC_TPLG_COMP_CONNECT: + str2 = "COMP_CONNECT"; break; + case SOF_IPC_TPLG_PIPE_NEW: + str2 = "PIPE_NEW"; break; + case SOF_IPC_TPLG_PIPE_FREE: + str2 = "PIPE_FREE"; break; + case SOF_IPC_TPLG_PIPE_CONNECT: + str2 = "PIPE_CONNECT"; break; + case SOF_IPC_TPLG_PIPE_COMPLETE: + str2 = "PIPE_COMPLETE"; break; + case SOF_IPC_TPLG_BUFFER_NEW: + str2 = "BUFFER_NEW"; break; + case SOF_IPC_TPLG_BUFFER_FREE: + str2 = "BUFFER_FREE"; break; + default: + str2 = "unknown type"; break; + } + break; + case SOF_IPC_GLB_PM_MSG: + str = "GLB_PM_MSG"; + switch (type) { + case SOF_IPC_PM_CTX_SAVE: + str2 = "CTX_SAVE"; break; + case SOF_IPC_PM_CTX_RESTORE: + str2 = "CTX_RESTORE"; break; + case SOF_IPC_PM_CTX_SIZE: + str2 = "CTX_SIZE"; break; + case SOF_IPC_PM_CLK_SET: + str2 = "CLK_SET"; break; + case SOF_IPC_PM_CLK_GET: + str2 = "CLK_GET"; break; + case SOF_IPC_PM_CLK_REQ: + str2 = "CLK_REQ"; break; + case SOF_IPC_PM_CORE_ENABLE: + str2 = "CORE_ENABLE"; break; + case SOF_IPC_PM_GATE: + str2 = "GATE"; break; + default: + str2 = "unknown type"; break; + } + break; + case SOF_IPC_GLB_COMP_MSG: + str = "GLB_COMP_MSG"; + switch (type) { + case SOF_IPC_COMP_SET_VALUE: + str2 = "SET_VALUE"; break; + case SOF_IPC_COMP_GET_VALUE: + str2 = "GET_VALUE"; break; + case SOF_IPC_COMP_SET_DATA: + str2 = "SET_DATA"; break; + case SOF_IPC_COMP_GET_DATA: + str2 = "GET_DATA"; break; + default: + str2 = "unknown type"; break; + } + break; + case SOF_IPC_GLB_STREAM_MSG: + str = "GLB_STREAM_MSG"; + switch (type) { + case SOF_IPC_STREAM_PCM_PARAMS: + str2 = "PCM_PARAMS"; break; + case SOF_IPC_STREAM_PCM_PARAMS_REPLY: + str2 = "PCM_REPLY"; break; + case SOF_IPC_STREAM_PCM_FREE: + str2 = "PCM_FREE"; break; + case SOF_IPC_STREAM_TRIG_START: + str2 = "TRIG_START"; break; + case SOF_IPC_STREAM_TRIG_STOP: + str2 = "TRIG_STOP"; break; + case SOF_IPC_STREAM_TRIG_PAUSE: + str2 = "TRIG_PAUSE"; break; + case SOF_IPC_STREAM_TRIG_RELEASE: + str2 = "TRIG_RELEASE"; break; + case SOF_IPC_STREAM_TRIG_DRAIN: + str2 = "TRIG_DRAIN"; break; + case SOF_IPC_STREAM_TRIG_XRUN: + str2 = "TRIG_XRUN"; break; + case SOF_IPC_STREAM_POSITION: + vdbg = true; + str2 = "POSITION"; break; + case SOF_IPC_STREAM_VORBIS_PARAMS: + str2 = "VORBIS_PARAMS"; break; + case SOF_IPC_STREAM_VORBIS_FREE: + str2 = "VORBIS_FREE"; break; + default: + str2 = "unknown type"; break; + } + break; + case SOF_IPC_FW_READY: + str = "FW_READY"; break; + case SOF_IPC_GLB_DAI_MSG: + str = "GLB_DAI_MSG"; + switch (type) { + case SOF_IPC_DAI_CONFIG: + str2 = "CONFIG"; break; + case SOF_IPC_DAI_LOOPBACK: + str2 = "LOOPBACK"; break; + default: + str2 = "unknown type"; break; + } + break; + case SOF_IPC_GLB_TRACE_MSG: + str = "GLB_TRACE_MSG"; + switch (type) { + case SOF_IPC_TRACE_DMA_PARAMS: + str2 = "DMA_PARAMS"; break; + case SOF_IPC_TRACE_DMA_POSITION: + str2 = "DMA_POSITION"; break; + case SOF_IPC_TRACE_DMA_PARAMS_EXT: + str2 = "DMA_PARAMS_EXT"; break; + case SOF_IPC_TRACE_FILTER_UPDATE: + str2 = "FILTER_UPDATE"; break; + case SOF_IPC_TRACE_DMA_FREE: + str2 = "DMA_FREE"; break; + default: + str2 = "unknown type"; break; + } + break; + case SOF_IPC_GLB_TEST_MSG: + str = "GLB_TEST_MSG"; + switch (type) { + case SOF_IPC_TEST_IPC_FLOOD: + str2 = "IPC_FLOOD"; break; + default: + str2 = "unknown type"; break; + } + break; + case SOF_IPC_GLB_DEBUG: + str = "GLB_DEBUG"; + switch (type) { + case SOF_IPC_DEBUG_MEM_USAGE: + str2 = "MEM_USAGE"; break; + default: + str2 = "unknown type"; break; + } + break; + case SOF_IPC_GLB_PROBE: + str = "GLB_PROBE"; + switch (type) { + case SOF_IPC_PROBE_INIT: + str2 = "INIT"; break; + case SOF_IPC_PROBE_DEINIT: + str2 = "DEINIT"; break; + case SOF_IPC_PROBE_DMA_ADD: + str2 = "DMA_ADD"; break; + case SOF_IPC_PROBE_DMA_INFO: + str2 = "DMA_INFO"; break; + case SOF_IPC_PROBE_DMA_REMOVE: + str2 = "DMA_REMOVE"; break; + case SOF_IPC_PROBE_POINT_ADD: + str2 = "POINT_ADD"; break; + case SOF_IPC_PROBE_POINT_INFO: + str2 = "POINT_INFO"; break; + case SOF_IPC_PROBE_POINT_REMOVE: + str2 = "POINT_REMOVE"; break; + default: + str2 = "unknown type"; break; + } + break; + default: + str = "unknown GLB command"; break; + } + + if (str2) { + if (vdbg) + dev_vdbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2); + else + dev_dbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2); + } else { + dev_dbg(dev, "%s: 0x%x: %s\n", text, cmd, str); + } +} +#else +static inline void ipc3_log_header(struct device *dev, u8 *text, u32 cmd) +{ + if ((cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_TRACE_MSG) + dev_dbg(dev, "%s: 0x%x\n", text, cmd); +} +#endif + +static int sof_ipc3_get_reply(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg = sdev->msg; + struct sof_ipc_reply *reply; + int ret = 0; + + /* get the generic reply */ + reply = msg->reply_data; + snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, reply, sizeof(*reply)); + + if (reply->error < 0) + return reply->error; + + if (!reply->hdr.size) { + /* Reply should always be >= sizeof(struct sof_ipc_reply) */ + if (msg->reply_size) + dev_err(sdev->dev, + "empty reply received, expected %zu bytes\n", + msg->reply_size); + else + dev_err(sdev->dev, "empty reply received\n"); + + return -EINVAL; + } + + if (msg->reply_size > 0) { + if (reply->hdr.size == msg->reply_size) { + ret = 0; + } else if (reply->hdr.size < msg->reply_size) { + dev_dbg(sdev->dev, + "reply size (%u) is less than expected (%zu)\n", + reply->hdr.size, msg->reply_size); + + msg->reply_size = reply->hdr.size; + ret = 0; + } else { + dev_err(sdev->dev, + "reply size (%u) exceeds the buffer size (%zu)\n", + reply->hdr.size, msg->reply_size); + ret = -EINVAL; + } + + /* + * get the full message if reply->hdr.size <= msg->reply_size + * and the reply->hdr.size > sizeof(struct sof_ipc_reply) + */ + if (!ret && msg->reply_size > sizeof(*reply)) + snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, + msg->reply_data, msg->reply_size); + } + + return ret; +} + +/* wait for IPC message reply */ +static int ipc3_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data) +{ + struct snd_sof_ipc_msg *msg = &ipc->msg; + struct sof_ipc_cmd_hdr *hdr = msg->msg_data; + struct snd_sof_dev *sdev = ipc->sdev; + int ret; + + /* wait for DSP IPC completion */ + ret = wait_event_timeout(msg->waitq, msg->ipc_complete, + msecs_to_jiffies(sdev->ipc_timeout)); + + if (ret == 0) { + dev_err(sdev->dev, + "ipc tx timed out for %#x (msg/reply size: %d/%zu)\n", + hdr->cmd, hdr->size, msg->reply_size); + snd_sof_handle_fw_exception(ipc->sdev); + ret = -ETIMEDOUT; + } else { + ret = msg->reply_error; + if (ret < 0) { + dev_err(sdev->dev, + "ipc tx error for %#x (msg/reply size: %d/%zu): %d\n", + hdr->cmd, hdr->size, msg->reply_size, ret); + } else { + ipc3_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd); + if (msg->reply_size) + /* copy the data returned from DSP */ + memcpy(reply_data, msg->reply_data, + msg->reply_size); + } + + /* re-enable dumps after successful IPC tx */ + if (sdev->ipc_dump_printed) { + sdev->dbg_dump_printed = false; + sdev->ipc_dump_printed = false; + } + } + + return ret; +} + +/* send IPC message from host to DSP */ +static int ipc3_tx_msg_unlocked(struct snd_sof_ipc *ipc, + void *msg_data, size_t msg_bytes, + void *reply_data, size_t reply_bytes) +{ + struct sof_ipc_cmd_hdr *hdr = msg_data; + struct snd_sof_dev *sdev = ipc->sdev; + int ret; + + ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes); + + if (ret) { + dev_err_ratelimited(sdev->dev, + "%s: ipc message send for %#x failed: %d\n", + __func__, hdr->cmd, ret); + return ret; + } + + ipc3_log_header(sdev->dev, "ipc tx", hdr->cmd); + + /* now wait for completion */ + return ipc3_wait_tx_done(ipc, reply_data); +} + +static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, + void *reply_data, size_t reply_bytes, bool no_pm) +{ + struct snd_sof_ipc *ipc = sdev->ipc; + int ret; + + if (!msg_data || msg_bytes < sizeof(struct sof_ipc_cmd_hdr)) { + dev_err_ratelimited(sdev->dev, "No IPC message to send\n"); + return -EINVAL; + } + + if (!no_pm) { + const struct sof_dsp_power_state target_state = { + .state = SOF_DSP_PM_D0, + }; + + /* ensure the DSP is in D0 before sending a new IPC */ + ret = snd_sof_dsp_set_power_state(sdev, &target_state); + if (ret < 0) { + dev_err(sdev->dev, "%s: resuming DSP failed: %d\n", + __func__, ret); + return ret; + } + } + + /* Serialise IPC TX */ + mutex_lock(&ipc->tx_mutex); + + ret = ipc3_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes); + + mutex_unlock(&ipc->tx_mutex); + + return ret; +} + +static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t data_bytes, + bool set) +{ + size_t msg_bytes, hdr_bytes, payload_size, send_bytes; + struct sof_ipc_ctrl_data *cdata = data; + struct sof_ipc_ctrl_data *cdata_chunk; + struct snd_sof_ipc *ipc = sdev->ipc; + size_t offset = 0; + u8 *src, *dst; + u32 num_msg; + int ret = 0; + int i; + + if (!cdata || data_bytes < sizeof(*cdata)) + return -EINVAL; + + if ((cdata->rhdr.hdr.cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_COMP_MSG) { + dev_err(sdev->dev, "%s: Not supported message type of %#x\n", + __func__, cdata->rhdr.hdr.cmd); + return -EINVAL; + } + + /* send normal size ipc in one part */ + if (cdata->rhdr.hdr.size <= ipc->max_payload_size) + return sof_ipc3_tx_msg(sdev, cdata, cdata->rhdr.hdr.size, + cdata, cdata->rhdr.hdr.size, false); + + cdata_chunk = kzalloc(ipc->max_payload_size, GFP_KERNEL); + if (!cdata_chunk) + return -ENOMEM; + + switch (cdata->type) { + case SOF_CTRL_TYPE_VALUE_CHAN_GET: + case SOF_CTRL_TYPE_VALUE_CHAN_SET: + hdr_bytes = sizeof(struct sof_ipc_ctrl_data); + if (set) { + src = (u8 *)cdata->chanv; + dst = (u8 *)cdata_chunk->chanv; + } else { + src = (u8 *)cdata_chunk->chanv; + dst = (u8 *)cdata->chanv; + } + break; + case SOF_CTRL_TYPE_DATA_GET: + case SOF_CTRL_TYPE_DATA_SET: + hdr_bytes = sizeof(struct sof_ipc_ctrl_data) + sizeof(struct sof_abi_hdr); + if (set) { + src = (u8 *)cdata->data->data; + dst = (u8 *)cdata_chunk->data->data; + } else { + src = (u8 *)cdata_chunk->data->data; + dst = (u8 *)cdata->data->data; + } + break; + default: + kfree(cdata_chunk); + return -EINVAL; + } + + msg_bytes = cdata->rhdr.hdr.size - hdr_bytes; + payload_size = ipc->max_payload_size - hdr_bytes; + num_msg = DIV_ROUND_UP(msg_bytes, payload_size); + + /* copy the header data */ + memcpy(cdata_chunk, cdata, hdr_bytes); + + /* Serialise IPC TX */ + mutex_lock(&sdev->ipc->tx_mutex); + + /* copy the payload data in a loop */ + for (i = 0; i < num_msg; i++) { + send_bytes = min(msg_bytes, payload_size); + cdata_chunk->num_elems = send_bytes; + cdata_chunk->rhdr.hdr.size = hdr_bytes + send_bytes; + cdata_chunk->msg_index = i; + msg_bytes -= send_bytes; + cdata_chunk->elems_remaining = msg_bytes; + + if (set) + memcpy(dst, src + offset, send_bytes); + + ret = ipc3_tx_msg_unlocked(sdev->ipc, + cdata_chunk, cdata_chunk->rhdr.hdr.size, + cdata_chunk, cdata_chunk->rhdr.hdr.size); + if (ret < 0) + break; + + if (!set) + memcpy(dst + offset, src, send_bytes); + + offset += payload_size; + } + + mutex_unlock(&sdev->ipc->tx_mutex); + + kfree(cdata_chunk); + + return ret; +} + +int sof_ipc3_get_ext_windows(struct snd_sof_dev *sdev, + const struct sof_ipc_ext_data_hdr *ext_hdr) +{ + const struct sof_ipc_window *w = + container_of(ext_hdr, struct sof_ipc_window, ext_hdr); + + if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS) + return -EINVAL; + + if (sdev->info_window) { + if (memcmp(sdev->info_window, w, ext_hdr->hdr.size)) { + dev_err(sdev->dev, "mismatch between window descriptor from extended manifest and mailbox"); + return -EINVAL; + } + return 0; + } + + /* keep a local copy of the data */ + sdev->info_window = devm_kmemdup(sdev->dev, w, ext_hdr->hdr.size, GFP_KERNEL); + if (!sdev->info_window) + return -ENOMEM; + + return 0; +} + +int sof_ipc3_get_cc_info(struct snd_sof_dev *sdev, + const struct sof_ipc_ext_data_hdr *ext_hdr) +{ + int ret; + + const struct sof_ipc_cc_version *cc = + container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr); + + if (sdev->cc_version) { + if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) { + dev_err(sdev->dev, + "Receive diverged cc_version descriptions"); + return -EINVAL; + } + return 0; + } + + dev_dbg(sdev->dev, + "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n", + cc->name, cc->major, cc->minor, cc->micro, cc->desc, cc->optim); + + /* create read-only cc_version debugfs to store compiler version info */ + /* use local copy of the cc_version to prevent data corruption */ + if (sdev->first_boot) { + sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size, + GFP_KERNEL); + + if (!sdev->cc_version) + return -ENOMEM; + + memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size); + ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version, + cc->ext_hdr.hdr.size, + "cc_version", 0444); + + /* errors are only due to memory allocation, not debugfs */ + if (ret < 0) { + dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n"); + return ret; + } + } + + return 0; +} + +/* parse the extended FW boot data structures from FW boot message */ +static int ipc3_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset) +{ + struct sof_ipc_ext_data_hdr *ext_hdr; + void *ext_data; + int ret = 0; + + ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!ext_data) + return -ENOMEM; + + /* get first header */ + snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data, + sizeof(*ext_hdr)); + ext_hdr = ext_data; + + while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { + /* read in ext structure */ + snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, + offset + sizeof(*ext_hdr), + (void *)((u8 *)ext_data + sizeof(*ext_hdr)), + ext_hdr->hdr.size - sizeof(*ext_hdr)); + + dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n", + ext_hdr->type, ext_hdr->hdr.size); + + /* process structure data */ + switch (ext_hdr->type) { + case SOF_IPC_EXT_WINDOW: + ret = sof_ipc3_get_ext_windows(sdev, ext_hdr); + break; + case SOF_IPC_EXT_CC_INFO: + ret = sof_ipc3_get_cc_info(sdev, ext_hdr); + break; + case SOF_IPC_EXT_UNUSED: + case SOF_IPC_EXT_PROBE_INFO: + case SOF_IPC_EXT_USER_ABI_INFO: + /* They are supported but we don't do anything here */ + break; + default: + dev_info(sdev->dev, "unknown ext header type %d size 0x%x\n", + ext_hdr->type, ext_hdr->hdr.size); + ret = 0; + break; + } + + if (ret < 0) { + dev_err(sdev->dev, "Failed to parse ext data type %d\n", + ext_hdr->type); + break; + } + + /* move to next header */ + offset += ext_hdr->hdr.size; + snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data, + sizeof(*ext_hdr)); + ext_hdr = ext_data; + } + + kfree(ext_data); + return ret; +} + +static void ipc3_get_windows(struct snd_sof_dev *sdev) +{ + struct sof_ipc_window_elem *elem; + u32 outbox_offset = 0; + u32 stream_offset = 0; + u32 inbox_offset = 0; + u32 outbox_size = 0; + u32 stream_size = 0; + u32 inbox_size = 0; + u32 debug_size = 0; + u32 debug_offset = 0; + int window_offset; + int i; + + if (!sdev->info_window) { + dev_err(sdev->dev, "%s: No window info present\n", __func__); + return; + } + + for (i = 0; i < sdev->info_window->num_windows; i++) { + elem = &sdev->info_window->window[i]; + + window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id); + if (window_offset < 0) { + dev_warn(sdev->dev, "No offset for window %d\n", elem->id); + continue; + } + + switch (elem->type) { + case SOF_IPC_REGION_UPBOX: + inbox_offset = window_offset + elem->offset; + inbox_size = elem->size; + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + inbox_offset, + elem->size, "inbox", + SOF_DEBUGFS_ACCESS_D0_ONLY); + break; + case SOF_IPC_REGION_DOWNBOX: + outbox_offset = window_offset + elem->offset; + outbox_size = elem->size; + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + outbox_offset, + elem->size, "outbox", + SOF_DEBUGFS_ACCESS_D0_ONLY); + break; + case SOF_IPC_REGION_TRACE: + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + window_offset + elem->offset, + elem->size, "etrace", + SOF_DEBUGFS_ACCESS_D0_ONLY); + break; + case SOF_IPC_REGION_DEBUG: + debug_offset = window_offset + elem->offset; + debug_size = elem->size; + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + window_offset + elem->offset, + elem->size, "debug", + SOF_DEBUGFS_ACCESS_D0_ONLY); + break; + case SOF_IPC_REGION_STREAM: + stream_offset = window_offset + elem->offset; + stream_size = elem->size; + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + stream_offset, + elem->size, "stream", + SOF_DEBUGFS_ACCESS_D0_ONLY); + break; + case SOF_IPC_REGION_REGS: + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + window_offset + elem->offset, + elem->size, "regs", + SOF_DEBUGFS_ACCESS_D0_ONLY); + break; + case SOF_IPC_REGION_EXCEPTION: + sdev->dsp_oops_offset = window_offset + elem->offset; + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + window_offset + elem->offset, + elem->size, "exception", + SOF_DEBUGFS_ACCESS_D0_ONLY); + break; + default: + dev_err(sdev->dev, "%s: Illegal window info: %u\n", + __func__, elem->type); + return; + } + } + + if (outbox_size == 0 || inbox_size == 0) { + dev_err(sdev->dev, "%s: Illegal mailbox window\n", __func__); + return; + } + + sdev->dsp_box.offset = inbox_offset; + sdev->dsp_box.size = inbox_size; + + sdev->host_box.offset = outbox_offset; + sdev->host_box.size = outbox_size; + + sdev->stream_box.offset = stream_offset; + sdev->stream_box.size = stream_size; + + sdev->debug_box.offset = debug_offset; + sdev->debug_box.size = debug_size; + + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", + stream_offset, stream_size); + dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n", + debug_offset, debug_size); +} + +static int ipc3_init_reply_data_buffer(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg = &sdev->ipc->msg; + + msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!msg->reply_data) + return -ENOMEM; + + sdev->ipc->max_payload_size = SOF_IPC_MSG_MAX_SIZE; + + return 0; +} + +int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev) +{ + struct sof_ipc_fw_ready *ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &ready->version; + + dev_info(sdev->dev, + "Firmware info: version %d:%d:%d-%s\n", v->major, v->minor, + v->micro, v->tag); + dev_info(sdev->dev, + "Firmware: ABI %d:%d:%d Kernel ABI %d:%d:%d\n", + SOF_ABI_VERSION_MAJOR(v->abi_version), + SOF_ABI_VERSION_MINOR(v->abi_version), + SOF_ABI_VERSION_PATCH(v->abi_version), + SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH); + + if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, v->abi_version)) { + dev_err(sdev->dev, "incompatible FW ABI version\n"); + return -EINVAL; + } + + if (SOF_ABI_VERSION_MINOR(v->abi_version) > SOF_ABI_MINOR) { + if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) { + dev_warn(sdev->dev, "FW ABI is more recent than kernel\n"); + } else { + dev_err(sdev->dev, "FW ABI is more recent than kernel\n"); + return -EINVAL; + } + } + + if (ready->flags & SOF_IPC_INFO_BUILD) { + dev_info(sdev->dev, + "Firmware debug build %d on %s-%s - options:\n" + " GDB: %s\n" + " lock debug: %s\n" + " lock vdebug: %s\n", + v->build, v->date, v->time, + (ready->flags & SOF_IPC_INFO_GDB) ? + "enabled" : "disabled", + (ready->flags & SOF_IPC_INFO_LOCKS) ? + "enabled" : "disabled", + (ready->flags & SOF_IPC_INFO_LOCKSV) ? + "enabled" : "disabled"); + } + + /* copy the fw_version into debugfs at first boot */ + memcpy(&sdev->fw_version, v, sizeof(*v)); + + return 0; +} + +static int ipc3_fw_ready(struct snd_sof_dev *sdev, u32 cmd) +{ + struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; + int offset; + int ret; + + /* mailbox must be on 4k boundary */ + offset = snd_sof_dsp_get_mailbox_offset(sdev); + if (offset < 0) { + dev_err(sdev->dev, "%s: no mailbox offset\n", __func__); + return offset; + } + + dev_dbg(sdev->dev, "DSP is ready 0x%8.8x offset 0x%x\n", cmd, offset); + + /* no need to re-check version/ABI for subsequent boots */ + if (!sdev->first_boot) + return 0; + + /* + * copy data from the DSP FW ready offset + * Subsequent error handling is not needed for BLK_TYPE_SRAM + */ + ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready, + sizeof(*fw_ready)); + if (ret) { + dev_err(sdev->dev, + "Unable to read fw_ready, read from TYPE_SRAM failed\n"); + return ret; + } + + /* make sure ABI version is compatible */ + ret = sof_ipc3_validate_fw_version(sdev); + if (ret < 0) + return ret; + + /* now check for extended data */ + ipc3_fw_parse_ext_data(sdev, offset + sizeof(struct sof_ipc_fw_ready)); + + ipc3_get_windows(sdev); + + return ipc3_init_reply_data_buffer(sdev); +} + +/* IPC stream position. */ +static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct snd_soc_component *scomp = sdev->component; + struct snd_sof_pcm_stream *stream; + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm; + int direction, ret; + + spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); + if (!spcm) { + dev_err(sdev->dev, "period elapsed for unknown stream, msg_id %d\n", + msg_id); + return; + } + + stream = &spcm->stream[direction]; + ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + if (ret < 0) { + dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); + return; + } + + dev_vdbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n", + posn.host_posn, posn.dai_posn, posn.wallclock); + + memcpy(&stream->posn, &posn, sizeof(posn)); + + if (spcm->pcm.compress) + snd_sof_compr_fragment_elapsed(stream->cstream); + else if (stream->substream->runtime && + !stream->substream->runtime->no_period_wakeup) + /* only inform ALSA for period_wakeup mode */ + snd_sof_pcm_period_elapsed(stream->substream); +} + +/* DSP notifies host of an XRUN within FW */ +static void ipc3_xrun(struct snd_sof_dev *sdev, u32 msg_id) +{ + struct snd_soc_component *scomp = sdev->component; + struct snd_sof_pcm_stream *stream; + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm; + int direction, ret; + + spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); + if (!spcm) { + dev_err(sdev->dev, "XRUN for unknown stream, msg_id %d\n", + msg_id); + return; + } + + stream = &spcm->stream[direction]; + ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + if (ret < 0) { + dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret); + return; + } + + dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n", + posn.host_posn, posn.xrun_comp_id, posn.xrun_size); + +#if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP) + /* stop PCM on XRUN - used for pipeline debug */ + memcpy(&stream->posn, &posn, sizeof(posn)); + snd_pcm_stop_xrun(stream->substream); +#endif +} + +/* stream notifications from firmware */ +static void ipc3_stream_message(struct snd_sof_dev *sdev, void *msg_buf) +{ + struct sof_ipc_cmd_hdr *hdr = msg_buf; + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; + u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd); + + switch (msg_type) { + case SOF_IPC_STREAM_POSITION: + ipc3_period_elapsed(sdev, msg_id); + break; + case SOF_IPC_STREAM_TRIG_XRUN: + ipc3_xrun(sdev, msg_id); + break; + default: + dev_err(sdev->dev, "unhandled stream message %#x\n", + msg_id); + break; + } +} + +/* component notifications from firmware */ +static void ipc3_comp_notification(struct snd_sof_dev *sdev, void *msg_buf) +{ + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + struct sof_ipc_cmd_hdr *hdr = msg_buf; + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; + + switch (msg_type) { + case SOF_IPC_COMP_GET_VALUE: + case SOF_IPC_COMP_GET_DATA: + break; + default: + dev_err(sdev->dev, "unhandled component message %#x\n", msg_type); + return; + } + + if (tplg_ops->control->update) + tplg_ops->control->update(sdev, msg_buf); +} + +static void ipc3_trace_message(struct snd_sof_dev *sdev, void *msg_buf) +{ + struct sof_ipc_cmd_hdr *hdr = msg_buf; + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; + + switch (msg_type) { + case SOF_IPC_TRACE_DMA_POSITION: + ipc3_dtrace_posn_update(sdev, msg_buf); + break; + default: + dev_err(sdev->dev, "unhandled trace message %#x\n", msg_type); + break; + } +} + +/* DSP firmware has sent host a message */ +static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev) +{ + ipc3_rx_callback rx_callback = NULL; + struct sof_ipc_cmd_hdr hdr; + void *msg_buf; + u32 cmd; + int err; + + /* read back header */ + err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr)); + if (err < 0) { + dev_warn(sdev->dev, "failed to read IPC header: %d\n", err); + return; + } + + if (hdr.size < sizeof(hdr)) { + dev_err(sdev->dev, "The received message size is invalid\n"); + return; + } + + ipc3_log_header(sdev->dev, "ipc rx", hdr.cmd); + + cmd = hdr.cmd & SOF_GLB_TYPE_MASK; + + /* check message type */ + switch (cmd) { + case SOF_IPC_GLB_REPLY: + dev_err(sdev->dev, "ipc reply unknown\n"); + break; + case SOF_IPC_FW_READY: + /* check for FW boot completion */ + if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) { + err = ipc3_fw_ready(sdev, cmd); + if (err < 0) + sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED); + else + sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK); + + /* wake up firmware loader */ + wake_up(&sdev->boot_wait); + } + break; + case SOF_IPC_GLB_COMPOUND: + case SOF_IPC_GLB_TPLG_MSG: + case SOF_IPC_GLB_PM_MSG: + break; + case SOF_IPC_GLB_COMP_MSG: + rx_callback = ipc3_comp_notification; + break; + case SOF_IPC_GLB_STREAM_MSG: + rx_callback = ipc3_stream_message; + break; + case SOF_IPC_GLB_TRACE_MSG: + rx_callback = ipc3_trace_message; + break; + default: + dev_err(sdev->dev, "%s: Unknown DSP message: 0x%x\n", __func__, cmd); + break; + } + + /* read the full message */ + msg_buf = kmalloc(hdr.size, GFP_KERNEL); + if (!msg_buf) + return; + + err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size); + if (err < 0) { + dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err); + } else { + /* Call local handler for the message */ + if (rx_callback) + rx_callback(sdev, msg_buf); + + /* Notify registered clients */ + sof_client_ipc_rx_dispatcher(sdev, msg_buf); + } + + kfree(msg_buf); + + ipc3_log_header(sdev->dev, "ipc rx done", hdr.cmd); +} static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd) { @@ -19,8 +1046,8 @@ static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd) struct sof_ipc_reply reply; /* send ctx save ipc to dsp */ - return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx, - sizeof(pm_ctx), &reply, sizeof(reply)); + return sof_ipc3_tx_msg(sdev, &pm_ctx, sizeof(pm_ctx), + &reply, sizeof(reply), false); } static int sof_ipc3_ctx_save(struct snd_sof_dev *sdev) @@ -42,4 +1069,11 @@ const struct sof_ipc_ops ipc3_ops = { .tplg = &ipc3_tplg_ops, .pm = &ipc3_pm_ops, .pcm = &ipc3_pcm_ops, + .fw_loader = &ipc3_loader_ops, + .fw_tracing = &ipc3_dtrace_ops, + + .tx_msg = sof_ipc3_tx_msg, + .rx_msg = sof_ipc3_rx_msg, + .set_get_data = sof_ipc3_set_get_data, + .get_reply = sof_ipc3_get_reply, }; diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c new file mode 100644 index 000000000000..9fadae8fd011 --- /dev/null +++ b/sound/soc/sof/ipc4-loader.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. + +#include <linux/firmware.h> +#include <sound/sof/ext_manifest4.h> +#include <sound/sof/ipc4/header.h> +#include "ipc4-priv.h" +#include "sof-audio.h" +#include "sof-priv.h" +#include "ops.h" + +static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev) +{ + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct snd_sof_pdata *plat_data = sdev->pdata; + struct sof_man4_fw_binary_header *fw_header; + const struct firmware *fw = plat_data->fw; + struct sof_ext_manifest4_hdr *ext_man_hdr; + struct sof_man4_module_config *fm_config; + struct sof_ipc4_fw_module *fw_module; + struct sof_man4_module *fm_entry; + ssize_t remaining; + u32 fw_hdr_offset; + int i; + + if (!ipc4_data) { + dev_err(sdev->dev, "%s: ipc4_data is not available\n", __func__); + return -EINVAL; + } + + remaining = fw->size; + if (remaining <= sizeof(*ext_man_hdr)) { + dev_err(sdev->dev, "Firmware size is too small: %zu\n", remaining); + return -EINVAL; + } + + ext_man_hdr = (struct sof_ext_manifest4_hdr *)fw->data; + + fw_hdr_offset = ipc4_data->manifest_fw_hdr_offset; + if (!fw_hdr_offset) + return -EINVAL; + + if (remaining <= ext_man_hdr->len + fw_hdr_offset + sizeof(*fw_header)) { + dev_err(sdev->dev, "Invalid firmware size %zu, should be at least %zu\n", + remaining, ext_man_hdr->len + fw_hdr_offset + sizeof(*fw_header)); + return -EINVAL; + } + + fw_header = (struct sof_man4_fw_binary_header *) + (fw->data + ext_man_hdr->len + fw_hdr_offset); + remaining -= (ext_man_hdr->len + fw_hdr_offset); + + if (remaining <= fw_header->len) { + dev_err(sdev->dev, "Invalid fw_header->len %u\n", fw_header->len); + return -EINVAL; + } + + dev_info(sdev->dev, "Loaded firmware version: %u.%u.%u.%u\n", + fw_header->major_version, fw_header->minor_version, + fw_header->hotfix_version, fw_header->build_version); + dev_dbg(sdev->dev, "Firmware name: %s, header length: %u, module count: %u\n", + fw_header->name, fw_header->len, fw_header->num_module_entries); + + ipc4_data->fw_modules = devm_kmalloc_array(sdev->dev, + fw_header->num_module_entries, + sizeof(*fw_module), GFP_KERNEL); + if (!ipc4_data->fw_modules) + return -ENOMEM; + + ipc4_data->num_fw_modules = fw_header->num_module_entries; + fw_module = ipc4_data->fw_modules; + + fm_entry = (struct sof_man4_module *)((u8 *)fw_header + fw_header->len); + remaining -= fw_header->len; + + if (remaining < fw_header->num_module_entries * sizeof(*fm_entry)) { + dev_err(sdev->dev, "Invalid num_module_entries %u\n", + fw_header->num_module_entries); + return -EINVAL; + } + + fm_config = (struct sof_man4_module_config *) + (fm_entry + fw_header->num_module_entries); + remaining -= (fw_header->num_module_entries * sizeof(*fm_entry)); + for (i = 0; i < fw_header->num_module_entries; i++) { + memcpy(&fw_module->man4_module_entry, fm_entry, sizeof(*fm_entry)); + + if (fm_entry->cfg_count) { + if (remaining < (fm_entry->cfg_offset + fm_entry->cfg_count) * + sizeof(*fm_config)) { + dev_err(sdev->dev, "Invalid module cfg_offset %u\n", + fm_entry->cfg_offset); + return -EINVAL; + } + + /* a module's config is always the same size */ + fw_module->bss_size = fm_config[fm_entry->cfg_offset].is_bytes; + + dev_dbg(sdev->dev, + "module %s: UUID %pUL cfg_count: %u, bss_size: %#x\n", + fm_entry->name, &fm_entry->uuid, fm_entry->cfg_count, + fw_module->bss_size); + } else { + fw_module->bss_size = 0; + + dev_dbg(sdev->dev, "module %s: UUID %pUL\n", fm_entry->name, + &fm_entry->uuid); + } + + fw_module->man4_module_entry.id = i; + ida_init(&fw_module->m_ida); + fw_module->private = NULL; + + fw_module++; + fm_entry++; + } + + return ext_man_hdr->len; +} + +static int sof_ipc4_validate_firmware(struct snd_sof_dev *sdev) +{ + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + u32 fw_hdr_offset = ipc4_data->manifest_fw_hdr_offset; + struct snd_sof_pdata *plat_data = sdev->pdata; + struct sof_man4_fw_binary_header *fw_header; + const struct firmware *fw = plat_data->fw; + struct sof_ext_manifest4_hdr *ext_man_hdr; + + ext_man_hdr = (struct sof_ext_manifest4_hdr *)fw->data; + fw_header = (struct sof_man4_fw_binary_header *) + (fw->data + ext_man_hdr->len + fw_hdr_offset); + + /* TODO: Add firmware verification code here */ + + dev_dbg(sdev->dev, "Validated firmware version: %u.%u.%u.%u\n", + fw_header->major_version, fw_header->minor_version, + fw_header->hotfix_version, fw_header->build_version); + + return 0; +} + +static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) +{ + const struct sof_ipc_ops *iops = sdev->ipc->ops; + struct sof_ipc4_fw_version *fw_ver; + struct sof_ipc4_tuple *tuple; + struct sof_ipc4_msg msg; + size_t offset = 0; + int ret; + + /* Get the firmware configuration */ + msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID); + msg.primary |= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID); + msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_FW_CONFIG); + + msg.data_size = sdev->ipc->max_payload_size; + msg.data_ptr = kzalloc(msg.data_size, GFP_KERNEL); + if (!msg.data_ptr) + return -ENOMEM; + + ret = iops->set_get_data(sdev, &msg, msg.data_size, false); + if (ret) + goto out; + + while (offset < msg.data_size) { + tuple = (struct sof_ipc4_tuple *)((u8 *)msg.data_ptr + offset); + + switch (tuple->type) { + case SOF_IPC4_FW_CFG_FW_VERSION: + fw_ver = (struct sof_ipc4_fw_version *)tuple->value; + + dev_info(sdev->dev, + "Booted firmware version: %u.%u.%u.%u\n", + fw_ver->major, fw_ver->minor, fw_ver->hotfix, + fw_ver->build); + break; + case SOF_IPC4_FW_CFG_DL_MAILBOX_BYTES: + dev_vdbg(sdev->dev, "DL mailbox size: %u\n", *tuple->value); + break; + case SOF_IPC4_FW_CFG_UL_MAILBOX_BYTES: + dev_vdbg(sdev->dev, "UL mailbox size: %u\n", *tuple->value); + break; + case SOF_IPC4_FW_CFG_TRACE_LOG_BYTES: + dev_vdbg(sdev->dev, "Trace log size: %u\n", *tuple->value); + break; + default: + break; + } + + offset += sizeof(*tuple) + tuple->size; + } + +out: + kfree(msg.data_ptr); + + return ret; +} + +const struct sof_ipc_fw_loader_ops ipc4_loader_ops = { + .validate = sof_ipc4_validate_firmware, + .parse_ext_manifest = sof_ipc4_fw_parse_ext_man, + .query_fw_configuration = sof_ipc4_query_fw_configuration, +}; diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h new file mode 100644 index 000000000000..2b71d5675933 --- /dev/null +++ b/sound/soc/sof/ipc4-priv.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2022 Intel Corporation. All rights reserved. + */ + +#ifndef __SOUND_SOC_SOF_IPC4_PRIV_H +#define __SOUND_SOC_SOF_IPC4_PRIV_H + +#include <linux/idr.h> +#include <sound/sof/ext_manifest4.h> +#include "sof-priv.h" + +/** + * struct sof_ipc4_fw_data - IPC4-specific data + * @manifest_fw_hdr_offset: FW header offset in the manifest + * @num_fw_modules : Number of modules in base FW + * @fw_modules: Array of base FW modules + */ +struct sof_ipc4_fw_data { + u32 manifest_fw_hdr_offset; + int num_fw_modules; + void *fw_modules; +}; + +/** + * struct sof_ipc4_fw_module - IPC4 module info + * @sof_man4_module : Module info + * @m_ida: Module instance identifier + * @bss_size: Module object size + * @private: Module private data + */ +struct sof_ipc4_fw_module { + struct sof_man4_module man4_module_entry; + struct ida m_ida; + u32 bss_size; + void *private; +}; + +extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops; + +#endif diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c new file mode 100644 index 000000000000..658802c86685 --- /dev/null +++ b/sound/soc/sof/ipc4.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. +// +// Authors: Rander Wang <rander.wang@linux.intel.com> +// Peter Ujfalusi <peter.ujfalusi@linux.intel.com> +// +#include <sound/sof/header.h> +#include <sound/sof/ipc4/header.h> +#include "sof-priv.h" +#include "sof-audio.h" +#include "ipc4-priv.h" +#include "ops.h" + +#ifdef DEBUG_VERBOSE +#define sof_ipc4_dump_payload(sdev, ipc_data, size) \ + print_hex_dump_debug("Message payload: ", \ + DUMP_PREFIX_OFFSET, \ + 16, 4, ipc_data, size, false) +#else +#define sof_ipc4_dump_payload(sdev, ipc_data, size) do { } while (0) +#endif + +static const struct sof_ipc4_fw_status { + int status; + char *msg; +} ipc4_status[] = { + {0, "The operation was successful"}, + {1, "Invalid parameter specified"}, + {2, "Unknown message type specified"}, + {3, "Not enough space in the IPC reply buffer to complete the request"}, + {4, "The system or resource is busy"}, + {5, "Replaced ADSP IPC PENDING (unused)"}, + {6, "Unknown error while processing the request"}, + {7, "Unsupported operation requested"}, + {8, "Reserved (ADSP_STAGE_UNINITIALIZED removed)"}, + {9, "Specified resource not found"}, + {10, "A resource's ID requested to be created is already assigned"}, + {11, "Reserved (ADSP_IPC_OUT_OF_MIPS removed)"}, + {12, "Required resource is in invalid state"}, + {13, "Requested power transition failed to complete"}, + {14, "Manifest of the library being loaded is invalid"}, + {15, "Requested service or data is unavailable on the target platform"}, + {42, "Library target address is out of storage memory range"}, + {43, "Reserved"}, + {44, "Image verification by CSE failed"}, + {100, "General module management error"}, + {101, "Module loading failed"}, + {102, "Integrity check of the loaded module content failed"}, + {103, "Attempt to unload code of the module in use"}, + {104, "Other failure of module instance initialization request"}, + {105, "Reserved (ADSP_IPC_OUT_OF_MIPS removed)"}, + {106, "Reserved (ADSP_IPC_CONFIG_GET_ERROR removed)"}, + {107, "Reserved (ADSP_IPC_CONFIG_SET_ERROR removed)"}, + {108, "Reserved (ADSP_IPC_LARGE_CONFIG_GET_ERROR removed)"}, + {109, "Reserved (ADSP_IPC_LARGE_CONFIG_SET_ERROR removed)"}, + {110, "Invalid (out of range) module ID provided"}, + {111, "Invalid module instance ID provided"}, + {112, "Invalid queue (pin) ID provided"}, + {113, "Invalid destination queue (pin) ID provided"}, + {114, "Reserved (ADSP_IPC_BIND_UNBIND_DST_SINK_UNSUPPORTED removed)"}, + {115, "Reserved (ADSP_IPC_UNLOAD_INST_EXISTS removed)"}, + {116, "Invalid target code ID provided"}, + {117, "Injection DMA buffer is too small for probing the input pin"}, + {118, "Extraction DMA buffer is too small for probing the output pin"}, + {120, "Invalid ID of configuration item provided in TLV list"}, + {121, "Invalid length of configuration item provided in TLV list"}, + {122, "Invalid structure of configuration item provided"}, + {140, "Initialization of DMA Gateway failed"}, + {141, "Invalid ID of gateway provided"}, + {142, "Setting state of DMA Gateway failed"}, + {143, "DMA_CONTROL message targeting gateway not allocated yet"}, + {150, "Attempt to configure SCLK while I2S port is running"}, + {151, "Attempt to configure MCLK while I2S port is running"}, + {152, "Attempt to stop SCLK that is not running"}, + {153, "Attempt to stop MCLK that is not running"}, + {160, "Reserved (ADSP_IPC_PIPELINE_NOT_INITIALIZED removed)"}, + {161, "Reserved (ADSP_IPC_PIPELINE_NOT_EXIST removed)"}, + {162, "Reserved (ADSP_IPC_PIPELINE_SAVE_FAILED removed)"}, + {163, "Reserved (ADSP_IPC_PIPELINE_RESTORE_FAILED removed)"}, + {165, "Reserved (ADSP_IPC_PIPELINE_ALREADY_EXISTS removed)"}, +}; + +static int sof_ipc4_check_reply_status(struct snd_sof_dev *sdev, u32 status) +{ + int i, ret; + + status &= SOF_IPC4_REPLY_STATUS; + + if (!status) + return 0; + + for (i = 0; i < ARRAY_SIZE(ipc4_status); i++) { + if (ipc4_status[i].status == status) { + dev_err(sdev->dev, "FW reported error: %u - %s\n", + status, ipc4_status[i].msg); + goto to_errno; + } + } + + if (i == ARRAY_SIZE(ipc4_status)) + dev_err(sdev->dev, "FW reported error: %u - Unknown\n", status); + +to_errno: + switch (status) { + case 8: + case 11: + case 105 ... 109: + case 114 ... 115: + case 160 ... 163: + case 165: + ret = -ENOENT; + break; + case 4: + case 150: + case 151: + ret = -EBUSY; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC) +#define DBG_IPC4_MSG_TYPE_ENTRY(type) [SOF_IPC4_##type] = #type +static const char * const ipc4_dbg_mod_msg_type[] = { + DBG_IPC4_MSG_TYPE_ENTRY(MOD_INIT_INSTANCE), + DBG_IPC4_MSG_TYPE_ENTRY(MOD_CONFIG_GET), + DBG_IPC4_MSG_TYPE_ENTRY(MOD_CONFIG_SET), + DBG_IPC4_MSG_TYPE_ENTRY(MOD_LARGE_CONFIG_GET), + DBG_IPC4_MSG_TYPE_ENTRY(MOD_LARGE_CONFIG_SET), + DBG_IPC4_MSG_TYPE_ENTRY(MOD_BIND), + DBG_IPC4_MSG_TYPE_ENTRY(MOD_UNBIND), + DBG_IPC4_MSG_TYPE_ENTRY(MOD_SET_DX), + DBG_IPC4_MSG_TYPE_ENTRY(MOD_SET_D0IX), + DBG_IPC4_MSG_TYPE_ENTRY(MOD_ENTER_MODULE_RESTORE), + DBG_IPC4_MSG_TYPE_ENTRY(MOD_EXIT_MODULE_RESTORE), + DBG_IPC4_MSG_TYPE_ENTRY(MOD_DELETE_INSTANCE), +}; + +static const char * const ipc4_dbg_glb_msg_type[] = { + DBG_IPC4_MSG_TYPE_ENTRY(GLB_BOOT_CONFIG), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_ROM_CONTROL), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_IPCGATEWAY_CMD), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_PERF_MEASUREMENTS_CMD), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_CHAIN_DMA), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_MULTIPLE_MODULES), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_UNLOAD_MULTIPLE_MODULES), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_CREATE_PIPELINE), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_DELETE_PIPELINE), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_SET_PIPELINE_STATE), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_GET_PIPELINE_STATE), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_GET_PIPELINE_CONTEXT_SIZE), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_SAVE_PIPELINE), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_RESTORE_PIPELINE), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_LIBRARY), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_INTERNAL_MESSAGE), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_NOTIFICATION), +}; + +#define DBG_IPC4_NOTIFICATION_TYPE_ENTRY(type) [SOF_IPC4_NOTIFY_##type] = #type +static const char * const ipc4_dbg_notification_type[] = { + DBG_IPC4_NOTIFICATION_TYPE_ENTRY(PHRASE_DETECTED), + DBG_IPC4_NOTIFICATION_TYPE_ENTRY(RESOURCE_EVENT), + DBG_IPC4_NOTIFICATION_TYPE_ENTRY(LOG_BUFFER_STATUS), + DBG_IPC4_NOTIFICATION_TYPE_ENTRY(TIMESTAMP_CAPTURED), + DBG_IPC4_NOTIFICATION_TYPE_ENTRY(FW_READY), + DBG_IPC4_NOTIFICATION_TYPE_ENTRY(FW_AUD_CLASS_RESULT), + DBG_IPC4_NOTIFICATION_TYPE_ENTRY(EXCEPTION_CAUGHT), + DBG_IPC4_NOTIFICATION_TYPE_ENTRY(MODULE_NOTIFICATION), + DBG_IPC4_NOTIFICATION_TYPE_ENTRY(PROBE_DATA_AVAILABLE), + DBG_IPC4_NOTIFICATION_TYPE_ENTRY(ASYNC_MSG_SRVC_MESSAGE), +}; + +static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg, + bool data_size_valid) +{ + u32 val, type; + const u8 *str2 = NULL; + const u8 *str = NULL; + + val = msg->primary & SOF_IPC4_MSG_TARGET_MASK; + type = SOF_IPC4_MSG_TYPE_GET(msg->primary); + + if (val == SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG)) { + /* Module message */ + if (type < SOF_IPC4_MOD_TYPE_LAST) + str = ipc4_dbg_mod_msg_type[type]; + if (!str) + str = "Unknown Module message type"; + } else { + /* Global FW message */ + if (type < SOF_IPC4_GLB_TYPE_LAST) + str = ipc4_dbg_glb_msg_type[type]; + if (!str) + str = "Unknown Global message type"; + + if (type == SOF_IPC4_GLB_NOTIFICATION) { + /* Notification message */ + u32 notif = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary); + + if (notif < SOF_IPC4_NOTIFY_TYPE_LAST) + str2 = ipc4_dbg_notification_type[notif]; + if (!str2) + str2 = "Unknown Global notification"; + } + } + + if (str2) { + if (data_size_valid && msg->data_size) + dev_dbg(dev, "%s: %#x|%#x: %s|%s [data size: %zu]\n", + text, msg->primary, msg->extension, str, str2, + msg->data_size); + else + dev_dbg(dev, "%s: %#x|%#x: %s|%s\n", text, msg->primary, + msg->extension, str, str2); + } else { + if (data_size_valid && msg->data_size) + dev_dbg(dev, "%s: %#x|%#x: %s [data size: %zu]\n", + text, msg->primary, msg->extension, str, + msg->data_size); + else + dev_dbg(dev, "%s: %#x|%#x: %s\n", text, msg->primary, + msg->extension, str); + } +} +#else /* CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC */ +static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg, + bool data_size_valid) +{ + if (data_size_valid && msg->data_size) + dev_dbg(dev, "%s: %#x|%#x [data size: %zu]\n", text, + msg->primary, msg->extension, msg->data_size); + else + dev_dbg(dev, "%s: %#x|%#x\n", text, msg->primary, msg->extension); +} +#endif + +static int sof_ipc4_get_reply(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg = sdev->msg; + struct sof_ipc4_msg *ipc4_reply; + int ret; + + /* get the generic reply */ + ipc4_reply = msg->reply_data; + + sof_ipc4_log_header(sdev->dev, "ipc tx reply", ipc4_reply, false); + + ret = sof_ipc4_check_reply_status(sdev, ipc4_reply->primary); + if (ret) + return ret; + + /* No other information is expected for non large config get replies */ + if (!msg->reply_size || !SOF_IPC4_MSG_IS_MODULE_MSG(ipc4_reply->primary) || + (SOF_IPC4_MSG_TYPE_GET(ipc4_reply->primary) != SOF_IPC4_MOD_LARGE_CONFIG_GET)) + return 0; + + /* Read the requested payload */ + snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, ipc4_reply->data_ptr, + msg->reply_size); + + return 0; +} + +/* wait for IPC message reply */ +static int ipc4_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data) +{ + struct snd_sof_ipc_msg *msg = &ipc->msg; + struct sof_ipc4_msg *ipc4_msg = msg->msg_data; + struct snd_sof_dev *sdev = ipc->sdev; + int ret; + + /* wait for DSP IPC completion */ + ret = wait_event_timeout(msg->waitq, msg->ipc_complete, + msecs_to_jiffies(sdev->ipc_timeout)); + if (ret == 0) { + dev_err(sdev->dev, "ipc timed out for %#x|%#x\n", + ipc4_msg->primary, ipc4_msg->extension); + return -ETIMEDOUT; + } + + if (msg->reply_error) { + dev_err(sdev->dev, "ipc error for msg %#x|%#x\n", + ipc4_msg->primary, ipc4_msg->extension); + ret = msg->reply_error; + } else { + if (reply_data) { + struct sof_ipc4_msg *ipc4_reply = msg->reply_data; + struct sof_ipc4_msg *ipc4_reply_data = reply_data; + + /* Copy the header */ + ipc4_reply_data->header_u64 = ipc4_reply->header_u64; + if (msg->reply_size && ipc4_reply_data->data_ptr) { + /* copy the payload returned from DSP */ + memcpy(ipc4_reply_data->data_ptr, ipc4_reply->data_ptr, + msg->reply_size); + ipc4_reply_data->data_size = msg->reply_size; + } + } + + ret = 0; + sof_ipc4_log_header(sdev->dev, "ipc tx done ", ipc4_msg, true); + } + + /* re-enable dumps after successful IPC tx */ + if (sdev->ipc_dump_printed) { + sdev->dbg_dump_printed = false; + sdev->ipc_dump_printed = false; + } + + return ret; +} + +static int ipc4_tx_msg_unlocked(struct snd_sof_ipc *ipc, + void *msg_data, size_t msg_bytes, + void *reply_data, size_t reply_bytes) +{ + struct sof_ipc4_msg *ipc4_msg = msg_data; + struct snd_sof_dev *sdev = ipc->sdev; + int ret; + + if (msg_bytes > ipc->max_payload_size || reply_bytes > ipc->max_payload_size) + return -EINVAL; + + ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes); + if (ret) { + dev_err_ratelimited(sdev->dev, + "%s: ipc message send for %#x|%#x failed: %d\n", + __func__, ipc4_msg->primary, ipc4_msg->extension, ret); + return ret; + } + + sof_ipc4_log_header(sdev->dev, "ipc tx ", msg_data, true); + + /* now wait for completion */ + return ipc4_wait_tx_done(ipc, reply_data); +} + +static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, + void *reply_data, size_t reply_bytes, bool no_pm) +{ + struct snd_sof_ipc *ipc = sdev->ipc; +#ifdef DEBUG_VERBOSE + struct sof_ipc4_msg *msg = NULL; +#endif + int ret; + + if (!msg_data) + return -EINVAL; + + /* Serialise IPC TX */ + mutex_lock(&ipc->tx_mutex); + + ret = ipc4_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes); + + mutex_unlock(&ipc->tx_mutex); + +#ifdef DEBUG_VERBOSE + /* payload is indicated by non zero msg/reply_bytes */ + if (msg_bytes) + msg = msg_data; + else if (reply_bytes) + msg = reply_data; + + if (msg) + sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size); +#endif + + return ret; +} + +static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data, + size_t payload_bytes, bool set) +{ + size_t payload_limit = sdev->ipc->max_payload_size; + struct sof_ipc4_msg *ipc4_msg = data; + struct sof_ipc4_msg tx = {{ 0 }}; + struct sof_ipc4_msg rx = {{ 0 }}; + size_t remaining = payload_bytes; + size_t offset = 0; + size_t chunk_size; + int ret; + + if (!data) + return -EINVAL; + + if ((ipc4_msg->primary & SOF_IPC4_MSG_TARGET_MASK) != + SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG)) + return -EINVAL; + + ipc4_msg->primary &= ~SOF_IPC4_MSG_TYPE_MASK; + tx.primary = ipc4_msg->primary; + tx.extension = ipc4_msg->extension; + + if (set) + tx.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET); + else + tx.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_GET); + + tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_SIZE_MASK; + tx.extension |= SOF_IPC4_MOD_EXT_MSG_SIZE(payload_bytes); + + tx.extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1); + + /* Serialise IPC TX */ + mutex_lock(&sdev->ipc->tx_mutex); + + do { + size_t tx_size, rx_size; + + if (remaining > payload_limit) { + chunk_size = payload_limit; + } else { + chunk_size = remaining; + if (set) + tx.extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1); + } + + if (offset) { + tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK; + tx.extension &= ~SOF_IPC4_MOD_EXT_MSG_SIZE_MASK; + tx.extension |= SOF_IPC4_MOD_EXT_MSG_SIZE(offset); + } + + if (set) { + tx.data_size = chunk_size; + tx.data_ptr = ipc4_msg->data_ptr + offset; + + tx_size = chunk_size; + rx_size = 0; + } else { + rx.primary = 0; + rx.extension = 0; + rx.data_size = chunk_size; + rx.data_ptr = ipc4_msg->data_ptr + offset; + + tx_size = 0; + rx_size = chunk_size; + } + + /* Send the message for the current chunk */ + ret = ipc4_tx_msg_unlocked(sdev->ipc, &tx, tx_size, &rx, rx_size); + if (ret < 0) { + dev_err(sdev->dev, + "%s: large config %s failed at offset %zu: %d\n", + __func__, set ? "set" : "get", offset, ret); + goto out; + } + + if (!set && rx.extension & SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK_MASK) { + /* Verify the firmware reported total payload size */ + rx_size = rx.extension & SOF_IPC4_MOD_EXT_MSG_SIZE_MASK; + + if (rx_size > payload_bytes) { + dev_err(sdev->dev, + "%s: Receive buffer (%zu) is too small for %zu\n", + __func__, payload_bytes, rx_size); + ret = -ENOMEM; + goto out; + } + + if (rx_size < chunk_size) { + chunk_size = rx_size; + remaining = rx_size; + } else if (rx_size < payload_bytes) { + remaining = rx_size; + } + } + + offset += chunk_size; + remaining -= chunk_size; + } while (remaining); + + /* Adjust the received data size if needed */ + if (!set && payload_bytes != offset) + ipc4_msg->data_size = offset; + + sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size); + +out: + mutex_unlock(&sdev->ipc->tx_mutex); + + return ret; +} + +static int sof_ipc4_init_msg_memory(struct snd_sof_dev *sdev) +{ + struct sof_ipc4_msg *ipc4_msg; + struct snd_sof_ipc_msg *msg = &sdev->ipc->msg; + + /* TODO: get max_payload_size from firmware */ + sdev->ipc->max_payload_size = SOF_IPC4_MSG_MAX_SIZE; + + /* Allocate memory for the ipc4 container and the maximum payload */ + msg->reply_data = devm_kzalloc(sdev->dev, sdev->ipc->max_payload_size + + sizeof(struct sof_ipc4_msg), GFP_KERNEL); + if (!msg->reply_data) + return -ENOMEM; + + ipc4_msg = msg->reply_data; + ipc4_msg->data_ptr = msg->reply_data + sizeof(struct sof_ipc4_msg); + + return 0; +} + +static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg) +{ + int inbox_offset, inbox_size, outbox_offset, outbox_size; + + /* no need to re-check version/ABI for subsequent boots */ + if (!sdev->first_boot) + return 0; + + /* Set up the windows for IPC communication */ + inbox_offset = snd_sof_dsp_get_mailbox_offset(sdev); + if (inbox_offset < 0) { + dev_err(sdev->dev, "%s: No mailbox offset\n", __func__); + return inbox_offset; + } + inbox_size = SOF_IPC4_MSG_MAX_SIZE; + outbox_offset = snd_sof_dsp_get_window_offset(sdev, 1); + outbox_size = SOF_IPC4_MSG_MAX_SIZE; + + sdev->dsp_box.offset = inbox_offset; + sdev->dsp_box.size = inbox_size; + sdev->host_box.offset = outbox_offset; + sdev->host_box.size = outbox_size; + + dev_dbg(sdev->dev, "mailbox upstream 0x%x - size 0x%x\n", + inbox_offset, inbox_size); + dev_dbg(sdev->dev, "mailbox downstream 0x%x - size 0x%x\n", + outbox_offset, outbox_size); + + return sof_ipc4_init_msg_memory(sdev); +} + +static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) +{ + struct sof_ipc4_msg *ipc4_msg = sdev->ipc->msg.rx_data; + size_t data_size = 0; + int err; + + if (!ipc4_msg || !SOF_IPC4_MSG_IS_NOTIFICATION(ipc4_msg->primary)) + return; + + ipc4_msg->data_ptr = NULL; + ipc4_msg->data_size = 0; + + sof_ipc4_log_header(sdev->dev, "ipc rx ", ipc4_msg, false); + + switch (SOF_IPC4_NOTIFICATION_TYPE_GET(ipc4_msg->primary)) { + case SOF_IPC4_NOTIFY_FW_READY: + /* check for FW boot completion */ + if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) { + err = ipc4_fw_ready(sdev, ipc4_msg); + if (err < 0) + sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED); + else + sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK); + + /* wake up firmware loader */ + wake_up(&sdev->boot_wait); + } + + break; + case SOF_IPC4_NOTIFY_RESOURCE_EVENT: + data_size = sizeof(struct sof_ipc4_notify_resource_data); + break; + default: + dev_dbg(sdev->dev, "%s: Unhandled DSP message: %#x|%#x\n", __func__, + ipc4_msg->primary, ipc4_msg->extension); + break; + } + + if (data_size) { + ipc4_msg->data_ptr = kmalloc(data_size, GFP_KERNEL); + if (!ipc4_msg->data_ptr) + return; + + ipc4_msg->data_size = data_size; + snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr, ipc4_msg->data_size); + } + + sof_ipc4_log_header(sdev->dev, "ipc rx done ", ipc4_msg, true); + + if (data_size) { + kfree(ipc4_msg->data_ptr); + ipc4_msg->data_ptr = NULL; + ipc4_msg->data_size = 0; + } +} + +const struct sof_ipc_ops ipc4_ops = { + .tx_msg = sof_ipc4_tx_msg, + .rx_msg = sof_ipc4_rx_msg, + .set_get_data = sof_ipc4_set_get_data, + .get_reply = sof_ipc4_get_reply, + .fw_loader = &ipc4_loader_ops, +}; diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 697f03565a70..5f51d936b306 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -11,690 +11,9 @@ // #include <linux/firmware.h> -#include <sound/sof.h> -#include <sound/sof/ext_manifest.h> #include "sof-priv.h" #include "ops.h" -static int get_ext_windows(struct snd_sof_dev *sdev, - const struct sof_ipc_ext_data_hdr *ext_hdr) -{ - const struct sof_ipc_window *w = - container_of(ext_hdr, struct sof_ipc_window, ext_hdr); - - if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS) - return -EINVAL; - - if (sdev->info_window) { - if (memcmp(sdev->info_window, w, ext_hdr->hdr.size)) { - dev_err(sdev->dev, "error: mismatch between window descriptor from extended manifest and mailbox"); - return -EINVAL; - } - return 0; - } - - /* keep a local copy of the data */ - sdev->info_window = devm_kmemdup(sdev->dev, w, ext_hdr->hdr.size, - GFP_KERNEL); - if (!sdev->info_window) - return -ENOMEM; - - return 0; -} - -static int get_cc_info(struct snd_sof_dev *sdev, - const struct sof_ipc_ext_data_hdr *ext_hdr) -{ - int ret; - - const struct sof_ipc_cc_version *cc = - container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr); - - if (sdev->cc_version) { - if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) { - dev_err(sdev->dev, "error: receive diverged cc_version descriptions"); - return -EINVAL; - } - return 0; - } - - dev_dbg(sdev->dev, "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n", - cc->name, cc->major, cc->minor, cc->micro, cc->desc, - cc->optim); - - /* create read-only cc_version debugfs to store compiler version info */ - /* use local copy of the cc_version to prevent data corruption */ - if (sdev->first_boot) { - sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size, - GFP_KERNEL); - - if (!sdev->cc_version) - return -ENOMEM; - - memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size); - ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version, - cc->ext_hdr.hdr.size, - "cc_version", 0444); - - /* errors are only due to memory allocation, not debugfs */ - if (ret < 0) { - dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n"); - return ret; - } - } - - return 0; -} - -/* parse the extended FW boot data structures from FW boot message */ -static int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset) -{ - struct sof_ipc_ext_data_hdr *ext_hdr; - void *ext_data; - int ret = 0; - - ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!ext_data) - return -ENOMEM; - - /* get first header */ - snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data, - sizeof(*ext_hdr)); - ext_hdr = ext_data; - - while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { - /* read in ext structure */ - snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, - offset + sizeof(*ext_hdr), - (void *)((u8 *)ext_data + sizeof(*ext_hdr)), - ext_hdr->hdr.size - sizeof(*ext_hdr)); - - dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n", - ext_hdr->type, ext_hdr->hdr.size); - - /* process structure data */ - switch (ext_hdr->type) { - case SOF_IPC_EXT_WINDOW: - ret = get_ext_windows(sdev, ext_hdr); - break; - case SOF_IPC_EXT_CC_INFO: - ret = get_cc_info(sdev, ext_hdr); - break; - case SOF_IPC_EXT_UNUSED: - case SOF_IPC_EXT_PROBE_INFO: - case SOF_IPC_EXT_USER_ABI_INFO: - /* They are supported but we don't do anything here */ - break; - default: - dev_info(sdev->dev, "unknown ext header type %d size 0x%x\n", - ext_hdr->type, ext_hdr->hdr.size); - ret = 0; - break; - } - - if (ret < 0) { - dev_err(sdev->dev, "error: failed to parse ext data type %d\n", - ext_hdr->type); - break; - } - - /* move to next header */ - offset += ext_hdr->hdr.size; - snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data, - sizeof(*ext_hdr)); - ext_hdr = ext_data; - } - - kfree(ext_data); - return ret; -} - -static int ext_man_get_fw_version(struct snd_sof_dev *sdev, - const struct sof_ext_man_elem_header *hdr) -{ - const struct sof_ext_man_fw_version *v = - container_of(hdr, struct sof_ext_man_fw_version, hdr); - - memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version)); - sdev->fw_ready.flags = v->flags; - - /* log ABI versions and check FW compatibility */ - return snd_sof_ipc_valid(sdev); -} - -static int ext_man_get_windows(struct snd_sof_dev *sdev, - const struct sof_ext_man_elem_header *hdr) -{ - const struct sof_ext_man_window *w; - - w = container_of(hdr, struct sof_ext_man_window, hdr); - - return get_ext_windows(sdev, &w->ipc_window.ext_hdr); -} - -static int ext_man_get_cc_info(struct snd_sof_dev *sdev, - const struct sof_ext_man_elem_header *hdr) -{ - const struct sof_ext_man_cc_version *cc; - - cc = container_of(hdr, struct sof_ext_man_cc_version, hdr); - - return get_cc_info(sdev, &cc->cc_version.ext_hdr); -} - -static int ext_man_get_dbg_abi_info(struct snd_sof_dev *sdev, - const struct sof_ext_man_elem_header *hdr) -{ - const struct ext_man_dbg_abi *dbg_abi = - container_of(hdr, struct ext_man_dbg_abi, hdr); - - if (sdev->first_boot) - dev_dbg(sdev->dev, - "Firmware: DBG_ABI %d:%d:%d\n", - SOF_ABI_VERSION_MAJOR(dbg_abi->dbg_abi.abi_dbg_version), - SOF_ABI_VERSION_MINOR(dbg_abi->dbg_abi.abi_dbg_version), - SOF_ABI_VERSION_PATCH(dbg_abi->dbg_abi.abi_dbg_version)); - - return 0; -} - -static int ext_man_get_config_data(struct snd_sof_dev *sdev, - const struct sof_ext_man_elem_header *hdr) -{ - const struct sof_ext_man_config_data *config = - container_of(hdr, struct sof_ext_man_config_data, hdr); - const struct sof_config_elem *elem; - int elems_counter; - int elems_size; - int ret = 0; - int i; - - /* calculate elements counter */ - elems_size = config->hdr.size - sizeof(struct sof_ext_man_elem_header); - elems_counter = elems_size / sizeof(struct sof_config_elem); - - dev_dbg(sdev->dev, "%s can hold up to %d config elements\n", - __func__, elems_counter); - - for (i = 0; i < elems_counter; ++i) { - elem = &config->elems[i]; - dev_dbg(sdev->dev, "%s get index %d token %d val %d\n", - __func__, i, elem->token, elem->value); - switch (elem->token) { - case SOF_EXT_MAN_CONFIG_EMPTY: - /* unused memory space is zero filled - mapped to EMPTY elements */ - break; - case SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE: - /* TODO: use ipc msg size from config data */ - break; - case SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN: - if (sdev->first_boot && elem->value) - ret = snd_sof_dbg_memory_info_init(sdev); - break; - default: - dev_info(sdev->dev, "Unknown firmware configuration token %d value %d", - elem->token, elem->value); - break; - } - if (ret < 0) { - dev_err(sdev->dev, "error: processing sof_ext_man_config_data failed for token %d value 0x%x, %d\n", - elem->token, elem->value, ret); - return ret; - } - } - - return 0; -} - -static ssize_t snd_sof_ext_man_size(const struct firmware *fw) -{ - const struct sof_ext_man_header *head; - - head = (struct sof_ext_man_header *)fw->data; - - /* - * assert fw size is big enough to contain extended manifest header, - * it prevents from reading unallocated memory from `head` in following - * step. - */ - if (fw->size < sizeof(*head)) - return -EINVAL; - - /* - * When fw points to extended manifest, - * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER. - */ - if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER) - return head->full_size; - - /* otherwise given fw don't have an extended manifest */ - return 0; -} - -/* parse extended FW manifest data structures */ -static int snd_sof_fw_ext_man_parse(struct snd_sof_dev *sdev, - const struct firmware *fw) -{ - const struct sof_ext_man_elem_header *elem_hdr; - const struct sof_ext_man_header *head; - ssize_t ext_man_size; - ssize_t remaining; - uintptr_t iptr; - int ret = 0; - - head = (struct sof_ext_man_header *)fw->data; - remaining = head->full_size - head->header_size; - ext_man_size = snd_sof_ext_man_size(fw); - - /* Assert firmware starts with extended manifest */ - if (ext_man_size <= 0) - return ext_man_size; - - /* incompatible version */ - if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION, - head->header_version)) { - dev_err(sdev->dev, "error: extended manifest version 0x%X differ from used 0x%X\n", - head->header_version, SOF_EXT_MAN_VERSION); - return -EINVAL; - } - - /* get first extended manifest element header */ - iptr = (uintptr_t)fw->data + head->header_size; - - while (remaining > sizeof(*elem_hdr)) { - elem_hdr = (struct sof_ext_man_elem_header *)iptr; - - dev_dbg(sdev->dev, "found sof_ext_man header type %d size 0x%X\n", - elem_hdr->type, elem_hdr->size); - - if (elem_hdr->size < sizeof(*elem_hdr) || - elem_hdr->size > remaining) { - dev_err(sdev->dev, "error: invalid sof_ext_man header size, type %d size 0x%X\n", - elem_hdr->type, elem_hdr->size); - return -EINVAL; - } - - /* process structure data */ - switch (elem_hdr->type) { - case SOF_EXT_MAN_ELEM_FW_VERSION: - ret = ext_man_get_fw_version(sdev, elem_hdr); - break; - case SOF_EXT_MAN_ELEM_WINDOW: - ret = ext_man_get_windows(sdev, elem_hdr); - break; - case SOF_EXT_MAN_ELEM_CC_VERSION: - ret = ext_man_get_cc_info(sdev, elem_hdr); - break; - case SOF_EXT_MAN_ELEM_DBG_ABI: - ret = ext_man_get_dbg_abi_info(sdev, elem_hdr); - break; - case SOF_EXT_MAN_ELEM_CONFIG_DATA: - ret = ext_man_get_config_data(sdev, elem_hdr); - break; - case SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA: - ret = snd_sof_dsp_parse_platform_ext_manifest(sdev, elem_hdr); - break; - default: - dev_info(sdev->dev, "unknown sof_ext_man header type %d size 0x%X\n", - elem_hdr->type, elem_hdr->size); - break; - } - - if (ret < 0) { - dev_err(sdev->dev, "error: failed to parse sof_ext_man header type %d size 0x%X\n", - elem_hdr->type, elem_hdr->size); - return ret; - } - - remaining -= elem_hdr->size; - iptr += elem_hdr->size; - } - - if (remaining) { - dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n"); - return -EINVAL; - } - - return ext_man_size; -} - -/* - * IPC Firmware ready. - */ -static void sof_get_windows(struct snd_sof_dev *sdev) -{ - struct sof_ipc_window_elem *elem; - u32 outbox_offset = 0; - u32 stream_offset = 0; - u32 inbox_offset = 0; - u32 outbox_size = 0; - u32 stream_size = 0; - u32 inbox_size = 0; - u32 debug_size = 0; - u32 debug_offset = 0; - int window_offset; - int i; - - if (!sdev->info_window) { - dev_err(sdev->dev, "error: have no window info\n"); - return; - } - - for (i = 0; i < sdev->info_window->num_windows; i++) { - elem = &sdev->info_window->window[i]; - - window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id); - if (window_offset < 0) { - dev_warn(sdev->dev, "warn: no offset for window %d\n", - elem->id); - continue; - } - - switch (elem->type) { - case SOF_IPC_REGION_UPBOX: - inbox_offset = window_offset + elem->offset; - inbox_size = elem->size; - snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, - inbox_offset, - elem->size, "inbox", - SOF_DEBUGFS_ACCESS_D0_ONLY); - break; - case SOF_IPC_REGION_DOWNBOX: - outbox_offset = window_offset + elem->offset; - outbox_size = elem->size; - snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, - outbox_offset, - elem->size, "outbox", - SOF_DEBUGFS_ACCESS_D0_ONLY); - break; - case SOF_IPC_REGION_TRACE: - snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, - window_offset + elem->offset, - elem->size, "etrace", - SOF_DEBUGFS_ACCESS_D0_ONLY); - break; - case SOF_IPC_REGION_DEBUG: - debug_offset = window_offset + elem->offset; - debug_size = elem->size; - snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, - window_offset + elem->offset, - elem->size, "debug", - SOF_DEBUGFS_ACCESS_D0_ONLY); - break; - case SOF_IPC_REGION_STREAM: - stream_offset = window_offset + elem->offset; - stream_size = elem->size; - snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, - stream_offset, - elem->size, "stream", - SOF_DEBUGFS_ACCESS_D0_ONLY); - break; - case SOF_IPC_REGION_REGS: - snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, - window_offset + elem->offset, - elem->size, "regs", - SOF_DEBUGFS_ACCESS_D0_ONLY); - break; - case SOF_IPC_REGION_EXCEPTION: - sdev->dsp_oops_offset = window_offset + elem->offset; - snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, - window_offset + elem->offset, - elem->size, "exception", - SOF_DEBUGFS_ACCESS_D0_ONLY); - break; - default: - dev_err(sdev->dev, "error: get illegal window info\n"); - return; - } - } - - if (outbox_size == 0 || inbox_size == 0) { - dev_err(sdev->dev, "error: get illegal mailbox window\n"); - return; - } - - sdev->dsp_box.offset = inbox_offset; - sdev->dsp_box.size = inbox_size; - - sdev->host_box.offset = outbox_offset; - sdev->host_box.size = outbox_size; - - sdev->stream_box.offset = stream_offset; - sdev->stream_box.size = stream_size; - - sdev->debug_box.offset = debug_offset; - sdev->debug_box.size = debug_size; - - dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", - inbox_offset, inbox_size); - dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", - outbox_offset, outbox_size); - dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", - stream_offset, stream_size); - dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n", - debug_offset, debug_size); -} - -/* check for ABI compatibility and create memory windows on first boot */ -int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) -{ - struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; - int offset; - int ret; - - /* mailbox must be on 4k boundary */ - offset = snd_sof_dsp_get_mailbox_offset(sdev); - if (offset < 0) { - dev_err(sdev->dev, "error: have no mailbox offset\n"); - return offset; - } - - dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", - msg_id, offset); - - /* no need to re-check version/ABI for subsequent boots */ - if (!sdev->first_boot) - return 0; - - /* - * copy data from the DSP FW ready offset - * Subsequent error handling is not needed for BLK_TYPE_SRAM - */ - ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready, - sizeof(*fw_ready)); - if (ret) { - dev_err(sdev->dev, - "error: unable to read fw_ready, read from TYPE_SRAM failed\n"); - return ret; - } - - /* make sure ABI version is compatible */ - ret = snd_sof_ipc_valid(sdev); - if (ret < 0) - return ret; - - /* now check for extended data */ - snd_sof_fw_parse_ext_data(sdev, offset + sizeof(struct sof_ipc_fw_ready)); - - sof_get_windows(sdev); - - return sof_ipc_init_msg_memory(sdev); -} -EXPORT_SYMBOL(sof_fw_ready); - -/* generic module parser for mmaped DSPs */ -int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, - struct snd_sof_mod_hdr *module) -{ - struct snd_sof_blk_hdr *block; - int count, ret; - u32 offset; - size_t remaining; - - dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n", - module->size, module->num_blocks, module->type); - - block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module)); - - /* module->size doesn't include header size */ - remaining = module->size; - for (count = 0; count < module->num_blocks; count++) { - /* check for wrap */ - if (remaining < sizeof(*block)) { - dev_err(sdev->dev, "error: not enough data remaining\n"); - return -EINVAL; - } - - /* minus header size of block */ - remaining -= sizeof(*block); - - if (block->size == 0) { - dev_warn(sdev->dev, - "warning: block %d size zero\n", count); - dev_warn(sdev->dev, " type 0x%x offset 0x%x\n", - block->type, block->offset); - continue; - } - - switch (block->type) { - case SOF_FW_BLK_TYPE_RSRVD0: - case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14: - continue; /* not handled atm */ - case SOF_FW_BLK_TYPE_IRAM: - case SOF_FW_BLK_TYPE_DRAM: - case SOF_FW_BLK_TYPE_SRAM: - offset = block->offset; - break; - default: - dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n", - block->type, count); - return -EINVAL; - } - - dev_dbg(sdev->dev, - "block %d type 0x%x size 0x%x ==> offset 0x%x\n", - count, block->type, block->size, offset); - - /* checking block->size to avoid unaligned access */ - if (block->size % sizeof(u32)) { - dev_err(sdev->dev, "error: invalid block size 0x%x\n", - block->size); - return -EINVAL; - } - ret = snd_sof_dsp_block_write(sdev, block->type, offset, - block + 1, block->size); - if (ret < 0) { - dev_err(sdev->dev, "error: write to block type 0x%x failed\n", - block->type); - return ret; - } - - if (remaining < block->size) { - dev_err(sdev->dev, "error: not enough data remaining\n"); - return -EINVAL; - } - - /* minus body size of block */ - remaining -= block->size; - /* next block */ - block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block) - + block->size); - } - - return 0; -} -EXPORT_SYMBOL(snd_sof_parse_module_memcpy); - -static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw, - size_t fw_offset) -{ - struct snd_sof_fw_header *header; - size_t fw_size = fw->size - fw_offset; - - if (fw->size <= fw_offset) { - dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); - return -EINVAL; - } - - /* Read the header information from the data pointer */ - header = (struct snd_sof_fw_header *)(fw->data + fw_offset); - - /* verify FW sig */ - if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) { - dev_err(sdev->dev, "error: invalid firmware signature\n"); - return -EINVAL; - } - - /* check size is valid */ - if (fw_size != header->file_size + sizeof(*header)) { - dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n", - fw_size, header->file_size + sizeof(*header)); - return -EINVAL; - } - - dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n", - header->file_size, header->num_modules, - header->abi, sizeof(*header)); - - return 0; -} - -static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw, - size_t fw_offset) -{ - struct snd_sof_fw_header *header; - struct snd_sof_mod_hdr *module; - int (*load_module)(struct snd_sof_dev *sof_dev, - struct snd_sof_mod_hdr *hdr); - int ret, count; - size_t remaining; - - header = (struct snd_sof_fw_header *)(fw->data + fw_offset); - load_module = sof_ops(sdev)->load_module; - if (!load_module) - return -EINVAL; - - /* parse each module */ - module = (struct snd_sof_mod_hdr *)(fw->data + fw_offset + - sizeof(*header)); - remaining = fw->size - sizeof(*header) - fw_offset; - /* check for wrap */ - if (remaining > fw->size) { - dev_err(sdev->dev, "error: fw size smaller than header size\n"); - return -EINVAL; - } - - for (count = 0; count < header->num_modules; count++) { - /* check for wrap */ - if (remaining < sizeof(*module)) { - dev_err(sdev->dev, "error: not enough data remaining\n"); - return -EINVAL; - } - - /* minus header size of module */ - remaining -= sizeof(*module); - - /* module */ - ret = load_module(sdev, module); - if (ret < 0) { - dev_err(sdev->dev, "error: invalid module %d\n", count); - return ret; - } - - if (remaining < module->size) { - dev_err(sdev->dev, "error: not enough data remaining\n"); - return -EINVAL; - } - - /* minus body size of module */ - remaining -= module->size; - module = (struct snd_sof_mod_hdr *)((u8 *)module - + sizeof(*module) + module->size); - } - - return 0; -} - int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) { struct snd_sof_pdata *plat_data = sdev->pdata; @@ -726,7 +45,7 @@ int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) } /* check for extended manifest */ - ext_man_size = snd_sof_fw_ext_man_parse(sdev, plat_data->fw); + ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev); if (ext_man_size > 0) { /* when no error occurred, drop extended manifest */ plat_data->fw_offset = ext_man_size; @@ -756,7 +75,7 @@ int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) return ret; /* make sure the FW header and file is valid */ - ret = check_header(sdev, plat_data->fw, plat_data->fw_offset); + ret = sdev->ipc->ops->fw_loader->validate(sdev); if (ret < 0) { dev_err(sdev->dev, "error: invalid FW header\n"); goto error; @@ -770,10 +89,12 @@ int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) } /* parse and load firmware modules to DSP */ - ret = load_modules(sdev, plat_data->fw, plat_data->fw_offset); - if (ret < 0) { - dev_err(sdev->dev, "error: invalid FW modules\n"); - goto error; + if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) { + ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev); + if (ret < 0) { + dev_err(sdev->dev, "Firmware loading failed\n"); + goto error; + } } return 0; @@ -854,6 +175,9 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) dev_dbg(sdev->dev, "firmware boot complete\n"); sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); + if (sdev->first_boot && sdev->ipc->ops->fw_loader->query_fw_configuration) + return sdev->ipc->ops->fw_loader->query_fw_configuration(sdev); + return 0; } EXPORT_SYMBOL(snd_sof_run_firmware); diff --git a/sound/soc/sof/mediatek/Kconfig b/sound/soc/sof/mediatek/Kconfig index aeacf0e5bfbb..a149dd1b3f44 100644 --- a/sound/soc/sof/mediatek/Kconfig +++ b/sound/soc/sof/mediatek/Kconfig @@ -21,9 +21,20 @@ config SND_SOC_SOF_MTK_COMMON This option is not user-selectable but automagically handled by 'select' statements at a higher level +config SND_SOC_SOF_MT8186 + tristate "SOF support for MT8186 audio DSP" + select SND_SOC_SOF_MTK_COMMON + depends on MTK_ADSP_IPC + help + This adds support for Sound Open Firmware for Mediatek platforms + using the mt8186 processors. + Say Y if you have such a device. + If unsure select "N". + config SND_SOC_SOF_MT8195 tristate "SOF support for MT8195 audio DSP" select SND_SOC_SOF_MTK_COMMON + depends on MTK_ADSP_IPC help This adds support for Sound Open Firmware for Mediatek platforms using the mt8195 processors. diff --git a/sound/soc/sof/mediatek/Makefile b/sound/soc/sof/mediatek/Makefile index e8ec6da981de..29c5afb2f3d6 100644 --- a/sound/soc/sof/mediatek/Makefile +++ b/sound/soc/sof/mediatek/Makefile @@ -1,2 +1,4 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +obj-$(CONFIG_SND_SOC_SOF_MTK_COMMON) += mtk-adsp-common.o obj-$(CONFIG_SND_SOC_SOF_MT8195) += mt8195/ +obj-$(CONFIG_SND_SOC_SOF_MT8186) += mt8186/ diff --git a/sound/soc/sof/mediatek/adsp_helper.h b/sound/soc/sof/mediatek/adsp_helper.h index 6734e2c0c6b1..4ab998756bbc 100644 --- a/sound/soc/sof/mediatek/adsp_helper.h +++ b/sound/soc/sof/mediatek/adsp_helper.h @@ -7,37 +7,41 @@ #ifndef __MTK_ADSP_HELPER_H__ #define __MTK_ADSP_HELPER_H__ +#include <linux/firmware/mediatek/mtk-adsp-ipc.h> + /* * Global important adsp data structure. */ -#define DSP_MBOX_NUM 3 - struct mtk_adsp_chip_info { phys_addr_t pa_sram; phys_addr_t pa_dram; /* adsp dram physical base */ phys_addr_t pa_shared_dram; /* adsp dram physical base */ phys_addr_t pa_cfgreg; - phys_addr_t pa_mboxreg[DSP_MBOX_NUM]; u32 sramsize; u32 dramsize; u32 cfgregsize; void __iomem *va_sram; /* corresponding to pa_sram */ void __iomem *va_dram; /* corresponding to pa_dram */ void __iomem *va_cfgreg; - void __iomem *va_mboxreg[DSP_MBOX_NUM]; void __iomem *shared_sram; /* part of va_sram */ void __iomem *shared_dram; /* part of va_dram */ phys_addr_t adsp_bootup_addr; int dram_offset; /*dram offset between system and dsp view*/ + + phys_addr_t pa_secreg; + u32 secregsize; + void __iomem *va_secreg; + + phys_addr_t pa_busreg; + u32 busregsize; + void __iomem *va_busreg; }; struct adsp_priv { struct device *dev; struct snd_sof_dev *sdev; - - /* DSP IPC handler */ - struct mbox_controller *adsp_mbox; - + struct mtk_adsp_ipc *dsp_ipc; + struct platform_device *ipc_dev; struct mtk_adsp_chip_info *adsp; struct clk **clk; u32 (*ap2adsp_addr)(u32 addr, void *data); diff --git a/sound/soc/sof/mediatek/mt8186/Makefile b/sound/soc/sof/mediatek/mt8186/Makefile new file mode 100644 index 000000000000..c1f5fc4e2495 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8186/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +snd-sof-mt8186-objs := mt8186.o mt8186-clk.o mt8186-loader.o +obj-$(CONFIG_SND_SOC_SOF_MT8186) += snd-sof-mt8186.o + diff --git a/sound/soc/sof/mediatek/mt8186/mt8186-clk.c b/sound/soc/sof/mediatek/mt8186/mt8186-clk.c new file mode 100644 index 000000000000..22220fd50b62 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8186/mt8186-clk.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2022 Mediatek Corporation. All rights reserved. +// +// Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com> +// Tinghan Shen <tinghan.shen@mediatek.com> +// +// Hardware interface for mt8186 DSP clock + +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <linux/io.h> + +#include "../../sof-audio.h" +#include "../../ops.h" +#include "../adsp_helper.h" +#include "mt8186.h" +#include "mt8186-clk.h" + +static const char *adsp_clks[ADSP_CLK_MAX] = { + [CLK_TOP_AUDIODSP] = "audiodsp_sel", + [CLK_TOP_ADSP_BUS] = "adsp_bus_sel", +}; + +int mt8186_adsp_init_clock(struct snd_sof_dev *sdev) +{ + struct adsp_priv *priv = sdev->pdata->hw_pdata; + struct device *dev = sdev->dev; + int i; + + priv->clk = devm_kcalloc(dev, ADSP_CLK_MAX, sizeof(*priv->clk), GFP_KERNEL); + if (!priv->clk) + return -ENOMEM; + + for (i = 0; i < ADSP_CLK_MAX; i++) { + priv->clk[i] = devm_clk_get(dev, adsp_clks[i]); + + if (IS_ERR(priv->clk[i])) + return PTR_ERR(priv->clk[i]); + } + + return 0; +} + +static int adsp_enable_all_clock(struct snd_sof_dev *sdev) +{ + struct adsp_priv *priv = sdev->pdata->hw_pdata; + struct device *dev = sdev->dev; + int ret; + + ret = clk_prepare_enable(priv->clk[CLK_TOP_AUDIODSP]); + if (ret) { + dev_err(dev, "%s clk_prepare_enable(audiodsp) fail %d\n", + __func__, ret); + return ret; + } + + ret = clk_prepare_enable(priv->clk[CLK_TOP_ADSP_BUS]); + if (ret) { + dev_err(dev, "%s clk_prepare_enable(adsp_bus) fail %d\n", + __func__, ret); + clk_disable_unprepare(priv->clk[CLK_TOP_AUDIODSP]); + return ret; + } + + return 0; +} + +static void adsp_disable_all_clock(struct snd_sof_dev *sdev) +{ + struct adsp_priv *priv = sdev->pdata->hw_pdata; + + clk_disable_unprepare(priv->clk[CLK_TOP_ADSP_BUS]); + clk_disable_unprepare(priv->clk[CLK_TOP_AUDIODSP]); +} + +int mt8186_adsp_clock_on(struct snd_sof_dev *sdev) +{ + struct device *dev = sdev->dev; + int ret; + + ret = adsp_enable_all_clock(sdev); + if (ret) { + dev_err(dev, "failed to adsp_enable_clock: %d\n", ret); + return ret; + } + snd_sof_dsp_write(sdev, DSP_REG_BAR, ADSP_CK_EN, + UART_EN | DMA_EN | TIMER_EN | COREDBG_EN | CORE_CLK_EN); + snd_sof_dsp_write(sdev, DSP_REG_BAR, ADSP_UART_CTRL, + UART_BCLK_CG | UART_RSTN); + + return 0; +} + +void mt8186_adsp_clock_off(struct snd_sof_dev *sdev) +{ + snd_sof_dsp_write(sdev, DSP_REG_BAR, ADSP_CK_EN, 0); + snd_sof_dsp_write(sdev, DSP_REG_BAR, ADSP_UART_CTRL, 0); + adsp_disable_all_clock(sdev); +} + diff --git a/sound/soc/sof/mediatek/mt8186/mt8186-clk.h b/sound/soc/sof/mediatek/mt8186/mt8186-clk.h new file mode 100644 index 000000000000..89c23caf0fee --- /dev/null +++ b/sound/soc/sof/mediatek/mt8186/mt8186-clk.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +/* + * Copyright (c) 2022 MediaTek Corporation. All rights reserved. + * + * Header file for the mt8186 DSP clock definition + */ + +#ifndef __MT8186_CLK_H +#define __MT8186_CLK_H + +struct snd_sof_dev; + +/* DSP clock */ +enum adsp_clk_id { + CLK_TOP_AUDIODSP, + CLK_TOP_ADSP_BUS, + ADSP_CLK_MAX +}; + +int mt8186_adsp_init_clock(struct snd_sof_dev *sdev); +int mt8186_adsp_clock_on(struct snd_sof_dev *sdev); +void mt8186_adsp_clock_off(struct snd_sof_dev *sdev); +#endif diff --git a/sound/soc/sof/mediatek/mt8186/mt8186-loader.c b/sound/soc/sof/mediatek/mt8186/mt8186-loader.c new file mode 100644 index 000000000000..946e6c43204f --- /dev/null +++ b/sound/soc/sof/mediatek/mt8186/mt8186-loader.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright (c) 2022 Mediatek Corporation. All rights reserved. +// +// Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com> +// Tinghan Shen <tinghan.shen@mediatek.com> +// +// Hardware interface for mt8186 DSP code loader + +#include <sound/sof.h> +#include "mt8186.h" +#include "../../ops.h" + +void mt8186_sof_hifixdsp_boot_sequence(struct snd_sof_dev *sdev, u32 boot_addr) +{ + /* set RUNSTALL to stop core */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_HIFI_IO_CONFIG, + RUNSTALL, RUNSTALL); + + /* enable mbox 0 & 1 IRQ */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_MBOX_IRQ_EN, + DSP_MBOX0_IRQ_EN | DSP_MBOX1_IRQ_EN, + DSP_MBOX0_IRQ_EN | DSP_MBOX1_IRQ_EN); + + /* set core boot address */ + snd_sof_dsp_write(sdev, DSP_SECREG_BAR, ADSP_ALTVEC_C0, boot_addr); + snd_sof_dsp_write(sdev, DSP_SECREG_BAR, ADSP_ALTVECSEL, ADSP_ALTVECSEL_C0); + + /* assert core reset */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_CFGREG_SW_RSTN, + SW_RSTN_C0 | SW_DBG_RSTN_C0, + SW_RSTN_C0 | SW_DBG_RSTN_C0); + + /* hardware requirement */ + udelay(1); + + /* release core reset */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_CFGREG_SW_RSTN, + SW_RSTN_C0 | SW_DBG_RSTN_C0, + 0); + + /* clear RUNSTALL (bit31) to start core */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_HIFI_IO_CONFIG, + RUNSTALL, 0); +} + +void mt8186_sof_hifixdsp_shutdown(struct snd_sof_dev *sdev) +{ + /* set RUNSTALL to stop core */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_HIFI_IO_CONFIG, + RUNSTALL, RUNSTALL); + + /* assert core reset */ + snd_sof_dsp_update_bits(sdev, DSP_REG_BAR, ADSP_CFGREG_SW_RSTN, + SW_RSTN_C0 | SW_DBG_RSTN_C0, + SW_RSTN_C0 | SW_DBG_RSTN_C0); +} + diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c new file mode 100644 index 000000000000..3333a0634e29 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2022 Mediatek Inc. All rights reserved. +// +// Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com> +// Tinghan Shen <tinghan.shen@mediatek.com> + +/* + * Hardware interface for audio DSP on mt8186 + */ + +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/of_reserved_mem.h> +#include <linux/module.h> + +#include <sound/sof.h> +#include <sound/sof/xtensa.h> +#include "../../ops.h" +#include "../../sof-of-dev.h" +#include "../../sof-audio.h" +#include "../adsp_helper.h" +#include "mt8186.h" +#include "mt8186-clk.h" + +static int mt8186_get_mailbox_offset(struct snd_sof_dev *sdev) +{ + return MBOX_OFFSET; +} + +static int mt8186_get_window_offset(struct snd_sof_dev *sdev, u32 id) +{ + return MBOX_OFFSET; +} + +static int mt8186_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + struct adsp_priv *priv = sdev->pdata->hw_pdata; + + sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + + return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ); +} + +static void mt8186_get_reply(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg = sdev->msg; + struct sof_ipc_reply reply; + int ret = 0; + + if (!msg) { + dev_warn(sdev->dev, "unexpected ipc interrupt\n"); + return; + } + + /* get reply */ + sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + memcpy(msg->reply_data, &reply, sizeof(reply)); + ret = reply.error; + } else { + /* reply has correct size? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", + msg->reply_size, reply.hdr.size); + ret = -EINVAL; + } + + /* read the message */ + if (msg->reply_size > 0) + sof_mailbox_read(sdev, sdev->host_box.offset, + msg->reply_data, msg->reply_size); + } + + msg->reply_error = ret; +} + +static void mt8186_dsp_handle_reply(struct mtk_adsp_ipc *ipc) +{ + struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); + unsigned long flags; + + spin_lock_irqsave(&priv->sdev->ipc_lock, flags); + mt8186_get_reply(priv->sdev); + snd_sof_ipc_reply(priv->sdev, 0); + spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); +} + +static void mt8186_dsp_handle_request(struct mtk_adsp_ipc *ipc) +{ + struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); + u32 p; /* panic code */ + int ret; + + /* Read the message from the debug box. */ + sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, + &p, sizeof(p)); + + /* Check to see if the message is a panic code 0x0dead*** */ + if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(priv->sdev, p, true); + } else { + snd_sof_ipc_msgs_rx(priv->sdev); + + /* tell DSP cmd is done */ + ret = mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_RSP, MTK_ADSP_IPC_OP_RSP); + if (ret) + dev_err(priv->dev, "request send ipc failed"); + } +} + +static struct mtk_adsp_ipc_ops dsp_ops = { + .handle_reply = mt8186_dsp_handle_reply, + .handle_request = mt8186_dsp_handle_request, +}; + +static int platform_parse_resource(struct platform_device *pdev, void *data) +{ + struct resource *mmio; + struct resource res; + struct device_node *mem_region; + struct device *dev = &pdev->dev; + struct mtk_adsp_chip_info *adsp = data; + int ret; + + mem_region = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!mem_region) { + dev_err(dev, "no dma memory-region phandle\n"); + return -ENODEV; + } + + ret = of_address_to_resource(mem_region, 0, &res); + of_node_put(mem_region); + if (ret) { + dev_err(dev, "of_address_to_resource dma failed\n"); + return ret; + } + + dev_dbg(dev, "DMA %pR\n", &res); + + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_err(dev, "of_reserved_mem_device_init failed\n"); + return ret; + } + + mem_region = of_parse_phandle(dev->of_node, "memory-region", 1); + if (!mem_region) { + dev_err(dev, "no memory-region sysmem phandle\n"); + return -ENODEV; + } + + ret = of_address_to_resource(mem_region, 0, &res); + of_node_put(mem_region); + if (ret) { + dev_err(dev, "of_address_to_resource sysmem failed\n"); + return ret; + } + + adsp->pa_dram = (phys_addr_t)res.start; + if (adsp->pa_dram & DRAM_REMAP_MASK) { + dev_err(dev, "adsp memory(%#x) is not 4K-aligned\n", + (u32)adsp->pa_dram); + return -EINVAL; + } + + adsp->dramsize = resource_size(&res); + if (adsp->dramsize < TOTAL_SIZE_SHARED_DRAM_FROM_TAIL) { + dev_err(dev, "adsp memory(%#x) is not enough for share\n", + adsp->dramsize); + return -EINVAL; + } + + dev_dbg(dev, "dram pbase=%pa size=%#x\n", &adsp->pa_dram, adsp->dramsize); + + mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + if (!mmio) { + dev_err(dev, "no ADSP-CFG register resource\n"); + return -ENXIO; + } + + adsp->va_cfgreg = devm_ioremap_resource(dev, mmio); + if (IS_ERR(adsp->va_cfgreg)) + return PTR_ERR(adsp->va_cfgreg); + + adsp->pa_cfgreg = (phys_addr_t)mmio->start; + adsp->cfgregsize = resource_size(mmio); + + dev_dbg(dev, "cfgreg pbase=%pa size=%#x\n", &adsp->pa_cfgreg, adsp->cfgregsize); + + mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + if (!mmio) { + dev_err(dev, "no SRAM resource\n"); + return -ENXIO; + } + + adsp->pa_sram = (phys_addr_t)mmio->start; + adsp->sramsize = resource_size(mmio); + + dev_dbg(dev, "sram pbase=%pa size=%#x\n", &adsp->pa_sram, adsp->sramsize); + + mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sec"); + if (!mmio) { + dev_err(dev, "no SEC register resource\n"); + return -ENXIO; + } + + adsp->va_secreg = devm_ioremap_resource(dev, mmio); + if (IS_ERR(adsp->va_secreg)) + return PTR_ERR(adsp->va_secreg); + + adsp->pa_secreg = (phys_addr_t)mmio->start; + adsp->secregsize = resource_size(mmio); + + dev_dbg(dev, "secreg pbase=%pa size=%#x\n", &adsp->pa_secreg, adsp->secregsize); + + mmio = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bus"); + if (!mmio) { + dev_err(dev, "no BUS register resource\n"); + return -ENXIO; + } + + adsp->va_busreg = devm_ioremap_resource(dev, mmio); + if (IS_ERR(adsp->va_busreg)) + return PTR_ERR(adsp->va_busreg); + + adsp->pa_busreg = (phys_addr_t)mmio->start; + adsp->busregsize = resource_size(mmio); + + dev_dbg(dev, "busreg pbase=%pa size=%#x\n", &adsp->pa_busreg, adsp->busregsize); + + return 0; +} + +static void adsp_sram_power_on(struct snd_sof_dev *sdev) +{ + snd_sof_dsp_update_bits(sdev, DSP_BUSREG_BAR, ADSP_SRAM_POOL_CON, + DSP_SRAM_POOL_PD_MASK, 0); +} + +static void adsp_sram_power_off(struct snd_sof_dev *sdev) +{ + snd_sof_dsp_update_bits(sdev, DSP_BUSREG_BAR, ADSP_SRAM_POOL_CON, + DSP_SRAM_POOL_PD_MASK, DSP_SRAM_POOL_PD_MASK); +} + +/* Init the basic DSP DRAM address */ +static int adsp_memory_remap_init(struct snd_sof_dev *sdev, struct mtk_adsp_chip_info *adsp) +{ + u32 offset; + + offset = adsp->pa_dram - DRAM_PHYS_BASE_FROM_DSP_VIEW; + adsp->dram_offset = offset; + offset >>= DRAM_REMAP_SHIFT; + + dev_dbg(sdev->dev, "adsp->pa_dram %pa, offset %#x\n", &adsp->pa_dram, offset); + + snd_sof_dsp_write(sdev, DSP_BUSREG_BAR, DSP_C0_EMI_MAP_ADDR, offset); + snd_sof_dsp_write(sdev, DSP_BUSREG_BAR, DSP_C0_DMAEMI_MAP_ADDR, offset); + + if (offset != snd_sof_dsp_read(sdev, DSP_BUSREG_BAR, DSP_C0_EMI_MAP_ADDR) || + offset != snd_sof_dsp_read(sdev, DSP_BUSREG_BAR, DSP_C0_DMAEMI_MAP_ADDR)) { + dev_err(sdev->dev, "emi remap fail\n"); + return -EIO; + } + + return 0; +} + +static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data) +{ + struct device *dev = &pdev->dev; + struct mtk_adsp_chip_info *adsp = data; + u32 shared_size; + + /* remap shared-dram base to be non-cachable */ + shared_size = TOTAL_SIZE_SHARED_DRAM_FROM_TAIL; + adsp->pa_shared_dram = adsp->pa_dram + adsp->dramsize - shared_size; + if (adsp->va_dram) { + adsp->shared_dram = adsp->va_dram + DSP_DRAM_SIZE - shared_size; + } else { + adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram, + shared_size); + if (!adsp->shared_dram) { + dev_err(dev, "ioremap failed for shared DRAM\n"); + return -ENOMEM; + } + } + dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n", + adsp->shared_dram, &adsp->pa_shared_dram, shared_size); + + return 0; +} + +static int mt8186_run(struct snd_sof_dev *sdev) +{ + u32 adsp_bootup_addr; + + adsp_bootup_addr = SRAM_PHYS_BASE_FROM_DSP_VIEW; + dev_dbg(sdev->dev, "HIFIxDSP boot from base : 0x%08X\n", adsp_bootup_addr); + mt8186_sof_hifixdsp_boot_sequence(sdev, adsp_bootup_addr); + + return 0; +} + +static int mt8186_dsp_probe(struct snd_sof_dev *sdev) +{ + struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + struct adsp_priv *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + sdev->pdata->hw_pdata = priv; + priv->dev = sdev->dev; + priv->sdev = sdev; + + priv->adsp = devm_kzalloc(&pdev->dev, sizeof(struct mtk_adsp_chip_info), GFP_KERNEL); + if (!priv->adsp) + return -ENOMEM; + + ret = platform_parse_resource(pdev, priv->adsp); + if (ret) + return ret; + + sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, + priv->adsp->pa_sram, + priv->adsp->sramsize); + if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { + dev_err(sdev->dev, "failed to ioremap base %pa size %#x\n", + &priv->adsp->pa_sram, priv->adsp->sramsize); + return -ENOMEM; + } + + sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, + priv->adsp->pa_dram, + priv->adsp->dramsize); + if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { + dev_err(sdev->dev, "failed to ioremap base %pa size %#x\n", + &priv->adsp->pa_dram, priv->adsp->dramsize); + return -ENOMEM; + } + + priv->adsp->va_dram = sdev->bar[SOF_FW_BLK_TYPE_SRAM]; + + ret = adsp_shared_base_ioremap(pdev, priv->adsp); + if (ret) { + dev_err(sdev->dev, "adsp_shared_base_ioremap fail!\n"); + return ret; + } + + sdev->bar[DSP_REG_BAR] = priv->adsp->va_cfgreg; + sdev->bar[DSP_SECREG_BAR] = priv->adsp->va_secreg; + sdev->bar[DSP_BUSREG_BAR] = priv->adsp->va_busreg; + + sdev->mmio_bar = SOF_FW_BLK_TYPE_SRAM; + sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; + + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = mt8186_get_mailbox_offset(sdev); + + ret = adsp_memory_remap_init(sdev, priv->adsp); + if (ret) { + dev_err(sdev->dev, "adsp_memory_remap_init fail!\n"); + return ret; + } + + /* enable adsp clock before touching registers */ + ret = mt8186_adsp_init_clock(sdev); + if (ret) { + dev_err(sdev->dev, "mt8186_adsp_init_clock failed\n"); + return ret; + } + + ret = mt8186_adsp_clock_on(sdev); + if (ret) { + dev_err(sdev->dev, "mt8186_adsp_clock_on fail!\n"); + return ret; + } + + adsp_sram_power_on(sdev); + + priv->ipc_dev = platform_device_register_data(&pdev->dev, "mtk-adsp-ipc", + PLATFORM_DEVID_NONE, + pdev, sizeof(*pdev)); + if (IS_ERR(priv->ipc_dev)) { + ret = IS_ERR(priv->ipc_dev); + dev_err(sdev->dev, "failed to create mtk-adsp-ipc device\n"); + goto err_adsp_off; + } + + priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev); + if (!priv->dsp_ipc) { + ret = -EPROBE_DEFER; + dev_err(sdev->dev, "failed to get drvdata\n"); + goto exit_pdev_unregister; + } + + mtk_adsp_ipc_set_data(priv->dsp_ipc, priv); + priv->dsp_ipc->ops = &dsp_ops; + + return 0; + +exit_pdev_unregister: + platform_device_unregister(priv->ipc_dev); +err_adsp_off: + adsp_sram_power_off(sdev); + mt8186_adsp_clock_off(sdev); + + return ret; +} + +static int mt8186_dsp_remove(struct snd_sof_dev *sdev) +{ + struct adsp_priv *priv = sdev->pdata->hw_pdata; + + platform_device_unregister(priv->ipc_dev); + mt8186_sof_hifixdsp_shutdown(sdev); + adsp_sram_power_off(sdev); + mt8186_adsp_clock_off(sdev); + + return 0; +} + +static int mt8186_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) +{ + mt8186_sof_hifixdsp_shutdown(sdev); + adsp_sram_power_off(sdev); + mt8186_adsp_clock_off(sdev); + + return 0; +} + +static int mt8186_dsp_resume(struct snd_sof_dev *sdev) +{ + int ret; + + ret = mt8186_adsp_clock_on(sdev); + if (ret) { + dev_err(sdev->dev, "mt8186_adsp_clock_on fail!\n"); + return ret; + } + + adsp_sram_power_on(sdev); + + return ret; +} + +/* on mt8186 there is 1 to 1 match between type and BAR idx */ +static int mt8186_get_bar_index(struct snd_sof_dev *sdev, u32 type) +{ + return type; +} + +static int mt8186_ipc_msg_data(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + void *p, size_t sz) +{ + sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); + return 0; +} + +/* mt8186 ops */ +static struct snd_sof_dsp_ops sof_mt8186_ops = { + /* probe and remove */ + .probe = mt8186_dsp_probe, + .remove = mt8186_dsp_remove, + + /* DSP core boot */ + .run = mt8186_run, + + /* Block IO */ + .block_read = sof_block_read, + .block_write = sof_block_write, + + /* Register IO */ + .write = sof_io_write, + .read = sof_io_read, + .write64 = sof_io_write64, + .read64 = sof_io_read64, + + /* ipc */ + .send_msg = mt8186_send_msg, + .get_mailbox_offset = mt8186_get_mailbox_offset, + .get_window_offset = mt8186_get_window_offset, + .ipc_msg_data = mt8186_ipc_msg_data, + .set_stream_data_offset = sof_set_stream_data_offset, + + /* misc */ + .get_bar_index = mt8186_get_bar_index, + + /* firmware loading */ + .load_firmware = snd_sof_load_firmware_memcpy, + + /* Firmware ops */ + .dsp_arch_ops = &sof_xtensa_arch_ops, + + /* PM */ + .suspend = mt8186_dsp_suspend, + .resume = mt8186_dsp_resume, + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + +static const struct sof_dev_desc sof_of_mt8186_desc = { + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "mediatek/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "mediatek/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-mt8186.ri", + }, + .nocodec_tplg_filename = "sof-mt8186-nocodec.tplg", + .ops = &sof_mt8186_ops, +}; + +static const struct of_device_id sof_of_mt8186_ids[] = { + { .compatible = "mediatek,mt8186-dsp", .data = &sof_of_mt8186_desc}, + { } +}; +MODULE_DEVICE_TABLE(of, sof_of_mt8186_ids); + +/* DT driver definition */ +static struct platform_driver snd_sof_of_mt8186_driver = { + .probe = sof_of_probe, + .remove = sof_of_remove, + .driver = { + .name = "sof-audio-of-mt8186", + .pm = &sof_of_pm, + .of_match_table = sof_of_mt8186_ids, + }, +}; +module_platform_driver(snd_sof_of_mt8186_driver); + +MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); +MODULE_IMPORT_NS(SND_SOC_SOF_MTK_COMMON); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.h b/sound/soc/sof/mediatek/mt8186/mt8186.h new file mode 100644 index 000000000000..98b2965e5ba6 --- /dev/null +++ b/sound/soc/sof/mediatek/mt8186/mt8186.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +/* + * Copyright (c) 2022 MediaTek Corporation. All rights reserved. + * + * Header file for the mt8186 DSP register definition + */ + +#ifndef __MT8186_H +#define __MT8186_H + +struct mtk_adsp_chip_info; +struct snd_sof_dev; + +#define DSP_REG_BAR 4 +#define DSP_SECREG_BAR 5 +#define DSP_BUSREG_BAR 6 + +/***************************************************************************** + * R E G I S T E R TABLE + *****************************************************************************/ +/* dsp cfg */ +#define ADSP_CFGREG_SW_RSTN 0x0000 +#define SW_DBG_RSTN_C0 BIT(0) +#define SW_RSTN_C0 BIT(4) +#define ADSP_HIFI_IO_CONFIG 0x000C +#define TRACEMEMREADY BIT(15) +#define RUNSTALL BIT(31) +#define ADSP_IRQ_MASK 0x0030 +#define ADSP_DVFSRC_REQ 0x0040 +#define ADSP_DDREN_REQ_0 0x0044 +#define ADSP_SEMAPHORE 0x0064 +#define ADSP_WDT_CON_C0 0x007C +#define ADSP_MBOX_IRQ_EN 0x009C +#define DSP_MBOX0_IRQ_EN BIT(0) +#define DSP_MBOX1_IRQ_EN BIT(1) +#define DSP_MBOX2_IRQ_EN BIT(2) +#define DSP_MBOX3_IRQ_EN BIT(3) +#define DSP_MBOX4_IRQ_EN BIT(4) +#define DSP_PDEBUGPC 0x013C +#define ADSP_CK_EN 0x1000 +#define CORE_CLK_EN BIT(0) +#define COREDBG_EN BIT(1) +#define TIMER_EN BIT(3) +#define DMA_EN BIT(4) +#define UART_EN BIT(5) +#define ADSP_UART_CTRL 0x1010 +#define UART_BCLK_CG BIT(0) +#define UART_RSTN BIT(3) + +/* dsp sec */ +#define ADSP_PRID 0x0 +#define ADSP_ALTVEC_C0 0x04 +#define ADSP_ALTVECSEL 0x0C +#define ADSP_ALTVECSEL_C0 BIT(1) + +/* dsp bus */ +#define ADSP_SRAM_POOL_CON 0x190 +#define DSP_SRAM_POOL_PD_MASK 0xF00F /* [0:3] and [12:15] */ +#define DSP_C0_EMI_MAP_ADDR 0xA00 /* ADSP Core0 To EMI Address Remap */ +#define DSP_C0_DMAEMI_MAP_ADDR 0xA08 /* DMA0 To EMI Address Remap */ + +/* DSP memories */ +#define MBOX_OFFSET 0x500000 /* DRAM */ +#define MBOX_SIZE 0x1000 /* consistent with which in memory.h of sof fw */ +#define DSP_DRAM_SIZE 0xA00000 /* 16M */ + +/*remap dram between AP and DSP view, 4KB aligned*/ +#define SRAM_PHYS_BASE_FROM_DSP_VIEW 0x4E100000 /* MT8186 DSP view */ +#define DRAM_PHYS_BASE_FROM_DSP_VIEW 0x60000000 /* MT8186 DSP view */ +#define DRAM_REMAP_SHIFT 12 +#define DRAM_REMAP_MASK 0xFFF + +#define SIZE_SHARED_DRAM_DL 0x40000 /*Shared buffer for Downlink*/ +#define SIZE_SHARED_DRAM_UL 0x40000 /*Shared buffer for Uplink*/ +#define TOTAL_SIZE_SHARED_DRAM_FROM_TAIL (SIZE_SHARED_DRAM_DL + SIZE_SHARED_DRAM_UL) + +void mt8186_sof_hifixdsp_boot_sequence(struct snd_sof_dev *sdev, u32 boot_addr); +void mt8186_sof_hifixdsp_shutdown(struct snd_sof_dev *sdev); +#endif diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 3ab12f352935..30111ab23bf5 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -24,9 +24,103 @@ #include "../../sof-of-dev.h" #include "../../sof-audio.h" #include "../adsp_helper.h" +#include "../mtk-adsp-common.h" #include "mt8195.h" #include "mt8195-clk.h" +static int mt8195_get_mailbox_offset(struct snd_sof_dev *sdev) +{ + return MBOX_OFFSET; +} + +static int mt8195_get_window_offset(struct snd_sof_dev *sdev, u32 id) +{ + return MBOX_OFFSET; +} + +static int mt8195_send_msg(struct snd_sof_dev *sdev, + struct snd_sof_ipc_msg *msg) +{ + struct adsp_priv *priv = sdev->pdata->hw_pdata; + + sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + + return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ); +} + +static void mt8195_get_reply(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg = sdev->msg; + struct sof_ipc_reply reply; + int ret = 0; + + if (!msg) { + dev_warn(sdev->dev, "unexpected ipc interrupt\n"); + return; + } + + /* get reply */ + sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); + if (reply.error < 0) { + memcpy(msg->reply_data, &reply, sizeof(reply)); + ret = reply.error; + } else { + /* reply has correct size? */ + if (reply.hdr.size != msg->reply_size) { + dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", + msg->reply_size, reply.hdr.size); + ret = -EINVAL; + } + + /* read the message */ + if (msg->reply_size > 0) + sof_mailbox_read(sdev, sdev->host_box.offset, + msg->reply_data, msg->reply_size); + } + + msg->reply_error = ret; +} + +static void mt8195_dsp_handle_reply(struct mtk_adsp_ipc *ipc) +{ + struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); + unsigned long flags; + + spin_lock_irqsave(&priv->sdev->ipc_lock, flags); + mt8195_get_reply(priv->sdev); + snd_sof_ipc_reply(priv->sdev, 0); + spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); +} + +static void mt8195_dsp_handle_request(struct mtk_adsp_ipc *ipc) +{ + struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); + u32 p; /* panic code */ + int ret; + + /* Read the message from the debug box. */ + sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, + &p, sizeof(p)); + + /* Check to see if the message is a panic code 0x0dead*** */ + if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(priv->sdev, p, true); + } else { + snd_sof_ipc_msgs_rx(priv->sdev); + + /* tell DSP cmd is done */ + ret = mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_RSP, MTK_ADSP_IPC_OP_RSP); + if (ret) + dev_err(priv->dev, "request send ipc failed"); + } +} + +static struct mtk_adsp_ipc_ops dsp_ops = { + .handle_reply = mt8195_dsp_handle_reply, + .handle_request = mt8195_dsp_handle_request, +}; + static int platform_parse_resource(struct platform_device *pdev, void *data) { struct resource *mmio; @@ -285,15 +379,36 @@ static int mt8195_dsp_probe(struct snd_sof_dev *sdev) } sdev->bar[DSP_REG_BAR] = priv->adsp->va_cfgreg; - sdev->bar[DSP_MBOX0_BAR] = priv->adsp->va_mboxreg[0]; - sdev->bar[DSP_MBOX1_BAR] = priv->adsp->va_mboxreg[1]; - sdev->bar[DSP_MBOX2_BAR] = priv->adsp->va_mboxreg[2]; sdev->mmio_bar = SOF_FW_BLK_TYPE_SRAM; sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = mt8195_get_mailbox_offset(sdev); + + priv->ipc_dev = platform_device_register_data(&pdev->dev, "mtk-adsp-ipc", + PLATFORM_DEVID_NONE, + pdev, sizeof(*pdev)); + if (IS_ERR(priv->ipc_dev)) { + ret = PTR_ERR(priv->ipc_dev); + dev_err(sdev->dev, "failed to register mtk-adsp-ipc device\n"); + goto err_adsp_sram_power_off; + } + + priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev); + if (!priv->dsp_ipc) { + ret = -EPROBE_DEFER; + dev_err(sdev->dev, "failed to get drvdata\n"); + goto exit_pdev_unregister; + } + + mtk_adsp_ipc_set_data(priv->dsp_ipc, priv); + priv->dsp_ipc->ops = &dsp_ops; + return 0; +exit_pdev_unregister: + platform_device_unregister(priv->ipc_dev); err_adsp_sram_power_off: adsp_sram_power_on(&pdev->dev, false); exit_clk_disable: @@ -302,10 +417,17 @@ exit_clk_disable: return ret; } +static int mt8195_dsp_shutdown(struct snd_sof_dev *sdev) +{ + return snd_sof_suspend(sdev->dev); +} + static int mt8195_dsp_remove(struct snd_sof_dev *sdev) { struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + struct adsp_priv *priv = sdev->pdata->hw_pdata; + platform_device_unregister(priv->ipc_dev); adsp_sram_power_on(&pdev->dev, false); adsp_clock_off(sdev); @@ -356,6 +478,39 @@ static int mt8195_get_bar_index(struct snd_sof_dev *sdev, u32 type) return type; } +static int mt8195_ipc_msg_data(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + void *p, size_t sz) +{ + sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); + return 0; +} + +static void mt8195_adsp_dump(struct snd_sof_dev *sdev, u32 flags) +{ + u32 dbg_pc, dbg_data, dbg_bus0, dbg_bus1, dbg_inst; + u32 dbg_ls0stat, dbg_ls1stat, faultbus, faultinfo, swrest; + + /* dump debug registers */ + dbg_pc = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGPC); + dbg_data = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGDATA); + dbg_bus0 = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGBUS0); + dbg_bus1 = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGBUS1); + dbg_inst = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGINST); + dbg_ls0stat = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGLS0STAT); + dbg_ls1stat = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGLS1STAT); + faultbus = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PFAULTBUS); + faultinfo = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PFAULTINFO); + swrest = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_RESET_SW); + + dev_info(sdev->dev, "adsp dump : pc %#x, data %#x, bus0 %#x, bus1 %#x, swrest %#x", + dbg_pc, dbg_data, dbg_bus0, dbg_bus1, swrest); + dev_info(sdev->dev, "dbg_inst %#x, ls0stat %#x, ls1stat %#x, faultbus %#x, faultinfo %#x", + dbg_inst, dbg_ls0stat, dbg_ls1stat, faultbus, faultinfo); + + mtk_adsp_dump(sdev, flags); +} + static struct snd_soc_dai_driver mt8195_dai[] = { { .name = "SOF_DL2", @@ -388,10 +543,11 @@ static struct snd_soc_dai_driver mt8195_dai[] = { }; /* mt8195 ops */ -static const struct snd_sof_dsp_ops sof_mt8195_ops = { +static struct snd_sof_dsp_ops sof_mt8195_ops = { /* probe and remove */ .probe = mt8195_dsp_probe, .remove = mt8195_dsp_remove, + .shutdown = mt8195_dsp_shutdown, /* DSP core boot */ .run = mt8195_run, @@ -406,17 +562,25 @@ static const struct snd_sof_dsp_ops sof_mt8195_ops = { .write64 = sof_io_write64, .read64 = sof_io_read64, + /* ipc */ + .send_msg = mt8195_send_msg, + .get_mailbox_offset = mt8195_get_mailbox_offset, + .get_window_offset = mt8195_get_window_offset, + .ipc_msg_data = mt8195_ipc_msg_data, + .set_stream_data_offset = sof_set_stream_data_offset, + /* misc */ .get_bar_index = mt8195_get_bar_index, - /* module loading */ - .load_module = snd_sof_parse_module_memcpy, /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, /* Firmware ops */ .dsp_arch_ops = &sof_xtensa_arch_ops, + /* Debug information */ + .dbg_dump = mt8195_adsp_dump, + /* DAI drivers */ .drv = mt8195_dai, .num_drv = ARRAY_SIZE(mt8195_dai), @@ -434,11 +598,20 @@ static const struct snd_sof_dsp_ops sof_mt8195_ops = { }; static const struct sof_dev_desc sof_of_mt8195_desc = { - .default_fw_path = "mediatek/sof", - .default_tplg_path = "mediatek/sof-tplg", - .default_fw_filename = "sof-mt8195.ri", + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "mediatek/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "mediatek/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-mt8195.ri", + }, .nocodec_tplg_filename = "sof-mt8195-nocodec.tplg", .ops = &sof_mt8195_ops, + .ipc_timeout = 1000, }; static const struct of_device_id sof_of_mt8195_ids[] = { @@ -451,6 +624,7 @@ MODULE_DEVICE_TABLE(of, sof_of_mt8195_ids); static struct platform_driver snd_sof_of_mt8195_driver = { .probe = sof_of_probe, .remove = sof_of_remove, + .shutdown = sof_of_shutdown, .driver = { .name = "sof-audio-of-mt8195", .pm = &sof_of_pm, diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c new file mode 100644 index 000000000000..1e0769c668a7 --- /dev/null +++ b/sound/soc/sof/mediatek/mtk-adsp-common.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2022 MediaTek Inc. All rights reserved. +// +// Author: YC Hung <yc.hung@mediatek.com> + +/* + * Common helpers for the audio DSP on MediaTek platforms + */ + +#include <linux/module.h> +#include <sound/sof/xtensa.h> +#include "../ops.h" +#include "mtk-adsp-common.h" + +/** + * mtk_adsp_get_registers() - This function is called in case of DSP oops + * in order to gather information about the registers, filename and + * linenumber and stack. + * @sdev: SOF device + * @xoops: Stores information about registers. + * @panic_info: Stores information about filename and line number. + * @stack: Stores the stack dump. + * @stack_words: Size of the stack dump. + */ +static void mtk_adsp_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + struct sof_ipc_panic_info *panic_info, + u32 *stack, size_t stack_words) +{ + u32 offset = sdev->dsp_oops_offset; + + /* first read registers */ + sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops)); + + /* then get panic info */ + if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) { + dev_err(sdev->dev, "invalid header size 0x%x\n", + xoops->arch_hdr.totalsize); + return; + } + offset += xoops->arch_hdr.totalsize; + sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info)); + + /* then get the stack */ + offset += sizeof(*panic_info); + sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32)); +} + +/** + * mtk_adsp_dump() - This function is called when a panic message is + * received from the firmware. + * @sdev: SOF device + * @flags: parameter not used but required by ops prototype + */ +void mtk_adsp_dump(struct snd_sof_dev *sdev, u32 flags) +{ + char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR; + struct sof_ipc_dsp_oops_xtensa xoops; + struct sof_ipc_panic_info panic_info; + u32 stack[MTK_ADSP_STACK_DUMP_SIZE]; + u32 status; + + /* Get information about the panic status from the debug box area. + * Compute the trace point based on the status. + */ + sof_mailbox_read(sdev, sdev->debug_box.offset + 0x4, &status, 4); + + /* Get information about the registers, the filename and line + * number and the stack. + */ + mtk_adsp_get_registers(sdev, &xoops, &panic_info, stack, + MTK_ADSP_STACK_DUMP_SIZE); + + /* Print the information to the console */ + sof_print_oops_and_stack(sdev, level, status, status, &xoops, &panic_info, + stack, MTK_ADSP_STACK_DUMP_SIZE); +} +EXPORT_SYMBOL(mtk_adsp_dump); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.h b/sound/soc/sof/mediatek/mtk-adsp-common.h new file mode 100644 index 000000000000..612cff1f38f7 --- /dev/null +++ b/sound/soc/sof/mediatek/mtk-adsp-common.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +#ifndef __MEDIATEK_ADSP_COMMON_H__ +#define __MEDIATEK_ADSP_COMMON_H__ + +#define EXCEPT_MAX_HDR_SIZE 0x400 +#define MTK_ADSP_STACK_DUMP_SIZE 32 + +void mtk_adsp_dump(struct snd_sof_dev *sdev, u32 flags); +#endif diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 235e2ef72178..ff066de4ceb9 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -177,7 +177,7 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverabl snd_sof_dsp_dbg_dump(sdev, "DSP panic!", SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); sof_set_fw_state(sdev, SOF_FW_CRASHED); - snd_sof_trace_notify_for_error(sdev); + sof_fw_trace_fw_crashed(sdev); } else { snd_sof_dsp_dbg_dump(sdev, "DSP panic (recovery will be attempted)", diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index a19474663767..b79ae4f66eba 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -21,6 +21,14 @@ #define sof_ops(sdev) \ ((sdev)->pdata->desc->ops) +static inline int sof_ops_init(struct snd_sof_dev *sdev) +{ + if (sdev->pdata->desc->ops_init) + return sdev->pdata->desc->ops_init(sdev); + + return 0; +} + /* Mandatory operations are verified during probing */ /* init */ @@ -367,32 +375,6 @@ static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev, return sof_ops(sdev)->send_msg(sdev, msg); } -/* host DMA trace */ -static inline int snd_sof_dma_trace_init(struct snd_sof_dev *sdev, - struct sof_ipc_dma_trace_params_ext *dtrace_params) -{ - if (sof_ops(sdev)->trace_init) - return sof_ops(sdev)->trace_init(sdev, dtrace_params); - - return 0; -} - -static inline int snd_sof_dma_trace_release(struct snd_sof_dev *sdev) -{ - if (sof_ops(sdev)->trace_release) - return sof_ops(sdev)->trace_release(sdev); - - return 0; -} - -static inline int snd_sof_dma_trace_trigger(struct snd_sof_dev *sdev, int cmd) -{ - if (sof_ops(sdev)->trace_trigger) - return sof_ops(sdev)->trace_trigger(sdev, cmd); - - return 0; -} - /* host PCM ops */ static inline int snd_sof_pcm_platform_open(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 658cd8966c9a..a76d0b5b2ad9 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -82,8 +82,10 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); -int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, - struct snd_sof_pcm *spcm, int dir) +static int +sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, + struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params, int dir) { struct snd_soc_dai *dai; int ret, j; @@ -102,7 +104,7 @@ int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm spcm->stream[dir].list = list; - ret = sof_widget_list_setup(sdev, spcm, dir); + ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); if (ret < 0) { dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n", spcm->pcm.pcm_id, dir); @@ -150,9 +152,16 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); + ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params); + if (ret < 0) { + dev_err(component->dev, "platform hw params failed\n"); + return ret; + } + /* if this is a repeated hw_params without hw_free, skip setting up widgets */ if (!spcm->stream[substream->stream].list) { - ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream); + ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params, + substream->stream); if (ret < 0) return ret; } @@ -166,12 +175,6 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, return ret; } - ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params); - if (ret < 0) { - dev_err(component->dev, "platform hw params failed\n"); - return ret; - } - if (pcm_ops->hw_params) { ret = pcm_ops->hw_params(component, substream, params, &platform_params); if (ret < 0) @@ -347,12 +350,9 @@ static int sof_pcm_trigger(struct snd_soc_component *component, snd_sof_pcm_platform_trigger(sdev, substream, cmd); /* free PCM if reset_hw_params is set and the STOP IPC is successful */ - if (!ret && reset_hw_params) { + if (!ret && reset_hw_params) ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, free_widget_list); - if (ret < 0) - return ret; - } return ret; } @@ -396,7 +396,7 @@ static int sof_pcm_open(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct snd_sof_dsp_ops *ops = sof_ops(sdev); + struct snd_sof_dsp_ops *ops = sof_ops(sdev); struct snd_sof_pcm *spcm; struct snd_soc_tplg_stream_caps *caps; int ret; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 1c319582ca6f..18eb327a57f0 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -102,11 +102,18 @@ static int sof_resume(struct device *dev, bool runtime_resume) /* * Nothing further to be done for platforms that support the low power - * D0 substate. + * D0 substate. Resume trace and return when resuming from + * low-power D0 substate */ if (!runtime_resume && sof_ops(sdev)->set_power_state && - old_state == SOF_DSP_PM_D0) + old_state == SOF_DSP_PM_D0) { + ret = sof_fw_trace_resume(sdev); + if (ret < 0) + /* non fatal */ + dev_warn(sdev->dev, + "failed to enable trace after resume %d\n", ret); return 0; + } sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); @@ -135,8 +142,8 @@ static int sof_resume(struct device *dev, bool runtime_resume) return ret; } - /* resume DMA trace, only need send ipc */ - ret = snd_sof_init_trace_ipc(sdev); + /* resume DMA trace */ + ret = sof_fw_trace_resume(sdev); if (ret < 0) { /* non fatal */ dev_warn(sdev->dev, @@ -187,7 +194,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) /* prepare for streams to be resumed properly upon resume */ if (!runtime_suspend) { - ret = sof_set_hw_params_upon_resume(sdev->dev); + ret = snd_sof_dsp_hw_params_upon_resume(sdev); if (ret < 0) { dev_err(sdev->dev, "error: setting hw_params flag during suspend %d\n", @@ -201,6 +208,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) /* Skip to platform-specific suspend if DSP is entering D0 */ if (target_state == SOF_DSP_PM_D0) { + sof_fw_trace_suspend(sdev, pm_state); /* Notify clients not managed by pm framework about core suspend */ sof_suspend_clients(sdev, pm_state); goto suspend; @@ -209,8 +217,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) if (tplg_ops->tear_down_all_pipelines) tplg_ops->tear_down_all_pipelines(sdev, false); - /* release trace */ - snd_sof_release_trace(sdev); + /* suspend DMA trace */ + sof_fw_trace_suspend(sdev, pm_state); /* Notify clients not managed by pm framework about core suspend */ sof_suspend_clients(sdev, pm_state); diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index 74982c04497b..1b04dcb33293 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -74,20 +74,20 @@ int sof_acpi_probe(struct platform_device *pdev, const struct sof_dev_desc *desc sof_pdata->desc = desc; sof_pdata->dev = &pdev->dev; - sof_pdata->fw_filename = desc->default_fw_filename; + sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC]; /* alternate fw and tplg filenames ? */ if (fw_path) sof_pdata->fw_filename_prefix = fw_path; else sof_pdata->fw_filename_prefix = - sof_pdata->desc->default_fw_path; + sof_pdata->desc->default_fw_path[SOF_IPC]; if (tplg_path) sof_pdata->tplg_filename_prefix = tplg_path; else sof_pdata->tplg_filename_prefix = - sof_pdata->desc->default_tplg_path; + sof_pdata->desc->default_tplg_path[SOF_IPC]; /* set callback to be called on successful device probe to enable runtime_pm */ sof_pdata->sof_probe_complete = sof_acpi_probe_complete; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index b2f009a0c5b7..8d740635a4bb 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -12,61 +12,18 @@ #include "sof-audio.h" #include "ops.h" -static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) -{ - int ret; - - /* reset readback offset for scontrol */ - scontrol->readback_offset = 0; - - ret = snd_sof_ipc_set_get_comp_data(scontrol, true); - if (ret < 0) - dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n", - scontrol->comp_id); - - return ret; -} - -static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) -{ - struct snd_sof_control *scontrol; - int ret; - - /* set up all controls for the widget */ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) - if (scontrol->comp_id == swidget->comp_id) { - /* set kcontrol data in DSP */ - ret = sof_kcontrol_setup(sdev, scontrol); - if (ret < 0) { - dev_err(sdev->dev, "error: fail to set up kcontrols for widget %s\n", - swidget->widget->name); - return ret; - } - - /* - * Read back the data from the DSP for static widgets. This is particularly - * useful for binary kcontrols associated with static pipeline widgets to - * initialize the data size to match that in the DSP. - */ - if (swidget->dynamic_pipeline_widget) - continue; - - ret = snd_sof_ipc_set_get_comp_data(scontrol, false); - if (ret < 0) - dev_warn(sdev->dev, "Failed kcontrol get for control in widget %s\n", - swidget->widget->name); - } - - return 0; -} - static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget) { + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; struct snd_sof_route *sroute; list_for_each_entry(sroute, &sdev->route_list, list) - if (sroute->src_widget == widget || sroute->sink_widget == widget) + if (sroute->src_widget == widget || sroute->sink_widget == widget) { + if (sroute->setup && tplg_ops->route_free) + tplg_ops->route_free(sdev, sroute); + sroute->setup = false; + } } int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) @@ -82,6 +39,9 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (--swidget->use_count) return 0; + /* reset route setup status for all routes that contain this widget */ + sof_reset_route_setup_status(sdev, swidget); + /* continue to disable core even if IPC fails */ if (tplg_ops->widget_free) err = tplg_ops->widget_free(sdev, swidget); @@ -98,10 +58,6 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) err = ret; } - /* reset route setup status for all routes that contain this widget */ - sof_reset_route_setup_status(sdev, swidget); - swidget->complete = 0; - /* * free the scheduler widget (same as pipe_widget) associated with the current swidget. * skip for static pipelines @@ -110,6 +66,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) ret = sof_widget_free(sdev, swidget->pipe_widget); if (ret < 0 && !err) err = ret; + swidget->pipe_widget->complete = 0; } if (!err) @@ -179,11 +136,10 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) } /* restore kcontrols for widget */ - ret = sof_widget_kcontrol_setup(sdev, swidget); - if (ret < 0) { - dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n", - swidget->widget->name); - goto widget_free; + if (tplg_ops->control->widget_kcontrol_setup) { + ret = tplg_ops->control->widget_kcontrol_setup(sdev, swidget); + if (ret < 0) + goto widget_free; } dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name); @@ -301,28 +257,249 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, return 0; } -int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) +static void +sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget) +{ + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; + struct snd_sof_widget *swidget = widget->dobj.private; + struct snd_soc_dapm_path *p; + + if (!widget_ops[widget->id].ipc_unprepare || !swidget->prepared) + goto sink_unprepare; + + /* unprepare the source widget */ + widget_ops[widget->id].ipc_unprepare(swidget); + swidget->prepared = false; + +sink_unprepare: + /* unprepare all widgets in the sink paths */ + snd_soc_dapm_widget_for_each_sink_path(widget, p) { + if (!p->walking && p->sink->dobj.private) { + p->walking = true; + sof_unprepare_widgets_in_path(sdev, p->sink); + p->walking = false; + } + } +} + +static int +sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + struct snd_pcm_hw_params *pipeline_params, int dir) +{ + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; + struct snd_sof_widget *swidget = widget->dobj.private; + struct snd_soc_dapm_path *p; + int ret; + + if (!widget_ops[widget->id].ipc_prepare || swidget->prepared) + goto sink_prepare; + + /* prepare the source widget */ + ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params, + pipeline_params, dir); + if (ret < 0) { + dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name); + return ret; + } + + swidget->prepared = true; + +sink_prepare: + /* prepare all widgets in the sink paths */ + snd_soc_dapm_widget_for_each_sink_path(widget, p) { + if (!p->walking && p->sink->dobj.private) { + p->walking = true; + ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params, + platform_params, pipeline_params, dir); + p->walking = false; + if (ret < 0) { + /* unprepare the source widget */ + if (!widget_ops[widget->id].ipc_unprepare && swidget->prepared) { + widget_ops[widget->id].ipc_unprepare(swidget); + swidget->prepared = false; + } + return ret; + } + } + } + + return 0; +} + +/* + * free all widgets in the sink path starting from the source widget + * (DAI type for capture, AIF type for playback) + */ +static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, + int dir) +{ + struct snd_soc_dapm_path *p; + int err; + int ret = 0; + + /* free all widgets even in case of error to keep use counts balanced */ + snd_soc_dapm_widget_for_each_sink_path(widget, p) { + if (!p->walking && p->sink->dobj.private && widget->dobj.private) { + p->walking = true; + if (WIDGET_IS_AIF_OR_DAI(widget->id)) { + err = sof_widget_free(sdev, widget->dobj.private); + if (err < 0) + ret = err; + } + + err = sof_widget_free(sdev, p->sink->dobj.private); + if (err < 0) + ret = err; + + err = sof_free_widgets_in_path(sdev, p->sink, dir); + if (err < 0) + ret = err; + p->walking = false; + } + } + + return ret; +} + +/* + * set up all widgets in the sink path starting from the source widget + * (DAI type for capture, AIF type for playback). + * The error path in this function ensures that all successfully set up widgets getting freed. + */ +static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, + int dir) +{ + struct snd_soc_dapm_path *p; + int ret; + + snd_soc_dapm_widget_for_each_sink_path(widget, p) { + if (!p->walking && p->sink->dobj.private && widget->dobj.private) { + p->walking = true; + if (WIDGET_IS_AIF_OR_DAI(widget->id)) { + ret = sof_widget_setup(sdev, widget->dobj.private); + if (ret < 0) + goto out; + } + + ret = sof_widget_setup(sdev, p->sink->dobj.private); + if (ret < 0) { + if (WIDGET_IS_AIF_OR_DAI(widget->id)) + sof_widget_free(sdev, widget->dobj.private); + goto out; + } + + ret = sof_set_up_widgets_in_path(sdev, p->sink, dir); + if (ret < 0) { + if (WIDGET_IS_AIF_OR_DAI(widget->id)) + sof_widget_free(sdev, widget->dobj.private); + sof_widget_free(sdev, p->sink->dobj.private); + } +out: + p->walking = false; + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int +sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, int dir, + enum sof_widget_op op) +{ + struct snd_soc_dapm_widget *widget; + char *str; + int ret = 0; + int i; + + for_each_dapm_widgets(list, i, widget) { + /* starting widget for playback is AIF type */ + if (dir == SNDRV_PCM_STREAM_PLAYBACK && !WIDGET_IS_AIF(widget->id)) + continue; + + /* starting widget for capture is DAI type */ + if (dir == SNDRV_PCM_STREAM_CAPTURE && !WIDGET_IS_DAI(widget->id)) + continue; + + switch (op) { + case SOF_WIDGET_SETUP: + ret = sof_set_up_widgets_in_path(sdev, widget, dir); + str = "set up"; + break; + case SOF_WIDGET_FREE: + ret = sof_free_widgets_in_path(sdev, widget, dir); + str = "free"; + break; + case SOF_WIDGET_PREPARE: + { + struct snd_pcm_hw_params pipeline_params; + + str = "prepare"; + /* + * When walking the list of connected widgets, the pipeline_params for each + * widget is modified by the source widget in the path. Use a local + * copy of the runtime params as the pipeline_params so that the runtime + * params does not get overwritten. + */ + memcpy(&pipeline_params, fe_params, sizeof(*fe_params)); + + ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, + platform_params, &pipeline_params, dir); + break; + } + case SOF_WIDGET_UNPREPARE: + sof_unprepare_widgets_in_path(sdev, widget); + break; + default: + dev_err(sdev->dev, "Invalid widget op %d\n", op); + return -EINVAL; + } + if (ret < 0) { + dev_err(sdev->dev, "Failed to %s connected widgets\n", str); + return ret; + } + } + + return 0; +} + +int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + int dir) { const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; struct snd_soc_dapm_widget *widget; - int i, ret, num_widgets; + int i, ret; /* nothing to set up */ if (!list) return 0; - /* set up widgets in the list */ - for_each_dapm_widgets(list, num_widgets, widget) { - struct snd_sof_widget *swidget = widget->dobj.private; - - if (!swidget) - continue; + /* + * Prepare widgets for set up. The prepare step is used to allocate memory, assign + * instance ID and pick the widget configuration based on the runtime PCM params. + */ + ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, + dir, SOF_WIDGET_PREPARE); + if (ret < 0) + return ret; - /* set up the widget */ - ret = sof_widget_setup(sdev, swidget); - if (ret < 0) - goto widget_free; + /* Set up is used to send the IPC to the DSP to create the widget */ + ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, + dir, SOF_WIDGET_SETUP); + if (ret < 0) { + ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, + dir, SOF_WIDGET_UNPREPARE); + return ret; } /* @@ -364,18 +541,9 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in return 0; widget_free: - /* free all widgets that have been set up successfully */ - for_each_dapm_widgets(list, i, widget) { - struct snd_sof_widget *swidget = widget->dobj.private; - - if (!swidget) - continue; - - if (!num_widgets--) - break; - - sof_widget_free(sdev, swidget); - } + sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir, + SOF_WIDGET_FREE); + sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); return ret; } @@ -383,37 +551,22 @@ widget_free: int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) { struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; - struct snd_soc_dapm_widget *widget; - int i, ret; - int ret1 = 0; + int ret; /* nothing to free */ if (!list) return 0; - /* - * Free widgets in the list. This can fail but continue freeing other widgets to keep - * use_counts balanced. - */ - for_each_dapm_widgets(list, i, widget) { - struct snd_sof_widget *swidget = widget->dobj.private; - - if (!swidget) - continue; + /* send IPC to free widget in the DSP */ + ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE); - /* - * free widget and its pipe_widget. Either of these can fail, but free as many as - * possible before freeing the list and returning the error. - */ - ret = sof_widget_free(sdev, swidget); - if (ret < 0) - ret1 = ret; - } + /* unprepare the widget */ + sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); snd_soc_dapm_dai_free_widgets(&list); spcm->stream[dir].list = NULL; - return ret1; + return ret; } /* @@ -462,42 +615,6 @@ bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) return false; } -int sof_set_hw_params_upon_resume(struct device *dev) -{ - struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct snd_pcm_substream *substream; - struct snd_sof_pcm *spcm; - snd_pcm_state_t state; - int dir; - - /* - * SOF requires hw_params to be set-up internally upon resume. - * So, set the flag to indicate this for those streams that - * have been suspended. - */ - list_for_each_entry(spcm, &sdev->pcm_list, list) { - for_each_pcm_streams(dir) { - /* - * do not reset hw_params upon resume for streams that - * were kept running during suspend - */ - if (spcm->stream[dir].suspend_ignored) - continue; - - substream = spcm->stream[dir].substream; - if (!substream || !substream->runtime) - continue; - - state = substream->runtime->status->state; - if (state == SNDRV_PCM_STATE_SUSPENDED) - spcm->prepared[dir] = false; - } - } - - /* set internal flag for BE */ - return snd_sof_dsp_hw_params_upon_resume(sdev); -} - int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_sof_pcm *spcm, int dir, bool free_widget_list) { @@ -701,7 +818,8 @@ int sof_machine_check(struct snd_sof_dev *sdev) return -ENOMEM; mach->drv_name = "sof-nocodec"; - sof_pdata->tplg_filename = desc->nocodec_tplg_filename; + if (!sof_pdata->tplg_filename) + sof_pdata->tplg_filename = desc->nocodec_tplg_filename; sof_pdata->machine = mach; snd_sof_set_mach_params(mach, sdev); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 7f15b3bc8196..27cc5fb642e5 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -29,16 +29,47 @@ #define DMA_CHAN_INVALID 0xFFFFFFFF #define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out) +#define WIDGET_IS_AIF(id) ((id) == snd_soc_dapm_aif_in || (id) == snd_soc_dapm_aif_out) +#define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id)) #define SOF_DAI_CLK_INTEL_SSP_MCLK 0 #define SOF_DAI_CLK_INTEL_SSP_BCLK 1 +enum sof_widget_op { + SOF_WIDGET_PREPARE, + SOF_WIDGET_SETUP, + SOF_WIDGET_FREE, + SOF_WIDGET_UNPREPARE, +}; + /* * Volume fractional word length define to 16 sets * the volume linear gain value to use Qx.16 format */ #define VOLUME_FWL 16 +#define SOF_TLV_ITEMS 3 + +static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) +{ + if (value >= size) + return volume_map[size - 1]; + + return volume_map[value]; +} + +static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (volume_map[i] >= value) + return i; + } + + return i - 1; +} + struct snd_sof_widget; struct snd_sof_route; struct snd_sof_control; @@ -86,6 +117,11 @@ struct sof_ipc_tplg_control_ops { const unsigned int __user *binary_data, unsigned int size); /* update control data based on notification from the DSP */ void (*update)(struct snd_sof_dev *sdev, void *ipc_control_message); + /* Optional callback to setup kcontrols associated with an swidget */ + int (*widget_kcontrol_setup)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); + /* mandatory callback to set up volume table for volume kcontrols */ + int (*set_up_volume_table)(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], + int size); }; /** @@ -95,6 +131,8 @@ struct sof_ipc_tplg_control_ops { * @token_list: List of token ID's that should be parsed for the widget * @token_list_size: number of elements in token_list * @bind_event: Function pointer for binding events to the widget + * @ipc_prepare: Optional op for preparing a widget for set up + * @ipc_unprepare: Optional op for unpreparing a widget */ struct sof_ipc_tplg_widget_ops { int (*ipc_setup)(struct snd_sof_widget *swidget); @@ -103,6 +141,11 @@ struct sof_ipc_tplg_widget_ops { int token_list_size; int (*bind_event)(struct snd_soc_component *scomp, struct snd_sof_widget *swidget, u16 event_type); + int (*ipc_prepare)(struct snd_sof_widget *swidget, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + struct snd_pcm_hw_params *source_params, int dir); + void (*ipc_unprepare)(struct snd_sof_widget *swidget); }; /** @@ -112,6 +155,7 @@ struct sof_ipc_tplg_widget_ops { * initialized to 0. * @control: Pointer to the IPC-specific ops for topology kcontrol IO * @route_setup: Function pointer for setting up pipeline connections + * @route_free: Optional op for freeing pipeline connections. * @token_list: List of all tokens supported by the IPC version. The size of the token_list * array should be SOF_TOKEN_COUNT. The unused elements in the array will be * initialized to 0. @@ -129,6 +173,7 @@ struct sof_ipc_tplg_ops { const struct sof_ipc_tplg_widget_ops *widget; const struct sof_ipc_tplg_control_ops *control; int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute); + int (*route_free)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute); const struct sof_token_info *token_list; int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); int (*control_free)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); @@ -247,7 +292,6 @@ struct snd_sof_control { int max_volume_step; /* max volume step for volume_table */ int num_channels; unsigned int access; - u32 readback_offset; /* offset to mmapped data if used */ int info_type; int index; /* pipeline ID */ void *priv; /* private data copied from topology */ @@ -292,10 +336,24 @@ struct snd_sof_widget { struct snd_soc_component *scomp; int comp_id; int pipeline_id; + /* + * complete flag is used to indicate that pipeline set up is complete for scheduler type + * widgets. It is unused for all other widget types. + */ int complete; + /* + * the prepared flag is used to indicate that a widget has been prepared for getting set + * up in the DSP. + */ + bool prepared; int use_count; /* use_count will be protected by the PCM mutex held by the core */ int core; - int id; + int id; /* id is the DAPM widget type */ + /* + * Instance ID is set dynamically when the widget gets set up in the FW. It should be + * unique for each module type across all pipelines. This will not be used in SOF_IPC. + */ + int instance_id; /* * Flag indicating if the widget should be set up dynamically when a PCM is opened. @@ -310,6 +368,7 @@ struct snd_sof_widget { struct snd_soc_dapm_widget *widget; struct list_head list; /* list in sdev widget list */ struct snd_sof_widget *pipe_widget; + void *module_info; const guid_t uuid; @@ -403,8 +462,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_soc_component *scomp, struct snd_soc_pcm_runtime *rtd) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - - struct snd_sof_pcm *spcm = NULL; + struct snd_sof_pcm *spcm; list_for_each_entry(spcm, &sdev->pcm_list, list) { if (le32_to_cpu(spcm->pcm.dai_id) == rtd->dai_link->id) @@ -430,16 +488,10 @@ static inline void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstre static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { } #endif -/* - * Mixer IPC - */ -int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set); - /* DAI link fixup */ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); /* PM */ -int sof_set_hw_params_upon_resume(struct device *dev); bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev); bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev); @@ -453,7 +505,10 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc struct snd_soc_dapm_widget *wsink); /* PCM */ -int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); +int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, + struct snd_pcm_hw_params *fe_params, + struct snd_sof_platform_stream_params *platform_params, + int dir); int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); @@ -467,6 +522,5 @@ int get_token_uuid(void *elem, void *object, u32 offset); int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id, struct snd_sof_tuple *tuples, int num_tuples, size_t object_size, int token_instance_num); -int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, - struct snd_sof_pcm *spcm, int dir); +u32 vol_compute_gain(u32 value, int *tlv); #endif diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c index db3a052c5dd2..4bdecd80248a 100644 --- a/sound/soc/sof/sof-client-ipc-flood-test.c +++ b/sound/soc/sof/sof-client-ipc-flood-test.c @@ -217,10 +217,9 @@ static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buf ipc_count = MAX_IPC_FLOOD_COUNT; } - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); - pm_runtime_put_noidle(dev); goto out; } diff --git a/sound/soc/sof/sof-client-ipc-msg-injector.c b/sound/soc/sof/sof-client-ipc-msg-injector.c index dba6cfd7db09..03490a4d4ae7 100644 --- a/sound/soc/sof/sof-client-ipc-msg-injector.c +++ b/sound/soc/sof/sof-client-ipc-msg-injector.c @@ -15,6 +15,7 @@ #include <linux/slab.h> #include <linux/uaccess.h> #include <sound/sof/header.h> +#include <sound/sof/ipc4/header.h> #include "sof-client.h" @@ -22,6 +23,8 @@ struct sof_msg_inject_priv { struct dentry *dfs_file; + size_t max_msg_size; + enum sof_ipc_type ipc_type; void *tx_buffer; void *rx_buffer; @@ -66,44 +69,157 @@ static ssize_t sof_msg_inject_dfs_read(struct file *file, char __user *buffer, return count; } -static ssize_t sof_msg_inject_dfs_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) +static ssize_t sof_msg_inject_ipc4_dfs_read(struct file *file, + char __user *buffer, + size_t count, loff_t *ppos) { struct sof_client_dev *cdev = file->private_data; struct sof_msg_inject_priv *priv = cdev->data; - struct device *dev = &cdev->auxdev.dev; - int ret, err; - size_t size; + struct sof_ipc4_msg *ipc4_msg = priv->rx_buffer; + size_t header_size = sizeof(ipc4_msg->header_u64); + size_t remaining; - if (*ppos) + if (!ipc4_msg->header_u64 || !count || *ppos) return 0; - size = simple_write_to_buffer(priv->tx_buffer, SOF_IPC_MSG_MAX_SIZE, - ppos, buffer, count); - if (size != count) - return size > 0 ? -EFAULT : size; + /* we need space for the header at minimum (u64) */ + if (count < header_size) + return -ENOSPC; + + remaining = header_size; + + /* Only get large config have payload */ + if (SOF_IPC4_MSG_IS_MODULE_MSG(ipc4_msg->primary) && + (SOF_IPC4_MSG_TYPE_GET(ipc4_msg->primary) == SOF_IPC4_MOD_LARGE_CONFIG_GET)) + remaining += ipc4_msg->data_size; + + if (count > remaining) + count = remaining; + else if (count < remaining) + remaining = count; + + /* copy the header first */ + if (copy_to_user(buffer, &ipc4_msg->header_u64, header_size)) + return -EFAULT; + + *ppos += header_size; + remaining -= header_size; + + if (!remaining) + return count; + + if (remaining > ipc4_msg->data_size) + remaining = ipc4_msg->data_size; + + /* Copy the payload */ + if (copy_to_user(buffer + *ppos, ipc4_msg->data_ptr, remaining)) + return -EFAULT; + + *ppos += remaining; + return count; +} - ret = pm_runtime_get_sync(dev); +static int sof_msg_inject_send_message(struct sof_client_dev *cdev) +{ + struct sof_msg_inject_priv *priv = cdev->data; + struct device *dev = &cdev->auxdev.dev; + int ret, err; + + ret = pm_runtime_resume_and_get(dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); - pm_runtime_put_noidle(dev); return ret; } /* send the message */ - memset(priv->rx_buffer, 0, SOF_IPC_MSG_MAX_SIZE); ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer, priv->rx_buffer, - SOF_IPC_MSG_MAX_SIZE); + priv->max_msg_size); + if (ret) + dev_err(dev, "IPC message send failed: %d\n", ret); + pm_runtime_mark_last_busy(dev); err = pm_runtime_put_autosuspend(dev); if (err < 0) dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err); - /* return size if test is successful */ - if (ret >= 0) - ret = size; - return ret; +} + +static ssize_t sof_msg_inject_dfs_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_msg_inject_priv *priv = cdev->data; + size_t size; + int ret; + + if (*ppos) + return 0; + + size = simple_write_to_buffer(priv->tx_buffer, priv->max_msg_size, + ppos, buffer, count); + if (size != count) + return size > 0 ? -EFAULT : size; + + memset(priv->rx_buffer, 0, priv->max_msg_size); + + ret = sof_msg_inject_send_message(cdev); + + /* return the error code if test failed */ + if (ret < 0) + size = ret; + + return size; +}; + +static ssize_t sof_msg_inject_ipc4_dfs_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_msg_inject_priv *priv = cdev->data; + struct sof_ipc4_msg *ipc4_msg = priv->tx_buffer; + size_t size; + int ret; + + if (*ppos) + return 0; + + if (count < sizeof(ipc4_msg->header_u64)) + return -EINVAL; + + /* copy the header first */ + size = simple_write_to_buffer(&ipc4_msg->header_u64, + sizeof(ipc4_msg->header_u64), + ppos, buffer, count); + if (size != sizeof(ipc4_msg->header_u64)) + return size > 0 ? -EFAULT : size; + + count -= size; + if (!count) { + /* Copy the payload */ + size = simple_write_to_buffer(ipc4_msg->data_ptr, + priv->max_msg_size, ppos, buffer, + count); + if (size != count) + return size > 0 ? -EFAULT : size; + } + + ipc4_msg->data_size = count; + + /* Initialize the reply storage */ + ipc4_msg = priv->rx_buffer; + ipc4_msg->header_u64 = 0; + ipc4_msg->data_size = priv->max_msg_size; + memset(ipc4_msg->data_ptr, 0, priv->max_msg_size); + + ret = sof_msg_inject_send_message(cdev); + + /* return the error code if test failed */ + if (ret < 0) + size = ret; + + return size; }; static int sof_msg_inject_dfs_release(struct inode *inode, struct file *file) @@ -123,28 +239,61 @@ static const struct file_operations sof_msg_inject_fops = { .owner = THIS_MODULE, }; +static const struct file_operations sof_msg_inject_ipc4_fops = { + .open = sof_msg_inject_dfs_open, + .read = sof_msg_inject_ipc4_dfs_read, + .write = sof_msg_inject_ipc4_dfs_write, + .llseek = default_llseek, + .release = sof_msg_inject_dfs_release, + + .owner = THIS_MODULE, +}; + static int sof_msg_inject_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev); + static const struct file_operations *fops; struct device *dev = &auxdev->dev; struct sof_msg_inject_priv *priv; + size_t alloc_size; /* allocate memory for client data */ priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->tx_buffer = devm_kmalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); - priv->rx_buffer = devm_kzalloc(dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + priv->ipc_type = sof_client_get_ipc_type(cdev); + priv->max_msg_size = sof_client_get_ipc_max_payload_size(cdev); + alloc_size = priv->max_msg_size; + + if (priv->ipc_type == SOF_INTEL_IPC4) + alloc_size += sizeof(struct sof_ipc4_msg); + + priv->tx_buffer = devm_kmalloc(dev, alloc_size, GFP_KERNEL); + priv->rx_buffer = devm_kzalloc(dev, alloc_size, GFP_KERNEL); if (!priv->tx_buffer || !priv->rx_buffer) return -ENOMEM; + if (priv->ipc_type == SOF_INTEL_IPC4) { + struct sof_ipc4_msg *ipc4_msg; + + ipc4_msg = priv->tx_buffer; + ipc4_msg->data_ptr = priv->tx_buffer + sizeof(struct sof_ipc4_msg); + + ipc4_msg = priv->rx_buffer; + ipc4_msg->data_ptr = priv->rx_buffer + sizeof(struct sof_ipc4_msg); + + fops = &sof_msg_inject_ipc4_fops; + } else { + fops = &sof_msg_inject_fops; + } + cdev->data = priv; priv->dfs_file = debugfs_create_file("ipc_msg_inject", 0644, debugfs_root, - cdev, &sof_msg_inject_fops); + cdev, fops); /* enable runtime PM */ pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS); diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c index 797dedb26163..34e6bd356e71 100644 --- a/sound/soc/sof/sof-client-probes.c +++ b/sound/soc/sof/sof-client-probes.c @@ -132,6 +132,7 @@ static int sof_probes_deinit(struct sof_client_dev *cdev) static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd, void **params, size_t *num_params) { + size_t max_msg_size = sof_client_get_ipc_max_payload_size(cdev); struct sof_ipc_probe_info_params msg = {{{0}}}; struct sof_ipc_probe_info_params *reply; size_t bytes; @@ -140,13 +141,13 @@ static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd, *params = NULL; *num_params = 0; - reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + reply = kzalloc(max_msg_size, GFP_KERNEL); if (!reply) return -ENOMEM; msg.rhdr.hdr.size = sizeof(msg); msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; - ret = sof_client_ipc_tx_message(cdev, &msg, reply, SOF_IPC_MSG_MAX_SIZE); + ret = sof_client_ipc_tx_message(cdev, &msg, reply, max_msg_size); if (ret < 0 || reply->rhdr.error < 0) goto exit; @@ -503,10 +504,9 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to, if (!buf) return -ENOMEM; - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret); - pm_runtime_put_noidle(dev); goto exit; } @@ -568,10 +568,9 @@ sof_probes_dfs_points_write(struct file *file, const char __user *from, desc = (struct sof_probe_point_desc *)tkns; - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); - pm_runtime_put_noidle(dev); goto exit; } @@ -621,10 +620,9 @@ sof_probes_dfs_points_remove_write(struct file *file, const char __user *from, goto exit; } - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) { dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); - pm_runtime_put_noidle(dev); goto exit; } diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 686ad0c3bb61..16cca666bb85 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> +#include <sound/sof/ipc4/header.h> #include "ops.h" #include "sof-client.h" #include "sof-priv.h" @@ -72,6 +73,9 @@ static int sof_register_ipc_flood_test(struct snd_sof_dev *sdev) int ret = 0; int i; + if (sdev->pdata->ipc_type != SOF_IPC) + return 0; + for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) { ret = sof_client_dev_register(sdev, "ipc_flood", i, NULL, 0); if (ret < 0) @@ -245,10 +249,19 @@ EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT); int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, void *reply_data, size_t reply_bytes) { - struct sof_ipc_cmd_hdr *hdr = ipc_msg; + if (cdev->sdev->pdata->ipc_type == SOF_IPC) { + struct sof_ipc_cmd_hdr *hdr = ipc_msg; + + return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, hdr->size, + reply_data, reply_bytes); + } else if (cdev->sdev->pdata->ipc_type == SOF_INTEL_IPC4) { + struct sof_ipc4_msg *msg = ipc_msg; - return sof_ipc_tx_message(cdev->sdev->ipc, hdr->cmd, ipc_msg, hdr->size, - reply_data, reply_bytes); + return sof_ipc_tx_message(cdev->sdev->ipc, ipc_msg, msg->data_size, + reply_data, reply_bytes); + } + + return -EINVAL; } EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT); @@ -319,6 +332,22 @@ const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev } EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT); +size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->ipc->max_payload_size; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_max_payload_size, SND_SOC_SOF_CLIENT); + +enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->pdata->ipc_type; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, SND_SOC_SOF_CLIENT); + /* module refcount management of SOF core */ int sof_client_core_module_get(struct sof_client_dev *cdev) { @@ -342,9 +371,22 @@ EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, SND_SOC_SOF_CLIENT); /* IPC event handling */ void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf) { - struct sof_ipc_cmd_hdr *hdr = msg_buf; - u32 msg_type = hdr->cmd & SOF_GLB_TYPE_MASK; struct sof_ipc_event_entry *event; + u32 msg_type; + + if (sdev->pdata->ipc_type == SOF_IPC) { + struct sof_ipc_cmd_hdr *hdr = msg_buf; + + msg_type = hdr->cmd & SOF_GLB_TYPE_MASK; + } else if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) { + struct sof_ipc4_msg *msg = msg_buf; + + msg_type = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary); + } else { + dev_dbg_once(sdev->dev, "%s: Not supported IPC version: %d\n", + __func__, sdev->pdata->ipc_type); + return; + } mutex_lock(&sdev->client_event_handler_mutex); @@ -363,9 +405,21 @@ int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct sof_ipc_event_entry *event; - if (!callback || !(ipc_msg_type & SOF_GLB_TYPE_MASK)) + if (!callback) return -EINVAL; + if (cdev->sdev->pdata->ipc_type == SOF_IPC) { + if (!(ipc_msg_type & SOF_GLB_TYPE_MASK)) + return -EINVAL; + } else if (cdev->sdev->pdata->ipc_type == SOF_INTEL_IPC4) { + if (!(ipc_msg_type & SOF_IPC4_NOTIFICATION_TYPE_MASK)) + return -EINVAL; + } else { + dev_warn(sdev->dev, "%s: Not supported IPC version: %d\n", + __func__, sdev->pdata->ipc_type); + return -EINVAL; + } + event = kmalloc(sizeof(*event), GFP_KERNEL); if (!event) return -ENOMEM; diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 4b6394b4c694..46b215d9200f 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -41,6 +41,8 @@ int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev); struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev); const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev); +size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev); +enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev); /* module refcount management of SOF core */ int sof_client_core_module_get(struct sof_client_dev *cdev); diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index e3718638f9ce..53faeccedd4f 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -64,17 +64,17 @@ int sof_of_probe(struct platform_device *pdev) sof_pdata->desc = desc; sof_pdata->dev = &pdev->dev; - sof_pdata->fw_filename = desc->default_fw_filename; + sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC]; if (fw_path) sof_pdata->fw_filename_prefix = fw_path; else - sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path; + sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path[SOF_IPC]; if (tplg_path) sof_pdata->tplg_filename_prefix = tplg_path; else - sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path; + sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path[SOF_IPC]; /* set callback to be called on successful device probe to enable runtime_pm */ sof_pdata->sof_probe_complete = sof_of_probe_complete; @@ -95,4 +95,10 @@ int sof_of_remove(struct platform_device *pdev) } EXPORT_SYMBOL(sof_of_remove); +void sof_of_shutdown(struct platform_device *pdev) +{ + snd_sof_device_shutdown(&pdev->dev); +} +EXPORT_SYMBOL(sof_of_shutdown); + MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/sof-of-dev.h b/sound/soc/sof/sof-of-dev.h index 4e0f6588dad9..fd950a222ba4 100644 --- a/sound/soc/sof/sof-of-dev.h +++ b/sound/soc/sof/sof-of-dev.h @@ -13,5 +13,6 @@ extern const struct dev_pm_ops sof_of_pm; int sof_of_probe(struct platform_device *pdev); int sof_of_remove(struct platform_device *pdev); +void sof_of_shutdown(struct platform_device *pdev); #endif diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 7fa2649e56e5..d627092b399d 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -12,6 +12,7 @@ #include <linux/dmi.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/platform_data/x86/soc.h> #include <linux/pm_runtime.h> #include <sound/soc-acpi.h> #include <sound/soc-acpi-intel-match.h> @@ -23,21 +24,34 @@ static char *fw_path; module_param(fw_path, charp, 0444); MODULE_PARM_DESC(fw_path, "alternate path for SOF firmware."); +static char *fw_filename; +module_param(fw_filename, charp, 0444); +MODULE_PARM_DESC(fw_filename, "alternate filename for SOF firmware."); + static char *tplg_path; module_param(tplg_path, charp, 0444); MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology."); +static char *tplg_filename; +module_param(tplg_filename, charp, 0444); +MODULE_PARM_DESC(tplg_filename, "alternate filename for SOF topology."); + static int sof_pci_debug; module_param_named(sof_pci_debug, sof_pci_debug, int, 0444); MODULE_PARM_DESC(sof_pci_debug, "SOF PCI debug options (0x0 all off)"); -static const char *sof_override_tplg_name; +static int sof_pci_ipc_type = -1; +module_param_named(ipc_type, sof_pci_ipc_type, int, 0444); +MODULE_PARM_DESC(ipc_type, "SOF IPC type (0): SOF, (1) Intel CAVS"); + +static const char *sof_dmi_override_tplg_name; +static bool sof_dmi_use_community_key; #define SOF_PCI_DISABLE_PM_RUNTIME BIT(0) static int sof_tplg_cb(const struct dmi_system_id *id) { - sof_override_tplg_name = id->driver_data; + sof_dmi_override_tplg_name = id->driver_data; return 1; } @@ -94,15 +108,35 @@ static const struct dmi_system_id sof_tplg_table[] = { {} }; +/* all Up boards use the community key */ +static int up_use_community_key(const struct dmi_system_id *id) +{ + sof_dmi_use_community_key = true; + return 1; +} + +/* + * For ApolloLake Chromebooks we want to force the use of the Intel production key. + * All newer platforms use the community key + */ +static int chromebook_use_community_key(const struct dmi_system_id *id) +{ + if (!soc_intel_is_apl()) + sof_dmi_use_community_key = true; + return 1; +} + static const struct dmi_system_id community_key_platforms[] = { { .ident = "Up boards", + .callback = up_use_community_key, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), } }, { .ident = "Google Chromebooks", + .callback = chromebook_use_community_key, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Google"), } @@ -178,7 +212,36 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) sof_pdata->name = pci_name(pci); sof_pdata->desc = desc; sof_pdata->dev = dev; - sof_pdata->fw_filename = desc->default_fw_filename; + + sof_pdata->ipc_type = desc->ipc_default; + + if (sof_pci_ipc_type < 0) { + sof_pdata->ipc_type = desc->ipc_default; + } else { + dev_info(dev, "overriding default IPC %d to requested %d\n", + desc->ipc_default, sof_pci_ipc_type); + if (sof_pci_ipc_type >= SOF_IPC_TYPE_COUNT) { + dev_err(dev, "invalid request value %d\n", sof_pci_ipc_type); + ret = -EINVAL; + goto out; + } + if (!(BIT(sof_pci_ipc_type) & desc->ipc_supported_mask)) { + dev_err(dev, "invalid request value %d, supported mask is %#x\n", + sof_pci_ipc_type, desc->ipc_supported_mask); + ret = -EINVAL; + goto out; + } + sof_pdata->ipc_type = sof_pci_ipc_type; + } + + if (fw_filename) { + sof_pdata->fw_filename = fw_filename; + + dev_dbg(dev, "Module parameter used, changed fw filename to %s\n", + sof_pdata->fw_filename); + } else { + sof_pdata->fw_filename = desc->default_fw_filename[sof_pdata->ipc_type]; + } /* * for platforms using the SOF community key, change the @@ -195,10 +258,10 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) "Module parameter used, changed fw path to %s\n", sof_pdata->fw_filename_prefix); - } else if (dmi_check_system(community_key_platforms)) { + } else if (dmi_check_system(community_key_platforms) && sof_dmi_use_community_key) { sof_pdata->fw_filename_prefix = devm_kasprintf(dev, GFP_KERNEL, "%s/%s", - sof_pdata->desc->default_fw_path, + sof_pdata->desc->default_fw_path[sof_pdata->ipc_type], "community"); dev_dbg(dev, @@ -206,24 +269,37 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) sof_pdata->fw_filename_prefix); } else { sof_pdata->fw_filename_prefix = - sof_pdata->desc->default_fw_path; + sof_pdata->desc->default_fw_path[sof_pdata->ipc_type]; } if (tplg_path) sof_pdata->tplg_filename_prefix = tplg_path; else sof_pdata->tplg_filename_prefix = - sof_pdata->desc->default_tplg_path; + sof_pdata->desc->default_tplg_path[sof_pdata->ipc_type]; - dmi_check_system(sof_tplg_table); - if (sof_override_tplg_name) - sof_pdata->tplg_filename = sof_override_tplg_name; + /* + * the topology filename will be provided in the machine descriptor, unless + * it is overridden by a module parameter or DMI quirk. + */ + if (tplg_filename) { + sof_pdata->tplg_filename = tplg_filename; + + dev_dbg(dev, "Module parameter used, changed tplg filename to %s\n", + sof_pdata->tplg_filename); + } else { + dmi_check_system(sof_tplg_table); + if (sof_dmi_override_tplg_name) + sof_pdata->tplg_filename = sof_dmi_override_tplg_name; + } /* set callback to be called on successful device probe to enable runtime_pm */ sof_pdata->sof_probe_complete = sof_pci_probe_complete; /* call sof helper for DSP hardware probe */ ret = snd_sof_device_probe(dev, sof_pdata); + +out: if (ret) pci_release_regions(pci); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 0d9b640ae24c..9d7f53ff9c70 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -181,11 +181,6 @@ struct snd_sof_dsp_ops { int (*load_firmware)(struct snd_sof_dev *sof_dev); /* mandatory */ int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr); /* optional */ - /* - * FW ready checks for ABI compatibility and creates - * memory windows at first boot - */ - int (*fw_ready)(struct snd_sof_dev *sdev, u32 msg_id); /* mandatory */ /* connect pcm substream to a host stream */ int (*pcm_open)(struct snd_sof_dev *sdev, @@ -259,8 +254,9 @@ struct snd_sof_dsp_ops { size_t size, const char *name, enum sof_debugfs_access_type access_type); /* optional */ - /* host DMA trace initialization */ + /* host DMA trace (IPC3) */ int (*trace_init)(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmatb, struct sof_ipc_dma_trace_params_ext *dtrace_params); /* optional */ int (*trace_release)(struct snd_sof_dev *sdev); /* optional */ int (*trace_trigger)(struct snd_sof_dev *sdev, @@ -349,18 +345,36 @@ struct snd_sof_mailbox { /* IPC message descriptor for host <-> DSP IO */ struct snd_sof_ipc_msg { /* message data */ - u32 header; void *msg_data; void *reply_data; size_t msg_size; size_t reply_size; int reply_error; + /* notification, firmware initiated messages */ + void *rx_data; + wait_queue_head_t waitq; bool ipc_complete; }; /** + * struct sof_ipc_fw_tracing_ops - IPC-specific firmware tracing ops + * @init: Function pointer for initialization of the tracing + * @free: Optional function pointer for freeing of the tracing + * @fw_crashed: Optional function pointer to notify the tracing of a firmware crash + * @suspend: Function pointer for system/runtime suspend + * @resume: Function pointer for system/runtime resume + */ +struct sof_ipc_fw_tracing_ops { + int (*init)(struct snd_sof_dev *sdev); + void (*free)(struct snd_sof_dev *sdev); + void (*fw_crashed)(struct snd_sof_dev *sdev); + void (*suspend)(struct snd_sof_dev *sdev, pm_message_t pm_state); + int (*resume)(struct snd_sof_dev *sdev); +}; + +/** * struct sof_ipc_pm_ops - IPC-specific PM ops * @ctx_save: Function pointer for context save * @ctx_restore: Function pointer for context restore @@ -370,6 +384,25 @@ struct sof_ipc_pm_ops { int (*ctx_restore)(struct snd_sof_dev *sdev); }; +/** + * struct sof_ipc_fw_loader_ops - IPC/FW-specific loader ops + * @validate: Function pointer for validating the firmware image + * @parse_ext_manifest: Function pointer for parsing the manifest of the firmware + * @load_fw_to_dsp: Optional function pointer for loading the firmware to the + * DSP. + * The function implements generic, hardware independent way + * of loading the initial firmware and its modules (if any). + * @query_fw_configuration: Optional function pointer to query information and + * configuration from the booted firmware. + * Executed after the first successful firmware boot. + */ +struct sof_ipc_fw_loader_ops { + int (*validate)(struct snd_sof_dev *sdev); + size_t (*parse_ext_manifest)(struct snd_sof_dev *sdev); + int (*load_fw_to_dsp)(struct snd_sof_dev *sdev); + int (*query_fw_configuration)(struct snd_sof_dev *sdev); +}; + struct sof_ipc_tplg_ops; struct sof_ipc_pcm_ops; @@ -378,11 +411,36 @@ struct sof_ipc_pcm_ops; * @tplg: Pointer to IPC-specific topology ops * @pm: Pointer to PM ops * @pcm: Pointer to PCM ops + * @fw_loader: Pointer to Firmware Loader ops + * @fw_tracing: Pointer to Firmware tracing ops + * + * @tx_msg: Function pointer for sending a 'short' IPC message + * @set_get_data: Function pointer for set/get data ('large' IPC message). This + * function may split up the 'large' message and use the @tx_msg + * path to transfer individual chunks, or use other means to transfer + * the message. + * @get_reply: Function pointer for fetching the reply to + * sdev->ipc->msg.reply_data + * @rx_msg: Function pointer for handling a received message + * + * Note: both @tx_msg and @set_get_data considered as TX functions and they are + * serialized for the duration of the instructed transfer. A large message sent + * via @set_get_data is a single transfer even if at the hardware level it is + * handled with multiple chunks. */ struct sof_ipc_ops { const struct sof_ipc_tplg_ops *tplg; const struct sof_ipc_pm_ops *pm; const struct sof_ipc_pcm_ops *pcm; + const struct sof_ipc_fw_loader_ops *fw_loader; + const struct sof_ipc_fw_tracing_ops *fw_tracing; + + int (*tx_msg)(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, + void *reply_data, size_t reply_bytes, bool no_pm); + int (*set_get_data)(struct snd_sof_dev *sdev, void *data, size_t data_bytes, + bool set); + int (*get_reply)(struct snd_sof_dev *sdev); + void (*rx_msg)(struct snd_sof_dev *sdev); }; /* SOF generic IPC data */ @@ -394,6 +452,9 @@ struct snd_sof_ipc { /* disables further sending of ipc's */ bool disable_ipc_tx; + /* Maximum allowed size of a single IPC message/reply */ + size_t max_payload_size; + struct snd_sof_ipc_msg msg; /* IPC ops based on version */ @@ -457,8 +518,6 @@ struct snd_sof_dev { bool ipc_dump_printed; /* firmware loader */ - struct snd_dma_buffer dmab; - struct snd_dma_buffer dmab_bdl; struct sof_ipc_fw_ready fw_ready; struct sof_ipc_fw_version fw_version; struct sof_ipc_cc_version *cc_version; @@ -473,6 +532,7 @@ struct snd_sof_dev { struct list_head route_list; struct snd_soc_component *component; u32 enabled_cores_mask; /* keep track of enabled cores */ + bool led_present; /* FW configuration */ struct sof_ipc_window *info_window; @@ -481,16 +541,9 @@ struct snd_sof_dev { int ipc_timeout; int boot_timeout; - /* DMA for Trace */ - struct snd_dma_buffer dmatb; - struct snd_dma_buffer dmatp; - int dma_trace_pages; - wait_queue_head_t trace_sleep; - u32 host_offset; - bool dtrace_is_supported; /* set with Kconfig or module parameter */ - bool dtrace_is_enabled; - bool dtrace_error; - bool dtrace_draining; + /* firmwre tracing */ + bool fw_trace_is_supported; /* set with Kconfig or module parameter */ + void *fw_trace_data; /* private data used by firmware tracing implementation */ bool msi_enabled; @@ -564,8 +617,6 @@ extern struct snd_compress_ops sof_compressed_ops; int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev); int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev); int snd_sof_run_firmware(struct snd_sof_dev *sdev); -int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, - struct snd_sof_mod_hdr *module); void snd_sof_fw_unload(struct snd_sof_dev *sdev); /* @@ -575,15 +626,17 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev); void snd_sof_ipc_free(struct snd_sof_dev *sdev); void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev); void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id); -void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev); -int snd_sof_ipc_valid(struct snd_sof_dev *sdev); -int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, - void *msg_data, size_t msg_bytes, void *reply_data, - size_t reply_bytes); -int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, - void *msg_data, size_t msg_bytes, +static inline void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) +{ + sdev->ipc->ops->rx_msg(sdev); +} +int sof_ipc_tx_message(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes, + void *reply_data, size_t reply_bytes); +int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes); -int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev); +int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, + size_t reply_bytes); + static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_id) { snd_sof_ipc_get_reply(sdev); @@ -593,27 +646,26 @@ static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_i /* * Trace/debug */ -int snd_sof_init_trace(struct snd_sof_dev *sdev); -void snd_sof_release_trace(struct snd_sof_dev *sdev); -void snd_sof_free_trace(struct snd_sof_dev *sdev); int snd_sof_dbg_init(struct snd_sof_dev *sdev); void snd_sof_free_debug(struct snd_sof_dev *sdev); int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, void *base, size_t size, const char *name, mode_t mode); -int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, - struct sof_ipc_dma_trace_posn *posn); -void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev); void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level, u32 panic_code, u32 tracep_code, void *oops, struct sof_ipc_panic_info *panic_info, void *stack, size_t stack_words); -int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev); void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev); int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev); int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, u32 offset, size_t size, const char *name, enum sof_debugfs_access_type access_type); +/* Firmware tracing */ +int sof_fw_trace_init(struct snd_sof_dev *sdev); +void sof_fw_trace_free(struct snd_sof_dev *sdev); +void sof_fw_trace_fw_crashed(struct snd_sof_dev *sdev); +void sof_fw_trace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state); +int sof_fw_trace_resume(struct snd_sof_dev *sdev); /* * DSP Architectures. @@ -654,8 +706,6 @@ int sof_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, u32 offset, void *dest, size_t size); -int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id); - int sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, void *p, size_t sz); @@ -721,4 +771,8 @@ static inline int sof_resume_clients(struct snd_sof_dev *sdev) } #endif /* CONFIG_SND_SOC_SOF_CLIENT */ +/* Main ops for IPC implementations */ +extern const struct sof_ipc_ops ipc3_ops; +extern const struct sof_ipc_ops ipc4_ops; + #endif diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 3e5b319b44c7..b1fcab7ce48e 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -32,7 +32,6 @@ #define VOL_HALF_DB_STEP 50 /* TLV data items */ -#define TLV_ITEMS 3 #define TLV_MIN 0 #define TLV_STEP 1 #define TLV_MUTE 2 @@ -134,7 +133,7 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so return 0; } -static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS]) +static inline int get_tlv_data(const int *p, int tlv[SOF_TLV_ITEMS]) { /* we only support dB scale TLV type at the moment */ if ((int)p[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE) @@ -224,7 +223,7 @@ static u32 vol_pow32(u32 a, int exp, u32 fwl) * Function to calculate volume gain from TLV data. * This function can only handle gain steps that are multiples of 0.5 dB */ -static u32 vol_compute_gain(u32 value, int *tlv) +u32 vol_compute_gain(u32 value, int *tlv) { int dB_gain; u32 linear_gain; @@ -263,20 +262,17 @@ static u32 vol_compute_gain(u32 value, int *tlv) * "size" specifies the number of entries in the table */ static int set_up_volume_table(struct snd_sof_control *scontrol, - int tlv[TLV_ITEMS], int size) + int tlv[SOF_TLV_ITEMS], int size) { - int j; - - /* init the volume table */ - scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL); - if (!scontrol->volume_table) - return -ENOMEM; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - /* populate the volume table */ - for (j = 0; j < size ; j++) - scontrol->volume_table[j] = vol_compute_gain(j, tlv); + if (tplg_ops->control->set_up_volume_table) + return tplg_ops->control->set_up_volume_table(scontrol, tlv, size); - return 0; + dev_err(scomp->dev, "Mandatory op %s not set\n", __func__); + return -EINVAL; } struct sof_dai_types { @@ -772,7 +768,8 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_mixer_control *mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); - int tlv[TLV_ITEMS]; + int tlv[SOF_TLV_ITEMS]; + unsigned int mask; int ret; /* validate topology data */ @@ -821,6 +818,16 @@ skip: goto err; } + if (scontrol->led_ctl.use_led) { + mask = scontrol->led_ctl.direction ? SNDRV_CTL_ELEM_ACCESS_MIC_LED : + SNDRV_CTL_ELEM_ACCESS_SPK_LED; + scontrol->access &= ~SNDRV_CTL_ELEM_ACCESS_LED_MASK; + scontrol->access |= mask; + kc->access &= ~SNDRV_CTL_ELEM_ACCESS_LED_MASK; + kc->access |= mask; + sdev->led_present = true; + } + dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", scontrol->comp_id, scontrol->num_channels); @@ -1001,15 +1008,18 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, struct snd_soc_dai *cpu_dai; int i; + if (!w->sname) { + dev_err(scomp->dev, "Widget %s does not have stream\n", w->name); + return -EINVAL; + } + list_for_each_entry(rtd, &card->rtd_list, list) { dev_vdbg(scomp->dev, "tplg: check widget: %s stream: %s dai stream: %s\n", w->name, w->sname, rtd->dai_link->stream_name); - if (!w->sname || !rtd->dai_link->stream_name) - continue; - /* does stream match DAI link ? */ - if (strcmp(w->sname, rtd->dai_link->stream_name)) + if (!rtd->dai_link->stream_name || + strcmp(w->sname, rtd->dai_link->stream_name)) continue; switch (w->id) { @@ -1140,7 +1150,6 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s const struct sof_token_info *token_list = ipc_tplg_ops->token_list; struct snd_soc_tplg_private *private = &tw->priv; int num_tuples = 0; - size_t size; int ret, i; if (count > 0 && !object_token_list) { @@ -1153,8 +1162,7 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s num_tuples += token_list[object_token_list[i]].count; /* allocate memory for tuples array */ - size = sizeof(struct snd_sof_tuple) * num_tuples; - swidget->tuples = kzalloc(size, GFP_KERNEL); + swidget->tuples = kcalloc(num_tuples, sizeof(*swidget->tuples), GFP_KERNEL); if (!swidget->tuples) return -ENOMEM; @@ -1595,7 +1603,6 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ const struct sof_token_info *token_list = ipc_tplg_ops->token_list; struct snd_soc_tplg_private *private = &cfg->priv; struct snd_sof_dai_link *slink; - size_t size; u32 token_id = 0; int num_tuples = 0; int ret, num_sets; @@ -1707,22 +1714,23 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ } /* allocate memory for tuples array */ - size = sizeof(struct snd_sof_tuple) * num_tuples; - slink->tuples = kzalloc(size, GFP_KERNEL); + slink->tuples = kcalloc(num_tuples, sizeof(*slink->tuples), GFP_KERNEL); if (!slink->tuples) { kfree(slink->hw_configs); kfree(slink); return -ENOMEM; } - /* parse one set of DAI link tokens */ - ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), - SOF_DAI_LINK_TOKENS, 1, slink->tuples, - num_tuples, &slink->num_tuples); - if (ret < 0) { - dev_err(scomp->dev, "failed to parse %s for dai link %s\n", - token_list[SOF_DAI_LINK_TOKENS].name, link->name); - goto err; + if (token_list[SOF_DAI_LINK_TOKENS].tokens) { + /* parse one set of DAI link tokens */ + ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + SOF_DAI_LINK_TOKENS, 1, slink->tuples, + num_tuples, &slink->num_tuples); + if (ret < 0) { + dev_err(scomp->dev, "failed to parse %s for dai link %s\n", + token_list[SOF_DAI_LINK_TOKENS].name, link->name); + goto err; + } } /* nothing more to do if there are no DAI type-specific tokens defined */ @@ -2075,6 +2083,7 @@ static struct snd_soc_tplg_ops sof_tplg_ops = { int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); const struct firmware *fw; int ret; @@ -2097,6 +2106,10 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) } release_firmware(fw); + + if (ret >= 0 && sdev->led_present) + ret = snd_ctl_led_request(); + return ret; } EXPORT_SYMBOL(snd_sof_load_topology); diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index ea8e4506d02e..6f662642d611 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -1,601 +1,51 @@ -// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -// -// This file is provided under a dual BSD/GPLv2 license. When using or -// redistributing this file, you may do so under either license. -// -// Copyright(c) 2018 Intel Corporation. All rights reserved. -// -// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> +// SPDX-License-Identifier: GPL-2.0-only // +// Copyright(c) 2022 Intel Corporation. All rights reserved. -#include <linux/debugfs.h> -#include <linux/sched/signal.h> #include "sof-priv.h" -#include "ops.h" -#include "sof-utils.h" - -#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 -#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024 - -static int trace_filter_append_elem(struct snd_sof_dev *sdev, uint32_t key, uint32_t value, - struct sof_ipc_trace_filter_elem *elem_list, - int capacity, int *counter) -{ - if (*counter >= capacity) - return -ENOMEM; - - elem_list[*counter].key = key; - elem_list[*counter].value = value; - ++*counter; - - return 0; -} - -static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line, - struct sof_ipc_trace_filter_elem *elem, - int capacity, int *counter) -{ - int len = strlen(line); - int cnt = *counter; - uint32_t uuid_id; - int log_level; - int pipe_id; - int comp_id; - int read; - int ret; - - /* ignore empty content */ - ret = sscanf(line, " %n", &read); - if (!ret && read == len) - return len; - - ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read); - if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) { - dev_err(sdev->dev, "error: invalid trace filter entry '%s'\n", line); - return -EINVAL; - } - - if (uuid_id > 0) { - ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID, - uuid_id, elem, capacity, &cnt); - if (ret) - return ret; - } - if (pipe_id >= 0) { - ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE, - pipe_id, elem, capacity, &cnt); - if (ret) - return ret; - } - if (comp_id >= 0) { - ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP, - comp_id, elem, capacity, &cnt); - if (ret) - return ret; - } - - ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL | - SOF_IPC_TRACE_FILTER_ELEM_FIN, - log_level, elem, capacity, &cnt); - if (ret) - return ret; - - /* update counter only when parsing whole entry passed */ - *counter = cnt; - - return len; -} - -static int trace_filter_parse(struct snd_sof_dev *sdev, char *string, - int *out_elem_cnt, - struct sof_ipc_trace_filter_elem **out) -{ - static const char entry_delimiter[] = ";"; - char *entry = string; - int capacity = 0; - int entry_len; - int cnt = 0; - - /* - * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY - * IPC elements, depending on content. Calculate IPC elements capacity - * for the input string where each element is set. - */ - while (entry) { - capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY; - entry = strchr(entry + 1, entry_delimiter[0]); - } - *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL); - if (!*out) - return -ENOMEM; - - /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */ - while ((entry = strsep(&string, entry_delimiter))) { - entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt); - if (entry_len < 0) { - dev_err(sdev->dev, "error: %s failed for '%s', %d\n", __func__, entry, - entry_len); - return -EINVAL; - } - } - - *out_elem_cnt = cnt; - - return 0; -} - -static int sof_ipc_trace_update_filter(struct snd_sof_dev *sdev, int num_elems, - struct sof_ipc_trace_filter_elem *elems) -{ - struct sof_ipc_trace_filter *msg; - struct sof_ipc_reply reply; - size_t size; - int ret; - - size = struct_size(msg, elems, num_elems); - if (size > SOF_IPC_MSG_MAX_SIZE) - return -EINVAL; - - msg = kmalloc(size, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - msg->hdr.size = size; - msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE; - msg->elem_cnt = num_elems; - memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems)); - - ret = pm_runtime_get_sync(sdev->dev); - if (ret < 0 && ret != -EACCES) { - pm_runtime_put_noidle(sdev->dev); - dev_err(sdev->dev, "error: enabling device failed: %d\n", ret); - goto error; - } - ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, - &reply, sizeof(reply)); - pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_put_autosuspend(sdev->dev); - -error: - kfree(msg); - return ret ? ret : reply.error; -} - -static ssize_t sof_dfsentry_trace_filter_write(struct file *file, const char __user *from, - size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct sof_ipc_trace_filter_elem *elems = NULL; - struct snd_sof_dev *sdev = dfse->sdev; - loff_t pos = 0; - int num_elems; - char *string; - int ret; - - if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) { - dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count, - TRACE_FILTER_MAX_CONFIG_STRING_LENGTH); - return -EINVAL; - } - - string = kmalloc(count + 1, GFP_KERNEL); - if (!string) - return -ENOMEM; - - /* assert null termination */ - string[count] = 0; - ret = simple_write_to_buffer(string, count, &pos, from, count); - if (ret < 0) - goto error; - - ret = trace_filter_parse(sdev, string, &num_elems, &elems); - if (ret < 0) { - dev_err(sdev->dev, "error: fail in trace_filter_parse, %d\n", ret); - goto error; - } - - if (num_elems) { - ret = sof_ipc_trace_update_filter(sdev, num_elems, elems); - if (ret < 0) { - dev_err(sdev->dev, "error: fail in sof_ipc_trace_update_filter %d\n", ret); - goto error; - } - } - ret = count; -error: - kfree(string); - kfree(elems); - return ret; -} - -static const struct file_operations sof_dfs_trace_filter_fops = { - .open = simple_open, - .write = sof_dfsentry_trace_filter_write, - .llseek = default_llseek, -}; - -static int trace_debugfs_filter_create(struct snd_sof_dev *sdev) -{ - struct snd_sof_dfsentry *dfse; - - dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); - if (!dfse) - return -ENOMEM; - - dfse->sdev = sdev; - dfse->type = SOF_DFSENTRY_TYPE_BUF; - - debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse, - &sof_dfs_trace_filter_fops); - /* add to dfsentry list */ - list_add(&dfse->list, &sdev->dfsentry_list); - - return 0; -} - -static size_t sof_trace_avail(struct snd_sof_dev *sdev, - loff_t pos, size_t buffer_size) -{ - loff_t host_offset = READ_ONCE(sdev->host_offset); - - /* - * If host offset is less than local pos, it means write pointer of - * host DMA buffer has been wrapped. We should output the trace data - * at the end of host DMA buffer at first. - */ - if (host_offset < pos) - return buffer_size - pos; - - /* If there is available trace data now, it is unnecessary to wait. */ - if (host_offset > pos) - return host_offset - pos; - - return 0; -} - -static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev, - loff_t pos, size_t buffer_size) -{ - wait_queue_entry_t wait; - size_t ret = sof_trace_avail(sdev, pos, buffer_size); - - /* data immediately available */ - if (ret) - return ret; - - if (!sdev->dtrace_is_enabled && sdev->dtrace_draining) { - /* - * tracing has ended and all traces have been - * read by client, return EOF - */ - sdev->dtrace_draining = false; - return 0; - } - - /* wait for available trace data from FW */ - init_waitqueue_entry(&wait, current); - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&sdev->trace_sleep, &wait); - - if (!signal_pending(current)) { - /* set timeout to max value, no error code */ - schedule_timeout(MAX_SCHEDULE_TIMEOUT); - } - remove_wait_queue(&sdev->trace_sleep, &wait); - return sof_trace_avail(sdev, pos, buffer_size); -} - -static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, - size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct snd_sof_dev *sdev = dfse->sdev; - unsigned long rem; - loff_t lpos = *ppos; - size_t avail, buffer_size = dfse->size; - u64 lpos_64; - - /* make sure we know about any failures on the DSP side */ - sdev->dtrace_error = false; - - /* check pos and count */ - if (lpos < 0) - return -EINVAL; - if (!count) - return 0; - - /* check for buffer wrap and count overflow */ - lpos_64 = lpos; - lpos = do_div(lpos_64, buffer_size); - - /* get available count based on current host offset */ - avail = sof_wait_trace_avail(sdev, lpos, buffer_size); - if (sdev->dtrace_error) { - dev_err(sdev->dev, "error: trace IO error\n"); - return -EIO; - } - - /* make sure count is <= avail */ - if (count > avail) - count = avail; - - /* - * make sure that all trace data is available for the CPU as the trace - * data buffer might be allocated from non consistent memory. - * Note: snd_dma_buffer_sync() is called for normal audio playback and - * capture streams also. - */ - snd_dma_buffer_sync(&sdev->dmatb, SNDRV_DMA_SYNC_CPU); - /* copy available trace data to debugfs */ - rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count); - if (rem) - return -EFAULT; - - *ppos += count; - - /* move debugfs reading position */ - return count; -} - -static int sof_dfsentry_trace_release(struct inode *inode, struct file *file) -{ - struct snd_sof_dfsentry *dfse = inode->i_private; - struct snd_sof_dev *sdev = dfse->sdev; - - /* avoid duplicate traces at next open */ - if (!sdev->dtrace_is_enabled) - sdev->host_offset = 0; - - return 0; -} - -static const struct file_operations sof_dfs_trace_fops = { - .open = simple_open, - .read = sof_dfsentry_trace_read, - .llseek = default_llseek, - .release = sof_dfsentry_trace_release, -}; - -static int trace_debugfs_create(struct snd_sof_dev *sdev) -{ - struct snd_sof_dfsentry *dfse; - int ret; - - if (!sdev) - return -EINVAL; - - ret = trace_debugfs_filter_create(sdev); - if (ret < 0) - dev_err(sdev->dev, "error: fail in %s, %d", __func__, ret); - - dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); - if (!dfse) - return -ENOMEM; - - dfse->type = SOF_DFSENTRY_TYPE_BUF; - dfse->buf = sdev->dmatb.area; - dfse->size = sdev->dmatb.bytes; - dfse->sdev = sdev; - - debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse, - &sof_dfs_trace_fops); - - return 0; -} - -int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) -{ - struct sof_ipc_fw_ready *ready = &sdev->fw_ready; - struct sof_ipc_fw_version *v = &ready->version; - struct sof_ipc_dma_trace_params_ext params; - struct sof_ipc_reply ipc_reply; - int ret; - - if (!sdev->dtrace_is_supported) - return 0; - - if (sdev->dtrace_is_enabled || !sdev->dma_trace_pages) - return -EINVAL; - - /* set IPC parameters */ - params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG; - /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */ - if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) { - params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext); - params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT; - params.timestamp_ns = ktime_get(); /* in nanosecond */ - } else { - params.hdr.size = sizeof(struct sof_ipc_dma_trace_params); - params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS; - } - params.buffer.phy_addr = sdev->dmatp.addr; - params.buffer.size = sdev->dmatb.bytes; - params.buffer.pages = sdev->dma_trace_pages; - params.stream_tag = 0; - - sdev->host_offset = 0; - sdev->dtrace_draining = false; - - ret = snd_sof_dma_trace_init(sdev, ¶ms); - if (ret < 0) { - dev_err(sdev->dev, - "error: fail in snd_sof_dma_trace_init %d\n", ret); - return ret; - } - dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag); - - /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, - params.hdr.cmd, ¶ms, sizeof(params), - &ipc_reply, sizeof(ipc_reply)); - if (ret < 0) { - dev_err(sdev->dev, - "error: can't set params for DMA for trace %d\n", ret); - goto trace_release; - } - - ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); - if (ret < 0) { - dev_err(sdev->dev, - "error: snd_sof_dma_trace_trigger: start: %d\n", ret); - goto trace_release; - } - - sdev->dtrace_is_enabled = true; - - return 0; - -trace_release: - snd_sof_dma_trace_release(sdev); - return ret; -} - -int snd_sof_init_trace(struct snd_sof_dev *sdev) +int sof_fw_trace_init(struct snd_sof_dev *sdev) { - int ret; + if (!sdev->ipc->ops->fw_tracing) { + dev_info(sdev->dev, "Firmware tracing is not available\n"); + sdev->fw_trace_is_supported = false; - if (!sdev->dtrace_is_supported) return 0; - - /* set false before start initialization */ - sdev->dtrace_is_enabled = false; - - /* allocate trace page table buffer */ - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, - PAGE_SIZE, &sdev->dmatp); - if (ret < 0) { - dev_err(sdev->dev, - "error: can't alloc page table for trace %d\n", ret); - return ret; - } - - /* allocate trace data buffer */ - ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev, - DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE, - &sdev->dmatb); - if (ret < 0) { - dev_err(sdev->dev, - "error: can't alloc buffer for trace %d\n", ret); - goto page_err; - } - - /* create compressed page table for audio firmware */ - ret = snd_sof_create_page_table(sdev->dev, &sdev->dmatb, - sdev->dmatp.area, sdev->dmatb.bytes); - if (ret < 0) - goto table_err; - - sdev->dma_trace_pages = ret; - dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n", - __func__, sdev->dma_trace_pages); - - if (sdev->first_boot) { - ret = trace_debugfs_create(sdev); - if (ret < 0) - goto table_err; } - init_waitqueue_head(&sdev->trace_sleep); - - ret = snd_sof_init_trace_ipc(sdev); - if (ret < 0) - goto table_err; - - return 0; -table_err: - sdev->dma_trace_pages = 0; - snd_dma_free_pages(&sdev->dmatb); -page_err: - snd_dma_free_pages(&sdev->dmatp); - return ret; + return sdev->ipc->ops->fw_tracing->init(sdev); } -EXPORT_SYMBOL(snd_sof_init_trace); -int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, - struct sof_ipc_dma_trace_posn *posn) +void sof_fw_trace_free(struct snd_sof_dev *sdev) { - if (!sdev->dtrace_is_supported) - return 0; - - if (sdev->dtrace_is_enabled && sdev->host_offset != posn->host_offset) { - sdev->host_offset = posn->host_offset; - wake_up(&sdev->trace_sleep); - } - - if (posn->overflow != 0) - dev_err(sdev->dev, - "error: DSP trace buffer overflow %u bytes. Total messages %d\n", - posn->overflow, posn->messages); + if (!sdev->fw_trace_is_supported || !sdev->ipc->ops->fw_tracing) + return; - return 0; + if (sdev->ipc->ops->fw_tracing->free) + sdev->ipc->ops->fw_tracing->free(sdev); } -/* an error has occurred within the DSP that prevents further trace */ -void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev) +void sof_fw_trace_fw_crashed(struct snd_sof_dev *sdev) { - if (!sdev->dtrace_is_supported) + if (!sdev->fw_trace_is_supported) return; - if (sdev->dtrace_is_enabled) { - sdev->dtrace_error = true; - wake_up(&sdev->trace_sleep); - } + if (sdev->ipc->ops->fw_tracing->fw_crashed) + sdev->ipc->ops->fw_tracing->fw_crashed(sdev); } -EXPORT_SYMBOL(snd_sof_trace_notify_for_error); -void snd_sof_release_trace(struct snd_sof_dev *sdev) +void sof_fw_trace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state) { - struct sof_ipc_fw_ready *ready = &sdev->fw_ready; - struct sof_ipc_fw_version *v = &ready->version; - struct sof_ipc_cmd_hdr hdr; - struct sof_ipc_reply ipc_reply; - int ret; - - if (!sdev->dtrace_is_supported || !sdev->dtrace_is_enabled) + if (!sdev->fw_trace_is_supported) return; - ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); - if (ret < 0) - dev_err(sdev->dev, - "error: snd_sof_dma_trace_trigger: stop: %d\n", ret); - - /* - * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from - * ABI 3.20.0 onwards - */ - if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) { - hdr.size = sizeof(hdr); - hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE; - - ret = sof_ipc_tx_message(sdev->ipc, hdr.cmd, &hdr, hdr.size, - &ipc_reply, sizeof(ipc_reply)); - if (ret < 0) - dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret); - } - - ret = snd_sof_dma_trace_release(sdev); - if (ret < 0) - dev_err(sdev->dev, - "error: fail in snd_sof_dma_trace_release %d\n", ret); - - sdev->dtrace_is_enabled = false; - sdev->dtrace_draining = true; - wake_up(&sdev->trace_sleep); + sdev->ipc->ops->fw_tracing->suspend(sdev, pm_state); } -EXPORT_SYMBOL(snd_sof_release_trace); -void snd_sof_free_trace(struct snd_sof_dev *sdev) +int sof_fw_trace_resume(struct snd_sof_dev *sdev) { - if (!sdev->dtrace_is_supported) - return; - - snd_sof_release_trace(sdev); + if (!sdev->fw_trace_is_supported) + return 0; - if (sdev->dma_trace_pages) { - snd_dma_free_pages(&sdev->dmatb); - snd_dma_free_pages(&sdev->dmatp); - sdev->dma_trace_pages = 0; - } + return sdev->ipc->ops->fw_tracing->resume(sdev); } -EXPORT_SYMBOL(snd_sof_free_trace); diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index cd454871d654..2482d9867357 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -85,6 +85,18 @@ config SND_SOC_TEGRA210_I2S compatible devices. Say Y or M if you want to add support for Tegra210 I2S module. +config SND_SOC_TEGRA186_ASRC + tristate "Tegra186 ASRC module" + help + Config to enable the Asynchronous Sample Rate Converter (ASRC), + which converts the sampling frequency of the input signal from + one frequency to another. It can handle over a wide range of + sample rate ratios (freq_in/freq_out) from 1:24 to 24:1. + ASRC has two modes of operation. One where ratio can be programmed + in SW and the other where it gets information from ratio estimator + module. + Say Y or M if you want to add support for Tegra186 ASRC module. + config SND_SOC_TEGRA186_DSPK tristate "Tegra186 DSPK module" help diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index f19d56690a0d..70a498ddb2fa 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -11,6 +11,7 @@ snd-soc-tegra30-i2s-objs := tegra30_i2s.o snd-soc-tegra210-ahub-objs := tegra210_ahub.o snd-soc-tegra210-dmic-objs := tegra210_dmic.o snd-soc-tegra210-i2s-objs := tegra210_i2s.o +snd-soc-tegra186-asrc-objs := tegra186_asrc.o snd-soc-tegra186-dspk-objs := tegra186_dspk.o snd-soc-tegra210-admaif-objs := tegra210_admaif.o snd-soc-tegra210-mvc-objs := tegra210_mvc.o @@ -29,6 +30,7 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o obj-$(CONFIG_SND_SOC_TEGRA210_DMIC) += snd-soc-tegra210-dmic.o obj-$(CONFIG_SND_SOC_TEGRA210_AHUB) += snd-soc-tegra210-ahub.o obj-$(CONFIG_SND_SOC_TEGRA210_I2S) += snd-soc-tegra210-i2s.o +obj-$(CONFIG_SND_SOC_TEGRA186_ASRC) += snd-soc-tegra186-asrc.o obj-$(CONFIG_SND_SOC_TEGRA186_DSPK) += snd-soc-tegra186-dspk.o obj-$(CONFIG_SND_SOC_TEGRA210_ADMAIF) += snd-soc-tegra210-admaif.o obj-$(CONFIG_SND_SOC_TEGRA210_MVC) += snd-soc-tegra210-mvc.o diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c new file mode 100644 index 000000000000..9f12faaa609d --- /dev/null +++ b/sound/soc/tegra/tegra186_asrc.c @@ -0,0 +1,1046 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra186_asrc.c - Tegra186 ASRC driver +// +// Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra186_asrc.h" +#include "tegra_cif.h" + +#define ASRC_STREAM_SOURCE_SELECT(id) \ + (TEGRA186_ASRC_CFG + ((id) * TEGRA186_ASRC_STREAM_STRIDE)) + +#define ASRC_STREAM_REG(reg, id) ((reg) + ((id) * TEGRA186_ASRC_STREAM_STRIDE)) + +#define ASRC_STREAM_REG_DEFAULTS(id) \ + { ASRC_STREAM_REG(TEGRA186_ASRC_CFG, id), \ + (((id) + 1) << 4) }, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, id), \ + 0x1 }, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, id), \ + 0x0 }, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_MUTE_UNMUTE_DURATION, id), \ + 0x400 }, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_RX_CIF_CTRL, id), \ + 0x7500 }, \ + { ASRC_STREAM_REG(TEGRA186_ASRC_TX_CIF_CTRL, id), \ + 0x7500 } + +static const struct reg_default tegra186_asrc_reg_defaults[] = { + ASRC_STREAM_REG_DEFAULTS(0), + ASRC_STREAM_REG_DEFAULTS(1), + ASRC_STREAM_REG_DEFAULTS(2), + ASRC_STREAM_REG_DEFAULTS(3), + ASRC_STREAM_REG_DEFAULTS(4), + ASRC_STREAM_REG_DEFAULTS(5), + + { TEGRA186_ASRC_GLOBAL_ENB, 0}, + { TEGRA186_ASRC_GLOBAL_SOFT_RESET, 0}, + { TEGRA186_ASRC_GLOBAL_CG, 0x1 }, + { TEGRA186_ASRC_GLOBAL_CFG, 0x0 }, + { TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR, 0}, + { TEGRA186_ASRC_GLOBAL_SCRATCH_CFG, 0x0c207980 }, + { TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL, 0x00115500 }, + { TEGRA186_ASRC_GLOBAL_INT_MASK, 0x0}, + { TEGRA186_ASRC_GLOBAL_INT_SET, 0x0}, + { TEGRA186_ASRC_GLOBAL_INT_CLEAR, 0x0}, + { TEGRA186_ASRC_GLOBAL_APR_CTRL, 0x0}, + { TEGRA186_ASRC_GLOBAL_APR_CTRL_ACCESS_CTRL, 0x0}, + { TEGRA186_ASRC_GLOBAL_DISARM_APR, 0x0}, + { TEGRA186_ASRC_GLOBAL_DISARM_APR_ACCESS_CTRL, 0x0}, + { TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS, 0x0}, + { TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS_CTRL, 0x0}, + { TEGRA186_ASRC_CYA, 0x0}, +}; + +static void tegra186_asrc_lock_stream(struct tegra186_asrc *asrc, + unsigned int id) +{ + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_LOCK_STATUS, + id), + 1); +} + +static int __maybe_unused tegra186_asrc_runtime_suspend(struct device *dev) +{ + struct tegra186_asrc *asrc = dev_get_drvdata(dev); + + regcache_cache_only(asrc->regmap, true); + regcache_mark_dirty(asrc->regmap); + + return 0; +} + +static int __maybe_unused tegra186_asrc_runtime_resume(struct device *dev) +{ + struct tegra186_asrc *asrc = dev_get_drvdata(dev); + int id; + + regcache_cache_only(asrc->regmap, false); + + /* + * Below sequence is recommended after a runtime PM cycle. + * This otherwise leads to transfer failures. The cache + * sync is done after this to restore other settings. + */ + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR, + TEGRA186_ASRC_ARAM_START_ADDR); + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_ENB, + TEGRA186_ASRC_GLOBAL_EN); + + regcache_sync(asrc->regmap); + + for (id = 0; id < TEGRA186_ASRC_STREAM_MAX; id++) { + if (asrc->lane[id].ratio_source != + TEGRA186_ASRC_RATIO_SOURCE_SW) + continue; + + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, + id), + asrc->lane[id].int_part); + + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, + id), + asrc->lane[id].frac_part); + + tegra186_asrc_lock_stream(asrc, id); + } + + return 0; +} + +static int tegra186_asrc_set_audio_cif(struct tegra186_asrc *asrc, + struct snd_pcm_hw_params *params, + unsigned int reg) +{ + int channels, audio_bits; + struct tegra_cif_conf cif_conf; + + memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); + + channels = params_channels(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + audio_bits = TEGRA_ACIF_BITS_16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + audio_bits = TEGRA_ACIF_BITS_32; + break; + default: + return -EINVAL; + } + + cif_conf.audio_ch = channels; + cif_conf.client_ch = channels; + cif_conf.audio_bits = audio_bits; + cif_conf.client_bits = TEGRA_ACIF_BITS_24; + + tegra_set_cif(asrc->regmap, reg, &cif_conf); + + return 0; +} + +static int tegra186_asrc_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = dai->dev; + struct tegra186_asrc *asrc = snd_soc_dai_get_drvdata(dai); + int ret, id = dai->id; + + /* Set input threshold */ + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, dai->id), + asrc->lane[id].input_thresh); + + ret = tegra186_asrc_set_audio_cif(asrc, params, + ASRC_STREAM_REG(TEGRA186_ASRC_RX_CIF_CTRL, dai->id)); + if (ret) { + dev_err(dev, "Can't set ASRC RX%d CIF: %d\n", dai->id, ret); + return ret; + } + + return ret; +} + +static int tegra186_asrc_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = dai->dev; + struct tegra186_asrc *asrc = snd_soc_dai_get_drvdata(dai); + int ret, id = dai->id - 7; + + /* Set output threshold */ + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, id), + asrc->lane[id].output_thresh); + + ret = tegra186_asrc_set_audio_cif(asrc, params, + ASRC_STREAM_REG(TEGRA186_ASRC_TX_CIF_CTRL, id)); + if (ret) { + dev_err(dev, "Can't set ASRC TX%d CIF: %d\n", id, ret); + return ret; + } + + /* Set ENABLE_HW_RATIO_COMP */ + if (asrc->lane[id].hwcomp_disable) { + regmap_update_bits(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_CFG, id), + TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK, + TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_DISABLE); + } else { + regmap_update_bits(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_CFG, id), + TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK, + TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_ENABLE); + + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_COMP, id), + TEGRA186_ASRC_STREAM_DEFAULT_HW_COMP_BIAS_VALUE); + } + + /* Set lock */ + regmap_update_bits(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_CFG, id), + 1, asrc->lane[id].ratio_source); + + if (asrc->lane[id].ratio_source == TEGRA186_ASRC_RATIO_SOURCE_SW) { + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, id), + asrc->lane[id].int_part); + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, id), + asrc->lane[id].frac_part); + tegra186_asrc_lock_stream(asrc, id); + } + + return ret; +} + +static int tegra186_asrc_get_ratio_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *asrc_private = + (struct soc_enum *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + ucontrol->value.enumerated.item[0] = asrc->lane[id].ratio_source; + + return 0; +} + +static int tegra186_asrc_put_ratio_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *asrc_private = + (struct soc_enum *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + bool change = false; + + asrc->lane[id].ratio_source = ucontrol->value.enumerated.item[0]; + + regmap_update_bits_check(asrc->regmap, asrc_private->reg, + TEGRA186_ASRC_STREAM_RATIO_TYPE_MASK, + asrc->lane[id].ratio_source, + &change); + + return change ? 1 : 0; +} + +static int tegra186_asrc_get_ratio_int(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + regmap_read(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, id), + &asrc->lane[id].int_part); + + ucontrol->value.integer.value[0] = asrc->lane[id].int_part; + + return 0; +} + +static int tegra186_asrc_put_ratio_int(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + bool change = false; + + if (asrc->lane[id].ratio_source == TEGRA186_ASRC_RATIO_SOURCE_ARAD) { + dev_err(cmpnt->dev, + "Lane %d ratio source is ARAD, invalid SW update\n", + id); + return -EINVAL; + } + + asrc->lane[id].int_part = ucontrol->value.integer.value[0]; + + regmap_update_bits_check(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, + id), + TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, + asrc->lane[id].int_part, &change); + + tegra186_asrc_lock_stream(asrc, id); + + return change ? 1 : 0; +} + +static int tegra186_asrc_get_ratio_frac(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mreg_control *asrc_private = + (struct soc_mreg_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->regbase / TEGRA186_ASRC_STREAM_STRIDE; + + regmap_read(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, id), + &asrc->lane[id].frac_part); + + ucontrol->value.integer.value[0] = asrc->lane[id].frac_part; + + return 0; +} + +static int tegra186_asrc_put_ratio_frac(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mreg_control *asrc_private = + (struct soc_mreg_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->regbase / TEGRA186_ASRC_STREAM_STRIDE; + bool change = false; + + if (asrc->lane[id].ratio_source == TEGRA186_ASRC_RATIO_SOURCE_ARAD) { + dev_err(cmpnt->dev, + "Lane %d ratio source is ARAD, invalid SW update\n", + id); + return -EINVAL; + } + + asrc->lane[id].frac_part = ucontrol->value.integer.value[0]; + + regmap_update_bits_check(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, + id), + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + asrc->lane[id].frac_part, &change); + + tegra186_asrc_lock_stream(asrc, id); + + return change ? 1 : 0; +} + +static int tegra186_asrc_get_hwcomp_disable(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + ucontrol->value.integer.value[0] = asrc->lane[id].hwcomp_disable; + + return 0; +} + +static int tegra186_asrc_put_hwcomp_disable(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + int value = ucontrol->value.integer.value[0]; + + if (value == asrc->lane[id].hwcomp_disable) + return 0; + + asrc->lane[id].hwcomp_disable = value; + + return 1; +} + +static int tegra186_asrc_get_input_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + ucontrol->value.integer.value[0] = (asrc->lane[id].input_thresh & 0x3); + + return 0; +} + +static int tegra186_asrc_put_input_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + int value = (asrc->lane[id].input_thresh & ~(0x3)) | + ucontrol->value.integer.value[0]; + + if (value == asrc->lane[id].input_thresh) + return 0; + + asrc->lane[id].input_thresh = value; + + return 1; +} + +static int tegra186_asrc_get_output_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + + ucontrol->value.integer.value[0] = (asrc->lane[id].output_thresh & 0x3); + + return 0; +} + +static int tegra186_asrc_put_output_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *asrc_private = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); + struct tegra186_asrc *asrc = snd_soc_component_get_drvdata(cmpnt); + unsigned int id = asrc_private->reg / TEGRA186_ASRC_STREAM_STRIDE; + int value = (asrc->lane[id].output_thresh & ~(0x3)) | + ucontrol->value.integer.value[0]; + + if (value == asrc->lane[id].output_thresh) + return 0; + + asrc->lane[id].output_thresh = value; + + return 1; +} + +static int tegra186_asrc_widget_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct tegra186_asrc *asrc = dev_get_drvdata(cmpnt->dev); + unsigned int id = + (w->reg - TEGRA186_ASRC_ENABLE) / TEGRA186_ASRC_STREAM_STRIDE; + + regmap_write(asrc->regmap, + ASRC_STREAM_REG(TEGRA186_ASRC_SOFT_RESET, id), + 0x1); + + return 0; +} + +static const struct snd_soc_dai_ops tegra186_asrc_in_dai_ops = { + .hw_params = tegra186_asrc_in_hw_params, +}; + +static const struct snd_soc_dai_ops tegra186_asrc_out_dai_ops = { + .hw_params = tegra186_asrc_out_hw_params, +}; + +#define IN_DAI(id) \ + { \ + .name = "ASRC-RX-CIF"#id, \ + .playback = { \ + .stream_name = "RX" #id "-CIF-Playback",\ + .channels_min = 1, \ + .channels_max = 12, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = "RX" #id "-CIF-Capture", \ + .channels_min = 1, \ + .channels_max = 12, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = &tegra186_asrc_in_dai_ops, \ + } + +#define OUT_DAI(id) \ + { \ + .name = "ASRC-TX-CIF"#id, \ + .playback = { \ + .stream_name = "TX" #id "-CIF-Playback",\ + .channels_min = 1, \ + .channels_max = 12, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .capture = { \ + .stream_name = "TX" #id "-CIF-Capture", \ + .channels_min = 1, \ + .channels_max = 12, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ + .formats = SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + }, \ + .ops = &tegra186_asrc_out_dai_ops, \ + } + +static struct snd_soc_dai_driver tegra186_asrc_dais[] = { + /* ASRC Input */ + IN_DAI(1), + IN_DAI(2), + IN_DAI(3), + IN_DAI(4), + IN_DAI(5), + IN_DAI(6), + IN_DAI(7), + /* ASRC Output */ + OUT_DAI(1), + OUT_DAI(2), + OUT_DAI(3), + OUT_DAI(4), + OUT_DAI(5), + OUT_DAI(6), +}; + +static const struct snd_soc_dapm_widget tegra186_asrc_widgets[] = { + SND_SOC_DAPM_AIF_IN("RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX3", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX4", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX5", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX6", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX7", NULL, 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT_E("TX1", NULL, 0, + ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 0), + TEGRA186_ASRC_STREAM_EN_SHIFT, 0, + tegra186_asrc_widget_event, + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("TX2", NULL, 0, + ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 1), + TEGRA186_ASRC_STREAM_EN_SHIFT, 0, + tegra186_asrc_widget_event, + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("TX3", NULL, 0, + ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 2), + TEGRA186_ASRC_STREAM_EN_SHIFT, 0, + tegra186_asrc_widget_event, + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("TX4", NULL, 0, + ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 3), + TEGRA186_ASRC_STREAM_EN_SHIFT, 0, + tegra186_asrc_widget_event, + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("TX5", NULL, 0, + ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 4), + TEGRA186_ASRC_STREAM_EN_SHIFT, 0, + tegra186_asrc_widget_event, + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("TX6", NULL, 0, + ASRC_STREAM_REG(TEGRA186_ASRC_ENABLE, 5), + TEGRA186_ASRC_STREAM_EN_SHIFT, 0, + tegra186_asrc_widget_event, + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Depacketizer", NULL), +}; + +#define ASRC_STREAM_ROUTE(id, sname) \ + { "RX" #id " XBAR-" sname, NULL, "RX" #id " XBAR-TX" }, \ + { "RX" #id "-CIF-" sname, NULL, "RX" #id " XBAR-" sname }, \ + { "RX" #id, NULL, "RX" #id "-CIF-" sname }, \ + { "TX" #id, NULL, "RX" #id }, \ + { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \ + { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \ + { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname }, + +#define ASRC_ROUTE(id) \ + ASRC_STREAM_ROUTE(id, "Playback") \ + ASRC_STREAM_ROUTE(id, "Capture") + +#define ASRC_RATIO_ROUTE(sname) \ + { "RX7 XBAR-" sname, NULL, "RX7 XBAR-TX" }, \ + { "RX7-CIF-" sname, NULL, "RX7 XBAR-" sname }, \ + { "RX7", NULL, "RX7-CIF-" sname }, \ + { "Depacketizer", NULL, "RX7" }, + +static const struct snd_soc_dapm_route tegra186_asrc_routes[] = { + ASRC_ROUTE(1) + ASRC_ROUTE(2) + ASRC_ROUTE(3) + ASRC_ROUTE(4) + ASRC_ROUTE(5) + ASRC_ROUTE(6) + ASRC_RATIO_ROUTE("Playback") + ASRC_RATIO_ROUTE("Capture") +}; + +static const char * const tegra186_asrc_ratio_source_text[] = { + "ARAD", + "SW", +}; + +#define ASRC_SOURCE_DECL(name, id) \ + static const struct soc_enum name = \ + SOC_ENUM_SINGLE(ASRC_STREAM_SOURCE_SELECT(id), \ + 0, 2, tegra186_asrc_ratio_source_text) + +ASRC_SOURCE_DECL(src_select1, 0); +ASRC_SOURCE_DECL(src_select2, 1); +ASRC_SOURCE_DECL(src_select3, 2); +ASRC_SOURCE_DECL(src_select4, 3); +ASRC_SOURCE_DECL(src_select5, 4); +ASRC_SOURCE_DECL(src_select6, 5); + +#define SOC_SINGLE_EXT_FRAC(xname, xregbase, xmax, xget, xput) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = (xname), \ + .info = snd_soc_info_xr_sx, \ + .get = xget, \ + .put = xput, \ + \ + .private_value = (unsigned long)&(struct soc_mreg_control) \ + { \ + .regbase = xregbase, \ + .regcount = 1, \ + .nbits = 32, \ + .invert = 0, \ + .min = 0, \ + .max = xmax \ + } \ +} + +static const struct snd_kcontrol_new tegra186_asrc_controls[] = { + /* Controls for integer part of ratio */ + SOC_SINGLE_EXT("Ratio1 Integer Part", + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 0), + 0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0, + tegra186_asrc_get_ratio_int, + tegra186_asrc_put_ratio_int), + + SOC_SINGLE_EXT("Ratio2 Integer Part", + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 1), + 0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0, + tegra186_asrc_get_ratio_int, + tegra186_asrc_put_ratio_int), + + SOC_SINGLE_EXT("Ratio3 Integer Part", + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 2), + 0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0, + tegra186_asrc_get_ratio_int, + tegra186_asrc_put_ratio_int), + + SOC_SINGLE_EXT("Ratio4 Integer Part", + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 3), + 0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0, + tegra186_asrc_get_ratio_int, + tegra186_asrc_put_ratio_int), + + SOC_SINGLE_EXT("Ratio5 Integer Part", + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 4), + 0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0, + tegra186_asrc_get_ratio_int, + tegra186_asrc_put_ratio_int), + + SOC_SINGLE_EXT("Ratio6 Integer Part", + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_INT_PART, 5), + 0, TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK, 0, + tegra186_asrc_get_ratio_int, + tegra186_asrc_put_ratio_int), + + /* Controls for fractional part of ratio */ + SOC_SINGLE_EXT_FRAC("Ratio1 Fractional Part", + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 0), + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + tegra186_asrc_get_ratio_frac, + tegra186_asrc_put_ratio_frac), + + SOC_SINGLE_EXT_FRAC("Ratio2 Fractional Part", + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 1), + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + tegra186_asrc_get_ratio_frac, + tegra186_asrc_put_ratio_frac), + + SOC_SINGLE_EXT_FRAC("Ratio3 Fractional Part", + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 2), + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + tegra186_asrc_get_ratio_frac, + tegra186_asrc_put_ratio_frac), + + SOC_SINGLE_EXT_FRAC("Ratio4 Fractional Part", + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 3), + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + tegra186_asrc_get_ratio_frac, + tegra186_asrc_put_ratio_frac), + + SOC_SINGLE_EXT_FRAC("Ratio5 Fractional Part", + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 4), + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + tegra186_asrc_get_ratio_frac, + tegra186_asrc_put_ratio_frac), + + SOC_SINGLE_EXT_FRAC("Ratio6 Fractional Part", + ASRC_STREAM_REG(TEGRA186_ASRC_RATIO_FRAC_PART, 5), + TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK, + tegra186_asrc_get_ratio_frac, + tegra186_asrc_put_ratio_frac), + + /* Source of ratio provider */ + SOC_ENUM_EXT("Ratio1 Source", src_select1, + tegra186_asrc_get_ratio_source, + tegra186_asrc_put_ratio_source), + + SOC_ENUM_EXT("Ratio2 Source", src_select2, + tegra186_asrc_get_ratio_source, + tegra186_asrc_put_ratio_source), + + SOC_ENUM_EXT("Ratio3 Source", src_select3, + tegra186_asrc_get_ratio_source, + tegra186_asrc_put_ratio_source), + + SOC_ENUM_EXT("Ratio4 Source", src_select4, + tegra186_asrc_get_ratio_source, + tegra186_asrc_put_ratio_source), + + SOC_ENUM_EXT("Ratio5 Source", src_select5, + tegra186_asrc_get_ratio_source, + tegra186_asrc_put_ratio_source), + + SOC_ENUM_EXT("Ratio6 Source", src_select6, + tegra186_asrc_get_ratio_source, + tegra186_asrc_put_ratio_source), + + /* Disable HW managed overflow/underflow issue at input and output */ + SOC_SINGLE_EXT("Stream1 HW Component Disable", + ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 0), 0, 1, 0, + tegra186_asrc_get_hwcomp_disable, + tegra186_asrc_put_hwcomp_disable), + + SOC_SINGLE_EXT("Stream2 HW Component Disable", + ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 1), 0, 1, 0, + tegra186_asrc_get_hwcomp_disable, + tegra186_asrc_put_hwcomp_disable), + + SOC_SINGLE_EXT("Stream3 HW Component Disable", + ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 2), 0, 1, 0, + tegra186_asrc_get_hwcomp_disable, + tegra186_asrc_put_hwcomp_disable), + + SOC_SINGLE_EXT("Stream4 HW Component Disable", + ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 3), 0, 1, 0, + tegra186_asrc_get_hwcomp_disable, + tegra186_asrc_put_hwcomp_disable), + + SOC_SINGLE_EXT("Stream5 HW Component Disable", + ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 4), 0, 1, 0, + tegra186_asrc_get_hwcomp_disable, + tegra186_asrc_put_hwcomp_disable), + + SOC_SINGLE_EXT("Stream6 HW Component Disable", + ASRC_STREAM_REG(TEGRA186_ASRC_CFG, 5), 0, 1, 0, + tegra186_asrc_get_hwcomp_disable, + tegra186_asrc_put_hwcomp_disable), + + /* Input threshold for watermark fields */ + SOC_SINGLE_EXT("Stream1 Input Threshold", + ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 0), 0, 3, 0, + tegra186_asrc_get_input_threshold, + tegra186_asrc_put_input_threshold), + + SOC_SINGLE_EXT("Stream2 Input Threshold", + ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 1), 0, 3, 0, + tegra186_asrc_get_input_threshold, + tegra186_asrc_put_input_threshold), + + SOC_SINGLE_EXT("Stream3 Input Threshold", + ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 2), 0, 3, 0, + tegra186_asrc_get_input_threshold, + tegra186_asrc_put_input_threshold), + + SOC_SINGLE_EXT("Stream4 Input Threshold", + ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 3), 0, 3, 0, + tegra186_asrc_get_input_threshold, + tegra186_asrc_put_input_threshold), + + SOC_SINGLE_EXT("Stream5 Input Threshold", + ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 4), 0, 3, 0, + tegra186_asrc_get_input_threshold, + tegra186_asrc_put_input_threshold), + + SOC_SINGLE_EXT("Stream6 Input Threshold", + ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 4), 0, 3, 0, + tegra186_asrc_get_input_threshold, + tegra186_asrc_put_input_threshold), + + /* Output threshold for watermark fields */ + SOC_SINGLE_EXT("Stream1 Output Threshold", + ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 0), 0, 3, 0, + tegra186_asrc_get_output_threshold, + tegra186_asrc_put_output_threshold), + + SOC_SINGLE_EXT("Stream2 Output Threshold", + ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 1), 0, 3, 0, + tegra186_asrc_get_output_threshold, + tegra186_asrc_put_output_threshold), + + SOC_SINGLE_EXT("Stream3 Output Threshold", + ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 2), 0, 3, 0, + tegra186_asrc_get_output_threshold, + tegra186_asrc_put_output_threshold), + + SOC_SINGLE_EXT("Stream4 Output Threshold", + ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 3), 0, 3, 0, + tegra186_asrc_get_output_threshold, + tegra186_asrc_put_output_threshold), + + SOC_SINGLE_EXT("Stream5 Output Threshold", + ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 4), 0, 3, 0, + tegra186_asrc_get_output_threshold, + tegra186_asrc_put_output_threshold), + + SOC_SINGLE_EXT("Stream6 Output Threshold", + ASRC_STREAM_REG(TEGRA186_ASRC_TX_THRESHOLD, 5), 0, 3, 0, + tegra186_asrc_get_output_threshold, + tegra186_asrc_put_output_threshold), +}; + +static const struct snd_soc_component_driver tegra186_asrc_cmpnt = { + .dapm_widgets = tegra186_asrc_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra186_asrc_widgets), + .dapm_routes = tegra186_asrc_routes, + .num_dapm_routes = ARRAY_SIZE(tegra186_asrc_routes), + .controls = tegra186_asrc_controls, + .num_controls = ARRAY_SIZE(tegra186_asrc_controls), +}; + +static bool tegra186_asrc_wr_reg(struct device *dev, unsigned int reg) +{ + if (reg < TEGRA186_ASRC_STREAM_LIMIT) + reg %= TEGRA186_ASRC_STREAM_STRIDE; + + switch (reg) { + case TEGRA186_ASRC_CFG ... TEGRA186_ASRC_RATIO_COMP: + case TEGRA186_ASRC_RX_CIF_CTRL: + case TEGRA186_ASRC_TX_CIF_CTRL: + case TEGRA186_ASRC_ENABLE: + case TEGRA186_ASRC_SOFT_RESET: + case TEGRA186_ASRC_GLOBAL_ENB ... TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL: + case TEGRA186_ASRC_GLOBAL_INT_MASK ... TEGRA186_ASRC_GLOBAL_INT_CLEAR: + case TEGRA186_ASRC_GLOBAL_APR_CTRL ... TEGRA186_ASRC_CYA: + return true; + default: + return false; + } +} + +static bool tegra186_asrc_rd_reg(struct device *dev, unsigned int reg) +{ + if (reg < TEGRA186_ASRC_STREAM_LIMIT) + reg %= TEGRA186_ASRC_STREAM_STRIDE; + + if (tegra186_asrc_wr_reg(dev, reg)) + return true; + + switch (reg) { + case TEGRA186_ASRC_RX_STATUS: + case TEGRA186_ASRC_TX_STATUS: + case TEGRA186_ASRC_STATUS ... TEGRA186_ASRC_OUTSAMPLEBUF_CFG: + case TEGRA186_ASRC_RATIO_UPD_RX_STATUS: + case TEGRA186_ASRC_GLOBAL_STATUS ... TEGRA186_ASRC_GLOBAL_INT_STATUS: + case TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG: + return true; + default: + return false; + } +} + +static bool tegra186_asrc_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg < TEGRA186_ASRC_STREAM_LIMIT) + reg %= TEGRA186_ASRC_STREAM_STRIDE; + + switch (reg) { + case TEGRA186_ASRC_RX_STATUS: + case TEGRA186_ASRC_TX_STATUS: + case TEGRA186_ASRC_SOFT_RESET: + case TEGRA186_ASRC_RATIO_INT_PART: + case TEGRA186_ASRC_RATIO_FRAC_PART: + case TEGRA186_ASRC_STATUS: + case TEGRA186_ASRC_RATIO_LOCK_STATUS: + case TEGRA186_ASRC_RATIO_UPD_RX_STATUS: + case TEGRA186_ASRC_GLOBAL_SOFT_RESET: + case TEGRA186_ASRC_GLOBAL_STATUS: + case TEGRA186_ASRC_GLOBAL_STREAM_ENABLE_STATUS: + case TEGRA186_ASRC_GLOBAL_INT_STATUS: + case TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG: + return true; + default: + return false; + } +} + +static const struct regmap_config tegra186_asrc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA186_ASRC_CYA, + .writeable_reg = tegra186_asrc_wr_reg, + .readable_reg = tegra186_asrc_rd_reg, + .volatile_reg = tegra186_asrc_volatile_reg, + .reg_defaults = tegra186_asrc_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tegra186_asrc_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static const struct of_device_id tegra186_asrc_of_match[] = { + { .compatible = "nvidia,tegra186-asrc" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra186_asrc_of_match); + +static int tegra186_asrc_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra186_asrc *asrc; + void __iomem *regs; + unsigned int i; + int err; + + asrc = devm_kzalloc(dev, sizeof(*asrc), GFP_KERNEL); + if (!asrc) + return -ENOMEM; + + dev_set_drvdata(dev, asrc); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + asrc->regmap = devm_regmap_init_mmio(dev, regs, + &tegra186_asrc_regmap_config); + if (IS_ERR(asrc->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(asrc->regmap); + } + + regcache_cache_only(asrc->regmap, true); + + regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_CFG, + TEGRA186_ASRC_GLOBAL_CFG_FRAC_32BIT_PRECISION); + + /* Initialize default output srate */ + for (i = 0; i < TEGRA186_ASRC_STREAM_MAX; i++) { + asrc->lane[i].ratio_source = TEGRA186_ASRC_RATIO_SOURCE_SW; + asrc->lane[i].int_part = 1; + asrc->lane[i].frac_part = 0; + asrc->lane[i].hwcomp_disable = 0; + asrc->lane[i].input_thresh = + TEGRA186_ASRC_STREAM_DEFAULT_INPUT_HW_COMP_THRESH_CFG; + asrc->lane[i].output_thresh = + TEGRA186_ASRC_STREAM_DEFAULT_OUTPUT_HW_COMP_THRESH_CFG; + } + + err = devm_snd_soc_register_component(dev, &tegra186_asrc_cmpnt, + tegra186_asrc_dais, + ARRAY_SIZE(tegra186_asrc_dais)); + if (err) { + dev_err(dev, "can't register ASRC component, err: %d\n", err); + return err; + } + + pm_runtime_enable(dev); + + return 0; +} + +static int tegra186_asrc_platform_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops tegra186_asrc_pm_ops = { + SET_RUNTIME_PM_OPS(tegra186_asrc_runtime_suspend, + tegra186_asrc_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver tegra186_asrc_driver = { + .driver = { + .name = "tegra186-asrc", + .of_match_table = tegra186_asrc_of_match, + .pm = &tegra186_asrc_pm_ops, + }, + .probe = tegra186_asrc_platform_probe, + .remove = tegra186_asrc_platform_remove, +}; +module_platform_driver(tegra186_asrc_driver) + +MODULE_AUTHOR("Junghyun Kim <juskim@nvidia.com>"); +MODULE_DESCRIPTION("Tegra186 ASRC ASoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra186_asrc.h b/sound/soc/tegra/tegra186_asrc.h new file mode 100644 index 000000000000..094fcc723c02 --- /dev/null +++ b/sound/soc/tegra/tegra186_asrc.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tegra186_asrc.h - Definitions for Tegra186 ASRC driver + * + * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA186_ASRC_H__ +#define __TEGRA186_ASRC_H__ + +/* ASRC stream related offset */ +#define TEGRA186_ASRC_CFG 0x0 +#define TEGRA186_ASRC_RATIO_INT_PART 0x4 +#define TEGRA186_ASRC_RATIO_FRAC_PART 0x8 +#define TEGRA186_ASRC_RATIO_LOCK_STATUS 0xc +#define TEGRA186_ASRC_MUTE_UNMUTE_DURATION 0x10 +#define TEGRA186_ASRC_TX_THRESHOLD 0x14 +#define TEGRA186_ASRC_RX_THRESHOLD 0x18 +#define TEGRA186_ASRC_RATIO_COMP 0x1c +#define TEGRA186_ASRC_RX_STATUS 0x20 +#define TEGRA186_ASRC_RX_CIF_CTRL 0x24 +#define TEGRA186_ASRC_TX_STATUS 0x2c +#define TEGRA186_ASRC_TX_CIF_CTRL 0x30 +#define TEGRA186_ASRC_ENABLE 0x38 +#define TEGRA186_ASRC_SOFT_RESET 0x3c +#define TEGRA186_ASRC_STATUS 0x4c +#define TEGRA186_ASRC_STATEBUF_ADDR 0x5c +#define TEGRA186_ASRC_STATEBUF_CFG 0x60 +#define TEGRA186_ASRC_INSAMPLEBUF_ADDR 0x64 +#define TEGRA186_ASRC_INSAMPLEBUF_CFG 0x68 +#define TEGRA186_ASRC_OUTSAMPLEBUF_ADDR 0x6c +#define TEGRA186_ASRC_OUTSAMPLEBUF_CFG 0x70 + +/* ASRC Global registers offset */ +#define TEGRA186_ASRC_GLOBAL_ENB 0x2f4 +#define TEGRA186_ASRC_GLOBAL_SOFT_RESET 0x2f8 +#define TEGRA186_ASRC_GLOBAL_CG 0x2fc +#define TEGRA186_ASRC_GLOBAL_CFG 0x300 +#define TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR 0x304 +#define TEGRA186_ASRC_GLOBAL_SCRATCH_CFG 0x308 +#define TEGRA186_ASRC_RATIO_UPD_RX_CIF_CTRL 0x30c +#define TEGRA186_ASRC_RATIO_UPD_RX_STATUS 0x310 +#define TEGRA186_ASRC_GLOBAL_STATUS 0x314 +#define TEGRA186_ASRC_GLOBAL_STREAM_ENABLE_STATUS 0x318 +#define TEGRA186_ASRC_GLOBAL_INT_STATUS 0x324 +#define TEGRA186_ASRC_GLOBAL_INT_MASK 0x328 +#define TEGRA186_ASRC_GLOBAL_INT_SET 0x32c +#define TEGRA186_ASRC_GLOBAL_INT_CLEAR 0x330 +#define TEGRA186_ASRC_GLOBAL_TRANSFER_ERROR_LOG 0x334 +#define TEGRA186_ASRC_GLOBAL_APR_CTRL 0x1000 +#define TEGRA186_ASRC_GLOBAL_APR_CTRL_ACCESS_CTRL 0x1004 +#define TEGRA186_ASRC_GLOBAL_DISARM_APR 0x1008 +#define TEGRA186_ASRC_GLOBAL_DISARM_APR_ACCESS_CTRL 0x100c +#define TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS 0x1010 +#define TEGRA186_ASRC_GLOBAL_RATIO_WR_ACCESS_CTRL 0x1014 +#define TEGRA186_ASRC_CYA 0x1018 + +#define TEGRA186_ASRC_STREAM_DEFAULT_HW_COMP_BIAS_VALUE 0xaaaa +#define TEGRA186_ASRC_STREAM_DEFAULT_INPUT_HW_COMP_THRESH_CFG 0x00201002 +#define TEGRA186_ASRC_STREAM_DEFAULT_OUTPUT_HW_COMP_THRESH_CFG 0x00201002 + +#define TEGRA186_ASRC_GLOBAL_CFG_FRAC_28BIT_PRECISION 0 +#define TEGRA186_ASRC_GLOBAL_CFG_FRAC_32BIT_PRECISION 1 + +#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT 31 +#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_MASK (1 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT) +#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_ENABLE (1 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT) +#define TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_DISABLE (0 << TEGRA186_ASRC_STREAM_ENABLE_HW_RATIO_COMP_SHIFT) + +#define TEGRA186_ASRC_STREAM_RATIO_TYPE_SHIFT 0 +#define TEGRA186_ASRC_STREAM_RATIO_TYPE_MASK (1 << TEGRA186_ASRC_STREAM_RATIO_TYPE_SHIFT) + +#define TEGRA186_ASRC_STREAM_EN_SHIFT 0 +#define TEGRA186_ASRC_STREAM_EN (1 << TEGRA186_ASRC_STREAM_EN_SHIFT) +#define TEGRA186_ASRC_GLOBAL_EN_SHIFT 0 +#define TEGRA186_ASRC_GLOBAL_EN (1 << TEGRA186_ASRC_GLOBAL_EN_SHIFT) + +#define TEGRA186_ASRC_STREAM_STATEBUF_CFG_SIZE_SHIFT 0 +#define TEGRA186_ASRC_STREAM_STATEBUF_CFG_SIZE_MASK (0xffff << TEGRA186_ASRC_STREAM_STATEBUF_CFG_SIZE_SHIFT) +#define TEGRA186_ASRC_STREAM_INSAMPLEBUF_CFG_SIZE_SHIFT 0 +#define TEGRA186_ASRC_STREAM_INSAMPLEBUF_CFG_SIZE_MASK (0xffff << TEGRA186_ASRC_STREAM_INSAMPLEBUF_CFG_SIZE_SHIFT) +#define TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CFG_SIZE_SHIFT 0 +#define TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CFG_SIZE_MASK (0xffff << TEGRA186_ASRC_STREAM_OUTSAMPLEBUF_CFG_SIZE_SHIFT) + +#define TEGRA186_ASRC_STREAM_RATIO_INT_PART_MASK 0x1f +#define TEGRA186_ASRC_STREAM_RATIO_FRAC_PART_MASK 0xffffffff + +#define TEGRA186_ASRC_STREAM_STRIDE 0x80 +#define TEGRA186_ASRC_STREAM_MAX 0x6 +#define TEGRA186_ASRC_STREAM_LIMIT 0x2f0 + +#define TEGRA186_ASRC_RATIO_SOURCE_ARAD 0x0 +#define TEGRA186_ASRC_RATIO_SOURCE_SW 0x1 + +#define TEGRA186_ASRC_ARAM_START_ADDR 0x3f800000 + +struct tegra186_asrc_lane { + unsigned int int_part; + unsigned int frac_part; + unsigned int ratio_source; + unsigned int hwcomp_disable; + unsigned int input_thresh; + unsigned int output_thresh; +}; + +struct tegra186_asrc { + struct tegra186_asrc_lane lane[TEGRA186_ASRC_STREAM_MAX]; + struct regmap *regmap; +}; + +#endif diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index bccf8b8ca714..e1f90daea7a1 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -280,6 +280,20 @@ static struct snd_soc_dai_driver tegra186_ahub_dais[] = { DAI(MIXER1 TX3), DAI(MIXER1 TX4), DAI(MIXER1 TX5), + /* XBAR -> ASRC -> XBAR */ + DAI(ASRC1 RX1), + DAI(ASRC1 TX1), + DAI(ASRC1 RX2), + DAI(ASRC1 TX2), + DAI(ASRC1 RX3), + DAI(ASRC1 TX3), + DAI(ASRC1 RX4), + DAI(ASRC1 TX4), + DAI(ASRC1 RX5), + DAI(ASRC1 TX5), + DAI(ASRC1 RX6), + DAI(ASRC1 TX6), + DAI(ASRC1 RX7), }; static const char * const tegra210_ahub_mux_texts[] = { @@ -388,6 +402,12 @@ static const char * const tegra186_ahub_mux_texts[] = { "MIXER1 TX3", "MIXER1 TX4", "MIXER1 TX5", + "ASRC1 TX1", + "ASRC1 TX2", + "ASRC1 TX3", + "ASRC1 TX4", + "ASRC1 TX5", + "ASRC1 TX6", }; static const unsigned int tegra210_ahub_mux_values[] = { @@ -513,6 +533,13 @@ static const unsigned int tegra186_ahub_mux_values[] = { MUX_VALUE(1, 2), MUX_VALUE(1, 3), MUX_VALUE(1, 4), + /* ASRC */ + MUX_VALUE(3, 24), + MUX_VALUE(3, 25), + MUX_VALUE(3, 26), + MUX_VALUE(3, 27), + MUX_VALUE(3, 28), + MUX_VALUE(3, 29), }; /* Controls for t210 */ @@ -623,6 +650,13 @@ MUX_ENUM_CTRL_DECL_186(t186_mixer17_tx, 0x26); MUX_ENUM_CTRL_DECL_186(t186_mixer18_tx, 0x27); MUX_ENUM_CTRL_DECL_186(t186_mixer19_tx, 0x28); MUX_ENUM_CTRL_DECL_186(t186_mixer110_tx, 0x29); +MUX_ENUM_CTRL_DECL_186(t186_asrc11_tx, 0x6c); +MUX_ENUM_CTRL_DECL_186(t186_asrc12_tx, 0x6d); +MUX_ENUM_CTRL_DECL_186(t186_asrc13_tx, 0x6e); +MUX_ENUM_CTRL_DECL_186(t186_asrc14_tx, 0x6f); +MUX_ENUM_CTRL_DECL_186(t186_asrc15_tx, 0x70); +MUX_ENUM_CTRL_DECL_186(t186_asrc16_tx, 0x71); +MUX_ENUM_CTRL_DECL_186(t186_asrc17_tx, 0x72); /* Controls for t234 */ MUX_ENUM_CTRL_DECL_234(t234_mvc1_tx, 0x44); @@ -651,6 +685,13 @@ MUX_ENUM_CTRL_DECL_234(t234_admaif17_tx, 0x60); MUX_ENUM_CTRL_DECL_234(t234_admaif18_tx, 0x61); MUX_ENUM_CTRL_DECL_234(t234_admaif19_tx, 0x62); MUX_ENUM_CTRL_DECL_234(t234_admaif20_tx, 0x63); +MUX_ENUM_CTRL_DECL_234(t234_asrc11_tx, 0x64); +MUX_ENUM_CTRL_DECL_234(t234_asrc12_tx, 0x65); +MUX_ENUM_CTRL_DECL_234(t234_asrc13_tx, 0x66); +MUX_ENUM_CTRL_DECL_234(t234_asrc14_tx, 0x67); +MUX_ENUM_CTRL_DECL_234(t234_asrc15_tx, 0x68); +MUX_ENUM_CTRL_DECL_234(t234_asrc16_tx, 0x69); +MUX_ENUM_CTRL_DECL_234(t234_asrc17_tx, 0x6a); /* * The number of entries in, and order of, this array is closely tied to the @@ -813,6 +854,19 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = { TX_WIDGETS("MIXER1 TX3"), TX_WIDGETS("MIXER1 TX4"), TX_WIDGETS("MIXER1 TX5"), + WIDGETS("ASRC1 RX1", t186_asrc11_tx), + WIDGETS("ASRC1 RX2", t186_asrc12_tx), + WIDGETS("ASRC1 RX3", t186_asrc13_tx), + WIDGETS("ASRC1 RX4", t186_asrc14_tx), + WIDGETS("ASRC1 RX5", t186_asrc15_tx), + WIDGETS("ASRC1 RX6", t186_asrc16_tx), + WIDGETS("ASRC1 RX7", t186_asrc17_tx), + TX_WIDGETS("ASRC1 TX1"), + TX_WIDGETS("ASRC1 TX2"), + TX_WIDGETS("ASRC1 TX3"), + TX_WIDGETS("ASRC1 TX4"), + TX_WIDGETS("ASRC1 TX5"), + TX_WIDGETS("ASRC1 TX6"), }; static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = { @@ -909,6 +963,19 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = { TX_WIDGETS("MIXER1 TX3"), TX_WIDGETS("MIXER1 TX4"), TX_WIDGETS("MIXER1 TX5"), + WIDGETS("ASRC1 RX1", t234_asrc11_tx), + WIDGETS("ASRC1 RX2", t234_asrc12_tx), + WIDGETS("ASRC1 RX3", t234_asrc13_tx), + WIDGETS("ASRC1 RX4", t234_asrc14_tx), + WIDGETS("ASRC1 RX5", t234_asrc15_tx), + WIDGETS("ASRC1 RX6", t234_asrc16_tx), + WIDGETS("ASRC1 RX7", t234_asrc17_tx), + TX_WIDGETS("ASRC1 TX1"), + TX_WIDGETS("ASRC1 TX2"), + TX_WIDGETS("ASRC1 TX3"), + TX_WIDGETS("ASRC1 TX4"), + TX_WIDGETS("ASRC1 TX5"), + TX_WIDGETS("ASRC1 TX6"), }; #define TEGRA_COMMON_MUX_ROUTES(name) \ @@ -975,7 +1042,13 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = { { name " Mux", "ADX4 TX1", "ADX4 TX1 XBAR-RX" }, \ { name " Mux", "ADX4 TX2", "ADX4 TX2 XBAR-RX" }, \ { name " Mux", "ADX4 TX3", "ADX4 TX3 XBAR-RX" }, \ - { name " Mux", "ADX4 TX4", "ADX4 TX4 XBAR-RX" }, + { name " Mux", "ADX4 TX4", "ADX4 TX4 XBAR-RX" }, \ + { name " Mux", "ASRC1 TX1", "ASRC1 TX1 XBAR-RX" }, \ + { name " Mux", "ASRC1 TX2", "ASRC1 TX2 XBAR-RX" }, \ + { name " Mux", "ASRC1 TX3", "ASRC1 TX3 XBAR-RX" }, \ + { name " Mux", "ASRC1 TX4", "ASRC1 TX4 XBAR-RX" }, \ + { name " Mux", "ASRC1 TX5", "ASRC1 TX5 XBAR-RX" }, \ + { name " Mux", "ASRC1 TX6", "ASRC1 TX6 XBAR-RX" }, #define TEGRA210_MUX_ROUTES(name) \ TEGRA_COMMON_MUX_ROUTES(name) @@ -1135,6 +1208,13 @@ static const struct snd_soc_dapm_route tegra186_ahub_routes[] = { TEGRA186_MUX_ROUTES("MIXER1 RX8") TEGRA186_MUX_ROUTES("MIXER1 RX9") TEGRA186_MUX_ROUTES("MIXER1 RX10") + TEGRA186_MUX_ROUTES("ASRC1 RX1") + TEGRA186_MUX_ROUTES("ASRC1 RX2") + TEGRA186_MUX_ROUTES("ASRC1 RX3") + TEGRA186_MUX_ROUTES("ASRC1 RX4") + TEGRA186_MUX_ROUTES("ASRC1 RX5") + TEGRA186_MUX_ROUTES("ASRC1 RX6") + TEGRA186_MUX_ROUTES("ASRC1 RX7") }; static const struct snd_soc_component_driver tegra210_ahub_component = { diff --git a/sound/soc/tegra/tegra_asoc_machine.c b/sound/soc/tegra/tegra_asoc_machine.c index a73404879aa1..78faa8bcae27 100644 --- a/sound/soc/tegra/tegra_asoc_machine.c +++ b/sound/soc/tegra/tegra_asoc_machine.c @@ -133,11 +133,11 @@ int tegra_asoc_machine_init(struct snd_soc_pcm_runtime *rtd) else jack_name = "Headphones Jack"; - err = snd_soc_card_jack_new(card, jack_name, - SND_JACK_HEADPHONE, - &tegra_machine_hp_jack, - tegra_machine_hp_jack_pins, - ARRAY_SIZE(tegra_machine_hp_jack_pins)); + err = snd_soc_card_jack_new_pins(card, jack_name, + SND_JACK_HEADPHONE, + &tegra_machine_hp_jack, + tegra_machine_hp_jack_pins, + ARRAY_SIZE(tegra_machine_hp_jack_pins)); if (err) { dev_err(rtd->dev, "Headphones Jack creation failed: %d\n", err); @@ -153,11 +153,11 @@ int tegra_asoc_machine_init(struct snd_soc_pcm_runtime *rtd) } if (machine->gpiod_hp_det && machine->asoc->add_headset_jack) { - err = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET, - &tegra_machine_headset_jack, - tegra_machine_headset_jack_pins, - ARRAY_SIZE(tegra_machine_headset_jack_pins)); + err = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET, + &tegra_machine_headset_jack, + tegra_machine_headset_jack_pins, + ARRAY_SIZE(tegra_machine_headset_jack_pins)); if (err) { dev_err(rtd->dev, "Headset Jack creation failed: %d\n", err); @@ -173,11 +173,11 @@ int tegra_asoc_machine_init(struct snd_soc_pcm_runtime *rtd) } if (machine->gpiod_mic_det && machine->asoc->add_mic_jack) { - err = snd_soc_card_jack_new(rtd->card, "Mic Jack", - SND_JACK_MICROPHONE, - &tegra_machine_mic_jack, - tegra_machine_mic_jack_pins, - ARRAY_SIZE(tegra_machine_mic_jack_pins)); + err = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, + &tegra_machine_mic_jack, + tegra_machine_mic_jack_pins, + ARRAY_SIZE(tegra_machine_mic_jack_pins)); if (err) { dev_err(rtd->dev, "Mic Jack creation failed: %d\n", err); return err; diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index 5751fb398c1a..b3cd0a34da63 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -79,11 +79,11 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_component *component = codec_dai->component; int shrt = 0; - err = snd_soc_card_jack_new(rtd->card, "Mic Jack", - SND_JACK_MICROPHONE, - machine->mic_jack, - tegra_wm8903_mic_jack_pins, - ARRAY_SIZE(tegra_wm8903_mic_jack_pins)); + err = snd_soc_card_jack_new_pins(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, + machine->mic_jack, + tegra_wm8903_mic_jack_pins, + ARRAY_SIZE(tegra_wm8903_mic_jack_pins)); if (err) { dev_err(rtd->dev, "Mic Jack creation failed: %d\n", err); return err; diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c index b1a32545babd..438e2fa843a0 100644 --- a/sound/soc/ti/ams-delta.c +++ b/sound/soc/ti/ams-delta.c @@ -471,8 +471,8 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) /* Add hook switch - can be used to control the codec from userspace * even if line discipline fails */ - ret = snd_soc_card_jack_new(card, "hook_switch", SND_JACK_HEADSET, - &ams_delta_hook_switch, NULL, 0); + ret = snd_soc_card_jack_new_pins(card, "hook_switch", SND_JACK_HEADSET, + &ams_delta_hook_switch, NULL, 0); if (ret) dev_warn(card->dev, "Failed to allocate resources for hook switch, " diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 2c146b91fca3..377be2e2b6ee 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -2047,6 +2047,8 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) return PCM_SDMA; else if (strstr(tmp, "udmap")) return PCM_UDMA; + else if (strstr(tmp, "bcdma")) + return PCM_UDMA; return PCM_EDMA; } diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index 4077e15ec48b..6a969874c927 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -630,17 +630,18 @@ static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx, codec_node = of_parse_phandle(node, "ti,cpb-codec", 0); if (!codec_node) { dev_err(priv->dev, "CPB codec node is not provided\n"); - return -EINVAL; + ret = -EINVAL; + goto put_dai_node; } domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_CPB]; ret = j721e_get_clocks(priv->dev, &domain->codec, "cpb-codec-scki"); if (ret) - return ret; + goto put_codec_node; ret = j721e_get_clocks(priv->dev, &domain->mcasp, "cpb-mcasp-auxclk"); if (ret) - return ret; + goto put_codec_node; /* * Common Processor Board, two links @@ -650,8 +651,10 @@ static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx, comp_count = 6; compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent), GFP_KERNEL); - if (!compnent) - return -ENOMEM; + if (!compnent) { + ret = -ENOMEM; + goto put_codec_node; + } comp_idx = 0; priv->dai_links[*link_idx].cpus = &compnent[comp_idx++]; @@ -702,6 +705,12 @@ static int j721e_soc_probe_cpb(struct j721e_priv *priv, int *link_idx, (*conf_idx)++; return 0; + +put_codec_node: + of_node_put(codec_node); +put_dai_node: + of_node_put(dai_node); + return ret; } static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx, @@ -726,23 +735,25 @@ static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx, codeca_node = of_parse_phandle(node, "ti,ivi-codec-a", 0); if (!codeca_node) { dev_err(priv->dev, "IVI codec-a node is not provided\n"); - return -EINVAL; + ret = -EINVAL; + goto put_dai_node; } codecb_node = of_parse_phandle(node, "ti,ivi-codec-b", 0); if (!codecb_node) { dev_warn(priv->dev, "IVI codec-b node is not provided\n"); - return 0; + ret = 0; + goto put_codeca_node; } domain = &priv->audio_domains[J721E_AUDIO_DOMAIN_IVI]; ret = j721e_get_clocks(priv->dev, &domain->codec, "ivi-codec-scki"); if (ret) - return ret; + goto put_codecb_node; ret = j721e_get_clocks(priv->dev, &domain->mcasp, "ivi-mcasp-auxclk"); if (ret) - return ret; + goto put_codecb_node; /* * IVI extension, two links @@ -754,8 +765,10 @@ static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx, comp_count = 8; compnent = devm_kzalloc(priv->dev, comp_count * sizeof(*compnent), GFP_KERNEL); - if (!compnent) - return -ENOMEM; + if (!compnent) { + ret = -ENOMEM; + goto put_codecb_node; + } comp_idx = 0; priv->dai_links[*link_idx].cpus = &compnent[comp_idx++]; @@ -816,6 +829,15 @@ static int j721e_soc_probe_ivi(struct j721e_priv *priv, int *link_idx, (*conf_idx)++; return 0; + + +put_codecb_node: + of_node_put(codecb_node); +put_codeca_node: + of_node_put(codeca_node); +put_dai_node: + of_node_put(dai_node); + return ret; } static int j721e_soc_probe(struct platform_device *pdev) diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c index da809c7f25a4..805ffbf89014 100644 --- a/sound/soc/ti/omap-abe-twl6040.c +++ b/sound/soc/ti/omap-abe-twl6040.c @@ -182,10 +182,10 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) /* Headset jack detection only if it is supported */ if (priv->jack_detection) { - ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", - SND_JACK_HEADSET, &hs_jack, - hs_jack_pins, - ARRAY_SIZE(hs_jack_pins)); + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET, &hs_jack, + hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (ret) return ret; diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c index 1da05a6cdc9f..950eec44503b 100644 --- a/sound/soc/ti/omap-twl4030.c +++ b/sound/soc/ti/omap-twl4030.c @@ -155,10 +155,10 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) if (priv->jack_detect > 0) { hs_jack_gpios[0].gpio = priv->jack_detect; - ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", - SND_JACK_HEADSET, &priv->hs_jack, - hs_jack_pins, - ARRAY_SIZE(hs_jack_pins)); + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET, + &priv->hs_jack, hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (ret) return ret; diff --git a/sound/soc/ti/osk5912.c b/sound/soc/ti/osk5912.c index 40e29dda7e7a..2790c8915f55 100644 --- a/sound/soc/ti/osk5912.c +++ b/sound/soc/ti/osk5912.c @@ -27,12 +27,12 @@ static struct clk *tlv320aic23_mclk; static int osk_startup(struct snd_pcm_substream *substream) { - return clk_enable(tlv320aic23_mclk); + return clk_prepare_enable(tlv320aic23_mclk); } static void osk_shutdown(struct snd_pcm_substream *substream) { - clk_disable(tlv320aic23_mclk); + clk_disable_unprepare(tlv320aic23_mclk); } static int osk_hw_params(struct snd_pcm_substream *substream, diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c index a2629ccc1dc8..322c398d209b 100644 --- a/sound/soc/ti/rx51.c +++ b/sound/soc/ti/rx51.c @@ -277,7 +277,7 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) /* AV jack detection */ err = snd_soc_card_jack_new(rtd->card, "AV Jack", SND_JACK_HEADSET | SND_JACK_VIDEOOUT, - &rx51_av_jack, NULL, 0); + &rx51_av_jack); if (err) { dev_err(card->dev, "Failed to add AV Jack\n"); return err; diff --git a/sound/soc/uniphier/aio-compress.c b/sound/soc/uniphier/aio-compress.c index 0f76bc601ca9..7d1492c15b57 100644 --- a/sound/soc/uniphier/aio-compress.c +++ b/sound/soc/uniphier/aio-compress.c @@ -139,7 +139,6 @@ static int uniphier_aio_compr_set_params(struct snd_soc_component *component, struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0)); struct uniphier_aio_sub *sub = &aio->sub[cstream->direction]; struct device *dev = &aio->chip->pdev->dev; - int ret; if (params->codec.id != SND_AUDIOCODEC_IEC61937) { dev_err(dev, "Codec ID is not supported(%d)\n", @@ -161,11 +160,7 @@ static int uniphier_aio_compr_set_params(struct snd_soc_component *component, aio_port_reset(sub); aio_src_reset(sub); - ret = uniphier_aio_compr_prepare(component, cstream); - if (ret) - return ret; - - return 0; + return uniphier_aio_compr_prepare(component, cstream); } static int uniphier_aio_compr_hw_free(struct snd_soc_component *component, diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index 3e654e708f78..1ea1729984a9 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -433,12 +433,9 @@ void mop500_ab8500_remove(struct snd_soc_card *card) { struct mop500_ab8500_drvdata *drvdata = snd_soc_card_get_drvdata(card); - if (drvdata->clk_ptr_sysclk != NULL) - clk_put(drvdata->clk_ptr_sysclk); - if (drvdata->clk_ptr_ulpclk != NULL) - clk_put(drvdata->clk_ptr_ulpclk); - if (drvdata->clk_ptr_intclk != NULL) - clk_put(drvdata->clk_ptr_intclk); + clk_put(drvdata->clk_ptr_sysclk); + clk_put(drvdata->clk_ptr_ulpclk); + clk_put(drvdata->clk_ptr_intclk); snd_soc_card_set_drvdata(card, drvdata); } |